3. 逻辑运算符 and、or 和 not 是经常隐藏 bug 的地方,比
如:
if (type(t) == 'table' and t.x == 'abc')
就算 t 不是 table 类型,那么 lua 的短路求值也不会对 t
进行求值,所以不会引发运行时错误
4. 用 xpcall debug
function errorFunc()
local a = 20
print(a[10])
end
function errorHandle()
print(debug.traceback())
end
if xpcall(errorFunc,errorHandle) then
print("This is OK.")
else
print("This is error.")
end
8. 简单的写log函数(排重)
local logsUniquify = {} ——是的,这是个全局数组
function log(message, level)
if ngx and type(ngx.log) == 'function' then
local level = level or ngx.EMERG
if not logsUniquify[level .. message] then
ngx.log(level, message)
logsUniquify[level .. message] = true
end
end
return nil
end
14. Sql Injection
local name = ngx.unescape_uri(ngx.var.arg_name)
local quoted_name = ngx.quote_sql_str(name)
local sql = "select * from users where name = " ..
quoted_name
15. htmlspecialchars的实现
function htmlspecialchars(str)
local rs = str or nil
if rs and type(rs) == 'string' then
rs = string.gsub(rs, '&', '&')
rs = string.gsub(rs, '"', '"')
rs = string.gsub(rs, "'", ''')
rs = string.gsub(rs, '<', '<')
rs = string.gsub(rs, '>', '>')
end
return rs
end
19. 过滤 v 中的疑似网址特征
local spos = find(v, 'www') or find(v, '%.')
or find(v, '。') or find(v, '点')
local substring = ''
if spos then
substring = string.sub(s, spos, epos)
substring = g.escapeMagic(substring)
v = string.gsub(s, substring, '')
end
20. Build Queries
bind['so_refer_key'] = g.trim(vinfo['so_refer_key'])
bind['request_from'] = 'info_relate'
bind['wt'] = 'json'
bind['qt'] = 'standard'
bind['56_version'] = ngx.var.arg_rvc
bind['so_refer_key'] = ngx.var.arg_so_refer_key or ''
local param = neturl.buildQuery(bind)
local host = 'related_video.solr.56.com' --
local port = '49715'
local uri = '/solrRelateVideo/select?' .. param
if ngx.var.arg_dg == 'ml' then
print(uri)
end
21. xssfilter
— Filter the XSS attack
local xssfilter = require("lib.xssfilter")
local xss_filter = xssfilter.new()
data['title'] = g.htmlentities(xss_filter:filter(data['title']))
22. Sandbox
— 使用 closure 创建 sandbox (安全运行环境),比如为 io.open 提供权限控制的功能:
do
local oldOpen = io.open
local checkAccess = function (file, mode)
-- check if current user with 'mode' can access the 'file'
end
io.open = function (file, mode)
if checkAccess(file, mode) then
return oldOpen(file, mode)
else
return nil, "access, denied"
end
end
end
27. unpack(2)
— 经典应用 redis 的 hmget :
local tmpVids = {}
for i,v in ipairs(res) do
table.insert(tmpVids, tostring(v['video_id']))
end
local vextinfos = {}
local tbTimes = rds:hmget(rhkey, unpack(tmpVids))
31. Random Pick
function array_rand(tbl, m)
local rs
if type(tbl) == 'table' and next(tbl) ~= nil then
rs = {}
local order = {}
local n = #tbl
for i = 1, n do
order[i] = {rnd = math.random(), idx = i}
end
table.sort(order, function(a,b) return a.rnd < b.rnd end)
for i = 1, m do
if order[i] then rs[i] = order[i].idx end
end
end
return rs
end
32. Intersect by Key
function array_intersect_key(t1, t2)
local rs = t1
if type(t1) == 'table' and type(t2) == 'table' then
rs = {}
for k,v in pairs(t1) do
if t2[k] ~= nil then rs[k] = v end
end
end
return rs
end
33. Deep Compare
function deepcompare(t1, t2)
local ty1 = type(t1)
local ty2 = type(t2)
if ty1 ~= ty2 then return false end
-- non-table types can be directly compared
if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
-- as well as tables which have the metamethod __eq
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if v2 == nil or not deepcompare(v1,v2) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if v1 == nil or not deepcompare(v1,v2) then return false end
end
return true
end
34. 模块初始化与 __index
— 定义模块实例化的方法:
--[[ init module ]]
module(...)
_VERSION = '1.0.0'
--[[ indexed by current module env. ]]
local mt = {__index = _M};
--[[
instantiation
@return table
]]
function new(self)
return setmetatable({}, mt);
end
35. Default Value
function setDefault(table, default)
local mt = {__index = function() return default end }
setmetatable(table,mt)
end
tab = {x = 10, y = 20}
setDefault(tab,0)
print(tab.x,tab.z) --10 0
36. Write Prohibited
setmetatable(_M, {
__newindex = function (table, key, val)
ngx.log(ngx.EMERG, 'attempt to write to undeclared variable "' ..
key .. '" in ' .. table._NAME);
end
})
_M.unexists = "hello" -- 报错:’attempt to write to undeclared variable
"unexists" in $module'
37. Table Traversal
for k,v in g.pairsByKeys(idDel) do
print(k .. ':' .. v .. "n")
end
function pairsByKeys(tb, f)
local a = {}
for n in pairs(tb) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function() -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], tb[a[i]]
end
end
return iter
end
38. Sort by Value
function sortAssoc(tb, order, limit)
local rs = nil
if type(tb) == 'table' then
rs = {}
local tmp = {}
for k,v in pairs(tb) do
table.insert(tmp, {key = k, val = tonumber(v)})
end
if next(tmp) ~= nil then
if order and order == 'DESC' then
table.sort(tmp, function(a, b) return b.val < a.val end)
else
table.sort(tmp, function(a, b) return b.val > a.val end)
end
end
for i,v in ipairs(tmp) do
table.insert(rs, {v['key'], v['val']})
if limit and (i >= limit) then break end
end
end
return rs
end
39. Multi-Array Sorting
local data = {
{id=3, data=421}, {id=23, data=321}, {id=3, data=422}, {id=5, data=321},
{id=1, data=4214}, {id=3, data=44}
}
table.sort(data, function(a, b)
if a['id'] < b['id'] then
return true
elseif a['id'] == b['id'] then
if a['data'] < b['data'] then
return true
else
return false
end
else
return false
end
end)
46. 形参 vs 实参
1. 实参多于形参,多出的部分被忽略
2. 形参多于实参,没被初始化的形参的缺省值为nil
function foo(a, b, c)
print(a, b, c)
end
foo('a', 'b', 'c', 'd') -- 'd' 被忽略
foo('a') -- 这时形参 b 和 c 都是 nil
49. Fastest Trim
function trim(s)
if type(s) == 'string' then
local match = string.match
return match(s,'^()%s*$') and '' or match(s,'^%s*(.*%S)')
end
return s
end
57. How to “continue”?
local bContinue
for k, v in pairs(tbl) do
bContinue = false
......
if not bContinue then
......
if needToContinue then
bContinue = true
end
end
if not bContinue then
-- continue to do things
end
end
58. elseif vs else if
if ... then
else
if ... then
end
end
if ... then
elseif ... then
end
62. Serialize
function serialize(o)
if type(o) == "number" then
io.write(o)
elseif type(o) == "string" then
--string.format函数的"%q"参数可以转义字符串中的元字符。
io.write(string.format("%q",o))
elseif type(o) == "table" then
io.write("{n")
--迭代table中的各个元素,同时递归的写出各个字段的value。
--由此可以看出,这个简单例子可以支持嵌套的table。
for k,v in pairs(o) do
--这样做是为了防止k中包含非法的Lua标识符。
io.write(" ["); serialize(k); io.write("] = ")
serialize(v)
io.write(",n")
end
io.write("}n")
else
error("cannot serialize a " .. type(o))
end
end
63. Size and Seek
1. local f = assert(io.open(filename,"r"))
2. local current = f:seek() --获取当前位置
3. local size = f:seek("end") --获取文件大小
4. f:seek("set",current) --恢复原有的当前位置
64. 读取文件优化
下面是Shell中wc命令的一个简单实现:
local BUFSIZE = 8192
local f = io.input(arg[1]) --打开输入文件
local cc, lc, wc, = 0, 0, 0 --分别计数字符、行和单词
while true do
local lines,rest = f:read(BUFSIZE,"*line")
if not lines then
break
end
if rest then
lines = lines .. rest .. "n"
end
cc = cc + #lines
--计算单词数量
local _, t = string.gsub(lines."%S+","")
wc = wc + t
--计算行数
_,t = string.gsub(line,"n","n")
lc = lc + t
end
print(lc,wc,cc)
66. Socket Connect
-- 用 ngx.socket.tcp 才能百分百支持 nonblocking
local sock = ngx.socket.tcp()
sock:settimeout(1000) -- one second
local ok, err = sock:connect("127.0.0.1", 11211)
local bytes, err = sock:send("flush_allrn")
if not bytes then
ngx.say("failed to send query: ", err)
return
end
local line, err = sock:receive()
if not line then
ngx.say("failed to receive a line: ", err)
return
end
ngx.say("result: ", line)
local ok, err = sock:setkeepalive(60000, 500)
if not ok then
ngx.say("failed to put the connection into pool with pool capacity 500 "
.. "and maximal idle time 60 sec")
return
end
67. Settimeout
sock:settimeout(1000)
ok, err = tcpsock:connect(host, port, options_table?)
sock:settimeout(1000)
bytes, err = tcpsock:send(data)
sock:settimeout(1000)
data, err, partial = tcpsock:receive(size) —读timeout是不会close掉连接的
local reader = sock:receiveuntil("rn--abcedhb")
sock:settimeout(1000)
while true do
……
end
ok, err = tcpsock:close()
69. 协程示例
协程的经典示例:“生产者-消费者”问题:
--消费者
function receive()
local status, value = coroutine.resume(producer) --消费
return value
end
--生产者
function send(x)
coroutine.yield(x) --挂起
end
--协程
producer = coroutine.create(
function()
while true do
local x = io.read() --产生新值
send(x)
end
end)
70. cosocket 读取大数据
local sock, err = ngx.req.socket()
if not sock then
ngx.say("failed to get request socket: ", err)
return
end
sock:settimeout(10000) -- 10 sec timeout
while true do
local chunk, err = sock:receive(4096)
if not chunk then
if err == "closed" then
break
end
ngx.say("faile to read: ", err)
return
end
process_chunk(chunk)
end