To follow up my parseUri()
function, here are several more UDFs I've written recently to help with URI management:
getPageUri()
Returns a struct containing the relative and absolute URIs of the current page. The difference betweengetPageUri().relative
andCGI.SCRIPT_NAME
is that the former will include the query string, if present.matchUri(testUri, [masterUri])
Returns a Boolean indicating whether or not two URIs are the same, disregarding the following differences:- Fragments (page anchors), e.g., "#top".
- Inclusion of "index.cfm" in paths, e.g., "/dir/" vs. "/dir/index.cfm" (supports trailing query strings).
masterUri
is not provided, the current page is used for comparison (supports both relative and absolute URIs).replaceUriQueryKey(uri, key, substring)
Replaces a URI query key and its value with a supplied key=value pair. Works with relative and absolute URIs, as well as standalone query strings (with or without a leading "?"). This is also used to support the following two UDFs:addUriQueryKey(uri, key, value)
Removes any existing instances of the supplied key, then appends it together with the provided value to the provided URI.removeUriQueryKey(uri, key)
Removes one or more query keys (comma delimited) and their values from the provided URI.
Now that I have these at my disposal, I frequently find myself using them in combination with each other, e.g.,
<a href="<cfoutput>#addUriQueryKey(
.
getPageUri().relative,
"key",
"value"
)#</cfoutput>">Link</a>
Let me know if you find any of these useful…
<!--- Returns the relative and absolute URIs of the current page ---> <cffunction name="getPageUri" returntype="struct" output="FALSE"> <cfset var pageProtocol = "http" /> <cfset var pageQuery = "" /> <cfset var uri = structNew() /> <!--- Get the protocol of the current page ---> <cfif CGI.HTTPS IS "ON"> <cfset pageProtocol = "https" /> </cfif> <!--- Get the query of the current page, including the leading question if the query is not empty ---> <cfset pageQuery = reReplace("?" & CGI.QUERY_STRING, "\?$", "") /> <!--- Construct the relative URI of the current page (excludes the protocol and domain) ---> <cfset uri.relative = CGI.SCRIPT_NAME & pageQuery /> <!--- Construct the absolute URI of the current page ---> <cfset uri.absolute = pageProtocol & "://" & CGI.SERVER_NAME & uri.relative /> <cfreturn uri /> </cffunction> <!--- Returns a Boolean indicating whether or not two URIs are the same, disregarding the following differences: • Fragments (page anchors), e.g., "#top". • Inclusion of "index.cfm" in paths, e.g., "/dir/" vs. "/dir/index.cfm" (supports trailing query strings). If masterUri is not provided, the current page is used for comparison (supports both relative and absolute URIs) ---> <cffunction name="matchUri" returntype="boolean" output="FALSE"> <cfargument name="testUri" type="string" required="TRUE" /> <cfargument name="masterUri" type="string" required="FALSE" default="" /> <!--- If a masterUri was not provided ---> <cfif len(masterUri) EQ 0> <!--- If testUri is an absolute URI ---> <cfif reFindNoCase("^https?://", testUri) EQ 1> <cfset masterUri = getPageUri().absolute /> <cfelse> <cfset masterUri = getPageUri().relative /> </cfif> </cfif> <cfreturn reReplaceNoCase(reReplace(testUri, "##.*", ""), "/index\.cfm(?=\?|$)", "/", "ONE") IS reReplaceNoCase(reReplace(masterUri, "##.*", ""), "/index\.cfm(?=\?|$)", "/", "ONE") /> </cffunction> <!--- Replace a URI query key and its value with a supplied key=value pair. Works with relative and absolute URIs, as well as standalone query strings (with or without a leading "?") ---> <cffunction name="replaceUriQueryKey" returntype="string" output="FALSE"> <cfargument name="uri" type="string" required="TRUE" /> <cfargument name="key" type="string" required="TRUE" /> <cfargument name="substring" type="string" required="TRUE" /> <cfset var preQueryComponents = "" /> <cfset var currentKey = "" /> <!--- Remove any existing fragment (page anchor) from uri, since it will mess with our processing, and is unlikely to be relevant and/or correct in the new URI ---> <cfset uri = reReplace(uri, "##.*", "", "ONE") /> <!--- Store any pre-query URI components. For this to work, the string must start with "protocol:", "//authority", or "/" (path). Otherwise, we will assume the uri is comprised entirely of a query component ---> <cfset preQueryComponents = reReplace(uri, "^((?:(?:[^:/?.]+:)?//[^/?]+)?(?:/[^?]*)?)?.*", "\1", "ONE") /> <!--- Remove any pre-query components and the leading question mark from uri ---> <cfset uri = reReplace(uri, "^(?:(?:[^:/?.]+:)?//[^/?]+)?(?:/[^?]*)?\??(.*)", "\1", "ONE") /> <!--- Remove any superfluous ampersands in the query (this cleans up the query but is not required, and in any case this function doesn't generate superfluous ampersands) ---> <cfset uri = reReplace(uri, "&(?=&)|&$", "", "ALL") /> <!--- For each key specified, remove the corresponding key=value pair from uri. Note that key names which contain regex special characters (.,*,+,?,^,$,{,},(,),|,[,],\) which are not percent-encoded may behave unpredictably ---> <cfloop index="currentKey" list="#key#" delimiters=","> <cfif len(currentKey) GT 0> <cfset uri = reReplaceNoCase(uri, ("(?:^|&)" & currentKey & "(?:=[^&]*)?"), "", "ALL") /> </cfif> </cfloop> <!--- If we still have a value in uri after the above processing (beyond what we're about to add) ---> <cfif len(uri) GT 0> <!--- Ensure the query is returned with only the necessary separator characters (? and &) ---> <cfreturn (preQueryComponents & "?" & reReplace(uri, "^&", "") & reReplace("&" & substring, "&$", "")) /> <cfelse> <!--- Append substring, including a leading question mark if substring is not empty ---> <cfreturn (preQueryComponents & reReplace("?" & substring, "\?$", "")) /> </cfif> </cffunction> <cffunction name="addUriQueryKey" returntype="string" output="FALSE"> <cfargument name="uri" type="string" required="TRUE" /> <cfargument name="key" type="string" required="TRUE" /> <cfargument name="value" type="string" required="TRUE" /> <!--- Until proper support is included for adding multiple keys with one call, use only the first key ---> <cfset key = listFirst(key, ",") /> <!--- Remove any existing instances of the key from uri, then add the new key=value pair. Do not include the trailing equals sign (=) if we're assigning an empty value to the added key ---> <cfreturn replaceUriQueryKey(removeUriQueryKey(uri, key), "", (key & reReplace("=" & value, "=$", ""))) /> </cffunction> <cffunction name="removeUriQueryKey" returntype="string" output="FALSE"> <cfargument name="uri" type="string" required="TRUE" /> <!--- Use a comma-delimited list to remove multiple keys with one call ---> <cfargument name="key" type="string" required="TRUE" /> <cfreturn replaceUriQueryKey(uri, key, "") /> </cffunction>
In other news, this cracked me up.
No comments:
Post a Comment