Module:User:Mr. Stradivarius/sandbox
Appearance
![]() | This is the module sandbox page for Module:User:Mr. Stradivarius. |
local checkType = require( 'libraryUtil' ).checkType
local uri = {}
local function rawencode( s, space )
space = space or '%20'
local ret = string.gsub( s, '([^a-zA-Z0-9_.~-])', function ( c )
if c == ' ' then
return space
else
return string.format( '%%%02X', string.byte( c, 1, 1 ) )
end
end );
return ret
end
local function rawdecode( s )
local ret = string.gsub( s, '%%(%x%x)', function ( hex )
return string.char( tonumber( hex, 16 ) )
end );
return ret
end
-- Lua tables are unsorted, but we want to preserve the insertion order.
-- So, track the insertion order explicitly.
local function makeQueryTable()
local ret = {}
local keys = {}
local seenkeys = {}
setmetatable( ret, {
__newindex = function ( t, k, v )
if seenkeys[k] and not t[k] then
for i = 1, #keys do
if keys[i] == k then
table.remove( keys, i )
break
end
end
end
seenkeys[k] = 1
keys[#keys+1] = k
rawset( t, k, v )
end,
__pairs = function ( t )
local i, l = 0, #keys
return function ()
while i < l do
i = i + 1
local k = keys[i]
if t[k] ~= nil then
return k, t[k]
end
end
return nil
end
end
} )
return ret
end
function uri.parseQueryString( s, i, j )
checkType( 'parseQueryString', 1, s, 'string' )
checkType( 'parseQueryString', 2, i, 'number', true )
checkType( 'parseQueryString', 3, j, 'number', true )
s = string.gsub( string.sub( s, i or 1, j or -1 ), '%+', ' ' )
i = 1
j = string.len( s )
local qs = makeQueryTable()
if string.sub( s, i, 1 ) == '?' then
i = i + 1
end
while i <= j do
local amp = string.find( s, '&', i, true )
if not amp or amp > j then
amp = j + 1
end
local eq = string.find( s, '=', i, true )
local key, val
if not eq or eq > amp then
key = rawdecode( string.sub( s, i, amp - 1 ) )
val = false
else
key = rawdecode( string.sub( s, i, eq - 1 ) )
val = rawdecode( string.sub( s, eq + 1, amp - 1 ) )
end
local iKey = 1
local jKey = string.len( key )
local prevqs = qs
while true do
local lb = string.find( key, '[', iKey, true )
local rb, subKey
if lb and lb <= jKey - 1 then
rb = string.find( key, ']', lb, true )
if rb and rb <= jKey then
if lb == iKey then
-- This is something like [bar]
subKey = string.sub( key, lb + 1, rb - 1 )
iKey = rb + 1
else
-- This is something like foo[bar]
subKey = string.sub( key, 1, lb - 1 )
iKey = lb
end
end
end
if not subKey then
subKey = key
iKey = jKey + 1
end
if subKey == '' then
-- Find the index for implicit array keys like foo[]
subKey = #prevqs + 1
elseif tonumber( subKey ) then
-- Adjust numbered keys for compatibility with Lua. Query array
-- keys start from 0, but Lua tables start at 1.
subKey = tonumber( subKey ) + 1
end
if iKey <= jKey then
-- We have more sub-keys to process.
if type( prevqs[subKey] ) ~= 'table' then
local tmp = prevqs[subKey]
prevqs[subKey] = makeQueryTable()
table.insert( prevqs[subKey], tmp )
end
prevqs = prevqs[subKey]
elseif prevqs[subKey] then
-- We have no more sub-keys to process, and the key already
-- exists in the query table.
if type( prevqs[subKey] ) == 'table' then
table.insert( prevqs[subKey], val )
else
prevqs[subKey] = { prevqs[subKey], val }
end
break
else
-- We have no more sub-keys to process, and we are adding a new
-- key.
prevqs[subKey] = val
break
end
end
i = amp + 1
end
return qs
end
local function buildQueryStringRecursive( val, key, ret, errorLevel )
ret = ret or {}
errorLevel = errorLevel or 3
local tp = type( val )
if tp == 'table' then
for k, v in pairs( val ) do
local ktp = type( k )
local subKey
if ktp == 'number' then
subKey = k - 1 -- Start array keys at zero, as that's what PHP does
elseif ktp == 'string' then
subKey = k
else
error( string.format(
"query keys must be strings or numbers (%s detected)",
ktp
), errorLevel )
end
local newKey
if key then
newKey = key .. '[' .. subKey .. ']'
else
newKey = subKey
end
buildQueryStringRecursive( v, newKey, ret, errorLevel + 1 )
end
elseif tp == 'string' or tp == 'number' or tp == 'boolean' then
ret[#ret+1] = '&'
ret[#ret+1] = rawencode( key, '+' )
if val then
ret[#ret+1] = '='
ret[#ret+1] = rawencode( val, '+' )
end
else
error( string.format(
"query values must be of type string, number, boolean or table (%s detected)",
tp
), errorLevel )
end
return table.concat( ret, '', 2 )
end
--[[
{ foo = { 'bar', baz = { 'a', 'b' } }, normal = 'val' }
foo[0]=bar&foo[baz][0]=a&foo[baz][1]=b&normal=val
--]]
function uri.buildQueryString( qs )
checkType( 'buildQueryString', 1, qs, 'table' )
return buildQueryStringRecursive( qs )
end
return uri