[wp-trac] [WordPress Trac] #19861: $wpdb->prepare() fails with localized floats

WordPress Trac wp-trac at lists.automattic.com
Wed Feb 1 22:16:09 UTC 2012


#19861: $wpdb->prepare() fails with localized floats
--------------------------+------------------------------
 Reporter:  laotse        |       Owner:
     Type:  defect (bug)  |      Status:  new
 Priority:  normal        |   Milestone:  Awaiting Review
Component:  Database      |     Version:
 Severity:  normal        |  Resolution:
 Keywords:                |
--------------------------+------------------------------

Comment (by laotse):

 I did convert my solution to a patch for the core function. However, there
 seem to be bugs in other parts of Wordpress, which pop up on this less
 tolerant solution. Obviously, some queries are generated with less format
 characters than arguments. Although I made my code somewhat more tolerant
 for this error, I still see various errors.

 I read the instructions about how to set up a test environment and perform
 unit tests. Something I would be able to do, but currently lack the time.
 So if someone, who has this all set up and has experience in using it
 would apply the patch and tell me about the results, I'd gladly assist in
 hunting bugs.

 Since I did not see any instructions as to where to upload my patch, I
 include it in this comment. The patch does 2 things. It defines a
 debugging function 'debug_log' in wp-config.php. This is just debugging
 and shall be removed later or be replaced by some canonical mechanism, if
 it exists. The other change is the replacement for wpdb::prepare().


 {{{
 diff --git a/wp-config.php b/wp-config.php
 index 8a883bf..c24267d 100644
 --- a/wp-config.php
 +++ b/wp-config.php
 @@ -111,7 +113,20 @@ define ('WPLANG', 'de_DE');
   * It is strongly recommended that plugin and theme developers use
 WP_DEBUG
   * in their development environments.
   */
 -define('WP_DEBUG', false);
 +define('WP_DEBUG', true);
 +if(WP_DEBUG === true){
 +       define('WP_DEBUG_DISPLAY', false);
 +       define('WP_DEBUG_LOG', true);
 +       function debug_log( $mMessage ){
 +               if( is_array( $mMessage ) || is_object( $mMessage )){
 +                       error_log( print_r( $mMessage, true ));
 +               } else {
 +                       error_log( $mMessage );
 +               }
 +       }
 +} else {
 +       function debug_log($mMessage) {}
 +}

  /* That's all, stop editing! Happy blogging. */

 diff --git a/wp-content/themes/tantra/header.php b/wp-
 content/themes/tantra/header.php
 index 817c3e0..e3417e5 100644
 --- a/wp-includes/wp-db.php
 +++ b/wp-includes/wp-db.php
 @@ -890,7 +890,7 @@ class wpdb {
          */
         function prepare( $query = null ) { // ( $query, *$args )
                 if ( is_null( $query ) )
 -                       return;
 +                       return null;

                 $args = func_get_args();
                 array_shift( $args );
 @@ -899,9 +899,92 @@ class wpdb {
                         $args = $args[0];
                 $query = str_replace( "'%s'", '%s', $query ); // in case
 someone mistakenly already singlequoted it
                 $query = str_replace( '"%s"', '%s', $query ); //
 doublequote unquoting
 -               $query = preg_replace( '|(?<!%)%s|', "'%s'", $query ); //
 quote the strings, avoiding escaped strings like %%s
 -               array_walk( $args, array( &$this, 'escape_by_ref' ) );
 -               return @vsprintf( $query, $args );
 +               $aParts = preg_split('/(%[^%])/', $query, null,
 PREG_SPLIT_DELIM_CAPTURE);
 +               $sResult = "";
 +               $pValue = null;
 +               foreach ($aParts as $sPart){
 +                       if(mb_strlen($sPart) != 2 || $sPart[0] != "%"){
 +                               // literal string to copy
 +                               $sResult .= $sPart;
 +                       } else {
 +                               if(count($args) <= 0){
 +                                       // ran out of arguments
 +                                       debug_log(__FILE__ . " (" .
 __LINE__ . ") wpdb::prepare(): provided more format codes than
 arguments!");
 +                                       return false;
 +                               }
 +                               $mArg = array_shift($args);
 +                               switch($sPart[1]) {
 +                                       // %% cannot happen due to the
 delimiter pattern
 +                                       default:
 +                                               debug_log(__FILE__ . " ("
 . __LINE__ . ") wpdb::prepare(): unknown qualifier '".$sPart."'!");
 +                                               // unknown qualifier
 +                                               return false;
 +                                       case 's':
 +                                               if(!is_null($mArg) &&
 !is_string($mArg)){
 +                                                       return false;
 +                                               }
 +                                               $sResult .=
 (is_null($mArg))? 'NULL' : "'" . $this->_real_escape($mArg) . "'";
 +                                               break;
 +                                       case 'd':
 +                                               if(!is_null($mArg) &&
 is_string($mArg)){
 +
 if(!preg_match('/^\d+$/',$mArg)){
 +                                                               // string
 is not an integer representation
 +
 debug_log(__FILE__ . " (" . __LINE__ . ") wpdb::prepare(): string
 '".$mArg."' is no integer!");
 +                                                               return
 false;
 +                                                       }
 +                                                       $mArg =
 intval($mArg);
 +                                               }
 +                                               if(!is_null($mArg) &&
 !is_int($mArg)){
 +                                                       // parameter is
 not int
 +                                                       debug_log(__FILE__
 . " (" . __LINE__ . ") wpdb::prepare(): parameter is no integer!");
 +                                                       return false;
 +                                               }
 +                                               $sResult .=
 (is_null($mArg))? 'NULL' : sprintf("%d",$mArg);
 +                                               break;
 +                                       case 'f':
 +                                               if(!is_null($mArg) &&
 is_string($mArg)){
 +
 if(!is_string($pLValue)){
 +                                                               // lazy
 initialization of a pattern for matching localized floats
 +                                                               $aLocale =
 localeconv();
 +                                                               $pSep =
 ($aLocale['mon_thousands_sep'] == '.')? '\.' :
 $aLocale['mon_thousands_sep'];
 +                                                               $pDot =
 ($aLocale['mon_decimal_point'] == '.')? '\.' :
 $aLocale['mon_decimal_point'];
 +                                                               $pLValue =
 '/^(:?\d+(:?'.$pSep.'\d+)*(:?'.$pDot.'\d*)?|'.$pDot.'\d+)$/';
 +                                                       }
 +
 if(preg_match($pLValue,$mArg)){
 +                                                               // looks
 like a server locale formatted float string
 +                                                               // we have
 $aLocale, since we have $pLValue!
 +                                                               $mArg =
 str_replace($aLocale['mon_thousands_sep'], '', $mArg);
 +                                                               $mArg =
 str_replace($aLocale['mon_decimal_point'], '.', $mArg);
 +                                                               $mArg =
 floatval($mArg);
 +                                                       }
 elseif(preg_match('/^(:?\d+(:?\.\d*)|\.\d+)$/',$mArg)){
 +                                                               // looks
 like an internal float string
 +                                                               $mArg =
 floatval($mArg);
 +                                                       } else {
 +                                                               // looks
 pretty unknown
 +
 debug_log(__FILE__ . " (" . __LINE__ . ") wpdb::prepare(): string
 '".$mArg."' is no float!");
 +                                                               return
 false;
 +                                                       }
 +                                               }
 +                                               if(!is_null($mArg) &&
 !is_float($mArg)){
 +                                                       // this is no
 float
 +                                                       debug_log(__FILE__
 . " (" . __LINE__ . ") wpdb::prepare(): parameter is no float!");
 +                                                       return false;
 +                                               }
 +                                               // SQL necessarily
 requires a decimal dot and no 1000 seps
 +                                               // %F is expected to
 provide this format
 +                                               $sResult .=
 (is_null($mArg))? 'NULL' : sprintf("%F",$mArg);
 +                                               break;
 +                               } // end switch(qualifier)
 +                       } // end if(string or qualifier)
 +               } // end foreach
 +               // check consistence
 +               if(count($args) > 0){
 +                       // more arguments than format codes
 +                       debug_log(__FILE__ . " (" . __LINE__ . ")
 wpdb::prepare(): provided less format codes than arguments in query:
 '".$query."'");
 +                       // should strictly be false, but some core
 functions are broken!
 +                       return $sResult;
 +               }
 +               return $sResult;
         }

         /**
 }}}

-- 
Ticket URL: <http://core.trac.wordpress.org/ticket/19861#comment:3>
WordPress Trac <http://core.trac.wordpress.org/>
WordPress blogging software


More information about the wp-trac mailing list