URI-Related UDFs
« Blog post
Source code
Source Code
<!--- UDFs by Steven Levithan ---> <!--- 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 EQ "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="yes" /> <cfargument name="masterUri" type="string" required="no" 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") EQ 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="yes" /> <cfargument name="key" type="string" required="yes" /> <cfargument name="substring" type="string" required="yes" /> <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="yes" /> <cfargument name="key" type="string" required="yes" /> <cfargument name="value" type="string" required="yes" /> <!--- 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="yes" /> <!--- Use a comma-delimited list to remove multiple keys with one call ---> <cfargument name="key" type="string" required="yes" /> <cfreturn replaceUriQueryKey(uri, key, "") /> </cffunction>
Feedback? Bugs?
Please leave comments on the related
blog post
.