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

@maxbeutel

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

@maxbeutel

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?

@nicolasff
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

@maxbeutel

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.

@maxbeutel

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

@nicolasff
Owner

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

@maxbeutel

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

@lstrojny

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

@lstrojny

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

@lstrojny

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.

@lstrojny

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?

@lstrojny

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

@lstrojny

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.

@lstrojny

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
View
6 library.c
@@ -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);
View
12 library.h
@@ -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
+
View
3  redis.c
@@ -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);
View
16 tests/TestRedis.php
@@ -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.