public static function createFromEnv(array $env)
    {
        // cache
        if (isset($env['__request_object'])) {
            return $env['__request_object'];
        }

        if (isset($env['PATH_INFO'])) {
            $path = $env['PATH_INFO'];
        } elseif (isset($env['REQUEST_URI'])) {
            $path = $env['REQUEST_URI'];
        } elseif (isset($env['_SERVER']['PATH_INFO'])) {
            $path = $env['_SERVER']['PATH_INFO'];
        } elseif (isset($env['_SERVER']['REQUEST_URI'])) {
            $path = $env['_SERVER']['REQUEST_URI'];
        } else {
            // XXX: check path or throw exception
            $path = '/';
        }

        $requestMethod = 'GET';
        if (isset($env['REQUEST_METHOD'])) {
            $requestMethod = $env['REQUEST_METHOD'];
        } else if (isset($env['_SERVER']['REQUEST_METHOD'])) { // compatibility for superglobal
            $requestMethod = $env['_SERVER']['REQUEST_METHOD'];
        }

        // create request object with request method and path,
        // we can assign other parameters later.
        $request = new self($requestMethod, $path);
        if (function_exists('getallheaders')) {
            $request->headers = getallheaders();
        } else {
            // TODO: filter array keys by their prefix, consider adding an extension function for this.
            $request->headers = self::createHeadersFromServerGlobal($env);
        }

        if (isset($env['_SERVER'])) {
            $request->serverParameters = $env['_SERVER'];
        } else {
            $request->serverParameters = $env;
        }
        $request->parameters = $env['_REQUEST'];
        $request->queryParameters = $env['_GET'];
        $request->bodyParameters = $env['_POST'];
        $request->cookieParameters = $env['_COOKIE'];
        $request->sessionParameters = $env['_SESSION'];

        return $env['__request_object'] = $request;
    }