static function getAttributes(
		$obj
		, $visibilityFlags = null
		, $getterPrefix = 'get'
		, $useCamelCase = true
		, $convertInternalObjects = false
		) {
		if ( ! isset( $obj ) ) {
			return array();
		}
		$flags = null === $visibilityFlags ? self::allFlags() : $visibilityFlags;
		$attributes = array();
		$reflectionObject = new \ReflectionObject( $obj );
		$currentClass = new \ReflectionClass( $obj );
		
		while ( $currentClass !== false && ! $currentClass->isInterface() ) {	
		
			$properties = $currentClass->getProperties( $flags );
				
			foreach ( $properties as $property ) {
				
				$attributeName = $property->getName();
				$methodName = $getterPrefix .
					( $useCamelCase ? self::mb_ucfirst( $attributeName ) : $attributeName );
				
				if ( $property->isPrivate() || $property->isProtected() ) {
				
					if ( $reflectionObject->hasMethod( $methodName ) ) {
						
						$method = $reflectionObject->getMethod( $methodName );
						if ( $method->isPublic() ) {
							$attributes[ $attributeName ] = $method->invoke( $obj );
						}
					} else { // maybe has a __call magic method
						$attributes[ $attributeName ] = $obj->{ $methodName }();
					}
					
				} else { // public method
					
					try {
						$attributes[ $attributeName ] = $obj->{ $attributeName };
					} catch ( \Exception $e ) {
						// Ignore
					}
				}
				
			}
			
			// No properties? -> try to retrieve public properties
			if ( count( $properties ) < 1 ) {
				$properties = get_object_vars( $obj );
				foreach ( $properties as $k => $v ) {
					$attributes[ $k ] = $v;
				}
			}
			
			$currentClass = $currentClass->getParentClass();
		}
		
		if ( $convertInternalObjects ) {
			// Analyse all internal objects
			foreach ( $attributes as $key => $value ) {
				if ( is_object( $value ) ) {
					$attributes[ $key ] =
						self::getAttributes( $value, $flags, $getterPrefix, $useCamelCase );
				}
			}
		}
		
		return $attributes;
	}