This module provides some utility functions for emulating certain parts of the inner-workings of Scribunto, as well as the underlying PHP-based MediaWiki platform.
They are used to support other modules that deal with template parameters or wikitext parsing (e.g. for template expansion).
function export.php_htmlspecialchars(str, quotes)
Lua equivalent of PHP's htmlspecialchars($string)
, which converts the characters &"'<>
to HTML entities.
If the quotes
flag is set to "compat"
, then '
will not be converted, and if it is set to "noquotes"
, then neither "
nor '
will be converted.
function export.php_string_equals(str1, str2)
Lua equivalent of PHP's ==
operator for strings.
function export.php_trim(str)
Lua equivalent of PHP's trim($string)
, which trims "\0"
, "\t"
, "\n"
, "\v"
, "\r"
and " "
. This is useful when dealing with template parameters, since the native parser trims them like this.
function export.php_ltrim(str)
Lua equivalent of PHP's ltrim($string)
, which trims "\0"
, "\t"
, "\n"
, "\v"
, "\r"
and " "
from the beginning of the input string.
function export.php_rtrim(str)
Lua equivalent of PHP's rtrim($string)
, which trims "\0"
, "\t"
, "\n"
, "\v"
, "\r"
and " "
from the end of the input string.
function export.scribunto_parameter_key(key, no_trim)
Takes a template or module parameter name as either a string or number, and returns the Scribunto-normalized form (i.e. the key that that parameter would have in a frame.args
table). For example, "1"
(a string) is normalized to 1
(a number), " foo "
is normalized to "foo"
, and 1.5
(a number) is normalized to "1.5"
(a string). Inputs which cannot be normalized (e.g. booleans) return nil
.
Strings are trimmed with export.php_trim
, unless the no_trim
flag is set. If it is, then string parameters are not trimmed, but strings may still be converted to numbers if they do not contain whitespace; this is necessary when normalizing keys into the form received by PHP during callbacks, before any trimming occurs (e.g. in the table of arguments when calling frame:expandTemplates()
).
After trimming (if applicable), keys are then converted to numbers if all of the following are true:
"2"
, but not "2.0"
or "02"
)."2"
or "-2"
, but not "+2"
or "-0"
).no_trim
flag is set).Numbers are converted to strings if either:
1.5
).When converted to strings, integers ≤ 263 and ≥ -263 are formatted as integers (i.e. all digits are given), which is the range of PHP's integer precision, though the actual output may be imprecise since Lua's integer precision is > 253 to < -253. All other numbers use the standard formatting output by tostring()
.
function export.scribunto_parameter_value(value, named)
Takes a template or module parameter value as either a string, number or boolean, and returns the Scribunto-normalized form (i.e. the value that that parameter would have in a frame.args
table), which is always a string. For example, "foo"
remains the same, 2
(a number) is normalized to "2"
(a string), true
is normalized to "1"
, and false
is normalized to ""
. Inputs which cannot be normalized (e.g. tables) return nil
.
By default, returned values are not trimmed, which matches the treatment of unnamed parameters (e.g. bar
in {{foo|bar}}
). If the named
flag is set, then returned values will be trimmed, which matches the treatment of named parameters (e.g. baz
in {{foo|bar=baz}}
).
local export = {}
local math_module = "Module:math"
local dump = mw.dumpObject
local format = string.format
local gsub = string.gsub
local match = string.match
local php_trim -- defined below
local sub = string.sub
local tonumber = tonumber
local tostring = tostring
local type = type
do
local php_htmlspecialchars_data
local function get_php_htmlspecialchars_data()
php_htmlspecialchars_data, get_php_htmlspecialchars_data = {
= """,
= "&",
= "'",
= "<",
= ">",
}, nil
return php_htmlspecialchars_data
end
--[==[Lua equivalent of PHP's {{code|php|htmlspecialchars($string)}}, which converts the characters `&"'<>` to HTML entities.
If the `quotes` flag is set to {"compat"}, then `'` will not be converted, and if it is set to {"noquotes"}, then neither `"` nor `'` will be converted.]==]
function export.php_htmlspecialchars(str, quotes)
if quotes == nil or quotes == "quotes" then
quotes = "'\""
elseif quotes == "compat" then
quotes = "\""
elseif quotes == "noquotes" then
quotes = ""
else
local quotes_type = type(quotes)
error('`quotes` must be "quotes", "compat", "noquotes" or nil; received ' ..
(quotes_type == "string" and dump(quotes) or "a " .. quotes_type))
end
return (gsub(str, "", php_htmlspecialchars_data or get_php_htmlspecialchars_data()))
end
end
do
local function tonumber_extended(...)
tonumber_extended = require(math_module).tonumber_extended
return tonumber_extended(...)
end
-- Normalizes a string for use in comparisons which emulate PHP's equals
-- operator, which coerces certain strings to numbers: those within the
-- range -2^63 to 2^63 - 1 which don't have decimal points or exponents are
-- coerced to integers, while any others are coerced to doubles if possible;
-- otherwise, they remain as strings. PHP and Lua have the same precision
-- for doubles, but Lua's integer precision range is -2^53 + 1 to 2^53 - 1.
-- Any integers within Lua's precision, as well as all doubles, are simply
-- coerced to numbers, but PHP integers outside of Lua's precision are
-- emulated as normalized strings, with leading 0s and any + sign removed.
-- The `not_long` flag is used for the second comparator if the first did
-- not get normalized to a long integer, as PHP will only coerce strings to
-- integers if it's possible for both comparators.
local function php_normalize_string(str, not_long)
local num = tonumber_extended(str, nil, true)
-- Must be a number that isn't ±infinity, NaN or hexadecimal.
if not num or match(str, "^%s*?0()") then
return str
-- If `not_long` is set or `num` is within Lua's precision, return as a
-- number.
elseif not_long or num < 9007199254740992 and num > -9007199254740992 then
return num, "number"
end
-- Check if it could be a long integer, and return as a double if not.
local sign, str_no_0 = match(str, "^%s*(?)0*(%d+)$")
if not str_no_0 then
return num, "number"
end
-- Otherwise, check if it's a long integer. 2^63 is 9223372036854775808,
-- so slice off the last 15 digits and deal with the two parts
-- separately. If the integer value would be too high/low, return as a
-- string.
local high = tonumber(sub(str_no_0, 1, -16))
if high > 9223 then
return str
elseif high == 9223 then
local low = tonumber(sub(str_no_0, -15))
-- Range is -2^63 to 2^63 - 1 (not symmetrical).
if low > 372036854775808 or low == 372036854775808 and sign ~= "-" then
return str
end
end
return (sign == "+" and "" or sign) .. str_no_0, "long integer", num
end
--==]
function export.php_string_equals(str1, str2)
if str1 == str2 then
return true
end
local str1, str1_type, str1_num = php_normalize_string(str1)
if str1 == str2 then
return true
elseif str1_type == "long integer" then
local str2, str2_type = php_normalize_string(str2)
return str2 == (str2_type == "number" and str1_num or str1)
elseif str1_type == "number" then
return str1 == php_normalize_string(str2, true)
end
return false
end
end
--==]
function export.php_trim(str)
return match(str, ".*%f") or ""
end
php_trim = export.php_trim
--==]
function export.php_ltrim(str)
return (gsub(str, "^+", ""))
end
--==]
function export.php_rtrim(str)
return match(str, "^.+%f") or ""
end
--[==[Takes a template or module parameter name as either a string or number, and returns the Scribunto-normalized form (i.e. the key that that parameter would have in a {frame.args} table). For example, {"1"} (a string) is normalized to {1} (a number), {" foo "} is normalized to {"foo"}, and {1.5} (a number) is normalized to {"1.5"} (a string). Inputs which cannot be normalized (e.g. booleans) return {nil}.
Strings are trimmed with {export.php_trim}, unless the `no_trim` flag is set. If it is, then string parameters are not trimmed, but strings may still be converted to numbers if they do not contain whitespace; this is necessary when normalizing keys into the form received by PHP during callbacks, before any trimming occurs (e.g. in the table of arguments when calling {frame:expandTemplates()}).
After trimming (if applicable), keys are then converted to numbers if '''all''' of the following are true:
# They are integers; i.e. no decimals or leading zeroes (e.g. {"2"}, but not {"2.0"} or {"02"}).
# They are ≤ 2{{sup|53}} and ≥ -2{{sup|53}}.
# There is no leading sign unless < 0 (e.g. {"2"} or {"-2"}, but not {"+2"} or {"-0"}).
# They contain no leading or trailing whitespace (which may be present when the `no_trim` flag is set).
Numbers are converted to strings if '''either''':
# They are not integers (e.g. {1.5}).
# They are > 2{{sup|53}} or < -2{{sup|53}}.
When converted to strings, integers ≤ 2{{sup|63}} and ≥ -2{{sup|63}} are formatted as integers (i.e. all digits are given), which is the range of PHP's integer precision, though the actual output may be imprecise since Lua's integer precision is > 2{{sup|53}} to < -2{{sup|53}}. All other numbers use the standard formatting output by {tostring()}.]==]
function export.scribunto_parameter_key(key, no_trim)
local key_type = type(key)
if key_type == "string" then
if not no_trim then
key = php_trim(key)
end
if match(key, "^()-?%d*$") then
local num = tonumber(key)
-- Lua integers are only precise to 2^53 - 1, so specifically check
-- for 2^53 and -2^53 as strings, since a numerical comparison won't
-- work as it can't distinguish 2^53 from 2^53 + 1.
return (
num <= 9007199254740991 and num >= -9007199254740991 or
key == "9007199254740992" or
key == "-9007199254740992"
) and num or key
end
return key == "0" and 0 or key
elseif key_type == "number" then
-- No special handling needed for inf or NaN.
return key % 1 == 0 and (
key <= 9007199254740992 and key >= -9007199254740992 and key or
key <= 9223372036854775808 and key >= -9223372036854775808 and format("%d", key)
) or tostring(key)
end
return nil
end
--[==[Takes a template or module parameter value as either a string, number or boolean, and returns the Scribunto-normalized form (i.e. the value that that parameter would have in a {frame.args} table), which is always a string. For example, {"foo"} remains the same, {2} (a number) is normalized to {"2"} (a string), {true} is normalized to {"1"}, and {false} is normalized to {""}. Inputs which cannot be normalized (e.g. tables) return {nil}.
By default, returned values are not trimmed, which matches the treatment of unnamed parameters (e.g. `bar` in {{tl|<nowiki/>foo|bar}}). If the `named` flag is set, then returned values will be trimmed, which matches the treatment of named parameters (e.g. `baz` in {{tl|<nowiki/>foo|bar=baz}}).]==]
function export.scribunto_parameter_value(value, named)
local value_type = type(value)
if value_type == "string" then
return named and php_trim(value) or value
elseif value_type == "number" then
return tostring(value)
elseif value_type == "boolean" then
return value and "1" or ""
end
return nil
end
return export