Uniqush is a free and open source software system which provides a unified push service for server side notification to apps on mobile devices.
lighttpd 1.4.69, PHP-fpm 8.2.3, debian 11, 10.5.18-MariaDB, redis-cache-pro - compression lz4 All packages are installed via apt
php info igbinary support - enabled igbinary version 3.2.13 redis - Available serializers php, json, igbinary session - Registered serializer handlers php_serialize php php_binary igbinary msgpack session.serialize_handler igbinary igbinary
php -i | grep igbinary /etc/php/8.2/cli/conf.d/20-igbinary.ini, igbinary igbinary support => enabled igbinary version => 3.2.13 igbinary APCu serializer ABI => 0 igbinary session support => yes igbinary.compact_strings => On => On igbinary support => yes Available serializers => php, json, igbinary Registered serializer handlers => php_serialize php php_binary igbinary msgpack session.serialize_handler => igbinary => igbinary
If you use php as the default serializer in the configuration, then everything works very quickly. But I read that igbinary_serialize works several times faster?
define( 'WP_REDIS_CONFIG', [ 'token' => '', 'host' => '127.0.0.1', 'port' => 6379, 'database' => 5, 'maxttl' => 3600 * 24 * 7, 'timeout' => 0.5, 'read_timeout' => 0.5, 'retry_interval' => 10, 'retries' => 3, //'serializer' => 'igbinary', 'compression' => 'lz4', 'async_flush' => true, 'client' => 'phpredis', 'split_alloptions' => true, 'shared' => true, 'prefetch' => true, 'debug' => false, 'save_commands' => false, ]); define('WP_REDIS_DISABLED', getenv('WP_REDIS_DISABLED') ?: false);
But when I turn on 'serializer' = > 'igbinary', in the config, I get a critical error in Wordpress.
2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:PHP message: PHP Fatal error: Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, string given in /www/free/wp-includes/class-wp-roles.php:291 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:Stack trace: 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#0 /www/free/wp-includes/class-wp-roles.php(291): array_keys() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#1 /www/free/wp-includes/class-wp-roles.php(332): WP_Roles->init_roles() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#2 /www/free/wp-includes/class-wp-roles.php(91): WP_Roles->for_site() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#3 /www/free/wp-settings.php(542): WP_Roles->__construct() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#4 /www/free/wp-config.php(101): require_once('...') 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#5 /www/free/wp-load.php(50): require_once('...') 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#6 /www/free/wp-admin/admin-post.php(19): require_once('...') 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#7 {main} 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr: thrown in /www/free/wp-includes/class-wp-roles.php on line 291; PHP message: PHP Fatal error: Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, string given in /www/free/wp-includes/class-wp-roles.php:291 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:Stack trace: 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#0 /www/free/wp-includes/class-wp-roles.php(291): array_keys() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#1 /www/free/wp-includes/class-wp-roles.php(332): WP_Roles->init_roles() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#2 /www/free/wp-includes/class-wp-roles.php(91): WP_Roles->for_site() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#3 /www/free/wp-includes/capabilities.php(997): WP_Roles->__construct() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#4 /www/free/wp-includes/class-wp-user.php(510): wp_roles() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#5 /www/free/wp-includes/class-wp-user.php(877): WP_User->get_role_caps() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#6 /www/free/wp-includes/class-wp-user.php(178): WP_User->for_site() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#7 /www/free/wp-includes/pluggable.php(109): WP_User->init() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#8 /www/free/wp-includes/pluggable.php(737): get_user_by() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#9 /www/free/wp-includes/class-wp-hook.php(308): wp_validate_auth_cookie() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#10 /www/free/wp-includes/plugin.php(205): WP_Hook->apply_filters() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#11 /www/free/wp-includes/user.php(3604): apply_filters() 2023-03-08 21:09:47: (mod_fastcgi.c.449) FastCGI-stderr:#12 /www/free/wp-includes/p... 2023-03-08 21:09:49: (mod_fastcgi.c.449) FastCGI-stderr:PHP message: PHP Fatal error: Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, string given in /www/free/wp-includes/class-wp-roles.php:291
But when I turn on 'serializer' = > 'igbinary', in the config, I get a critical error in Wordpress.
Did you restart apache?
Does the issue happen with an empty cache?
Are you running more than one server? If so, did you upgrade all of the servers? Does the issue happen all of the time?
What redis-cache-pro version is this?
Which of the advanced options are you using? https://github.com/rhubarbgroup/redis-cache/blob/77fcd714a58806819d85c4a017a8588e98f5fec2/README.md - WP_REDIS_SERIALIZER or WP_REDIS_IGBINARY? (I assume it's WP_REDIS_SERIALIZER and somehow extracted from WP_REDIS_CONFIG)
Does the issue happen with an empty cache?
Never mind, it isn't that, it'd return false if igbinary is loaded.
What are the values of WP_REDIS_*
constants you have set up or the cache configuration?
What were your original settings (e.g. did compression change?)
https://github.com/rhubarbgroup/redis-cache/blob/83c771dfc5bedc807133330080598553deb65dd9/includes/object-cache.php#L2691-L2698
Oh. Actually, it turned out to be obvious. It's a bug in redis-cache-pro. redis-cache-pro is unconditionally trying to unserialize the data it retrieved from redis (PHP serialize()) with igbinary (igbinary unserialize()). That fails, so it returns the original string, probably of the form a:...{...}
It wouldn't happen in installations that started with an empty redis cache or cleared it (not sure if that would cause issues for you)
It should be doing something similar to phpredis instead, and checking if substr( $original, 0, 4) === "\0\0\0\0"
or something similar, but it isn't.
case REDIS_SERIALIZER_IGBINARY:
#ifdef HAVE_REDIS_IGBINARY
/*
* Check if the given string starts with an igbinary header.
*
* A modern igbinary string consists of the following format:
*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
* | header (4) | type (1) | ... (n) | NUL (1) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*
* With header being either 0x00000001 or 0x00000002
* (encoded as big endian).
*
* Not all versions contain the trailing NULL byte though, so
* do not check for that.
*/
if (val_len < 5
|| (memcmp(val, "\x00\x00\x00\x01", 4) != 0
&& memcmp(val, "\x00\x00\x00\x02", 4) != 0))
{
/* This is most definitely not an igbinary string, so do
not try to unserialize this as one. */
break;
}
ret = !igbinary_unserialize((const uint8_t *)val, (size_t)val_len, z_ret);
#endif
break;
Can you create a minimal, self-contained repo or example file to reproduce this issue? (If possible, the an object/array which when serialized with igbinary, results in an array becoming a non-array)
I'm not familiar with where the redis cache is being used in https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-roles.php
I'm also not familiar with https://en-ca.wordpress.org/plugins/redis-cache/ , and a standalone example would be easier to work with
Also, what is the string it's being called with? Can you include it or steps to create a similar string that reproduces the issue?
Fix NEWS.md markdown
Use actions/checkout v3 with Node.js 16
As far as I can see, phan does not have an issue type for @throws annotations on methods that don't actually throw those exceptions (because I guess it might be hard to prove statically).
Yes, that and the fact that user @throws
may be API documentation for subclasses, or user code being incomplete.
I think this might be achieved by keeping two separate lists for @throws that were found in the method's own docblock, and for the ones inherited from an ancestor.
A non-standard @throws never
might work, though I think a class name is usually expected.
Two separate lists makes sense for analyzing the implementers and callers differently.
I have projects set up that rely on symlinks (e.g. where multiple applications depend on the same library as a symlinked folder)
It'd be better to have this as a .phan/config.php setting such as 'ignore_symlinks'
defaulting to false (see src/Phan/Config.php)
Use release version of PHP 8.2 in workflows
Merge pull request #4766 from bobvandevijver/patch-1
Use release version of PHP 8.2 in workflows
the latest release does not attach a phar for direct execution
Fixed by attaching phan.phar and phan.phar.asc built from the 5.4.2 release tag
...
mkdir: cc -I. -I/tmp/pear/temp/igbinary -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/include -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/main -I/tmp/pear/temp/igbinary -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O0 -D_GNU_SOURCE -g -O0 -O2 -Wall -Wpointer-arith -Wcast-align -Wwrite-strings -Wswitch -DZEND_COMPILE_DL_EXT=1 -c /tmp/pear/temp/igbinary/src/php7/igbinary.c -MMD -MF src/php7/igbinary.dep -MT src/php7/igbinary.lo -fPIC -DPIC -o src/php7/.libs/igbinary.o
cannot create directory ‘src/php7/.libs’: File exists
cc -I. -I/tmp/pear/temp/igbinary -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/include -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/main -I/tmp/pear/temp/igbinary -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O0 -D_GNU_SOURCE -g -O0 -O2 -Wall -Wpointer-arith -Wcast-align -Wwrite-strings -Wswitch -DZEND_COMPILE_DL_EXT=1 -c /tmp/pear/temp/igbinary/src/php7/hash_si.c -MMD -MF src/php7/hash_si.dep -MT src/php7/hash_si.lo -fPIC -DPIC -o src/php7/.libs/hash_si.o
/bin/sh /tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/libtool --mode=compile cc -I. -I/tmp/pear/temp/igbinary -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/include -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/main -I/tmp/pear/temp/igbinary -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O0 -D_GNU_SOURCE -g -O0 -O2 -Wall -Wpointer-arith -Wcast-align -Wwrite-strings -Wswitch -DZEND_COMPILE_DL_EXT=1 -c /tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c -o src/php7/hash_si_ptr.lo -MMD -MF src/php7/hash_si_ptr.dep -MT src/php7/hash_si_ptr.lo
cc -I. -I/tmp/pear/temp/igbinary -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/include -I/tmp/pear/temp/pear-build-defaultuserekdIGc/igbinary-3.2.13/main -I/tmp/pear/temp/igbinary -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O0 -D_GNU_SOURCE -g -O0 -O2 -Wall -Wpointer-arith -Wcast-align -Wwrite-strings -Wswitch -DZEND_COMPILE_DL_EXT=1 -c /tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c -MMD -MF src/php7/hash_si_ptr.dep -MT src/php7/hash_si_ptr.lo -fPIC -DPIC -o src/php7/.libs/hash_si_ptr.o
In file included from /tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c:20:
/tmp/pear/temp/igbinary/src/php7/hash_ptr.h:34:9: error: unknown type name 'zend_uintptr_t'
34 | zend_uintptr_t key; /**< The key: The address of a pointer, casted to an int (won't be dereferenced). */
| ^~~~~~~~~~~~~~
/tmp/pear/temp/igbinary/src/php7/hash_ptr.h:102:64: error: unknown type name 'zend_uintptr_t'
102 | size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value);
| ^~~~~~~~~~~~~~
/tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c:25:47: error: unknown type name 'zend_uintptr_t'; did you mean 'tsrm_uintptr_t'?
25 | inline static uint32_t inline_hash_of_address(zend_uintptr_t ptr) {
| ^~~~~~~~~~~~~~
| tsrm_uintptr_t
/tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c: In function 'hash_si_ptr_rehash':
/tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c:110:39: warning: implicit declaration of function 'inline_hash_of_address' [-Wimplicit-function-declaration]
110 | uint32_t hv = inline_hash_of_address(old_data[i].key) & mask;
| ^~~~~~~~~~~~~~~~~~~~~~
/tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c: At top level:
/tmp/pear/temp/igbinary/src/php7/hash_si_ptr.c:133:64: error: unknown type name 'zend_uintptr_t'
133 | size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value) {
| ^~~~~~~~~~~~~~
make: *** [Makefile:216: src/php7/hash_si_ptr.lo] Error 1
make: *** Waiting for unfinished jobs....
ERROR: `make -j2' failed
caused probably by https://github.com/php/php-src/pull/10597
Release 3.2.14: Fix php 8.3 compatibility error
Fixes #374
Merge pull request #375 from TysonAndre/uintptr_t-cleanup
Release 3.2.14: Fix php 8.3 compatibility error
Fixes #374
Fixes #374
Release 3.2.14: Fix php 8.3 compatibility error
Fixes #374
Even in php 7.0, it's typedef uintptr_t zend_uintptr_t;
in Zend/zend_types.h, so it's straightforward to change, even if headers were meant to be public (though src/php7/hash_ptr.h is an internal implementation detail)
https://github.com/google/cwisstable (or any other library that allows customization while supporting C compilers without C++ requirement)
CWISS_AllocPolicy should be used to make it use emalloc/efree
E.g. for ref_props when unserializing typed properties. Followup to #371
This issue ultimately boils down to the usage of unhashed integers as keys, which is an issue that crops up everywhere. The only real solution is built-in integer HashTable key hashing like siphash or similar. Does it really make sense to add extra APIs for this case instead of addressing the underlying problem?
Changing the hashing everywhere would probably negatively affect benchmarks for common use cases (and worsen cache locality for cases such as associative arrays with a few gaps)
As an alternative, @iluuu1994 mentioned https://github.com/google/cwisstable existed in another PR https://github.com/google/cwisstable https://github.com/google/cwisstable#compatibility-warnings
uintptr_t -> void*
with no garbage collection?CWISS_AllocPolicy
allowing the use of emalloc/efreeThere's use cases such as the destructor and ordering where HashTable would do better.
I haven't tried it yet, but meant to try that for igbinary to see the performance impact for data structures for serialization and unserialization
Getting agreement on what to add to the public API and whether it's necessary seems like it might be difficult when HashTable is good enough for use cases when used properly (for small amounts of data, cswisstable wasn't faster, but large amounts would be noticeable)
The following code:
<?php
$x = FFI::new('int');
array_walk($x, function($x) { echo "test\n"; });
Resulted in this output:
segmentation fault (core dumped)
valgrind output:
==73944== Process terminating with default action of signal 11 (SIGSEGV)
==73944== Bad permissions for mapped region at address 0x1D0352A
==73944== at 0xA8A598: zend_hash_iterator_add (zend_hash.c:503)
==73944== by 0x840B07: php_array_walk (array.c:1286)
==73944== by 0x84183B: zif_array_walk (array.c:1407)
==73944== by 0xAB4919: ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER (zend_vm_execute.h:1250)
==73944== by 0xB2B883: execute_ex (zend_vm_execute.h:55975)
==73944== by 0xB310D5: zend_execute (zend_vm_execute.h:60343)
==73944== by 0xA73DE2: zend_execute_scripts (zend.c:1780)
==73944== by 0x9CD491: php_execute_script (main.c:2480)
==73944== by 0xBEDD26: do_cli (php_cli.c:964)
==73944== by 0xBEE9E5: main (php_cli.c:1333)
But I expected this output instead:
// blank output
To fix this:
array_walk expects get_properties
to return a non-empty refcounted array. Options include:
get_properties_for
handler alongside the get_properties
handler to return nullzend_hash_num_elements(target_hash) == 0
and return early while continuing to free userdata
- this should avoid this crash?// ext/standard/array.c
// static int php_array_walk(
// php_array_walk_context *context, zval *array, zval *userdata, int recursive)
zend_hash_internal_pointer_reset_ex(target_hash, &pos);
ht_iter = zend_hash_iterator_add(target_hash, pos); // this tries to modify the immutable array
// ext/ffi/ffi.c
static HashTable *zend_fake_get_properties(zend_object *obj) /* {{{ */
{
return (HashTable*)&zend_empty_array; // This is a const immutable array
}
/* }}} */
7.4-8.3-dev
Any
Avoid crash for reset/end/next/prev() on ffi classes (#9711)
(And any PECLs returning zend_empty_array
in the handler->get_properties
overrides)
Closes GH-9697
This is similar to the fix used in d9651a941915eb5fb5ad557090b65256fd8509b6 for array_walk.
This should make it safer for php-src (and PECLs, long-term) to return
the empty immutable array in handler->get_properties
to avoid wasting memory.
See https://github.com/php/php-src/issues/9697#issuecomment-1273613175
The only possible internal iterator position for the empty array is at the end
of the empty array (nInternalPointer=0).
The zend_hash*del*
helpers will always set nInternalPointer to 0 when an
array becomes empty,
regardless of previous insertions/deletions/updates to the array.
Merge branch 'PHP-8.1' into PHP-8.2
Merge branch 'PHP-8.2'
Avoid crash for reset/end/next/prev() on ffi classes (#9711)
(And any PECLs returning zend_empty_array
in the handler->get_properties
overrides)
Closes GH-9697
This is similar to the fix used in d9651a941915eb5fb5ad557090b65256fd8509b6 for array_walk.
This should make it safer for php-src (and PECLs, long-term) to return
the empty immutable array in handler->get_properties
to avoid wasting memory.
See https://github.com/php/php-src/issues/9697#issuecomment-1273613175
The only possible internal iterator position for the empty array is at the end
of the empty array (nInternalPointer=0).
The zend_hash*del*
helpers will always set nInternalPointer to 0 when an
array becomes empty,
regardless of previous insertions/deletions/updates to the array.
Merge branch 'PHP-8.1' into PHP-8.2