Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix problem with floatvalue-scores in PHP 5.4 #189

Merged
merged 5 commits into from

4 participants

Max Beutel Nicolas Favre-Felix Lars Strojny Michael Grunder
Max Beutel

Due to a bug/weird behaviour in PHP 5.4 with _php_math_number_format float values were transformed to invalid values.

Test script:

$redis = new Redis();
$redis->connect('redis01');
$redis->zAdd('foo', 1337706476.8469, 'VAL');

Redis command sent with rev. f8f552e

"ZADD" "foo" "1" "VAL"

Expected command (at least within a certain precision range)

"ZADD" "foo" "1337706476.84689999" "VAL"

This patch circumvents the problem by not using _php_math_number_format at all and using spprintf which is also used internally in _php_math_number_format for getting the string length of the input.

See https://bugs.php.net/bug.php?id=62112
PHP Version: PHP 5.4.1 on Debian Squeeze

Max Beutel

Other functions like incrbyfloat might be affected as well because _php_math_number_format is also used in library.c which I didnt notice before, we only use zAdd.

I think it would make sense to replace all occurences of _php_math_number_format with spprintf, if you are ok with this solution proposed?

Nicolas Favre-Felix
Owner

Hello Max,

I think this is how I had implemented it in the first place, but there were issues with several locales, notably French which would use a comma instead of a period as a decimal separator.
Have you tried your change with these?

Nicolas

Max Beutel

Hi, I think you are right, that is an issue with sprintf and the like... I updated the pull request, we can use _php_math_number_format_ex in PHP 5.4 and give the thousand separator a zero length, then the function returns a correct value.

Max Beutel

This changes breaks the tests, didn´t check it again unter PHP 5.4, will fix it soon.

Nicolas Favre-Felix
Owner

That looks pretty good, I'll try it tonight.

Max Beutel

This should work now, I used the API version for comparision, thats also used in library.c. Thanks a lot to @lstrojny for help!

Lars Strojny

Can we get some feedback here?

Michael Grunder

Sorry guys,

It looks fine to me. I'll clone your fork and do some testing to see if anything breaks on various versions of PHP.

Thanks,
Mike

Lars Strojny

Thanks!

Michael Grunder michael-grunder referenced this pull request from a commit
Michael Grunder michael-grunder Use a custom method to encode float values, depending on our PHP vers…
…ion.

Many thanks to @maxbeutel and @lstrojny for reporting this issue and
coming up with a resolution!

This is related to fix/pull #189
b105920
Michael Grunder

Hey guys,

I have incorporated your changes into a new branch php-numencode that I will merge to the master branch after a bit more testing (over the weekend). So far it is working great for me. @nicolasff had some concerns about different locales, so I will test that specifically.

Apologies for not simply merging your changes, but I wasn't exactly sure of the process to manually merge changes within git, so I just typed them in.

Cheers!
Mike

Lars Strojny

The patch uses PHP's number_format() which is not locale aware.

Michael Grunder

@lstrojny I think this is OK though, because it's using \0 as the thousands separator, which (for the different versions of PHP I've tried this on) works.

Lars Strojny

Yep, it may not be locale aware. Using \0 as thousand separator no longer works with 5.4 though, that’s all the patch is about.

Michael Grunder

Man, i'm sorry. I must be confused about something. I tested with 5.4 and 5.3.10 and both work properly for me, whereas the described bug occures for me without the patch.

What am I missing?

Lars Strojny

Nothing. The patch fixes the issue with PHP 5.4 and we use it in production for a few weeks now.

Michael Grunder

Cool. I have since figured out how to manually merge in git, so I'll delete the branch I did and import the changes from the pull request so proper credit is given. :)

Cheers guys,
Mike

Lars Strojny

Any news?

Michael Grunder

I added the InterNations upstream and manually merged into a new branch. If it looks good to @nicolasff we'll merge into the master.

Lars Strojny

Any news? This pull requests has been sitting waiting for a while now, could we get it merged. If you need anything from us, please let us know.

Michael Grunder michael-grunder merged commit c12a873 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 31 additions and 6 deletions.
  1. +2 −4 library.c
  2. +12 −0 library.h
  3. +1 −2  redis.c
  4. +16 −0 tests/TestRedis.php
6 library.c
View
@@ -259,8 +259,7 @@ redis_cmd_format_static(char **ret, char *keyword, char *format, ...) {
case 'f':
case 'F': {
double d = va_arg(ap, double);
- dbl_str = _php_math_number_format(d, 8, '.', '\x00');
- dbl_len = strlen(dbl_str);
+ REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d)
smart_str_append_long(&buf, dbl_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, dbl_str, dbl_len);
@@ -328,8 +327,7 @@ redis_cmd_format(char **ret, char *format, ...) {
case 'F':
case 'f': {
double d = va_arg(ap, double);
- dbl_str = _php_math_number_format(d, 8, '.', '\x00');
- dbl_len = strlen(dbl_str);
+ REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, d)
smart_str_append_long(&buf, dbl_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
smart_str_appendl(&buf, dbl_str, dbl_len);
12 library.h
View
@@ -42,3 +42,15 @@ redis_key_prefix(RedisSock *redis_sock, char **key, int *key_len TSRMLS_DC);
PHPAPI int
redis_unserialize(RedisSock *redis_sock, const char *val, int val_len, zval **return_value TSRMLS_DC);
+#if ZEND_MODULE_API_NO >= 20100000
+#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
+ char dbl_decsep; \
+ dbl_decsep = '.'; \
+ dbl_str = _php_math_number_format_ex(dbl, 8, &dbl_decsep, 1, NULL, 0); \
+ dbl_len = strlen(dbl_str);
+#else
+#define REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, dbl) \
+ dbl_str = _php_math_number_format(dbl, 8, '.', '\x00'); \
+ dbl_len = strlen(dbl_str);
+#endif
+
3  redis.c
View
@@ -3662,8 +3662,7 @@ PHP_METHOD(Redis, zAdd) {
/* add score */
score = Z_DVAL_P(z_args[i]);
- dbl_str = _php_math_number_format(score, 8, '.', '\x00');
- dbl_len = strlen(dbl_str);
+ REDIS_DOUBLE_TO_STRING(dbl_str, dbl_len, score)
smart_str_appendc(&buf, '$');
smart_str_append_long(&buf, dbl_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
16 tests/TestRedis.php
View
@@ -1794,6 +1794,22 @@ public function testZX() {
$this->redis->delete('key2');
$this->redis->delete('key3');
+ $this->redis->zadd('key1', 2000.1, 'one');
+ $this->redis->zadd('key1', 3000.1, 'two');
+ $this->redis->zadd('key1', 4000.1, 'three');
+
+ $ret = $this->redis->zRange('key1', 0, -1, TRUE);
+ $this->assertTrue(count($ret) === 3);
+ $retValues = array_keys($ret);
+
+ $this->assertTrue(array('one', 'two', 'three') === $retValues);
+
+ // + 0 converts from string to float OR integer
+ $this->assertTrue(is_float($ret['one'] + 0));
+ $this->assertTrue(is_float($ret['two'] + 0));
+ $this->assertTrue(is_float($ret['three'] + 0));
+
+ $this->redis->delete('key1');
// ZREMRANGEBYRANK
$this->redis->zAdd('key1', 1, 'one');
Something went wrong with that request. Please try again.