static public function setValue($object, $propertyPath, $propertyValue, $failOnError = true)
    {
        if (!is_object($object))
        {
            throw new RuntimeException('Object expected, ' . gettype($object) . ' given');
        }

        // immediately return value if propertyPath contains only one level
        if (strpos($propertyPath, '.') === false)
        {
            return static::setBySetter($object, $propertyPath, $propertyValue, $failOnError);
        }

        list($firstPropertyPath, $pathRest) = explode('.', $propertyPath, 2);

        $firstObject = static::getByGetter($object, $firstPropertyPath, $failOnError);

        // set value recursively while propertyPath has many levels
        if (is_object($firstObject))
        {
            return static::setValue($firstObject, $pathRest, $propertyValue, $failOnError);
        }
        elseif ($failOnError === true)
        {
            throw new RuntimeException("Object expected for property path '{$firstPropertyPath}', '" .
                                        gettype($firstObject) . "' given");
        }
    }