Code for a Person DTO, with tests
In the tests (and anywhere else), you should use constants to ensure correct spelling of the properties :

const PERSON_NAME = 'name';
const PERSON_SURNAME = 'surname';
...
$expectedProperties = array(self::PERSON_NAME,self::PERSON_SURNAME);


Then the Filtering and Serialization tests are very similar while they should be atomic (one test tests one thing) : you should not test Filtering whike testing Serialization.

In the production code there is one error : you set the desired property (i.e. $this-&gt;name) while you get the internal array's key ($this-&gt;properties['name']). To keep the simplicity of defining an object as a class with public properties you should drop the internal array. This will also make the __sleep and __wakeup calls much clearer.

Also a design flaw is to use the Reflection : it seems unavoidable but depending on your usage there might be performance issues. Using a caching system such as a static array will help. In particular if you look at the __wakeup and __sleep function you will notice a repeted call to the getPropertyNames method.

Finally what might be problematic is that you don't check if the property exists when setting or getting. If it does not you should throw an exception. There are magic methods in PHP5.3 (__get, __set and __call) ; you may do better using them. This way you will be able to use $personName = $person-&gt;getName() / $person-&gt;setName('Simone') as well as $personName = $person-&gt;name / $person-&gt;name = 'Simone'

Tests :

const PERSON_NAME = 'name';
const PERSON_SURNAME = 'surname';

public function testProvideDtoPropertyNames()
{
    $propertyNames = Person::getPropertyNames();

    $expectedProperties = array(
        self::PERSON_NAME,
        self::PERSON_SURNAME,
    );

    $this-&gt;assertEquals(
        $expectedProperties,
        $propertyNames
    );
}
public function testProvidePropertyViaGeneralGetter()
{
    $dto = Person::createFromArray(array(
        self::PERSON_NAME =&gt; 'Simone',
    ));

    $this-&gt;assertEquals(
        'Simone',
        $dto-&gt;getName()
    );
}
public function testDTOAcceptOnlyItsOwnProperties()
{
    $dto = Person::createFromArray(array(
        self::PERSON_NAME =&gt; 'Simone',
        'non existent property' =&gt; 'Simone',
    ));

    $expectedProperties = array(
        self::PERSON_NAME =&gt; 'Simone',
        self::PERSON_SURNAME =&gt; null,
    );

    $this-&gt;assertEquals(
        $expectedProperties,
        $dto-&gt;asArray()
    );
}
public function testSerializationKeepSameProperties()
{
    $properties = array(
        self::PERSON_NAME =&gt; 'Simone',
        self::PERSON_SURNAME =&gt; null,
    );

    $dto = Person::createFromArray($properties);

    $serialized = serialize($dto);
    $unserialized = unserialize($serialized);

    $this-&gt;assertEquals(
        $dto-&gt;asArray(),
        $unserialized-&gt;asArray()
    );

    $this-&gt;assertEquals(
        $properties,
        $unserialized-&gt;asArray()
    );
}


Production code :

class DtoException extends Exception
{
}
abstract class Dto
{
    private static $propertyNamesByClass = array();

    private function __construct(array $properties)
    {
        foreach($properties as $propertyName=&gt;$propertyValue)
            if(Static::hasPropertyName($propertyName))
                $this-&gt;set($propertyName,$propertyValue);
    }
    public function __call($method, array $arguments)
    {
        $getOrSet = substr($method, 0, 3);
        if($getOrSet != 'get' &amp;&amp; $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-&gt;get($propertyName);
    }
    public function __set($propertyName, $propertyValue)
    {
        if(!Static::hasPropertyName($propertyName))
            throw new DtoException('"'.get_class($this).'" has no property "'.$propertyName.'"');

        return $this-&gt;set($propertyName, $propertyValue);
    }

    public static function createFromArray($properties)
    {
        return new Static($properties);
    }

    public function set($propertyName, $propertyValue)
    {
        $this-&gt;$propertyName = $propertyValue;
    }

    public function get($propertyName)
    {
        return $this-&gt;$propertyName;
    }

    public static function getPropertyNames()
    {
        $className = get_called_class();
        if(isset(self::$propertyNamesByClass[$className]))
            return self::$propertyNamesByClass[$className];

        $reflected = new ReflectionClass($className);
        $reflectedProperties = $reflected-&gt;getProperties(
            ReflectionProperty::IS_PUBLIC
        );

        $propertyNames = array();
        foreach($reflectedProperties as $reflectedProperty) {
            $propertyNames[] = $reflectedProperty-&gt;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-&gt;get($propertyName);
        }

        return $values;
    }

    public function __sleep()
    {
        $propertyNames = self::getPropertyNames();

        foreach ($propertyNames as $propertyName) {
            $propertyValue = $this-&gt;get($propertyName);
            $this-&gt;set($propertyName, $propertyValue);
        }

        return $propertyNames;
    }

    public function __wakeup()
    {
        $propertyNames = self::getPropertyNames();

        return $propertyNames;
    }
}


I tested it on PHP5.3 and it works, however I have to tell you that it does not work on PHP5.2- or PHP7+.