Page MenuHomePhabricator

Tables passed to PHP are missing keys provided by the __index metamethod
Open, Needs TriagePublicBUG REPORT

Description

Similar to T207623, a difference between string.gsub and mw.ustring.gsub:

local t = setmetatable({ a = '1' }, { __index = { b = '2' } })
mw.log('string.gsub', string.gsub('ab', '.', t)) -- return values: '12', 2
mw.log('mw.ustring.gsub', mw.ustring.gsub('ab', '.', t)) -- return values: '1b', 2

It looks like mw.ustring.gsub is not replacing 'b' because it does not find a field with that key in t, and it is doing the equivalent of rawget (ignoring the __index metamethod, which would return '2' for 'b') rather than a normal field access.

I ran into this in Module:Ancient Greek (ALA-LC) on Wikipedia, where I was using the __index metamethod to switch between two tables mapping Greek characters to their transliteration in a given transliteration system.

Event Timeline

Anomie renamed this task from Unlike string.gsub, mw.ustring.gsub does not query __index metamethod of table supplied as replacement argument to Tables passed to PHP are missing keys provided by the __index metamethod.Apr 15 2019, 7:12 PM
Anomie subscribed.

I think the ustring issue is an effect rather than a cause. The LuaSandbox PHP extension can't really know what keys are being provided by __index in order to convert them into PHP array members when calling the PHP code. I'm not sure if that's even fixable, unless we were to have LuaSandbox pass through an ArrayAccess object instead of an actual PHP array, which would likely cause other issues.

It looks like LuaStandalone has the same issue. Again, when producing the serialized version to send via IPC to the PHP listener it can't know all the keys being provided by __index.

In the mean time, does it work if you use the (slower) pure-Lua ustring library?

local ustring = require( 'ustring' );
mw.log( 'ustring.gsub', ustring.gsub( 'ab', '.', t ) );

Or another thing to try is to implement __pairs in addition to __index, I think both LuaSandbox and LuaStandalone find keys via that mechanism.

Yes, plugging pure-Lua ustring into my example does work:

local t = setmetatable({ a = '1' }, { __index = { b = '2' } })
mw.log('ustring.gsub', require 'ustring'.gsub('ab', '.', t)) -- return values: '12', 2

Fortunately the Ancient Greek module doesn't require mw.ustring or pure-Lua ustring in the place where it is using the table with an __index metamethod because it is simply replacing individual code points, but I'll keep these workarounds in mind. How to do this with the __pairs metamethod when both the table and its __index table are populated sounds like an interesting problem to work on.