public static function prop($value, $chain)
    {
        if (empty($chain)) {
            throw new \InvalidArgumentException("Value property name cannot be empty.");
        }

        if (!is_string($chain)) {
            throw new \InvalidArgumentException(sprintf(
                "Value property name must be a string, %s given.",
                gettype($chain)
            ));
        }

        $chain = explode('.', $chain);
        foreach ($chain as $property) {
            if (is_object($value)) {
                $getter = 'get' . ucfirst($property);
                if (is_callable(array($value, $getter))) {
                    $value = $value->$getter();
                } elseif (isset($value->{$property})) {
                    $value = $value->{$property};
                } else {
                    return null;
                }
            } elseif (Arrays::isArray($value)) {
                if (!isset($value[$property])) {
                    return null;
                }
                $value = $value[$property];
            } elseif (is_null($value)) {
                return null;
            } else {
                throw new \InvalidArgumentException(sprintf(
                    "Value property '%s' in chain '%s' has invalid type: '%s'.",
                    $property,
                    $chain,
                    gettype($value)
                ));
            }
        }

        return $value;
    }