[wp-trac] [WordPress Trac] #54042: Extending wpdb::prepare() to support table/field names, and IN() operator

WordPress Trac noreply at wordpress.org
Fri May 6 23:05:33 UTC 2022

#54042: Extending wpdb::prepare() to support table/field names, and IN() operator
 Reporter:  craigfrancis                         |       Owner:  (none)
     Type:  enhancement                          |      Status:  new
 Priority:  normal                               |   Milestone:  Future
                                                 |  Release
Component:  Database                             |     Version:
 Severity:  normal                               |  Resolution:
 Keywords:  has-patch dev-feedback needs-        |     Focuses:
  testing early has-unit-tests                   |

Comment (by apokalyptik):

 Over at A8C this has come up a number of times in the past month or so for
 both my team and others...

 It would be invaluable to have this functionality in core because tricks
 such as vsprintf argnum hacks used to get around code sniffers, etc,
 undermine the integrity and security that those checks are meant to

 I wanted to implement this as a flag: `%,d`, `%,f`, and `%,s`

 So I started looking into how to do this safely. It turns out that this is
 very complicated and difficult for a number of reasons. I'll list a couple

 1. prepare does not actually parse like vsprintf does, but uses a regex
 which is, i think, imperfect (aren't they all?)
 2. for legacy reasons argnum's aren't escaped (which is why they're used
 as hacks for IN() statements)
 3. to avoid (possibly infinite) recursion with wpbd::prepare() you want to
 expand the specifiers and the args but
 4. this changes the order, and number, of the arguments and thus what the
 argument numbers need to be to stay correct.

 The other thing is that it needed to be as fully backwards compatible with
 prepare() (or at least punt and return to prepare on some sort of error)
 as possible.  This meant that I needed to (and did) run my solutions
 through the entire wpdb unit testing suite (and extend it to include
 implosion specifiers.)  All tests pass, excepting assertions around
 doing_it_wrong where the tests don't expect my new doing_it_wrong's in
 another function.  To replicate this you have to edit wpdb::prepare() and
 comment out the checks which prevent this scanning from happening

 So I ended up having to write a parser to accomplish the task. Which I
 did. This was mostly very easy except in the argnum case (which is where
 all of the complication came in.)  For this case what I ended up doing is
 de-referencing the arguments and having them "point" to the newly de-
 referenced args element.  Originally I was just going to change them
 inline after de-referencing them from, say, %1$s to %s but because the
 placeholders for those come after the positional arguments that was a no-
 go because it severely mis-ordered the query. Also, as mentioned earlier,
 they are not escaped.  I would have just interpolated the parameters
 properly inline if it weren't for that.. Also I wanted prepare to continue
 handling the actual escaping of the data... So append and re-point is what
 I went with.

 The code is more complex than I would have liked but because of the need
 to parse argnums and reorder parameters correctly it is what it is...

 My code can be found here: https://github.com/apokalyptik/wordpress-

Ticket URL: <https://core.trac.wordpress.org/ticket/54042#comment:27>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform

More information about the wp-trac mailing list