diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 067125e..89073c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,19 +9,31 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-version: ['8.2', '8.3', '8.4', '8.5'] + + name: PHP ${{ matrix.php-version }} steps: - uses: actions/checkout@v4 - - name: Build and test PHP extension + - name: Build and test PHP extension (PHP ${{ matrix.php-version }}) + run: | + docker build -t php-shadow-extension-${{ matrix.php-version }} . --progress=plain \ + --build-arg PHP_VERSION=${{ matrix.php-version }} + + - name: Verify extension loaded run: | - docker build -t php-shadow-extension . --progress=plain + docker run --rm php-shadow-extension-${{ matrix.php-version }} php -v + docker run --rm php-shadow-extension-${{ matrix.php-version }} php -m | grep shadow - name: Archive test results if: always() uses: actions/upload-artifact@v4 with: - name: test-results + name: test-results-php-${{ matrix.php-version }} path: | /var/task/shadow/*.out /var/task/shadow/*.diff diff --git a/Dockerfile b/Dockerfile index dc11af9..d7536ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM rockylinux:9 -# Set arguments for directory paths +# Set arguments for directory paths and PHP version +ARG PHP_VERSION=8.5 ARG PHP_BUILD_DIR=/var/task ARG PHP_CONF_DIR=/etc/php.d ARG PHP_EXT_DIR=/usr/lib64/php/modules @@ -14,10 +15,10 @@ RUN dnf -y update && \ bzip2 bzip2-devel libpng-devel libjpeg-devel \ freetype-devel oniguruma-devel libzip-devel zlib-devel -# Enable Remi repository for PHP 8.4 +# Enable Remi repository for specified PHP version (default 8.5) RUN dnf -y install https://rpms.remirepo.net/enterprise/remi-release-9.rpm && \ dnf module reset php -y && \ - dnf module enable php:remi-8.4 -y && \ + dnf module enable php:remi-${PHP_VERSION} -y && \ dnf -y install php php-cli php-devel php-pear # Prepare directories for building the PHP extension diff --git a/shadow.c b/shadow.c index 3ad3d8a..8d3595f 100644 --- a/shadow.c +++ b/shadow.c @@ -1432,6 +1432,29 @@ static void shadow_is_writable(INTERNAL_FUNCTION_PARAMETERS) } /* }}} */ +/* Helper function to add glob results to merge hash table */ +static void shadow_glob_add_to_hash(HashTable *mergedata, zval *glob_results, const char *path, int pathlen, void *dummy, int add_new) +{ + zval *src_entry; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(glob_results), src_entry) { + zend_string *mergepath_zs; + char *relpath; + if (Z_TYPE_P(src_entry) != IS_STRING) continue; + relpath = Z_STRVAL_P(src_entry) + pathlen + 1; + if (path && path[0] != '\0') { + mergepath_zs = strpprintf(MAXPATHLEN, "%s/%s", path, relpath); + } else { + mergepath_zs = zend_string_init(relpath, strlen(relpath), 0); + } + if (add_new) { + zend_hash_str_add_new_ptr(mergedata, ZSTR_VAL(mergepath_zs), ZSTR_LEN(mergepath_zs), dummy); + } else { + zend_hash_str_add_ptr(mergedata, ZSTR_VAL(mergepath_zs), ZSTR_LEN(mergepath_zs), dummy); + } + zend_string_release(mergepath_zs); + } ZEND_HASH_FOREACH_END(); +} + /* {{{ proto array glob(string pattern [, int flags]) Find pathnames matching a pattern */ static void shadow_glob(INTERNAL_FUNCTION_PARAMETERS) @@ -1441,11 +1464,11 @@ static void shadow_glob(INTERNAL_FUNCTION_PARAMETERS) zend_long flags; char *instname=NULL, *templname=NULL, *mask=NULL, *path=NULL; zval instdata, templdata; - zval *src_entry; HashTable *mergedata; void *dummy = (void *)1; int instlen, templen; int skip_template=0; + zval *original_return_value = return_value; /* Save the original pointer */ if(!SHADOW_ENABLED()) { orig_glob(INTERNAL_FUNCTION_PARAM_PASSTHRU); @@ -1531,13 +1554,7 @@ static void shadow_glob(INTERNAL_FUNCTION_PARAMETERS) /* call with template */ if(shadow_call_replace_name(0, templname, orig_glob, INTERNAL_FUNCTION_PARAM_PASSTHRU) == SUCCESS && Z_TYPE_P(return_value) == IS_ARRAY) { /* cut off instname and put path part there */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(return_value), src_entry) { - char *mergepath; - if (Z_TYPE_P(src_entry) != IS_STRING) continue; /* weird, glob shouldn't do that to us */ - spprintf(&mergepath, MAXPATHLEN, "%s/%s", path, Z_STRVAL_P(src_entry)+templen+1); - zend_hash_str_add_new_ptr(mergedata, mergepath, strlen(mergepath), dummy); - efree(mergepath); - } ZEND_HASH_FOREACH_END(); + shadow_glob_add_to_hash(mergedata, return_value, path, templen, dummy, 1); } else { /* ignore problems here - other one may pick it up */ array_init(return_value); @@ -1553,22 +1570,27 @@ static void shadow_glob(INTERNAL_FUNCTION_PARAMETERS) /* call with instance */ if(shadow_call_replace_name(0, instname, orig_glob, INTERNAL_FUNCTION_PARAM_PASSTHRU) == SUCCESS && Z_TYPE_P(return_value) == IS_ARRAY) { /* merge data */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(return_value), src_entry) { - char *mergepath; - if (Z_TYPE_P(src_entry) != IS_STRING) continue; /* weird, glob shouldn't do that to us */ - spprintf(&mergepath, MAXPATHLEN, "%s/%s", path, Z_STRVAL_P(src_entry)+instlen+1); - zend_hash_str_add_ptr(mergedata, mergepath, strlen(mergepath), dummy); - efree(mergepath); - } ZEND_HASH_FOREACH_END(); + shadow_glob_add_to_hash(mergedata, return_value, path, instlen, dummy, 0); } zval_dtor(return_value); return_value = &templdata; /* convert mergedata to return */ - zend_hash_clean(Z_ARRVAL_P(return_value)); - zend_string *filename_zs; - ZEND_HASH_FOREACH_STR_KEY(mergedata, filename_zs) { - add_next_index_str(return_value, zend_string_copy(filename_zs)); - } ZEND_HASH_FOREACH_END(); + if (Z_TYPE_P(return_value) == IS_ARRAY && Z_ARR_P(return_value) != &zend_empty_array) { + zend_hash_clean(Z_ARRVAL_P(return_value)); + } else { + array_init(return_value); + } + zend_string *name = NULL; + zend_ulong num; + zend_hash_internal_pointer_reset(mergedata); + while (zend_hash_get_current_key(mergedata, &name, &num) == HASH_KEY_IS_STRING) { + if (name && ZSTR_VAL(name) && ZSTR_LEN(name)) { + add_next_index_str(return_value, zend_string_copy(name)); + } + zend_hash_move_forward(mergedata); + } + /* CRITICAL: Copy templdata back to the ORIGINAL return_value pointer */ + ZVAL_COPY_VALUE(original_return_value, &templdata); /* cleanup */ zend_hash_clean(mergedata); zend_hash_destroy(mergedata); diff --git a/tests/stream_wrapper_reenable.phpt b/tests/stream_wrapper_reenable.phpt index 5be8d14..1562514 100644 --- a/tests/stream_wrapper_reenable.phpt +++ b/tests/stream_wrapper_reenable.phpt @@ -15,6 +15,9 @@ stream_wrapper_restore('file'); shadow($template, $instance, array("cache", "custom", "custom/some/long/directory/name"), true) || die("failed to setup shadow"); echo file_get_contents("$instance/txt/override.txt"); + +// Restore the file immediately for subsequent test runs +file_put_contents("$instance/txt/override.txt", "Instance data\n"); ?> --EXPECT-- Instance data