sdlPtr get_sdl(zval *this_ptr, char *uri, zend_long cache_wsdl)
{
	char  fn[MAXPATHLEN];
	sdlPtr sdl = NULL;
	char* old_error_code = SOAP_GLOBAL(error_code);
	size_t uri_len = 0;
	php_stream_context *context=NULL;
	zval *tmp, *proxy_host, *proxy_port, orig_context, new_context;
	smart_str headers = {0};
	char* key = NULL;
	time_t t = time(0);
	zend_bool has_proxy_authorization = 0;
	zend_bool has_authorization = 0;

	ZVAL_UNDEF(&orig_context);
	ZVAL_UNDEF(&new_context);
	if (strchr(uri,':') != NULL || IS_ABSOLUTE_PATH(uri, uri_len)) {
		uri_len = strlen(uri);
	} else if (VCWD_REALPATH(uri, fn) == NULL) {
		cache_wsdl = WSDL_CACHE_NONE;
	} else {
		uri = fn;
		uri_len = strlen(uri);
	}

	if ((cache_wsdl & WSDL_CACHE_MEMORY) && SOAP_GLOBAL(mem_cache)) {
		sdl_cache_bucket *p;

		if (NULL != (p = zend_hash_str_find_ptr(SOAP_GLOBAL(mem_cache), uri, uri_len))) {
			if (p->time < t - SOAP_GLOBAL(cache_ttl)) {
				/* in-memory cache entry is expired */
				zend_hash_str_del(&EG(persistent_list), uri, uri_len);
			} else {
				return p->sdl;
			}
		}
	}

	if ((cache_wsdl & WSDL_CACHE_DISK) && (uri_len < MAXPATHLEN)) {
		time_t t = time(0);
		char md5str[33];
		PHP_MD5_CTX context;
		unsigned char digest[16];
		int len = strlen(SOAP_GLOBAL(cache_dir));
		time_t cached;
		char *user = php_get_current_user();
		int user_len = user ? strlen(user) + 1 : 0;

		md5str[0] = '\0';
		PHP_MD5Init(&context);
		PHP_MD5Update(&context, (unsigned char*)uri, uri_len);
		PHP_MD5Final(digest, &context);
		make_digest(md5str, digest);
		key = emalloc(len+sizeof("/wsdl-")-1+user_len+2+sizeof(md5str));
		memcpy(key,SOAP_GLOBAL(cache_dir),len);
		memcpy(key+len,"/wsdl-",sizeof("/wsdl-")-1);
		len += sizeof("/wsdl-")-1;
		if (user_len) {
			memcpy(key+len, user, user_len-1);
			len += user_len-1;
			key[len++] = '-';
		}
		if (WSDL_CACHE_VERSION <= 0x9f) {
			key[len++] = (WSDL_CACHE_VERSION >> 8) + '0';
		} else {
			key[len++] = (WSDL_CACHE_VERSION >> 8) - 10 + 'a';
		}
		if ((WSDL_CACHE_VERSION & 0xf) <= 0x9) {
			key[len++] = (WSDL_CACHE_VERSION & 0xf) + '0';
		} else {
			key[len++] = (WSDL_CACHE_VERSION & 0xf) - 10 + 'a';
		}
		memcpy(key+len,md5str,sizeof(md5str));

		if ((sdl = get_sdl_from_cache(key, uri, t-SOAP_GLOBAL(cache_ttl), &cached)) != NULL) {
			t = cached;
			efree(key);
			goto cache_in_memory;
		}
	}

	if (NULL != (tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr),
			"_stream_context", sizeof("_stream_context")-1))) {
		context = php_stream_context_from_zval(tmp, 0);
	} else {
		context = php_stream_context_alloc();
	}

	if ((tmp = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_user_agent", sizeof("_user_agent")-1)) != NULL &&
	    Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp) > 0) {
		smart_str_appends(&headers, "User-Agent: ");
		smart_str_appends(&headers, Z_STRVAL_P(tmp));
		smart_str_appends(&headers, "\r\n");
	}

	if ((proxy_host = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_host", sizeof("_proxy_host")-1)) != NULL &&
	    Z_TYPE_P(proxy_host) == IS_STRING &&
	    (proxy_port = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_proxy_port", sizeof("_proxy_port")-1)) != NULL &&
	    Z_TYPE_P(proxy_port) == IS_LONG) {
	        zval str_proxy;
	    	smart_str proxy = {0};
		smart_str_appends(&proxy,"tcp://");
		smart_str_appends(&proxy,Z_STRVAL_P(proxy_host));
		smart_str_appends(&proxy,":");
		smart_str_append_long(&proxy,Z_LVAL_P(proxy_port));
		smart_str_0(&proxy);
		ZVAL_NEW_STR(&str_proxy, proxy.s);

		if (!context) {
			context = php_stream_context_alloc();
		}
		php_stream_context_set_option(context, "http", "proxy", &str_proxy);
		zval_ptr_dtor(&str_proxy);

		if (uri_len < sizeof("https://")-1 ||
		    strncasecmp(uri, "https://", sizeof("https://")-1) != 0) {
			ZVAL_TRUE(&str_proxy);
			php_stream_context_set_option(context, "http", "request_fulluri", &str_proxy);
		}

		has_proxy_authorization = proxy_authentication(this_ptr, &headers);
	}

	has_authorization = basic_authentication(this_ptr, &headers);

	/* Use HTTP/1.1 with "Connection: close" by default */
	if ((tmp = php_stream_context_get_option(context, "http", "protocol_version")) == NULL) {
    	zval http_version;

		ZVAL_DOUBLE(&http_version, 1.1);
		php_stream_context_set_option(context, "http", "protocol_version", &http_version);
		smart_str_appendl(&headers, "Connection: close\r\n", sizeof("Connection: close\r\n")-1);
	}

	if (headers.s && ZSTR_LEN(headers.s) > 0) {
		zval str_headers;

		if (!context) {
			context = php_stream_context_alloc();
		} else {
			http_context_headers(context, has_authorization, has_proxy_authorization, 0, &headers);
		}

		smart_str_0(&headers);
		ZVAL_NEW_STR(&str_headers, headers.s);
		php_stream_context_set_option(context, "http", "header", &str_headers);
		zval_ptr_dtor(&str_headers);
	}

	if (context) {
		php_stream_context_to_zval(context, &new_context);
		php_libxml_switch_context(&new_context, &orig_context);
	}

	SOAP_GLOBAL(error_code) = "WSDL";

	sdl = load_wsdl(this_ptr, uri);
	if (sdl) {
		sdl->is_persistent = 0;
	}

	SOAP_GLOBAL(error_code) = old_error_code;

	if (context) {
		php_libxml_switch_context(&orig_context, NULL);
		zval_ptr_dtor(&new_context);
	}

	if ((cache_wsdl & WSDL_CACHE_DISK) && key) {
		if (sdl) {
			add_sdl_to_cache(key, uri, t, sdl);
		}
		efree(key);
	}

cache_in_memory:
	if (cache_wsdl & WSDL_CACHE_MEMORY) {
		if (sdl) {
			sdlPtr psdl;
			sdl_cache_bucket p;

			if (SOAP_GLOBAL(mem_cache) == NULL) {
				SOAP_GLOBAL(mem_cache) = malloc(sizeof(HashTable));
				zend_hash_init(SOAP_GLOBAL(mem_cache), 0, NULL, delete_psdl, 1);
			} else if (SOAP_GLOBAL(cache_limit) > 0 &&
			           SOAP_GLOBAL(cache_limit) <= (zend_long)zend_hash_num_elements(SOAP_GLOBAL(mem_cache))) {
				/* in-memory cache overflow */
				sdl_cache_bucket *q;
				time_t latest = t;
				zend_string *latest_key = NULL, *key;

				ZEND_HASH_FOREACH_STR_KEY_PTR(SOAP_GLOBAL(mem_cache), key, q) {
					if (q->time < latest) {
						latest = q->time;
						latest_key = key;
					}
				} ZEND_HASH_FOREACH_END();
				if (latest_key) {
					zend_hash_del(SOAP_GLOBAL(mem_cache), latest_key);
				} else {
					return sdl;
				}
			}

			psdl = make_persistent_sdl(sdl);
			psdl->is_persistent = 1;
			p.time = t;
			p.sdl = psdl;

			zend_hash_str_update_mem(SOAP_GLOBAL(mem_cache), uri,
											uri_len, &p, sizeof(sdl_cache_bucket));
			/* remove non-persitent sdl structure */
			delete_sdl_impl(sdl);
			/* and replace it with persistent one */
			sdl = psdl;
		}
	}

	return sdl;
}