diff --git a/.github/matrix.php b/.github/matrix.php index 111aebfb94d1..7970442d7050 100644 --- a/.github/matrix.php +++ b/.github/matrix.php @@ -61,7 +61,6 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re $test_macos = in_array('CI: macOS', $labels, true); $test_msan = in_array('CI: MSAN', $labels, true); $test_opcache_variation = in_array('CI: Opcache Variation', $labels, true); - $test_pecl = in_array('CI: PECL', $labels, true); $test_solaris = in_array('CI: Solaris', $labels, true); $test_windows = in_array('CI: Windows', $labels, true); @@ -137,9 +136,6 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re if ($all_jobs || $test_opcache_variation) { $jobs['OPCACHE_VARIATION'] = true; } - if (($all_jobs && $ref === 'master') || $test_pecl) { - $jobs['PECL'] = true; - } if (version_compare($php_version, '8.6', '>=') && ($all_jobs || $test_solaris)) { $jobs['SOLARIS'] = true; } diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index af25523805da..7b1850aa1075 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -827,114 +827,6 @@ jobs: uses: ./.github/actions/test-libmysqlclient - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files - PECL: - if: ${{ fromJson(inputs.branch).jobs.PECL }} - runs-on: ubuntu-24.04 - steps: - - name: git checkout PHP - uses: actions/checkout@v6 - with: - path: php - ref: ${{ fromJson(inputs.branch).ref }} - # Used for ccache action - - name: Move .github - run: mv php/.github . - - name: git checkout apcu - uses: actions/checkout@v6 - with: - repository: krakjoe/apcu - path: apcu - - name: git checkout imagick - uses: actions/checkout@v6 - with: - repository: Imagick/imagick - path: imagick - - name: git checkout memcached - uses: actions/checkout@v6 - with: - repository: php-memcached-dev/php-memcached - path: memcached - - name: git checkout redis - if: ${{ false }} - uses: actions/checkout@v6 - with: - repository: phpredis/phpredis - path: redis - - name: git checkout xdebug - uses: actions/checkout@v6 - with: - repository: xdebug/xdebug - path: xdebug - - name: git checkout yaml - uses: actions/checkout@v6 - with: - repository: php/pecl-file_formats-yaml - path: yaml - - name: apt - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - ccache \ - libmemcached-dev \ - imagemagick \ - libmagickwand-dev \ - bison \ - re2c - - name: ccache - uses: ./.github/actions/ccache - with: - name: "${{ github.job }}" - php_directory: php - - name: build PHP - run: | - cd php - ./buildconf --force - ./configure \ - --enable-option-checking=fatal \ - --prefix=/opt/php \ - --enable-cli \ - --disable-all \ - --enable-session \ - --enable-werror - make -j$(/usr/bin/nproc) - sudo make install - - name: build apcu - run: | - cd apcu - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build imagick - run: | - cd imagick - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build memcached - run: | - cd memcached - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build redis - if: ${{ false }} - run: | - cd redis - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build xdebug - run: | - cd xdebug - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build yaml - run: | - cd yaml - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) WINDOWS: if: ${{ fromJson(inputs.branch).jobs.WINDOWS }} strategy: diff --git a/NEWS b/NEWS index e37effff3afe..3c4d6993f46c 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,8 @@ PHP NEWS . Enabled the TAILCALL VM on Windows when compiling with Clang >= 19 x86_64. (henderkes) . Deprecate specifying a nullable return type for __debugInfo(). (timwolla) + . Fixed bug GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF). + (David Carlier) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) @@ -69,6 +71,8 @@ PHP NEWS argument handling now raises TypeError instead of Error. (Weilin Du) . IntlBreakIterator::getLocale() now raises ValueError for invalid locale types. (Weilin Du) + . Fixed MessageFormatter::parse() and parseMessage() returning PHP_INT_MIN + as float rather than int on 64-bit platforms. (Weilin Du) - JSON: . Enriched JSON last error / exception message with error location. @@ -105,6 +109,8 @@ PHP NEWS openssl_x509_parse() output). (StephenWall) . Added TLS session resumption support for streams with new context options and Openssl\Session class. (Jakub Zelenka) + . Added TLS external PSK support for streams with new context options and + Openssl\Psk class. (Jakub Zelenka) - PCNTL: . pcntl_exec() now throws a ValueError if the $args array is not a list @@ -156,6 +162,12 @@ PHP NEWS (Girgias) . Null bytes in session.cookie_path, session.cookie_domain, and session.cache_limiter are now rejected with a warning. (jorgsowa) + . session.cookie_samesite now rejects invalid values with a warning; only + "Strict", "Lax", "None", or "" are accepted. (jorgsowa) + . session.cookie_lifetime now rejects non-integer and out-of-range values + with a warning. (jorgsowa) + . Session file GC now recursively cleans nested subdirectories when + session.save_path uses the dirdepth prefix. (jorgsowa) . Changed defaults of session.use_strict_mode (now 1), session.cookie_httponly (now 1) and session.cookie_samesite (now "Lax"). (jorgsowa) @@ -216,12 +228,21 @@ PHP NEWS null bytes. (Weilin Du) . proc_open() now raises a ValueError when the $cwd argument contains null bytes. (Weilin Du) + . ini_get_all() now includes the built-in default value in the details. + (sebastian) + . Fixed bug GH-22171 (Invalid auth header generation in + http(s) stream wrapper). (David Carlier) - Streams: + . Added new stream errors API including new StreamException, StreamError + classes, StreamErrorStore, StreamErrorMode, StreamErrorCode enums, + stream_last_errors() and stream_clear_errors() functions, error_mode, + error_store and error_handler stream context options and extending some + stream functions with context param. (Jakub Zelenka) . Added so_keepalive, tcp_keepidle, tcp_keepintvl and tcp_keepcnt stream - socket context options. + socket context options. (Jakub Zelenka) . Added so_reuseaddr streams context socket option that allows disabling - address reuse. + address reuse. (Jakub Zelenka) . Fixed bug GH-20370 (User stream filters could violate typed property constraints). (alexandre-daubois) . Allowed filtered streams to be casted as fd for select. (Jakub Zelenka) @@ -231,6 +252,12 @@ PHP NEWS . Fixed bug #49874 (ftell() and fseek() inconsistency when using stream filters). (Jakub Zelenka) +- URI: + . Added Uri\Rfc3986\Uri:getUriType() and Uri\WhatWg\Url:isSpecialScheme(). + (kocsismate) + . Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType(). + (kocsismate) + - Zip: . Fixed ZipArchive callback being called after executor has shut down. (ilutov) diff --git a/SECURITY.md b/SECURITY.md index deb5a7a950a4..24801b3b4e43 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,6 +11,31 @@ Vulnerability reports remain private until published. When published, you will be credited as a contributor, and your contribution will reflect the MITRE Credit System. +# Classification + +Issues commonly reported that are _not_ considered security issues include (but +are not limited to): + +- Invocation of specially crafted, malicious code intended to cause memory + violations. This commonly includes malicious error handlers, destructors or + `__toString()` functions. PHP does not offer sandboxing, and the execution of + untrusted code is always considered unsafe. Such issues are bugs, but not + security issues. They may still be reported, though please avoid reporting + the known issues. + +- Passing malicious arguments to functions clearly not intended to receive + unsanitized values, e.g. `mysqli_query()`. `escapeshellarg()` on the other + hand should clearly be hardened against unsafe inputs. + +- The use of legacy APIs or settings known to be insecure, particularly those + documented as such, or those with a secure alternative. + +- The use of FFI. + +- `open_basedir` or `disable_functions` bypasses. + +- Malicious `unserialize()` inputs. + # Vulnerability Policy Our full policy is described at diff --git a/UPGRADING b/UPGRADING index a3b22d8c0560..95299dd5117f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -40,6 +40,10 @@ PHP 8.6 UPGRADE NOTES . IntlBreakIterator::getLocale() now raises a ValueError when the type is neither Locale::ACTUAL_LOCALE nor Locale::VALID_LOCALE instead of returning false. + . MessageFormatter::parse() and parseMessage() now return PHP_INT_MIN as + int, rather than float, on 64-bit platforms when parsing integer values. + . The $type parameter of IntlBreakIterator::getPartsIterator() has been + changed from string to int to match the underlying implementation. - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is @@ -194,6 +198,8 @@ PHP 8.6 UPGRADE NOTES requests, implementing custom server-side session storage, and controlling session cache behavior. RFC: https://wiki.php.net/rfc/tls_session_resumption + . Added TLS external PSK support for streams with new strem context options: + psk_client_cb and psk_server_cb. This allows setting and receiving PSK. - Phar: . Overriding the getMTime() and getPathname() methods of SplFileInfo now @@ -201,6 +207,10 @@ PHP 8.6 UPGRADE NOTES This makes it possible to override the timestamp and names of files. - Streams: + . Added new stream errors API including new classes, enums, functions and + internal API. It is controlled using error_mode, error_store and + error_handler stream context options. + RFC: https://wiki.php.net/rfc/stream_errors . Added stream socket context option so_reuseaddr that allows disabling address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on Windows. @@ -209,6 +219,12 @@ PHP 8.6 UPGRADE NOTES options. . Allowed casting casting filtered streams as file descriptor for select. +- URI: + . Added Uri\Rfc3986\Uri:getUriType() and Uri\WhatWg\Url:isSpecialScheme(). + RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection + . Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType(). + RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection + ======================================== 3. Changes in SAPI modules ======================================== @@ -268,6 +284,12 @@ PHP 8.6 UPGRADE NOTES when not null, and on failure, gives the error code (one of the EAI_* constants). +- Standard: + . ini_get_all() now includes a "builtin_default_value" element for each + directive when $details is true. It holds the built-in default value of + the directive (or null if it has none), independent of values set in + php.ini, on the command line, or at runtime. + ======================================== 6. New Functions ======================================== @@ -291,9 +313,12 @@ PHP 8.6 UPGRADE NOTES . `clamp()` returns the given value if in range, else return the nearest bound. RFC: https://wiki.php.net/rfc/clamp_v2 + . `stream_last_errors()` and `stream_clear_errors()`. + RFC: https://wiki.php.net/rfc/stream_errors - Zip: . Added ZipArchive::openString() method. + . Added ZipArchive::closeString() method. ======================================== 7. New Classes and Interfaces @@ -303,10 +328,17 @@ PHP 8.6 UPGRADE NOTES . Openssl\OpensslException . Openssl\Session RFC: https://wiki.php.net/rfc/tls_session_resumption + . Openssl\Psk - Standard: . enum SortDirection RFC: https://wiki.php.net/rfc/sort_direction_enum + . StreamError + . StreamException + . enum StreamErrorStore + . enum StreamErrorMode + . enum StreamErrorCode + RFC: https://wiki.php.net/rfc/stream_errors ======================================== 8. Removed Extensions and SAPIs @@ -416,7 +448,10 @@ PHP 8.6 UPGRADE NOTES . Improved performance of str_split(). - URI: - . Reduced allocations when reading RFC3986 IPv6/IPFuture hosts and paths. + . Reduced allocations when reading IPv6/IPFuture hosts and paths with + Uri\Rfc3986\Uri. + . Improved performance and memory consumption when using normalizing + (non-raw) getters on already-normalized URIs with Uri\Rfc3986\Uri. - Zip: . Avoid string copies in ZipArchive::addFromString(). diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 2daa75f798bb..c340ba64833c 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -108,6 +108,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES instead of copying it in zend_call_function(). Currently only a single consumed argument is supported. . Added ZEND_CONTAINER_OF(). + . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead + use php_check_open_basedir() directly. ======================== 2. Build system changes diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index cf74dd8fc147..a4ecb19c85ef 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -733,6 +733,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_SEND_VAR_NO_REF_EX: case ZEND_SEND_REF: case ZEND_SEND_FUNC_ARG: + case ZEND_SEND_PLACEHOLDER: case ZEND_CHECK_FUNC_ARG: if (opline->op2_type == IS_CONST) { opline->result.num = cache_size; @@ -745,6 +746,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx cache_size += sizeof(void *); } break; + case ZEND_CALLABLE_CONVERT_PARTIAL: + opline->op1.num = cache_size; + cache_size += 2 * sizeof(void *); + break; } opline++; } diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index 69c371207ddc..05cdce4fc4cf 100644 --- a/Zend/Optimizer/optimize_func_calls.c +++ b/Zend/Optimizer/optimize_func_calls.c @@ -191,6 +191,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: call--; if (call_stack[call].func && call_stack[call].opline) { zend_op *fcall = call_stack[call].opline; @@ -223,13 +224,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) * At this point we also know whether or not the result of * the DO opcode is used, allowing to optimize calls to * ZEND_ACC_NODISCARD functions. */ - if (opline->opcode != ZEND_CALLABLE_CONVERT) { + if (opline->opcode != ZEND_CALLABLE_CONVERT && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) { opline->opcode = zend_get_call_op(fcall, call_stack[call].func, !RESULT_UNUSED(opline)); } if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level) && call_stack[call].try_inline - && opline->opcode != ZEND_CALLABLE_CONVERT) { + && opline->opcode != ZEND_CALLABLE_CONVERT + && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) { zend_try_inline_call(op_array, fcall, opline, call_stack[call].func); } } diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c index bb80e21a2465..b67f57cf041d 100644 --- a/Zend/Optimizer/zend_call_graph.c +++ b/Zend/Optimizer/zend_call_graph.c @@ -124,6 +124,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32 case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: func_info->flags |= ZEND_FUNC_HAS_CALLS; if (call_info) { call_info->caller_call_opline = opline; @@ -140,6 +141,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32 case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_VAR_NO_REF_EX: case ZEND_SEND_USER: + case ZEND_SEND_PLACEHOLDER: if (call_info) { if (opline->op2_type == IS_CONST) { call_info->named_args = true; diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index b7b118c710c5..2a9353c9b86e 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -596,6 +596,7 @@ static const func_info_t func_infos[] = { F1("stream_get_line", MAY_BE_STRING|MAY_BE_FALSE), F1("stream_resolve_include_path", MAY_BE_STRING|MAY_BE_FALSE), F1("stream_get_wrappers", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + FN("stream_last_errors", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), F1("stream_get_transports", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), #if defined(HAVE_GETTIMEOFDAY) F1("uniqid", MAY_BE_STRING), diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 05d33d3d75fb..2e6cc70ec254 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3903,6 +3903,7 @@ static zend_always_inline zend_result _zend_update_type_info( } break; case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: UPDATE_SSA_TYPE(MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN, ssa_op->result_def); UPDATE_SSA_OBJ_TYPE(zend_ce_closure, /* is_instanceof */ false, ssa_op->result_def); break; diff --git a/Zend/tests/display_error_function_args.phpt b/Zend/tests/display_error_function_args.phpt new file mode 100644 index 000000000000..c28a4a2808b4 --- /dev/null +++ b/Zend/tests/display_error_function_args.phpt @@ -0,0 +1,30 @@ +--TEST-- +Displaying function arguments in errors +--INI-- +error_include_args=On +--FILE-- + "123456789012345678901" . chr(0), "cost" => 4]; +password_hash("test", PASSWORD_BCRYPT, $flags); + +ini_set("error_include_args", "Off"); + +unlink('/'); +password_hash("test", PASSWORD_BCRYPT, $flags); + +?> +--EXPECTF-- +Warning: unlink('/'): %s in %s on line %d + +Warning: password_hash(Object(SensitiveParameterValue), '2y', Array): The "salt" option has been ignored, since providing a custom salt is no longer supported in %s on line %d + +Warning: unlink(/): %s in %s on line %d + +Warning: password_hash(): The "salt" option has been ignored, since providing a custom salt is no longer supported in %s on line %d diff --git a/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt b/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt deleted file mode 100644 index 74e36a9ad0df..000000000000 --- a/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -First class callable error: more than one argument ---FILE-- - ---EXPECTF-- -Fatal error: Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders in %s on line %d diff --git a/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt b/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt deleted file mode 100644 index efbd13b7593b..000000000000 --- a/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -First class callable error: non-variadic placeholder ---FILE-- - ---EXPECTF-- -Fatal error: Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders in %s on line %d diff --git a/Zend/tests/partial_application/assert.phpt b/Zend/tests/partial_application/assert.phpt new file mode 100644 index 000000000000..fe36e687f8d5 --- /dev/null +++ b/Zend/tests/partial_application/assert.phpt @@ -0,0 +1,35 @@ +--TEST-- +PFA of assert() behaves like a dynamic call to assert() +--FILE-- +getMessage(), "\n"; +} + +try { + echo "# Dynamic call:\n"; + (function ($f) { $f(false); })('assert'); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +try { + echo "# PFA call:\n"; + $f = assert(?); + $f(false); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +?> +--EXPECT-- +# Static call: +AssertionError: assert(false) +# Dynamic call: +AssertionError: (no message) +# PFA call: +AssertionError: (no message) diff --git a/Zend/tests/partial_application/attributes_001.phpt b/Zend/tests/partial_application/attributes_001.phpt new file mode 100644 index 000000000000..827ad41321ec --- /dev/null +++ b/Zend/tests/partial_application/attributes_001.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA inherits NoDiscard and SensitiveParameter attributes +--FILE-- +getAttributes()); + + foreach ($r->getParameters() as $i => $p) { + echo "Parameter $i:\n"; + var_dump($p->getAttributes()); + } +} + +echo "# Orig attributes:\n"; + +dump_attributes('f'); + +$f = f(1, ?, ?, ...); + +echo "# PFA attributes:\n"; + +dump_attributes($f); + +?> +--EXPECTF-- +# Orig attributes: +array(2) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(9) "NoDiscard" + } + [1]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(4) "Test" + } +} +Parameter 0: +array(0) { +} +Parameter 1: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(18) "SensitiveParameter" + } +} +Parameter 2: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(4) "Test" + } +} +# PFA attributes: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(9) "NoDiscard" + } +} +Parameter 0: +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(18) "SensitiveParameter" + } +} +Parameter 1: +array(0) { +} +Parameter 2: +array(0) { +} diff --git a/Zend/tests/partial_application/attributes_002.phpt b/Zend/tests/partial_application/attributes_002.phpt new file mode 100644 index 000000000000..be1f1612f938 --- /dev/null +++ b/Zend/tests/partial_application/attributes_002.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA preserves #[SensitiveParameter] +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): f(1, Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue)) +#1 %s(%d): {closure:pfa:%s:7}(Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue)) +#2 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/attributes_003.phpt b/Zend/tests/partial_application/attributes_003.phpt new file mode 100644 index 000000000000..d113d02eb8f7 --- /dev/null +++ b/Zend/tests/partial_application/attributes_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA preserves #[NoDiscard] +--FILE-- + +--EXPECTF-- +Warning: The return value of function {closure:%s}() should either be used or intentionally ignored by casting it as (void) in %s on line 7 diff --git a/Zend/tests/partial_application/clone.phpt b/Zend/tests/partial_application/clone.phpt new file mode 100644 index 000000000000..f778d776b717 --- /dev/null +++ b/Zend/tests/partial_application/clone.phpt @@ -0,0 +1,50 @@ +--TEST-- +clone() can be partially applied +--FILE-- + 7])); + +$clone = clone(?, ['a' => 8]); +var_dump($clone(new C(9, 10))); + +?> +--EXPECTF-- +object(C)#%d (2) { + ["a"]=> + int(1) + ["b"]=> + int(2) +} +object(C)#%d (2) { + ["a"]=> + int(3) + ["b"]=> + int(4) +} +object(C)#%d (2) { + ["a"]=> + int(7) + ["b"]=> + int(6) +} +object(C)#%d (2) { + ["a"]=> + int(8) + ["b"]=> + int(10) +} diff --git a/Zend/tests/partial_application/compile_errors_001.phpt b/Zend/tests/partial_application/compile_errors_001.phpt new file mode 100644 index 000000000000..f08495a1f1e5 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_001.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: multiple variadic placeholders +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder may only appear once in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_002.phpt b/Zend/tests/partial_application/compile_errors_002.phpt new file mode 100644 index 000000000000..b6a2073a8363 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_002.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_003.phpt b/Zend/tests/partial_application/compile_errors_003.phpt new file mode 100644 index 000000000000..26ff8435111b --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_003.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: placeholders can not appear after named args +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_004.phpt b/Zend/tests/partial_application/compile_errors_004.phpt new file mode 100644 index 000000000000..ac7ec163c5da --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_004.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last, including after named args +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_005.phpt b/Zend/tests/partial_application/compile_errors_005.phpt new file mode 100644 index 000000000000..30e4aa12b488 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_005.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last, including after positional args +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_006.phpt b/Zend/tests/partial_application/compile_errors_006.phpt new file mode 100644 index 000000000000..8549c671e906 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_006.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: can not use unpacking in PFA, including with variadic placeholders +--FILE-- + "bar"], ...); +?> +--EXPECTF-- +Fatal error: Cannot combine partial application and unpacking in %s on line %d diff --git a/Zend/tests/partial_application/deprecated.phpt b/Zend/tests/partial_application/deprecated.phpt new file mode 100644 index 000000000000..5a6280347cfc --- /dev/null +++ b/Zend/tests/partial_application/deprecated.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFAs may emit deprecation warnings +--FILE-- + +--EXPECTF-- +Deprecated: Function f() is deprecated in %s on line %d diff --git a/Zend/tests/partial_application/errors_001.phpt b/Zend/tests/partial_application/errors_001.phpt new file mode 100644 index 000000000000..2d5348cb28af --- /dev/null +++ b/Zend/tests/partial_application/errors_001.phpt @@ -0,0 +1,62 @@ +--TEST-- +PFA errors: PFA instantiation follows the usual argument count validation +--FILE-- +getMessage()); +} + +try { + foo(?, ?, ?, ?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + $c = new C(); + $c->f(?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + property_exists(?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + usleep(?, ?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + foo(?, ?, ?, ?, ...); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +/* It is allowed to specify less than the number of required params, when there + * is a variadic placeholder */ +foo(?, ...); + +?> +--EXPECT-- +ArgumentCountError: Partial application of foo() expects exactly 3 arguments, 1 given +ArgumentCountError: Partial application of foo() expects at most 3 arguments, 4 given +ArgumentCountError: Partial application of C::f() expects exactly 3 arguments, 1 given +ArgumentCountError: Partial application of property_exists() expects exactly 2 arguments, 1 given +ArgumentCountError: Partial application of usleep() expects at most 1 arguments, 2 given +ArgumentCountError: Partial application of foo() expects at most 3 arguments, 4 given diff --git a/Zend/tests/partial_application/errors_002.phpt b/Zend/tests/partial_application/errors_002.phpt new file mode 100644 index 000000000000..0132d8873e2a --- /dev/null +++ b/Zend/tests/partial_application/errors_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA errors: named parameter that resolve to the position of a placeholder is an error +--FILE-- +getMessage()); +} +?> +--EXPECT-- +Error: Named parameter $a overwrites previous placeholder diff --git a/Zend/tests/partial_application/errors_003.phpt b/Zend/tests/partial_application/errors_003.phpt new file mode 100644 index 000000000000..85ecf398fa1b --- /dev/null +++ b/Zend/tests/partial_application/errors_003.phpt @@ -0,0 +1,74 @@ +--TEST-- +PFA errors: PFA call follows the usual argument count validation +--FILE-- +getMessage()); +} + +$foo = foo(?, ?); + +try { + $foo(1); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$bar = bar(?, ?, ...); + +try { + $bar(1); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +class Foo { + public function bar($a, ...$b) {} +} + +$foo = new Foo; + +$bar = $foo->bar(?); + +try { + $bar(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$repeat = str_repeat('a', ...); + +try { + $repeat(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$usleep = usleep(?); + +try { + $usleep(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$usleep(1, 2); + +?> +--EXPECTF-- +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 3 expected +ArgumentCountError: Too few arguments to function Foo::{closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected diff --git a/Zend/tests/partial_application/errors_004.phpt b/Zend/tests/partial_application/errors_004.phpt new file mode 100644 index 000000000000..e5e1432753b2 --- /dev/null +++ b/Zend/tests/partial_application/errors_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA errors: not specifying a required param is an error +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: f(): Argument #2 ($b) not passed in %s:6 +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/errors_005.phpt b/Zend/tests/partial_application/errors_005.phpt new file mode 100644 index 000000000000..23fc2a75dff3 --- /dev/null +++ b/Zend/tests/partial_application/errors_005.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA errors: Can not fetch default parameter value for Closure::__invoke() +--FILE-- +__invoke(0, 0, ?); +echo "No error\n"; + +try { + $g = $f->__invoke(0, 0, ...); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +No error +ArgumentCountError: Closure::__invoke(): Argument #3 ($c) must be passed explicitly, because the default value is not known diff --git a/Zend/tests/partial_application/errors_006.phpt b/Zend/tests/partial_application/errors_006.phpt new file mode 100644 index 000000000000..9534bb40ee54 --- /dev/null +++ b/Zend/tests/partial_application/errors_006.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA errors: Some internal function parameters have UNKNOWN default value +--FILE-- + array_keys($array, ???, true) + $f = array_keys(?, strict: true); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + // fn (array $array, mixed $filter_value = ???) => array_keys($array, $filter_value, true) + $f = array_keys(?, strict: true, ...); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +array(1) { + [0]=> + int(1) +} +array(0) { +} +array(1) { + [0]=> + int(1) +} +array(0) { +} +ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known +ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known diff --git a/Zend/tests/partial_application/export_001.phpt b/Zend/tests/partial_application/export_001.phpt new file mode 100644 index 000000000000..b48146bcc65a --- /dev/null +++ b/Zend/tests/partial_application/export_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA AST export +--INI-- +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +AssertionError: assert(0 && foo(?) && foo(new stdClass(), bar: 1, ...)) diff --git a/Zend/tests/partial_application/extra_named.phpt b/Zend/tests/partial_application/extra_named.phpt new file mode 100644 index 000000000000..4dd80cfa0127 --- /dev/null +++ b/Zend/tests/partial_application/extra_named.phpt @@ -0,0 +1,49 @@ +--TEST-- +PFA extra named parameters are forwarded to the actual function +--FILE-- + +--EXPECT-- +array(3) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" + ["baz"]=> + string(3) "baz" +} +array(2) { + ["bar"]=> + string(3) "bar" + ["baz"]=> + string(3) "baz" +} +array(2) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" +} diff --git a/Zend/tests/partial_application/function_name.phpt b/Zend/tests/partial_application/function_name.phpt new file mode 100644 index 000000000000..68d2ccf84871 --- /dev/null +++ b/Zend/tests/partial_application/function_name.phpt @@ -0,0 +1,40 @@ +--TEST-- +Partial application function name +--FILE-- +getName()); + } +} + +function f() { + echo "# From a function:\n"; + var_dump((new ReflectionFunction(g(?)))->getName()); + + echo "# Declared on same line:\n"; + [$a, $b] = [g(?), g(?)]; + var_dump((new ReflectionFunction($a))->getName(), (new ReflectionFunction($b))->getName()); +} + +f(); +C::m(); + +echo "# From global scope:\n"; +var_dump((new ReflectionFunction(g(?)))->getName()); + +?> +--EXPECTF-- +# From a function: +string(20) "{closure:pfa:f():14}" +# Declared on same line: +string(20) "{closure:pfa:f():17}" +string(20) "{closure:pfa:f():17}" +# From a method: +string(22) "{closure:pfa:C::m():8}" +# From global scope: +string(%d) "{closure:pfa:%sfunction_name.php:25}" diff --git a/Zend/tests/partial_application/fuzz_001.phpt b/Zend/tests/partial_application/fuzz_001.phpt new file mode 100644 index 000000000000..790162ba6a36 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Closure application fuzz 001 +--FILE-- + +--EXPECTF-- +Closure [ function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Bound Variables [2] { + Variable #0 [ $fn ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} diff --git a/Zend/tests/partial_application/fuzz_003.phpt b/Zend/tests/partial_application/fuzz_003.phpt new file mode 100644 index 000000000000..6e9d583fda99 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +Closure application fuzz 003 +--FILE-- +method(1, ...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/fuzz_004.phpt b/Zend/tests/partial_application/fuzz_004.phpt new file mode 100644 index 000000000000..ea005304a3af --- /dev/null +++ b/Zend/tests/partial_application/fuzz_004.phpt @@ -0,0 +1,19 @@ +--TEST-- +Closure application fuzz 004 +--FILE-- +__invoke(UNDEFINED); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/fuzz_005.phpt b/Zend/tests/partial_application/fuzz_005.phpt new file mode 100644 index 000000000000..ea04862d8399 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA fuzz 005 +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/partial_application/fuzz_006.phpt b/Zend/tests/partial_application/fuzz_006.phpt new file mode 100644 index 000000000000..26ec6e3e4dd1 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA fuzz 006 +--FILE-- + +--EXPECT-- +int(1) diff --git a/Zend/tests/partial_application/fuzz_007.phpt b/Zend/tests/partial_application/fuzz_007.phpt new file mode 100644 index 000000000000..123ce29d8b18 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_007.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA fuzz 007 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/hook.phpt b/Zend/tests/partial_application/hook.phpt new file mode 100644 index 000000000000..6402c5d01e74 --- /dev/null +++ b/Zend/tests/partial_application/hook.phpt @@ -0,0 +1,25 @@ +--TEST-- +Parent property hook call can not be partially applied +--FILE-- +a = 1; + +?> +--EXPECTF-- +Fatal error: Cannot create Closure for parent property hook call in %s on line %d diff --git a/Zend/tests/partial_application/instance_polymorphism.phpt b/Zend/tests/partial_application/instance_polymorphism.phpt new file mode 100644 index 000000000000..59fb5795d60f --- /dev/null +++ b/Zend/tests/partial_application/instance_polymorphism.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA: instance polymorphism +--FILE-- +m(?); + } +} + +class C extends P { + public function m(string|array $b): void { + echo __METHOD__, PHP_EOL; + var_dump($b); + } +} + +for ($i = 0; $i < 2; $i++) { + (new P())->get()(a: 'a'); + (new C())->get()(b: []); +} + +?> +--EXPECT-- +P::m +string(1) "a" +C::m +array(0) { +} +P::m +string(1) "a" +C::m +array(0) { +} diff --git a/Zend/tests/partial_application/invokable.phpt b/Zend/tests/partial_application/invokable.phpt new file mode 100644 index 000000000000..c21030e7733a --- /dev/null +++ b/Zend/tests/partial_application/invokable.phpt @@ -0,0 +1,51 @@ +--TEST-- +__invoke() can be partially applied +--FILE-- + +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 11 - 11 + + - Parameters [2] { + Parameter #0 [ int $a ] + Parameter #1 [ object $b ] + } + - Return [ C ] +} + +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 15 - 15 + + - Bound Variables [1] { + Variable #0 [ $b ] + } + + - Parameters [1] { + Parameter #0 [ int $a ] + } + - Return [ C ] +} + +int(1) +object(stdClass)#%d (0) { +} diff --git a/Zend/tests/partial_application/jit_001.phpt b/Zend/tests/partial_application/jit_001.phpt new file mode 100644 index 000000000000..84aefa05ab28 --- /dev/null +++ b/Zend/tests/partial_application/jit_001.phpt @@ -0,0 +1,9 @@ +--TEST-- +PFA JIT 001 +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt new file mode 100644 index 000000000000..6cd6044c62de --- /dev/null +++ b/Zend/tests/partial_application/magic_001.phpt @@ -0,0 +1,81 @@ +--TEST-- +__call() can be partially applied +--FILE-- +method(?); + +echo (string) new ReflectionFunction($bar); + +try { + $bar(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$bar(1, 2); +$bar(1); + +$bar = $foo->method(?, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(10); +$bar(10, 20); + +$bar = $foo->method(new Foo, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(100); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %s 12 - 12 + + - Parameters [1] { + Parameter #0 [ mixed $arguments0 ] + } +} +ArgumentCountError: Too few arguments to function Foo::{closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +Foo::method +int(1) +Foo::method +int(1) +Closure [ public method {closure:%s:%d} ] { + @@ %s 25 - 25 + + - Parameters [2] { + Parameter #0 [ mixed $arguments0 ] + Parameter #1 [ mixed ...$arguments ] + } +} +Foo::method +int(10) +Foo::method +int(10) +int(20) +Closure [ public method {closure:%s:%d} ] { + @@ %s 32 - 32 + + - Bound Variables [1] { + Variable #0 [ $arguments0 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$arguments ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) diff --git a/Zend/tests/partial_application/magic_002.phpt b/Zend/tests/partial_application/magic_002.phpt new file mode 100644 index 000000000000..d91ef7a7afe2 --- /dev/null +++ b/Zend/tests/partial_application/magic_002.phpt @@ -0,0 +1,72 @@ +--TEST-- +__callStatic() can be partially applied +--FILE-- + +--EXPECTF-- +Closure [ static public method {closure:%s:%d} ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ mixed $arguments0 ] + } +} +Foo::method +int(1) +Foo::method +int(1) +Closure [ static public method {closure:%s:%d} ] { + @@ %s 17 - 17 + + - Parameters [2] { + Parameter #0 [ mixed $arguments0 ] + Parameter #1 [ mixed ...$arguments ] + } +} +Foo::method +int(10) +Foo::method +int(10) +int(20) +Closure [ static public method {closure:%s:%d} ] { + @@ %s 24 - 24 + + - Bound Variables [1] { + Variable #0 [ $arguments0 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$arguments ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) diff --git a/Zend/tests/partial_application/magic_003.phpt b/Zend/tests/partial_application/magic_003.phpt new file mode 100644 index 000000000000..242403ea38fc --- /dev/null +++ b/Zend/tests/partial_application/magic_003.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA magic trampoline release (result unused) +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_004.phpt b/Zend/tests/partial_application/magic_004.phpt new file mode 100644 index 000000000000..8ef93a91ce83 --- /dev/null +++ b/Zend/tests/partial_application/magic_004.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA magic trampoline release (result used) +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_005.phpt b/Zend/tests/partial_application/magic_005.phpt new file mode 100644 index 000000000000..e70ed90b1767 --- /dev/null +++ b/Zend/tests/partial_application/magic_005.phpt @@ -0,0 +1,29 @@ +--TEST-- +PFA magic null ptr deref in arginfo +--FILE-- +method(?); +var_dump($bar); +?> +--EXPECTF-- +object(Closure)#%d (%d) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%smagic_005.php" + ["line"]=> + int(7) + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$arguments0"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/magic_006.phpt b/Zend/tests/partial_application/magic_006.phpt new file mode 100644 index 000000000000..29564d8b615a --- /dev/null +++ b/Zend/tests/partial_application/magic_006.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA magic varargs +--FILE-- +method(1,...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/named_placeholders.phpt b/Zend/tests/partial_application/named_placeholders.phpt new file mode 100644 index 000000000000..19ce8cc03860 --- /dev/null +++ b/Zend/tests/partial_application/named_placeholders.phpt @@ -0,0 +1,121 @@ +--TEST-- +PFA supports named placeholders +--FILE-- +getMessage(), "\n"; +} + +echo "# Case 4\n"; + +function bar($a = 1, $b = 2, ...$c) { + var_dump($a, $b, $c); +} + +$d = bar(b: ?, ...); + +echo (string) new ReflectionFunction($d); + +$d(new B, new A, new C); + +echo "# Case 5\n"; + +try { + $d = bar(?, a: ?); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Case 6\n"; + +try { + $d = bar(c: ?, ...); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +# Case 1 +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 13 - 13 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +# Case 2 +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 21 - 21 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +# Case 3 +ArgumentCountError: {closure:pfa:%s:29}(): Argument #1 ($a) not passed +# Case 4 +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 42 - 42 + + - Parameters [3] { + Parameter #0 [ $b ] + Parameter #1 [ $a = 1 ] + Parameter #2 [ ...$c ] + } +} +object(A)#%d (0) { +} +object(B)#%d (0) { +} +array(1) { + [0]=> + object(C)#%d (0) { + } +} +# Case 5 +Error: Named parameter $a overwrites previous placeholder +# Case 6 +Error: Cannot use named placeholder for unknown or variadic parameter $c diff --git a/Zend/tests/partial_application/never_cache_001.phpt b/Zend/tests/partial_application/never_cache_001.phpt new file mode 100644 index 000000000000..3c9e8a173e51 --- /dev/null +++ b/Zend/tests/partial_application/never_cache_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +Inline cache can not be used for ZEND_ACC_NEVER_CACHE functions +--FILE-- +__invoke(?)); +} + +?> +--EXPECT-- +string(1) "A" +string(1) "B" +string(1) "A" +string(1) "B" diff --git a/Zend/tests/partial_application/non_dynamic_call_funcs.phpt b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt new file mode 100644 index 000000000000..db25ef3dcc93 --- /dev/null +++ b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt @@ -0,0 +1,46 @@ +--TEST-- +Functions that can not be called dynamically, can not be partially applied +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECT-- +Error: Cannot call func_get_arg() dynamically +Error: Cannot call compact() dynamically +Error: Cannot call extract() dynamically +Error: Cannot call func_get_args() dynamically +Error: Cannot call func_num_args() dynamically +Error: Cannot call get_defined_vars() dynamically diff --git a/Zend/tests/partial_application/observers.phpt b/Zend/tests/partial_application/observers.phpt new file mode 100644 index 000000000000..8717b38ca326 --- /dev/null +++ b/Zend/tests/partial_application/observers.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA support observers +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.show_output=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + <{closure:%s}> + + + + +int(1) +int(2) + + + + diff --git a/Zend/tests/partial_application/optional_placeholder.phpt b/Zend/tests/partial_application/optional_placeholder.phpt new file mode 100644 index 000000000000..d6230241d906 --- /dev/null +++ b/Zend/tests/partial_application/optional_placeholder.phpt @@ -0,0 +1,77 @@ +--TEST-- +PFA optional placeholder: ? always generates required params; ... inherits optionality +--FILE-- +getParameters()[0]->isOptional()); + +$f = foo(?, ?); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional()); + +$f = foo(?, ?, ?); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional()); + +echo "# ... inherits optionality from the original function\n"; +$f = foo(?, ...); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional()); + +$f = foo(?, ?, ...); +$params = (new ReflectionFunction($f))->getParameters(); +var_dump($params[0]->isOptional(), $params[1]->isOptional(), $params[2]->isOptional()); + +echo "# Calling works: ? params are required, unspecified optional params use their defaults\n"; +$f = foo(?, ?); +var_dump($f(10, 'hello')); + +echo "# Named ? placeholders are also required\n"; + +function bar(int $x = 0, int $y = 0) { + return [$x, $y]; +} + +$f = bar(y: ?); +var_dump((new ReflectionFunction($f))->getParameters()[0]->isOptional()); +var_dump($f(42)); + +?> +--EXPECT-- +# ? makes targeted params required regardless of original optionality +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +# ... inherits optionality from the original function +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +# Calling works: ? params are required, unspecified optional params use their defaults +array(3) { + [0]=> + int(10) + [1]=> + string(5) "hello" + [2]=> + float(3.14) +} +# Named ? placeholders are also required +bool(false) +array(2) { + [0]=> + int(0) + [1]=> + int(42) +} diff --git a/Zend/tests/partial_application/param_reorder.phpt b/Zend/tests/partial_application/param_reorder.phpt new file mode 100644 index 000000000000..3ade1beb0af7 --- /dev/null +++ b/Zend/tests/partial_application/param_reorder.phpt @@ -0,0 +1,202 @@ +--TEST-- +Named parameters define the order of parameters in a PFA +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +# All named +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 13 - 13 + + - Parameters [4] { + Parameter #0 [ $d ] + Parameter #1 [ $c ] + Parameter #2 [ $b ] + Parameter #3 [ $a ] + } +} + +array(4) { + [0]=> + int(4) + [1]=> + int(3) + [2]=> + int(2) + [3]=> + int(1) +} +# Some named: Positional first, then named in specified order +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 19 - 19 + + - Parameters [4] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $d ] + Parameter #3 [ $c ] + } +} + +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(4) + [3]=> + int(3) +} +# Some named, one unspecified +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 25 - 25 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $c ] + Parameter #2 [ $b ] + } +} + +array(4) { + [0]=> + int(1) + [1]=> + int(3) + [2]=> + int(2) + [3]=> + NULL +} +# Some named, some implicit added by '...' +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 31 - 31 + + - Parameters [4] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + Parameter #2 [ $a ] + Parameter #3 [ $d = NULL ] + } +} + +array(4) { + [0]=> + int(3) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + int(4) +} +# Some named, some implicit added by '...' on variadic function +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 37 - 37 + + - Parameters [4] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + Parameter #2 [ $a ] + Parameter #3 [ ...$d ] + } +} + +array(4) { + [0]=> + int(3) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } +} +# Some prebound, some named +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 43 - 43 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $d ] + } + + - Parameters [2] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + } +} + +array(4) { + [0]=> + int(-1) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + int(-2) +} +# Some named, some required missing +ArgumentCountError: f(): Argument #1 ($a) not passed diff --git a/Zend/tests/partial_application/pipe_optimization_001.phpt b/Zend/tests/partial_application/pipe_optimization_001.phpt new file mode 100644 index 000000000000..9610f37b76ac --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PFA optimization: PFA with single placeholder arg can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a) { + var_dump($a); + } +} + +1 |> foo(?); + +?> +--EXPECTF-- +$_main: + ; (lines=9, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_001.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 DO_FCALL_BY_NAME +0008 RETURN int(1) + +foo: + ; (lines=5, args=1, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_001.php:4-6 +0000 CV0($a) = RECV 1 +0001 INIT_FCALL 1 %d string("var_dump") +0002 SEND_VAR CV0($a) 1 +0003 DO_ICALL +0004 RETURN null +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_002.phpt b/Zend/tests/partial_application/pipe_optimization_002.phpt new file mode 100644 index 000000000000..729e70d30e6b --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_002.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ?); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_002.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_002.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_003.phpt b/Zend/tests/partial_application/pipe_optimization_003.phpt new file mode 100644 index 000000000000..da112f8f3cea --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_003.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (placeholder first) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(?, 1); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_003.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(2) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_003.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(2) +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt new file mode 100644 index 000000000000..addee498d810 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA pipe optimization: PFA with multiple placeholders can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(?, ?); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=22, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_004.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_PLACEHOLDER 1 +0007 SEND_PLACEHOLDER 2 +0008 T1 = CALLABLE_CONVERT_PARTIAL %d +0009 INIT_DYNAMIC_CALL 1 T1 +0010 SEND_VAL_EX int(2) 1 +0011 DO_FCALL +0012 RETURN int(1) +0013 CV0($e) = CATCH string("Throwable") +0014 T1 = FETCH_CLASS_NAME CV0($e) +0015 ECHO T1 +0016 ECHO string(": ") +0017 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0018 T1 = DO_FCALL +0019 ECHO T1 +0020 ECHO string("\n") +0021 RETURN int(1) +EXCEPTION TABLE: + 0005, 0013, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_004.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=3, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-10 +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:10-10 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("foo") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 T2 = DO_UCALL +0006 RETURN T2 +ArgumentCountError: Too few arguments to function {closure:pfa:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected diff --git a/Zend/tests/partial_application/pipe_optimization_005.phpt b/Zend/tests/partial_application/pipe_optimization_005.phpt new file mode 100644 index 000000000000..3ccfec836609 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_005.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (variadic) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ...); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_005.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_005.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_006.phpt b/Zend/tests/partial_application/pipe_optimization_006.phpt new file mode 100644 index 000000000000..6e06477427a3 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_006.phpt @@ -0,0 +1,55 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (named) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +2 |> foo(1, c: ?); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_006.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("c") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_006.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +int(1) +NULL +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt new file mode 100644 index 000000000000..09c3c765d237 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA pipe optimization: PFA with multiple placeholders can not be optimized (named) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(a: ?, b: ?); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=22, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_007.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_PLACEHOLDER string("a") +0007 SEND_PLACEHOLDER string("b") +0008 T1 = CALLABLE_CONVERT_PARTIAL %d array(...) +0009 INIT_DYNAMIC_CALL 1 T1 +0010 SEND_VAL_EX int(2) 1 +0011 DO_FCALL +0012 RETURN int(1) +0013 CV0($e) = CATCH string("Throwable") +0014 T1 = FETCH_CLASS_NAME CV0($e) +0015 ECHO T1 +0016 ECHO string(": ") +0017 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0018 T1 = DO_FCALL +0019 ECHO T1 +0020 ECHO string("\n") +0021 RETURN int(1) +EXCEPTION TABLE: + 0005, 0013, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_007.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=3, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-10 +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:10-10 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("foo") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 T2 = DO_UCALL +0006 RETURN T2 +ArgumentCountError: Too few arguments to function {closure:pfa:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt new file mode 100644 index 000000000000..070074632c77 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -0,0 +1,99 @@ +--TEST-- +PFA pipe optimization: PFA with both a variadic placeholder and named arg can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(a: 1, ...); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=18, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_008.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_VAL_EX int(1) string("a") +0007 T1 = CALLABLE_CONVERT_PARTIAL %d +0008 INIT_DYNAMIC_CALL 1 T1 +0009 SEND_VAL_EX int(2) 1 +0010 DO_FCALL +0011 RETURN int(1) +0012 CV0($e) = CATCH string("Throwable") +0013 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0014 T1 = DO_FCALL +0015 ECHO T1 +0016 ECHO string("\n") +0017 RETURN int(1) +EXCEPTION TABLE: + 0005, 0012, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_008.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %s:1-10 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($a) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:%s:%d}: + ; (lines=18, args=1, vars=2, tmps=%d) + ; (after optimizer) + ; %s:10-10 +0000 CV0($b) = RECV 1 +0001 BIND_STATIC CV1($a) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("foo") +0006 SEND_VAR CV1($a) 1 +0007 SEND_VAR CV0($b) 2 +0008 T2 = DO_UCALL +0009 RETURN T2 +0010 INIT_FCALL 2 %d string("foo") +0011 SEND_VAR CV1($a) 1 +0012 SEND_VAR CV0($b) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 T2 = DO_UCALL +0017 RETURN T2 +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_009.phpt b/Zend/tests/partial_application/pipe_optimization_009.phpt new file mode 100644 index 000000000000..118482a3860b --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_009.phpt @@ -0,0 +1,102 @@ +--TEST-- +PFA pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function lhs() { + echo __FUNCTION__, "\n"; + return 0; + } + function arg1() { + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + echo __FUNCTION__, "\n"; + return 2; + } +} + +lhs() |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:1-24 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0008 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("lhs") 1 +0006 DECLARE_FUNCTION string("arg1") 2 +0007 DECLARE_FUNCTION string("arg2") 3 +0008 INIT_FCALL_BY_NAME 0 string("lhs") +0009 T0 = DO_FCALL_BY_NAME +0010 INIT_FCALL_BY_NAME 3 string("foo") +0011 INIT_FCALL_BY_NAME 0 string("arg1") +0012 V1 = DO_FCALL_BY_NAME +0013 SEND_VAR_NO_REF_EX V1 1 +0014 SEND_VAL_EX T0 2 +0015 INIT_FCALL_BY_NAME 0 string("arg2") +0016 V0 = DO_FCALL_BY_NAME +0017 SEND_VAR_NO_REF_EX V0 3 +0018 DO_FCALL_BY_NAME +0019 RETURN int(1) +LIVE RANGES: + 0: 0010 - 0014 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +lhs: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:7-10 +0000 ECHO string("lhs\n") +0001 RETURN int(0) + +arg1: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:11-14 +0000 ECHO string("arg1\n") +0001 RETURN int(1) + +arg2: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_009.php:15-18 +0000 ECHO string("arg2\n") +0001 RETURN int(2) +lhs +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_010.phpt b/Zend/tests/partial_application/pipe_optimization_010.phpt new file mode 100644 index 000000000000..f77c2a97732e --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_010.phpt @@ -0,0 +1,58 @@ +--TEST-- +PFA pipe optimization: References +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo(&$a, $b) { + var_dump($a, $b); + $a = 2; + } +} + +1 |> foo($a, ?); +var_dump($a); + +?> +--EXPECTF-- +$_main: + ; (lines=13, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_010.php:1-14 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAR_EX CV0($a) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 INIT_FCALL 1 %d string("var_dump") +0010 SEND_VAR CV0($a) 1 +0011 DO_ICALL +0012 RETURN int(1) + +foo: + ; (lines=8, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_010.php:4-7 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 ASSIGN CV0($a) int(2) +0007 RETURN null +NULL +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_011.phpt b/Zend/tests/partial_application/pipe_optimization_011.phpt new file mode 100644 index 000000000000..cd1c986d99e7 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_011.phpt @@ -0,0 +1,98 @@ +--TEST-- +PFA pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function arg1() { + global $a; + $a = 2; + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + global $a; + $a = 3; + echo __FUNCTION__, "\n"; + return 2; + } +} + +$a = 0; +$a |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=19, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:1-25 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0007 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("arg1") %d +0006 DECLARE_FUNCTION string("arg2") %d +0007 ASSIGN CV0($a) int(0) +0008 T1 = QM_ASSIGN CV0($a) +0009 INIT_FCALL_BY_NAME 3 string("foo") +0010 INIT_FCALL_BY_NAME 0 string("arg1") +0011 V2 = DO_FCALL_BY_NAME +0012 SEND_VAR_NO_REF_EX V2 1 +0013 SEND_VAL_EX T1 2 +0014 INIT_FCALL_BY_NAME 0 string("arg2") +0015 V1 = DO_FCALL_BY_NAME +0016 SEND_VAR_NO_REF_EX V1 3 +0017 DO_FCALL_BY_NAME +0018 RETURN int(1) +LIVE RANGES: + 1: 0009 - 0013 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +arg1: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:7-12 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(2) +0002 ECHO string("arg1\n") +0003 RETURN int(1) + +arg2: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %spipe_optimization_011.php:13-18 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(3) +0002 ECHO string("arg2\n") +0003 RETURN int(2) +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_012.phpt b/Zend/tests/partial_application/pipe_optimization_012.phpt new file mode 100644 index 000000000000..da172874adce --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_012.phpt @@ -0,0 +1,52 @@ +--TEST-- +PFA optimization: PFA with named args and placeholders can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +1 |> foo(?, b: 2); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("b") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_013.phpt b/Zend/tests/partial_application/pipe_optimization_013.phpt new file mode 100644 index 000000000000..7d1a48b2f2ed --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_013.phpt @@ -0,0 +1,87 @@ +--TEST-- +PFA optimization: PFA with named args and a variadic placeholder can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +1 |> foo(b: 2, ...); + +?> +--EXPECTF-- +$_main: + ; (lines=12, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_VAL_EX int(2) string("b") +0007 T0 = CALLABLE_CONVERT_PARTIAL 3 +0008 INIT_DYNAMIC_CALL 1 T0 +0009 SEND_VAL_EX int(1) 1 +0010 DO_FCALL +0011 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=4, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %s:1-9 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($b) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:pfa:%s:9}: + ; (lines=18, args=1, vars=2, tmps=%d) + ; (after optimizer) + ; %s:9-9 +0000 CV0($a) = RECV 1 +0001 BIND_STATIC CV1($b) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("foo") +0006 SEND_VAR CV0($a) 1 +0007 SEND_VAR CV1($b) 2 +0008 T2 = DO_UCALL +0009 RETURN T2 +0010 INIT_FCALL 2 %d string("foo") +0011 SEND_VAR CV0($a) 1 +0012 SEND_VAR CV1($b) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 T2 = DO_UCALL +0017 RETURN T2 +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_014.phpt b/Zend/tests/partial_application/pipe_optimization_014.phpt new file mode 100644 index 000000000000..6e66d7e0e99d --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_014.phpt @@ -0,0 +1,68 @@ +--TEST-- +PFA pipe optimization: PFA with unknown named parameter can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +try { + 2 |> foo(1, unknown: ?); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=%d) + ; (after optimizer) + ; %s:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 T2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) T2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("unknown") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) +0011 CV0($e) = CATCH string("Error") +0012 T1 = FETCH_CLASS_NAME CV0($e) +0013 ECHO T1 +0014 ECHO string(": ") +0015 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0016 T1 = DO_FCALL +0017 ECHO T1 +0018 ECHO string("\n") +0019 RETURN int(1) +EXCEPTION TABLE: + 0005, 0011, -, - + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +Error: Unknown named parameter $unknown diff --git a/Zend/tests/partial_application/pipe_optimization_015.phpt b/Zend/tests/partial_application/pipe_optimization_015.phpt new file mode 100644 index 000000000000..ce293c7a300c --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_015.phpt @@ -0,0 +1,55 @@ +--TEST-- +PFA pipe optimization: PFA with skipped optional parameter can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +3 |> foo(1, c: ?); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %s:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 T1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) T1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(3) string("c") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=9, args=3, vars=3, tmps=%d) + ; (after optimizer) + ; %s:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +int(1) +NULL +int(3) diff --git a/Zend/tests/partial_application/preloading.inc b/Zend/tests/partial_application/preloading.inc new file mode 100644 index 000000000000..885ed0c5b0f1 --- /dev/null +++ b/Zend/tests/partial_application/preloading.inc @@ -0,0 +1,13 @@ + diff --git a/Zend/tests/partial_application/preloading.phpt b/Zend/tests/partial_application/preloading.phpt new file mode 100644 index 000000000000..23ad6edf2c4c --- /dev/null +++ b/Zend/tests/partial_application/preloading.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA preloading +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.preload={PWD}/preloading.inc +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(2) +int(2) diff --git a/Zend/tests/partial_application/rebinding_001.phpt b/Zend/tests/partial_application/rebinding_001.phpt new file mode 100644 index 000000000000..d1913957a8c9 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_001.phpt @@ -0,0 +1,59 @@ +--TEST-- +PFA can only be rebound to an instanceof $this +--FILE-- +f(?); +$g = $c->g(?); + +echo "# Can be rebound to \$this of the same class:\n"; +$f->bindTo(new C)(1); + +echo "# Can be rebound to \$this of a sub-class:\n"; +$f->bindTo(new SubClass)(1); + +echo "# Cannot be rebound to an unrelated class:\n"; +try { + $f->bindTo(new Unrelated)(1); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Cannot unbind \$this on instance method:\n"; +try { + $f->bindTo(null)(1); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Can unbind \$this on static method:\n"; +$g->bindTo(null)(1); + +?> +--EXPECTF-- +# Can be rebound to $this of the same class: +object(C)#%d (0) { +} +# Can be rebound to $this of a sub-class: +object(SubClass)#%d (0) { +} +# Cannot be rebound to an unrelated class: + +Warning: Cannot bind method C::{closure:%s:%d}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Cannot unbind $this on instance method: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Can unbind $this on static method: +string(1) "C" diff --git a/Zend/tests/partial_application/rebinding_002.phpt b/Zend/tests/partial_application/rebinding_002.phpt new file mode 100644 index 000000000000..25e938120676 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_002.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA scope cannot be rebound +--FILE-- +f(?); +$g = g(?); + +echo "# Can be rebound to the same scope:\n"; +$f->bindTo($c, C::class)(1); + +echo "# Method cannot be rebound to a different scope:\n"; +try { + $f->bindTo($c, SubClass::class)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# Function cannot be rebound to a different scope:\n"; +try { + $g->bindTo($c, SubClass::class)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +# Can be rebound to the same scope: +string(1) "C" +# Method cannot be rebound to a different scope: + +Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable +# Function cannot be rebound to a different scope: + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable diff --git a/Zend/tests/partial_application/rebinding_003.phpt b/Zend/tests/partial_application/rebinding_003.phpt new file mode 100644 index 000000000000..9417bbc2d320 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_003.phpt @@ -0,0 +1,69 @@ +--TEST-- +Rebinding PFA of Closure rebinds inner Closure +--FILE-- +getF()(?); +$g = $c->getG()(?); + +echo "# Can be rebound to \$this of the same class:\n"; +$d = new C(); +$f->bindTo($d)($d); + +echo "# Can be rebound to \$this of a sub-class:\n"; +$d = new SubClass(); +$f->bindTo($d)($d); + +echo "# Cannot be rebound to an unrelated class:\n"; +try { + $f->bindTo(new Unrelated)($c); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Cannot unbind \$this on non-static closure:\n"; +try { + $f->bindTo(null)($c); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# Can unbind \$this on static closure:\n"; +$g->bindTo(null)($c); + +?> +--EXPECTF-- +# Can be rebound to $this of the same class: +object(C)#%d (0) { +} +bool(true) +# Can be rebound to $this of a sub-class: +object(SubClass)#%d (0) { +} +bool(true) +# Cannot be rebound to an unrelated class: + +Warning: Cannot bind method C::{closure:%s:%d}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Cannot unbind $this on non-static closure: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +Error: Value of type null is not callable +# Can unbind $this on static closure: +string(1) "C" +object(C)#%d (0) { +} diff --git a/Zend/tests/partial_application/recorded_warnings.phpt b/Zend/tests/partial_application/recorded_warnings.phpt new file mode 100644 index 000000000000..f25be826f525 --- /dev/null +++ b/Zend/tests/partial_application/recorded_warnings.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA compilation warnings are recorded and replayed +--FILE-- + +--EXPECTF-- +Deprecated: Using "_" as a type name is deprecated since 8.4 in %s on line 3 + +Deprecated: Using "_" as a type name is deprecated since 8.4 in %s on line 5 diff --git a/Zend/tests/partial_application/references_001.phpt b/Zend/tests/partial_application/references_001.phpt new file mode 100644 index 000000000000..080b3085acc0 --- /dev/null +++ b/Zend/tests/partial_application/references_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +PFA receives by val if the actual function does +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &string(49) "unchanged because foo() doesn't take by reference" +} +string(49) "unchanged because foo() doesn't take by reference" diff --git a/Zend/tests/partial_application/references_002.phpt b/Zend/tests/partial_application/references_002.phpt new file mode 100644 index 000000000000..42a90648c1ae --- /dev/null +++ b/Zend/tests/partial_application/references_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +PFA receives by ref if the actual function does +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &int(2) +} +int(2) diff --git a/Zend/tests/partial_application/references_003.phpt b/Zend/tests/partial_application/references_003.phpt new file mode 100644 index 000000000000..be116b06c79f --- /dev/null +++ b/Zend/tests/partial_application/references_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA receives by ref if the actual function does +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +{closure:%s}(): Argument #1 ($b) could not be passed by reference diff --git a/Zend/tests/partial_application/references_004.phpt b/Zend/tests/partial_application/references_004.phpt new file mode 100644 index 000000000000..8ef3ff002ce7 --- /dev/null +++ b/Zend/tests/partial_application/references_004.phpt @@ -0,0 +1,42 @@ +--TEST-- +PFA receives variadic param by ref if the actual function does +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreferences_004.php 13 - 13 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $args1 ] + } + + - Parameters [2] { + Parameter #0 [ &$args0 ] + Parameter #1 [ &...$args ] + } +} + +int(-2) +int(-3) +int(-4) diff --git a/Zend/tests/partial_application/references_005.phpt b/Zend/tests/partial_application/references_005.phpt new file mode 100644 index 000000000000..e8c7c27a07be --- /dev/null +++ b/Zend/tests/partial_application/references_005.phpt @@ -0,0 +1,26 @@ +--TEST-- +PFA inherits return by ref +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + int(1) +} +array(1) { + [0]=> + int(1) +} diff --git a/Zend/tests/partial_application/reflection_001.phpt b/Zend/tests/partial_application/reflection_001.phpt new file mode 100644 index 000000000000..ac079515d0a0 --- /dev/null +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +PFA reflection: optional parameters +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 6 - 6 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 10 - 10 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c = 10 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 14 - 14 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c ] + } +} diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt new file mode 100644 index 000000000000..da91a7af50cf --- /dev/null +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -0,0 +1,57 @@ +--TEST-- +PFA reflection: variadics +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 10 - 10 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ ...$b ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 14 - 14 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ $b0 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 18 - 18 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b0 ] + Parameter #2 [ $b1 ] + } +} diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt new file mode 100644 index 000000000000..90506d38a778 --- /dev/null +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA reflection: internal with variadics +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 2 - 2 + + - Parameters [1] { + Parameter #0 [ string $format ] + } + - Return [ string ] +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 6 - 6 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed ...$values ] + } + - Return [ string ] +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 10 - 10 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed $values0 ] + } + - Return [ string ] +} diff --git a/Zend/tests/partial_application/reflection_004.phpt b/Zend/tests/partial_application/reflection_004.phpt new file mode 100644 index 000000000000..7226383f5afa --- /dev/null +++ b/Zend/tests/partial_application/reflection_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA reflection: ReflectionFunction::isAnonymous() is true for partials +--FILE-- +isAnonymous()); + +var_dump((new ReflectionFunction(function () {}))->isAnonymous()); + +var_dump((new ReflectionFunction(sprintf(?)))->isAnonymous()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/Zend/tests/partial_application/reflection_005.phpt b/Zend/tests/partial_application/reflection_005.phpt new file mode 100644 index 000000000000..be86270c004c --- /dev/null +++ b/Zend/tests/partial_application/reflection_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA reflection: ReflectionFunction::isClosure() is true for partials +--FILE-- +isClosure()); + +var_dump((new ReflectionFunction(function () {}))->isClosure()); + +var_dump((new ReflectionFunction(sprintf(?)))->isClosure()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/Zend/tests/partial_application/relative_return_types.phpt b/Zend/tests/partial_application/relative_return_types.phpt new file mode 100644 index 000000000000..5729f7edfd02 --- /dev/null +++ b/Zend/tests/partial_application/relative_return_types.phpt @@ -0,0 +1,148 @@ +--TEST-- +PFA supports relative return types +--FILE-- + 0) { + trait T { + public function getSelf(object $o): self { + return $o; + } + public function getStatic(object $o): static { + return $o; + } + } +} + +class C { + use T; +} + +class D extends C { +} + +$c = new C; + +echo "# C::getSelf():\n"; + +$self = $c->getSelf(?); + +echo (string) new ReflectionFunction($self), "\n"; + +var_dump($self($c)); +var_dump($self(new D)); +try { + $self(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# C::getStatic():\n"; + +$static = $c->getStatic(?); + +echo (string) new ReflectionFunction($static), "\n"; + +var_dump($static($c)); +var_dump($static(new D)); +try { + $static(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +$d = new D; + +echo "# D::getSelf():\n"; + +$self = $d->getSelf(?); + +echo (string) new ReflectionFunction($self), "\n"; + +var_dump($self($d)); +var_dump($self(new C)); +try { + $self(new stdClass); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +echo "# D::getStatic():\n"; + +$static = $d->getStatic(?); + +echo (string) new ReflectionFunction($static), "\n"; + +var_dump($static($d)); +try { + var_dump($static(new C)); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +try { + $static(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +# C::getSelf(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 25 - 25 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ self ] +} + +object(C)#%d (0) { +} +object(D)#%d (0) { +} +C::getSelf(): Return value must be of type C, stdClass returned +# C::getStatic(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 39 - 39 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(C)#%d (0) { +} +object(D)#%d (0) { +} +C::getStatic(): Return value must be of type C, stdClass returned +# D::getSelf(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 55 - 55 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ self ] +} + +object(D)#%d (0) { +} +object(C)#%d (0) { +} +TypeError: C::getSelf(): Return value must be of type C, stdClass returned +# D::getStatic(): +Closure [ public method {closure:%s:%d} ] { + @@ %s 69 - 69 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(D)#%d (0) { +} +TypeError: C::getStatic(): Return value must be of type D, C returned +C::getStatic(): Return value must be of type D, stdClass returned diff --git a/Zend/tests/partial_application/return_type.phpt b/Zend/tests/partial_application/return_type.phpt new file mode 100644 index 000000000000..ae3738e2c660 --- /dev/null +++ b/Zend/tests/partial_application/return_type.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA inherits return type +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [0] { + } + - Return [ array ] +} diff --git a/Zend/tests/partial_application/rfc_examples.inc b/Zend/tests/partial_application/rfc_examples.inc new file mode 100644 index 000000000000..53babc07888b --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples.inc @@ -0,0 +1,76 @@ + [$pfa, $closure]) { + echo "# ", $test, ": "; + $pfaReflector = new ReflectionFunction($pfa); + $closureReflector = new ReflectionFunction($closure); + + try { + if (count($pfaReflector->getParameters()) !== count($closureReflector->getParameters())) { + throw new Exception(sprintf( + "Arity does not match: expected %d, got %d", + count($closureReflector->getParameters()), + count($pfaReflector->getParameters()), + )); + } + + $it = new MultipleIterator(); + $it->attachIterator(new ArrayIterator($pfaReflector->getParameters())); + $it->attachIterator(new ArrayIterator($closureReflector->getParameters())); + foreach ($it as $i => [$pfaParam, $closureParam]) { + [$i] = $i; + if ($pfaParam->getName() !== $closureParam->getName()) { + throw new Exception(sprintf("Name of param %d does not match: %s vs %s", + $i, + $pfaParam->getName(), + $closureParam->getName(), + )); + } + if ((string)$pfaParam->getType() !== (string)$closureParam->getType()) { + throw new Exception(sprintf("Type of param %d does not match: %s vs %s", + $i, + $pfaParam->getType(), + $closureParam->getType(), + )); + } + if ($pfaParam->isOptional() !== $closureParam->isOptional()) { + throw new Exception(sprintf("Optionalness of param %d does not match: %d vs %d", + $i, + $pfaParam->isOptional(), + $closureParam->isOptional(), + )); + } + } + + $args = []; + foreach ($pfaReflector->getParameters() as $i => $p) { + $args[] = match ((string) $p->getType()) { + 'int' => 100 + $i, + 'float' => 100.5 + $i, + '?float' => 100.5 + $i, + 'string' => (string) (100 + $i), + 'Point' => new Point, + 'mixed' => "mixed($i)", + '' => "mixed($i)", + }; + } + + if ($pfaReflector->getClosureThis() !== $closureReflector->getClosureThis()) { + throw new Exception("\$this differs"); + } + + if ($pfa(...$args) !== $closure(...$args)) { + throw new Exception("PFA is not equivalent to closure"); + } + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + echo $pfaReflector; + echo $closureReflector; + return; + } + + echo "Ok\n"; + } +} diff --git a/Zend/tests/partial_application/rfc_examples_const_expr.phpt b/Zend/tests/partial_application/rfc_examples_const_expr.phpt new file mode 100644 index 000000000000..e2fc57440256 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_const_expr.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA RFC examples: "Constant expressions" section +--XFAIL-- +PFA in constant expressions not implemented yet +--FILE-- + +==DONE== +--EXPECTF-- +==DONE== diff --git a/Zend/tests/partial_application/rfc_examples_debug.phpt b/Zend/tests/partial_application/rfc_examples_debug.phpt new file mode 100644 index 000000000000..9c5501090d4e --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_debug.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA RFC examples: "Debug output" section +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): g() +#1 %s(%d): f(1, Object(SensitiveParameterValue), 3) +#2 %s(%d): {closure:pfa:%s}(1, Object(SensitiveParameterValue)) +#3 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/rfc_examples_errors.phpt b/Zend/tests/partial_application/rfc_examples_errors.phpt new file mode 100644 index 000000000000..21818d7446e3 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_errors.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA RFC examples: "Error examples" section +--FILE-- +getMessage(), "\n"; +} + +try { + stuff(?, ?, ?, ?, ?, ?); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + stuff(?, ?, 3.5, $point, i: 5); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +ArgumentCountError: Partial application of stuff() expects at least 4 arguments, 1 given +ArgumentCountError: Partial application of stuff() expects at most 5 arguments, 6 given +Error: Named parameter $i overwrites previous placeholder diff --git a/Zend/tests/partial_application/rfc_examples_eval_order.phpt b/Zend/tests/partial_application/rfc_examples_eval_order.phpt new file mode 100644 index 000000000000..2fd6c9422e92 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_eval_order.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA RFC examples: "Evaluation order" section +--FILE-- + speak($who, getArg()); +print "Arnaud\n"; +$arrow('Larry'); + +$partial = speak(?, getArg()); +print "Arnaud\n"; +$partial('Larry'); + +?> +--EXPECT-- +Arnaud +getArg +Larry: hi +getArg +Arnaud +Larry: hi diff --git a/Zend/tests/partial_application/rfc_examples_extra_args.phpt b/Zend/tests/partial_application/rfc_examples_extra_args.phpt new file mode 100644 index 000000000000..f6a24df8cc71 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_extra_args.phpt @@ -0,0 +1,54 @@ +--TEST-- +PFA RFC examples: "Variadics, func_get_args(), and extraneous arguments" section +--FILE-- + [ + foo(1, ?), + static function (int $j) { return foo(1, $j); }, + ], + 'If a PFA call has a ... placeholder, then any extraneous arguments will be passed through to the underlying function' => [ + foo(1, ?, ...), + static function (int $j) { return foo(1, $j, ...array_slice(func_get_args(), 1)); }, + ], + 'If a PFA call has a ... placeholder and the underlying function is variadic, then the trailing arguments will be forwarded directly but will get “collected” by the variadic parameter as normal' => [ + foo2(1, ?, ...), + static function (int $j, ...$extra) { return foo2(1, $j, ...$extra); }, + ], +]; + +check_equivalence($tests); + +echo "# The extra parameter here will be passed to the closure object, which will simply ignore it:\n"; +var_dump(foo(1, ?)(4, 'ignore me')); + +echo "# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will be accessible only via ''func_get_args()'' et al:\n"; +var_dump(foo(1, ?, ...)(4, 'ignore me')); + +echo "# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will show up as part of the \$extra array:\n"; +var_dump(foo2(1, ?, ...)(4, 'ignore me')); + +?> +==DONE== +--EXPECT-- +# If a PFA call has no ... placeholder, then any extraneous arguments to the resulting closure will be ignored. That is consistent with how manually writing the equivalent closure would behave, and is the same regardless of whether the underlying function is variadic: Ok +# If a PFA call has a ... placeholder, then any extraneous arguments will be passed through to the underlying function: Ok +# If a PFA call has a ... placeholder and the underlying function is variadic, then the trailing arguments will be forwarded directly but will get “collected” by the variadic parameter as normal: Ok +# The extra parameter here will be passed to the closure object, which will simply ignore it: +int(2) +# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will be accessible only via ''func_get_args()'' et al: +int(3) +# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will show up as part of the $extra array: +int(3) +==DONE== diff --git a/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt new file mode 100644 index 000000000000..dd99cb229ae8 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA RFC examples: "Incompatible functions" section +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Cannot call func_get_arg() dynamically diff --git a/Zend/tests/partial_application/rfc_examples_magic_methods.phpt b/Zend/tests/partial_application/rfc_examples_magic_methods.phpt new file mode 100644 index 000000000000..7162a790c57c --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_magic_methods.phpt @@ -0,0 +1,47 @@ +--TEST-- +PFA RFC examples: "Magic methods" section +--FILE-- + [ + $f->method(?, ?), + (function ($f) { + return fn(mixed $arguments0, mixed $arguments1) => $f->method($arguments0, $arguments1); + })($f)->bindTo($f), + ], +]); + +try { + $f->method(?, ?)(a: 1, b: 2); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +# Test 1: Foo::method +Array +( + [0] => mixed(0) + [1] => mixed(1) +) +Foo::method +Array +( + [0] => mixed(0) + [1] => mixed(1) +) +Ok +Error: Unknown named parameter $a diff --git a/Zend/tests/partial_application/rfc_examples_overview.phpt b/Zend/tests/partial_application/rfc_examples_overview.phpt new file mode 100644 index 000000000000..b6fc8c0586c2 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_overview.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFA RFC examples: "Overview" section +--FILE-- + [ + foo(1, ?, 3, 4), + static fn(int $b): int => foo(1, $b, 3, 4), + ], + 'Test 2' => [ + foo(1, ?, 3, ?), + static fn(int $b, int $d): int => foo(1, $b, 3, $d), + ], + 'Test 3' => [ + foo(1, ...), + static fn(int $b, int $c, int $d): int => foo(1, $b, $c, $d), + ], + 'Test 4' => [ + foo(1, 2, ...), + static fn(int $c, int $d): int => foo(1, 2, $c, $d), + ], + 'Test 5' => [ + foo(1, ?, 3, ...), + static fn(int $b, int $d): int => foo(1, $b, 3, $d), + ], +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Test 1: Ok +# Test 2: Ok +# Test 3: Ok +# Test 4: Ok +# Test 5: Ok diff --git a/Zend/tests/partial_application/rfc_examples_scoping.phpt b/Zend/tests/partial_application/rfc_examples_scoping.phpt new file mode 100644 index 000000000000..232d225dca6a --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_scoping.phpt @@ -0,0 +1,113 @@ +--TEST-- +PFA RFC examples: "Scoping" section +--FILE-- + [ + foo(?, ?), + static fn(int $i, int $j): string => foo($i, $j), + ], + 'Static closure 2' => [ + Foo::bar(?, ?), + static fn(int $i, int $j): string => Foo::bar($i, $j), + ], + 'Static closure 3' => [ + foo(?, ?)(1, ?), + static fn(int $j): string => foo(1, $j), + ], + 'Static closure 4' => [ + foo(...)(?), + static fn(int $i): string => foo($i, 0), + ], +]; + +check_equivalence($tests); + +$c = new C(); +$f = $c->f(?); + +echo "# Cannot unbind \$this:\n"; +var_dump($f->bindTo(null, C::class)); // Warning: Cannot unbind $this of method, this will be an error in PHP 9 (returns null) + +echo "# Cannot rebind scope:\n"; +var_dump($f->bindTo($c, CSubClass::class)); // Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 (returns null) + +echo "# Can rebind \$this with subclass:\n"; +var_dump($f->bindTo(new CSubClass, C::class)); // Allowed + +echo "# Cannot rebind \$this with unrelated class:\n"; +$f = $f->bindTo(new Unrelated, C::class); // Warning: Cannot bind method C::{closure:/path/to/test.php:11}() to object of class Unrelated, this will be an error in PHP 9 (returns null) + +echo "# self resolution:\n"; +$c = new CSubClass(); +var_dump($c->f(?)(1)); // string(1) "C" +var_dump($c->g(?)(1)); // string(9) "CSubClass" +var_dump($c->h(1)(1)); // string(1) "C" + +?> +--EXPECTF-- +# Static closure 1: Ok +# Static closure 2: Ok +# Static closure 3: Ok +# Static closure 4: Ok +# Cannot unbind $this: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +NULL +# Cannot rebind scope: + +Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 in %s on line %d +NULL +# Can rebind $this with subclass: +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:pfa:%s}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(53) + ["this"]=> + object(CSubClass)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } +} +# Cannot rebind $this with unrelated class: + +Warning: Cannot bind method C::{closure:pfa:%s}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +# self resolution: +string(1) "C" +string(9) "CSubClass" +string(1) "C" diff --git a/Zend/tests/partial_application/rfc_examples_semantics.phpt b/Zend/tests/partial_application/rfc_examples_semantics.phpt new file mode 100644 index 000000000000..a3ea25c30d6d --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_semantics.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA RFC examples: "Placeholder Semantics" section +--FILE-- + [ + foo(?, ?, ?, ?), + static fn(int $a, int $b, string $c0, string $c1) => foo($a, $b, $c0, $c1), + ], + 'Test 2' => [ + stuff(1, ?, p: ?, f: 3.14, ...), + static fn(string $s, Point $p, int $m = 0) => stuff(1, $s, 3.14, $p, $m), + ], +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Test 1: Ok +# Test 2: Ok diff --git a/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt b/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt new file mode 100644 index 000000000000..d8c4a83ca074 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt @@ -0,0 +1,217 @@ +--TEST-- +PFA RFC examples: "Placeholder Semantics: Examples" section +--FILE-- + [ + stuff(?, ?, ...), + static fn(int $i1, string $s2, float $f3, Point $p4, int $m5 = 0): array + => stuff($i1, $s2, $f3, $p4, $m5), + ], + 'The degenerate "first class callables" case. (Supported since 8.1)' => [ + stuff(...), + static fn(int $i1, string $s2, float $f3, Point $p4, int $m5 = 0): array + => stuff($i1, $s2, $f3, $p4, $m5), + ], + 'Provide some values, require the rest to be provided later (1)' => [ + stuff(1, 'hi', ?, ?, ?), + static fn(float $f3, Point $p4, int $m5): array => stuff(1, 'hi', $f3, $p4, $m5), + ], + 'Provide some values, require the rest to be provided later (2)' => [ + stuff(1, 'hi', ...), + static fn(float $f3, Point $p4, int $m5 = 0): array => stuff(1, 'hi', $f3, $p4, $m5), + ], + 'Provide some values, but not just from the left (1)' => [ + stuff(1, ?, 3.5, ?, ?), + static fn(string $s2, Point $p4, int $m5): array => stuff(1, $s2, 3.5, $p4, $m5), + ], + 'Provide some values, but not just from the left (2)' => [ + stuff(1, ?, 3.5, ...), + static fn(string $s2, Point $p4, int $m5 = 0): array => stuff(1, $s2, 3.5, $p4, $m5), + ], + 'Provide just the last value' => [ + stuff(?, ?, ?, ?, 5), + static fn(int $i1, string $s2, float $f3, Point $p4): array + => stuff($i1, $s2, $f3, $p4, 5), + ], + 'Not accounting for an optional argument means it will always get its default value' => [ + stuff(?, ?, ?, ?), + static fn(int $i1, string $s2, float $f3, Point $p4): array + => stuff($i1, $s2, $f3, $p4), + ], + 'Named arguments can be pulled "out of order", and still work (1)' => [ + stuff(?, ?, f3: 3.5, p4: $point), + static fn(int $i1, string $s2): array => stuff($i1, $s2, 3.5, $point), + ], + 'Named arguments can be pulled "out of order", and still work (2)' => [ + stuff(?, ?, p4: $point, f3: 3.5), + static fn(int $i1, string $s2): array => stuff($i1, $s2, 3.5, $point), + ], + + 'But named placeholders adopt the order listed' => [ + stuff(s2: ?, i1: ?, p4: ?, f3: 3.5), + static fn(string $s2, int $i1, Point $p4): array => stuff($i1, $s2, 3.5, $p4), + ], + 'The ... "everything else" placeholder respects named arguments' => [ + stuff(?, ?, f3: 3.5, p4: $point, ...), + static fn(int $i1, string $s2, int $m5 = 0): array => stuff($i1, $s2, 3.5, $point, $m5), + ], + 'Prefill all parameters, making a "delayed call" or "thunk"' => [ + stuff(1, 'hi', 3.4, $point, 5, ...), + static fn(): array => stuff(1, 'hi', 3.4, $point, 5), + ], + + // Variadics + + 'FCC equivalent. The signature is unchanged' => [ + things(...), + static fn(int $i1, ?float $f3 = null, Point ...$points): array => things(...[$i1, $f3, ...$points]), + ], + 'Provide some values, but allow the variadic to remain variadic' => [ + things(1, 3.14, ...), + static fn(Point ...$points): array => things(1, 3.14, ...$points), + ], + 'In this version, the partial requires precisely four arguments, the last two of which will get received by things() in the variadic parameter. Note too that $f becomes required in this case' => [ + things(?, ?, ?, ?), + static fn(int $i1, ?float $f3, Point $points0, Point $points1): array => things($i1, $f3, $points0, $points1), + ], + + // Esoteric examples + + 'Esoteric 1' => [ + four(...), + static fn(int $a, int $b, int $c, int $d): string => four($a, $b, $c, $d), + ], + 'Esoteric 2' => [ + four(1, 2, ...), + static fn(int $c, int $d): string => four(1, 2, $c, $d), + ], + 'Esoteric 3' => [ + four(1, 2, 3, ?), + static fn(int $d): string => four(1, 2, 3, $d), + ], + 'Esoteric 4' => [ + four(1, ?, ?, 4), + static fn(int $b, int $c): string => four(1, $b, $c, 4), + ], + 'Esoteric 5' => [ + four(1, 2, 3, 4, ...), + static fn(): string => four(1, 2, 3, 4, ...array_slice(func_get_args(), 4)), + ], + 'Esoteric 6' => [ + four(d: 4, a: 1, ...), + static fn(int $b, int $c): string => four(1, $b, $c, 4, ...array_slice(func_get_args(), 4)), + ], + 'Esoteric 7' => [ + four(c: ?, d: 4, b: ?, a: 1), + static fn(int $c, int $b): string => four(1, $b, $c, 4, ...array_slice(func_get_args(), 4)), + ], + + // Other callable styles + + 'This is allowed. Note the method is static, thus the partial closure is static' => [ + E::make(1, ?), + static fn(int $y): E => E::make(1, $y), + ], + 'Note the method is non-static, so the partial closure is non-static' => (function () { + $eMaker = E::make(1, ?); + $e = $eMaker(2); + return [ + $e->foo(?, ?, 3), + (function ($e) { + return fn(int $a, int $b): array => $e->foo($a, $b, 3); + })($e)->bindTo($e), + ]; + })(), + '$c can then be further refined' => (function () { + $eMaker = E::make(1, ?); + $e = $eMaker(2); + $c = $e->foo(?, ?, 3); + return [ + $c(1, ?), + (function ($e) { + return fn(int $b): array => $e->foo(1, $b, 3); + })($e)->bindTo($e), + ]; + })(), + 'RunMe' => (function () { + $r = new RunMe(); + return [ + $r(?, 3), + (function ($r) { + return fn(int $a): string => $r($a, 3); + })($r)->bindTo($r), + ]; + })(), +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Manually specify the first two values, and pull the rest "as is": Ok +# The degenerate "first class callables" case. (Supported since 8.1): Ok +# Provide some values, require the rest to be provided later (1): Ok +# Provide some values, require the rest to be provided later (2): Ok +# Provide some values, but not just from the left (1): Ok +# Provide some values, but not just from the left (2): Ok +# Provide just the last value: Ok +# Not accounting for an optional argument means it will always get its default value: Ok +# Named arguments can be pulled "out of order", and still work (1): Ok +# Named arguments can be pulled "out of order", and still work (2): Ok +# But named placeholders adopt the order listed: Ok +# The ... "everything else" placeholder respects named arguments: Ok +# Prefill all parameters, making a "delayed call" or "thunk": Ok +# FCC equivalent. The signature is unchanged: Ok +# Provide some values, but allow the variadic to remain variadic: Ok +# In this version, the partial requires precisely four arguments, the last two of which will get received by things() in the variadic parameter. Note too that $f becomes required in this case: Ok +# Esoteric 1: Ok +# Esoteric 2: Ok +# Esoteric 3: Ok +# Esoteric 4: Ok +# Esoteric 5: Ok +# Esoteric 6: Ok +# Esoteric 7: Ok +# This is allowed. Note the method is static, thus the partial closure is static: Ok +# Note the method is non-static, so the partial closure is non-static: Ok +# $c can then be further refined: Ok +# RunMe: Ok diff --git a/Zend/tests/partial_application/static_method_001.phpt b/Zend/tests/partial_application/static_method_001.phpt new file mode 100644 index 000000000000..ce4151441c33 --- /dev/null +++ b/Zend/tests/partial_application/static_method_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +PFA supports static methods +--FILE-- + +--EXPECTF-- +Foo::method diff --git a/Zend/tests/partial_application/static_pfa_001.phpt b/Zend/tests/partial_application/static_pfa_001.phpt new file mode 100644 index 000000000000..bc0f65b1ffd8 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_001.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA of static method is static +--FILE-- +f(?); +echo new ReflectionFunction($f), "\n"; +$f(1); + +// Warns +var_dump($f->bindTo(new C)); + +?> +--EXPECTF-- +Closure [ static public method {closure:pfa:%s:9} ] { + @@ %s 9 - 9 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) +Closure [ static public method {closure:pfa:%s:13} ] { + @@ %s 13 - 13 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +NULL diff --git a/Zend/tests/partial_application/static_pfa_002.phpt b/Zend/tests/partial_application/static_pfa_002.phpt new file mode 100644 index 000000000000..5222109958b8 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_002.phpt @@ -0,0 +1,35 @@ +--TEST-- +PFA of instance method is not static +--FILE-- +f(?); +echo new ReflectionFunction($f), "\n"; +$f($c); + +$c2 = new C(); +$f->bindTo($c2)($c2); + +?> +--EXPECTF-- +Closure [ public method {closure:pfa:%s:10} ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +object(C)#%d (0) { +} +bool(true) +object(C)#%d (0) { +} +bool(true) diff --git a/Zend/tests/partial_application/static_pfa_003.phpt b/Zend/tests/partial_application/static_pfa_003.phpt new file mode 100644 index 000000000000..e7b3c187e187 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA of standalone function is static +--FILE-- +bindTo(new class {})); + +?> +--EXPECTF-- +Closure [ static function {closure:pfa:%s:7} ] { + @@ %s 7 - 7 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +NULL diff --git a/Zend/tests/partial_application/static_pfa_004.phpt b/Zend/tests/partial_application/static_pfa_004.phpt new file mode 100644 index 000000000000..093430232d43 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_004.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFA of non-static closure is not static +--FILE-- +getMessage(), "\n"; + } +}; + +echo "# Original PFA\n"; + +$f = $c(?); +echo new ReflectionFunction($f), "\n"; +$f($c); + +echo "# Re-bound PFA\n"; + +$c2 = new class {}; +$f->bindTo($c2)($c2); + +?> +--EXPECTF-- +# Original PFA +Closure [ function {closure:pfa:%s:13} ] { + @@ %s 13 - 13 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +Error: Using $this when not in object context +# Re-bound PFA +object(class@anonymous)#3 (0) { +} +bool(true) diff --git a/Zend/tests/partial_application/static_pfa_005.phpt b/Zend/tests/partial_application/static_pfa_005.phpt new file mode 100644 index 000000000000..2783485b2f49 --- /dev/null +++ b/Zend/tests/partial_application/static_pfa_005.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA of static closure is static +--FILE-- +bindTo(new class {})); + +?> +--EXPECTF-- +Closure [ static function {closure:pfa:%s:7} ] { + @@ %s 7 - 7 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $a ] + } +} + +int(1) + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +NULL diff --git a/Zend/tests/partial_application/static_polymorphism.phpt b/Zend/tests/partial_application/static_polymorphism.phpt new file mode 100644 index 000000000000..5f121c65c0e8 --- /dev/null +++ b/Zend/tests/partial_application/static_polymorphism.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA: static polymorphism +--FILE-- + +--EXPECT-- +P::m +string(1) "a" +C::m +array(0) { +} +P::m +string(1) "a" +C::m +array(0) { +} diff --git a/Zend/tests/partial_application/static_vars_001.phpt b/Zend/tests/partial_application/static_vars_001.phpt new file mode 100644 index 000000000000..610cddf37b54 --- /dev/null +++ b/Zend/tests/partial_application/static_vars_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +PFA static variables are shared (001) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/static_vars_002.phpt b/Zend/tests/partial_application/static_vars_002.phpt new file mode 100644 index 000000000000..4e6cc82b720d --- /dev/null +++ b/Zend/tests/partial_application/static_vars_002.phpt @@ -0,0 +1,22 @@ +--TEST-- +PFA static variables are shared (002) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/static_vars_003.phpt b/Zend/tests/partial_application/static_vars_003.phpt new file mode 100644 index 000000000000..04328bf717b8 --- /dev/null +++ b/Zend/tests/partial_application/static_vars_003.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA static variables are shared (003) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt b/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt new file mode 100644 index 000000000000..506092655217 --- /dev/null +++ b/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFAs forwards superfluous args iff a variadic placeholder is specified +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(1) { + [0]=> + int(1) +} +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/tests/partial_application/this.phpt b/Zend/tests/partial_application/this.phpt new file mode 100644 index 000000000000..bda6900bae39 --- /dev/null +++ b/Zend/tests/partial_application/this.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA $this +--FILE-- +method(new stdClass, ...); + +$baz = $bar(new stdClass, ...); + +var_dump($foo === $baz()); +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/partial_application/variation_call_001.phpt b/Zend/tests/partial_application/variation_call_001.phpt new file mode 100644 index 000000000000..b9c1c19ec9c6 --- /dev/null +++ b/Zend/tests/partial_application/variation_call_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +PFA variation: call +--FILE-- +method(?, new Param); + +$closure(1); + +$closure->call(/* newThis: */ new Foo(), 10); +?> +--EXPECT-- +Bar: 1, Param +Foo: 10, Param diff --git a/Zend/tests/partial_application/variation_closure_001.phpt b/Zend/tests/partial_application/variation_closure_001.phpt new file mode 100644 index 000000000000..1d31f22c7f8a --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +PFA variation: Closure +--FILE-- + +--EXPECTF-- +Closure [ function {closure:%s:%d} ] { + @@ %s 5 - 5 + + - Bound Variables [2] { + Variable #0 [ $fn ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +Closure [ function {closure:pfa:%s:%d} ] { + @@ %s 10 - 10 + + - Bound Variables [2] { + Variable #0 [ $fn2 ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $fn ] + } +} diff --git a/Zend/tests/partial_application/variation_closure_002.phpt b/Zend/tests/partial_application/variation_closure_002.phpt new file mode 100644 index 000000000000..41abe0aaab0e --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_002.phpt @@ -0,0 +1,28 @@ +--TEST-- +PFA variation: Closure::__invoke() +--FILE-- +__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +$function(10); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %svariation_closure_002.php 6 - 6 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +int(10) diff --git a/Zend/tests/partial_application/variation_closure_003.phpt b/Zend/tests/partial_application/variation_closure_003.phpt new file mode 100644 index 000000000000..da567e179853 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_003.phpt @@ -0,0 +1,46 @@ +--TEST-- +PFA variation: Closure::__invoke() with $this +--FILE-- +bar(); + +$function = $closure->__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +var_dump($function(10)); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %svariation_closure_003.php 14 - 14 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +array(2) { + [0]=> + object(Foo)#1 (0) { + } + [1]=> + array(2) { + [0]=> + int(1) + [1]=> + int(10) + } +} diff --git a/Zend/tests/partial_application/variation_debug_001.phpt b/Zend/tests/partial_application/variation_debug_001.phpt new file mode 100644 index 000000000000..b3e20b755e0d --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +PFA variation: var_dump(), user function +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_debug_001.php" + ["line"]=> + int(6) + ["static"]=> + array(4) { + ["b"]=> + object(stdClass)#%d (0) { + } + ["c"]=> + int(20) + ["c0"]=> + object(stdClass)#%d (0) { + } + ["extra_named_params"]=> + array(1) { + ["four"]=> + int(4) + } + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt new file mode 100644 index 000000000000..9a1121ed7918 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -0,0 +1,49 @@ +--TEST-- +PFA variation: var_dump(), internal function +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_debug_002.php" + ["line"]=> + int(2) + ["static"]=> + array(3) { + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["arrays0"]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } + ["extra_named_params"]=> + array(1) { + ["four"]=> + object(stdClass)#%d (0) { + } + } + } + ["parameter"]=> + array(2) { + ["$callback"]=> + string(10) "" + ["$arrays"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/variation_ex_001.phpt b/Zend/tests/partial_application/variation_ex_001.phpt new file mode 100644 index 000000000000..0f36e90f616d --- /dev/null +++ b/Zend/tests/partial_application/variation_ex_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA variation: UAF in cleanup unfinished calls +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +ArgumentCountError: Partial application of {closure:%s:%d}() expects at most 0 arguments, 1 given diff --git a/Zend/tests/partial_application/variation_gc_001.phpt b/Zend/tests/partial_application/variation_gc_001.phpt new file mode 100644 index 000000000000..2db49cc74363 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA variation: GC (001) +--FILE-- +method = self::__construct(new stdClass, ...); + } +} + +$foo = new Foo(new stdClass); +$foo->bar = $foo; + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_002.phpt b/Zend/tests/partial_application/variation_gc_002.phpt new file mode 100644 index 000000000000..31c721e8e681 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_002.phpt @@ -0,0 +1,11 @@ +--TEST-- +PFA variation: GC (002) +--FILE-- +prop = var_dump($obj, ?); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_003.phpt b/Zend/tests/partial_application/variation_gc_003.phpt new file mode 100644 index 000000000000..23cdaa1c666f --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_003.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA variation: GC (003) +--FILE-- +prop = test(?, x: $obj); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_invoke_001.phpt b/Zend/tests/partial_application/variation_invoke_001.phpt new file mode 100644 index 000000000000..9285c08da19b --- /dev/null +++ b/Zend/tests/partial_application/variation_invoke_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +PFA variation: __invoke() +--FILE-- +__invoke(32)); + +try { + $foo->nothing(); +} catch (Error $ex) { + echo $ex::class, ": ", $ex->getMessage(), "\n"; +} +?> +--EXPECT-- +int(42) +Error: Call to undefined method Closure::nothing() diff --git a/Zend/tests/partial_application/variation_nocall_001.phpt b/Zend/tests/partial_application/variation_nocall_001.phpt new file mode 100644 index 000000000000..3fbb3ec8d8c3 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_001.phpt @@ -0,0 +1,12 @@ +--TEST-- +PFA variation: no call args leak +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_nocall_002.phpt b/Zend/tests/partial_application/variation_nocall_002.phpt new file mode 100644 index 000000000000..cd4823f1bd05 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA variation: no call, order of destruction +--FILE-- +id, "\n"; + } +} +$foo = new Foo; +$f = $foo->method(?); +$g = $f(?); + +$map = new WeakMap(); +$map[$f] = new Dtor(1); +$map[$g] = new Dtor(2); + +unset($f); +unset($g); + +echo "OK"; +?> +--EXPECT-- +Dtor::__destruct 2 +Dtor::__destruct 1 +OK diff --git a/Zend/tests/partial_application/variation_parent_001.phpt b/Zend/tests/partial_application/variation_parent_001.phpt new file mode 100644 index 000000000000..98095f45009a --- /dev/null +++ b/Zend/tests/partial_application/variation_parent_001.phpt @@ -0,0 +1,76 @@ +--TEST-- +PFA variation: parent +--FILE-- +method(10, ...); +$baz = $bar(20, ...); + +var_dump($baz, $c = $baz(), $c() === $foo); +?> +--EXPECTF-- +object(Closure)#%d (6) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_parent_001.php" + ["line"]=> + int(12) + ["static"]=> + array(2) { + ["fn"]=> + object(Closure)#%d (6) { + ["name"]=> + string(%d) "{closure:%s:%d}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(11) + ["static"]=> + array(1) { + ["a"]=> + int(10) + } + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(2) { + ["$b"]=> + string(10) "" + ["$c"]=> + string(10) "" + } + } + ["b"]=> + int(20) + } + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$c"]=> + string(10) "" + } +} +object(Closure)#%d (4) { + ["name"]=> + string(25) "{closure:Foo::method():4}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(4) + ["this"]=> + object(Foo)#%d (0) { + } +} +bool(true) diff --git a/Zend/tests/partial_application/variation_scope_001.phpt b/Zend/tests/partial_application/variation_scope_001.phpt new file mode 100644 index 000000000000..0dcea0921c43 --- /dev/null +++ b/Zend/tests/partial_application/variation_scope_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +PFA variation: called scope +--FILE-- +method(new stdClass, ...); + +$bar(); +?> +--EXPECT-- +Foo::method diff --git a/Zend/tests/partial_application/variation_strict_001.phpt b/Zend/tests/partial_application/variation_strict_001.phpt new file mode 100644 index 000000000000..835f6e138055 --- /dev/null +++ b/Zend/tests/partial_application/variation_strict_001.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA variation: strict_types declared +--FILE-- +getMessage()); +} +?> +--EXPECTF-- +TypeError: {closure:%s:%d}(): Argument #1 ($int) must be of type int, string given, called in %s on line %d diff --git a/Zend/tests/partial_application/variation_variadics_001.phpt b/Zend/tests/partial_application/variation_variadics_001.phpt new file mode 100644 index 000000000000..4710ca4d3b99 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +PFA variation: variadics, user function +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $b0 ] + } + + - Parameters [1] { + Parameter #0 [ ...$b ] + } +} +int(10) +int(100) +int(1000) +int(10000) diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt new file mode 100644 index 000000000000..4269dd0e66f0 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA variation: variadics, internal function +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %svariation_variadics_002.php 2 - 2 + + - Bound Variables [2] { + Variable #0 [ $format ] + Variable #1 [ $values0 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$values ] + } + - Return [ string ] +} +100 1000 10000 diff --git a/Zend/tests/partial_application/variation_variadics_004.phpt b/Zend/tests/partial_application/variation_variadics_004.phpt new file mode 100644 index 000000000000..228404dc8272 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_004.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA variation: variadics and optional args +--FILE-- + +--EXPECT-- +# Bound year, pass day: +2006-01-02 +# Bound month, pass day: +2005-12-02 +# Bound month, bound year, pass day: +2016-12-02 +# Bound month, no args: +2005-12-01 +# Bound month, bound year, no args: +2016-12-01 diff --git a/Zend/tests/partial_application/variation_variadics_006.phpt b/Zend/tests/partial_application/variation_variadics_006.phpt new file mode 100644 index 000000000000..cda62a2f3bfb --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_006.phpt @@ -0,0 +1,17 @@ +--TEST-- +PFA variation: named may overwrite variadic placeholder +--FILE-- + +--EXPECTF-- +array(1) { + [0]=> + string(1) "a" +} diff --git a/Zend/tests/partial_application/variation_variadics_007.phpt b/Zend/tests/partial_application/variation_variadics_007.phpt new file mode 100644 index 000000000000..9624e05b449b --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_007.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA variation: extra through variadic +--FILE-- + $a + $b)); +?> +--EXPECT-- +int(3) diff --git a/Zend/tests/partial_application/variation_variadics_008.phpt b/Zend/tests/partial_application/variation_variadics_008.phpt new file mode 100644 index 000000000000..4190bb759e68 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_008.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA variation: variadics wrong signature checked +--FILE-- +getMessage() . PHP_EOL; +} +?> +--EXPECTF-- +Partial application of {closure:%s:%d}() expects at most 1 arguments, 2 given diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 65834adbafff..f567137b25d2 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -374,7 +374,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void) ); } -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va) /* {{{ */ +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic_ex( + const zend_function *function, uint32_t arg_num, + zend_class_entry *error_ce, const char *format, va_list va) { zend_string *func_name; const char *arg_name; @@ -383,8 +385,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_en return; } - func_name = get_active_function_or_method_name(); - arg_name = get_active_function_arg_name(arg_num); + func_name = get_function_or_method_name(function); + arg_name = get_function_arg_name(function, arg_num); zend_vspprintf(&message, 0, format, va); zend_throw_error(error_ce, "%s(): Argument #%d%s%s%s %s", @@ -394,8 +396,27 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_en efree(message); zend_string_release(func_name); } + +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va) /* {{{ */ +{ + ZEND_ASSERT(zend_is_executing()); + + const zend_function *function = zend_active_function(); + + zend_argument_error_variadic_ex(function, arg_num, error_ce, format, va); +} /* }}} */ +ZEND_API ZEND_COLD void zend_argument_error_ex(const zend_function *function, + uint32_t arg_num, zend_class_entry *error_ce, const char *format, ...) +{ + va_list va; + + va_start(va, format); + zend_argument_error_variadic_ex(function, arg_num, error_ce, format, va); + va_end(va); +} + ZEND_API ZEND_COLD void zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...) /* {{{ */ { va_list va; @@ -406,6 +427,18 @@ ZEND_API ZEND_COLD void zend_argument_error(zend_class_entry *error_ce, uint32_t } /* }}} */ +ZEND_API ZEND_COLD void zend_argument_type_error_ex( + const zend_function *function, uint32_t arg_num, + const char *format, ...) +{ + va_list va; + + va_start(va, format); + zend_argument_error_variadic_ex(function, arg_num, + zend_ce_type_error, format, va); + va_end(va); +} + ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format, ...) /* {{{ */ { va_list va; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 2487c8b632f2..c0e90cf7efd4 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -1573,8 +1573,16 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, ch ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_or_null_error(uint32_t num, char *error); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic_ex( + const zend_function *function, uint32_t arg_num, + zend_class_entry *error_ce, const char *format, va_list va); ZEND_API ZEND_COLD void zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...); +ZEND_API ZEND_COLD void zend_argument_error_ex(const zend_function *function, + uint32_t arg_num, zend_class_entry *error_ce, const char *format, ...); ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format, ...); +ZEND_API ZEND_COLD void zend_argument_type_error_ex( + const zend_function *function, uint32_t arg_num, + const char *format, ...); ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format, ...); ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num); ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, const zend_class_entry *old_ce); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a7e26711cd17..5b1ff675e1c0 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1459,6 +1459,16 @@ ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast) return ref; } +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast) +{ + ZEND_ASSERT(ast != NULL); + + zend_ast *buf = zend_ast_alloc(zend_ast_tree_size(ast)); + zend_ast_tree_copy(ast, buf); + + return buf; +} + ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) { tail_call: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 24b77d7d3493..f19843ed3f53 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -350,7 +350,10 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast *ast, zend_class_entry *scope, bool *short_circuited_ptr, zend_ast_evaluate_ctx *ctx); ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix); +/* Copies 'ast' to the heap, returns a refcounted AST reference */ ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast); +/* Duplicates 'ast' on the arena */ +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast); ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast); ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast); diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 840d2dbe32e1..9b3d6d8c6a60 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -27,6 +27,14 @@ #include "zend_globals.h" #include "zend_closures_arginfo.h" +/* Closure is a PFA */ +#define ZEND_PARTIAL OBJ_EXTRA_FLAG_PRIV_1 +/* Closure is a PFA of a Closure. Rebinding the PFA requires rebinding the inner Closure. */ +#define ZEND_PARTIAL_OF_CLOSURE OBJ_EXTRA_FLAG_PRIV_2 + +#define ZEND_CLOSURE_FLAGS(closure) ((closure)->std.extra_flags & (ZEND_PARTIAL|ZEND_PARTIAL_OF_CLOSURE)) +#define ZEND_CLOSURE_IS_FAKE(closure) ((closure)->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) + typedef struct _zend_closure { zend_object std; zend_function func; @@ -40,6 +48,7 @@ ZEND_API zend_class_entry *zend_ce_closure; static zend_object_handlers closure_handlers; static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags); ZEND_METHOD(Closure, __invoke) /* {{{ */ { @@ -77,7 +86,9 @@ static bool zend_valid_closure_binding( zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */ { zend_function *func = &closure->func; - bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + // TODO: rename variable + bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0 + || (closure->std.extra_flags & ZEND_PARTIAL); if (newthis) { if (func->common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9"); @@ -161,7 +172,9 @@ ZEND_METHOD(Closure, call) if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { zval new_closure; - zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); + zend_create_closure_ex(&new_closure, &closure->func, newclass, + closure->called_scope, newthis, + ZEND_CLOSURE_IS_FAKE(closure), ZEND_CLOSURE_FLAGS(closure)); closure = (zend_closure *) Z_OBJ(new_closure); fci_cache.function_handler = &closure->func; @@ -177,6 +190,7 @@ ZEND_METHOD(Closure, call) memset(&fake_closure->std, 0, sizeof(fake_closure->std)); fake_closure->std.gc.refcount = 1; fake_closure->std.gc.u.type_info = GC_NULL; + fake_closure->std.extra_flags = ZEND_CLOSURE_FLAGS(closure); ZVAL_UNDEF(&fake_closure->this_ptr); fake_closure->called_scope = NULL; my_function = &fake_closure->func; @@ -223,7 +237,7 @@ ZEND_METHOD(Closure, call) } /* }}} */ -static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str) +static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str) { zend_class_entry *ce, *called_scope; zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure); @@ -235,14 +249,15 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z ce = closure->func.common.scope; } else if ((ce = zend_lookup_class(scope_str)) == NULL) { zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str)); - RETURN_NULL(); + RETVAL_NULL(); + return FAILURE; } } else { ce = NULL; } if (!zend_valid_closure_binding(closure, newthis, ce)) { - return; + return FAILURE; } if (newthis) { @@ -251,7 +266,32 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z called_scope = ce; } - zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); + zend_create_closure_ex(return_value, &closure->func, ce, called_scope, newthis, + ZEND_CLOSURE_IS_FAKE(closure), ZEND_CLOSURE_FLAGS(closure)); + + if (ZEND_CLOSURE_FLAGS(closure) & ZEND_PARTIAL_OF_CLOSURE) { + /* Re-bind the inner closure */ + + closure = (zend_closure*)Z_OBJ_P(return_value); + HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); + ZEND_ASSERT(static_variables->nNumOfElements > 0); + zval *inner = &static_variables->arData[0].val; + ZEND_ASSERT(Z_TYPE_P(inner) == IS_OBJECT && Z_OBJCE_P(inner) == zend_ce_closure); + + zval new_inner; + if (do_closure_bind(&new_inner, inner, newthis, scope_obj, scope_str) != SUCCESS) { + ZEND_UNREACHABLE(); + zval_ptr_dtor(return_value); + ZVAL_NULL(return_value); + return FAILURE; + } + + zend_object *garbage = Z_OBJ_P(inner); + ZVAL_COPY_VALUE(inner, &new_inner); + zend_object_release(garbage); + } + + return SUCCESS; } /* {{{ Create a closure from another one and bind to another object and scope */ @@ -588,8 +628,9 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */ zend_closure *closure = (zend_closure *)zobject; zval result; - zend_create_closure(&result, &closure->func, - closure->func.common.scope, closure->called_scope, &closure->this_ptr); + zend_create_closure_ex(&result, &closure->func, + closure->func.common.scope, closure->called_scope, &closure->this_ptr, + ZEND_CLOSURE_IS_FAKE(closure), ZEND_CLOSURE_FLAGS(closure)); return Z_OBJ(result); } /* }}} */ @@ -757,7 +798,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ } /* }}} */ -static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */ +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags) /* {{{ */ { zend_closure *closure; void *ptr; @@ -765,6 +806,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en object_init_ex(res, zend_ce_closure); closure = (zend_closure *)Z_OBJ_P(res); + closure->std.extra_flags = flags; if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) { /* use dummy scope if we're binding an object without specifying a scope */ @@ -862,14 +904,14 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) { zend_create_closure_ex(res, func, scope, called_scope, this_ptr, - /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0); + /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0, 0); } ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ { zend_closure *closure; - zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true); + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true, 0); closure = (zend_closure *)Z_OBJ_P(res); closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; @@ -879,6 +921,16 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas } /* }}} */ +ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure) +{ + int flags = ZEND_PARTIAL; + if (partial_of_closure) { + flags |= ZEND_PARTIAL_OF_CLOSURE; + } + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, + /* is_fake */ false, flags); +} + void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */ zval instance; zend_internal_function trampoline; diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 2ff4934f2a3a..305d82e5015a 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -36,6 +36,7 @@ extern ZEND_API zend_class_entry *zend_ce_closure; ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); +ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure); ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj); ZEND_API zval* zend_get_closure_this_ptr(zval *obj); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 105f99d24171..33e24fea99b1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -28,6 +28,8 @@ #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_types.h" +#include "zend_portability.h" +#include "zend_string.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h" @@ -102,6 +104,8 @@ static void zend_compile_expr(znode *result, zend_ast *ast); static void zend_compile_stmt(zend_ast *ast); static void zend_compile_assign(znode *result, zend_ast *ast, bool stmt, uint32_t type); +static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg); + #ifdef ZEND_CHECK_STACK_LIMIT zend_never_inline static void zend_stack_limit_error(void) { @@ -3766,12 +3770,16 @@ static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg return (uint32_t) -1; } -static uint32_t zend_compile_args( - zend_ast *ast, const zend_function *fbc, bool *may_have_extra_named_args) /* {{{ */ +static uint32_t zend_compile_args_ex( + zend_ast *ast, const zend_function *fbc, + bool *may_have_extra_named_args, + bool is_call_partial, bool *uses_variadic_placeholder_p, + zval *named_positions) /* {{{ */ { const zend_ast_list *args = zend_ast_get_list(ast); uint32_t i; bool uses_arg_unpack = false; + bool uses_variadic_placeholder = false; uint32_t arg_count = 0; /* number of arguments not including unpacks */ /* Whether named arguments are used syntactically, to enforce language level limitations. @@ -3797,6 +3805,11 @@ static uint32_t zend_compile_args( "Cannot use argument unpacking after named arguments"); } + if (is_call_partial) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot combine partial application and unpacking"); + } + /* Unpack may contain named arguments. */ may_have_undef = true; if (!fbc || (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) { @@ -3837,18 +3850,75 @@ static uint32_t zend_compile_args( may_have_undef = true; *may_have_extra_named_args = true; } + + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder must be last"); + } } else { if (uses_arg_unpack) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after argument unpacking"); } - if (uses_named_args) { + bool is_variadic_placeholder = arg->kind == ZEND_AST_PLACEHOLDER_ARG + && arg->attr == ZEND_PLACEHOLDER_VARIADIC; + + if (uses_named_args && !is_variadic_placeholder) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after named argument"); } - arg_count++; + if (uses_variadic_placeholder) { + if (is_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder may only appear once"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder must be last"); + } + } + + if (!is_variadic_placeholder) { + arg_count++; + } + } + + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (uses_arg_unpack) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot combine partial application and unpacking"); + } + + if (arg->attr == ZEND_PLACEHOLDER_VARIADIC) { + uses_variadic_placeholder = true; + /* Do not emit ZEND_SEND_PLACEHOLDER: We represent the variadic + * placeholder with a flag on the ZEND_CONVERT_CALLABLE_PARTIAL + * op instead. */ + continue; + } + + if (arg_name) { + if (Z_ISUNDEF_P(named_positions)) { + array_init(named_positions); + } + zval tmp; + ZVAL_LONG(&tmp, zend_hash_num_elements(Z_ARRVAL_P(named_positions))); + zend_hash_add(Z_ARRVAL_P(named_positions), arg_name, &tmp); + } + + opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL); + if (arg_name) { + opline->op2_type = IS_CONST; + zend_string_addref(arg_name); + opline->op2.constant = zend_add_literal_string(&arg_name); + opline->result.num = zend_alloc_cache_slots(2); + } else if (arg->attr != ZEND_PLACEHOLDER_VARIADIC) { + opline->op2.opline_num = arg_num; + opline->result.var = EX_NUM_TO_VAR(arg_num - 1); + } + + continue; } /* Treat passing of $GLOBALS the same as passing a call. @@ -3963,14 +4033,24 @@ static uint32_t zend_compile_args( } } - if (may_have_undef) { - zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); + if (!is_call_partial) { + if (may_have_undef) { + zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); + } + } else { + *uses_variadic_placeholder_p = uses_variadic_placeholder; } return arg_count; } /* }}} */ +static uint32_t zend_compile_args(zend_ast *ast, const zend_function *fbc, + bool *may_have_extra_named_args) /* {{{ */ +{ + return zend_compile_args_ex(ast, fbc, may_have_extra_named_args, false, NULL, NULL); +} + ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *fbc, bool result_used) /* {{{ */ { uint32_t no_discard = result_used ? 0 : ZEND_ACC_NODISCARD; @@ -4004,6 +4084,38 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f } /* }}} */ +static void zend_compile_call_partial(znode *result, uint32_t arg_count, + bool may_have_extra_named_args, bool uses_variadic_placeholder, + zval *named_positions, uint32_t opnum_init, const zend_function *fbc) { + + zend_op *init_opline = &CG(active_op_array)->opcodes[opnum_init]; + + init_opline->extended_value = arg_count; + + ZEND_ASSERT(init_opline->opcode != ZEND_NEW); + + if (init_opline->opcode == ZEND_INIT_FCALL) { + init_opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); + } + + zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL, + NULL, NULL); + + opline->op1.num = zend_alloc_cache_slots(2); + + if (may_have_extra_named_args) { + opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; + } + if (uses_variadic_placeholder) { + opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER; + } + + if (!Z_ISUNDEF_P(named_positions)) { + opline->op2.constant = zend_add_literal(named_positions); + opline->op2_type = IS_CONST; + } +} + static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const zend_function *fbc, uint32_t lineno, uint32_t type) /* {{{ */ { zend_op *opline; @@ -4019,23 +4131,43 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression"); } - zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders"); - } + zend_ast_list *fcc_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (opcode == ZEND_INIT_FCALL) { - opline->op1.num = zend_vm_calc_used_stack(0, fbc); - } + /* FCCs are a special case of PFAs with a single variadic placeholder */ + if (fcc_args->children == 1 && fcc_args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC) { - zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); - if (opcode == ZEND_INIT_FCALL - || opcode == ZEND_INIT_FCALL_BY_NAME - || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { - callable_convert_op->extended_value = zend_alloc_cache_slot(); - } else { - callable_convert_op->extended_value = (uint32_t)-1; + if (opline->opcode == ZEND_INIT_FCALL) { + opline->op1.num = zend_vm_calc_used_stack(0, fbc); + } + + zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); + if (opcode == ZEND_INIT_FCALL + || opcode == ZEND_INIT_FCALL_BY_NAME + || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { + callable_convert_op->extended_value = zend_alloc_cache_slot(); + } else { + callable_convert_op->extended_value = (uint32_t)-1; + } + + return true; } + + args_ast = ((zend_ast_fcc*)args_ast)->args; + + bool may_have_extra_named_args; + bool uses_variadic_placeholder; + + zval named_positions; + ZVAL_UNDEF(&named_positions); + + uint32_t arg_count = zend_compile_args_ex(args_ast, fbc, + &may_have_extra_named_args, true, &uses_variadic_placeholder, + &named_positions); + + zend_compile_call_partial(result, arg_count, + may_have_extra_named_args, uses_variadic_placeholder, + &named_positions, opnum_init, fbc); + return true; } @@ -5121,42 +5253,21 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg } zend_ast *callback = args->child[0]; - - /* Bail out if the callback is not a FCC/PFA. */ - zend_ast *args_ast; - switch (callback->kind) { - case ZEND_AST_CALL: - case ZEND_AST_STATIC_CALL: - args_ast = zend_ast_call_get_args(callback); - if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { - return FAILURE; - } - - break; - default: - return FAILURE; - } - - /* Bail out if the callback is assert() due to the AST stringification logic - * breaking for the generated call. - */ - if (callback->kind == ZEND_AST_CALL - && callback->child[0]->kind == ZEND_AST_ZVAL - && Z_TYPE_P(zend_ast_get_zval(callback->child[0])) == IS_STRING - && zend_string_equals_literal_ci(zend_ast_get_str(callback->child[0]), "assert")) { - return FAILURE; - } - - zend_ast_list *callback_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (callback_args->children != 1 || callback_args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - /* Full PFA is not yet implemented, will fail in zend_compile_call_common(). */ + if (callback->kind != ZEND_AST_CALL && callback->kind != ZEND_AST_STATIC_CALL) { return FAILURE; } znode value; value.op_type = IS_TMP_VAR; value.u.op.var = get_temporary_variable(); - zend_ast *call_args = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&value)); + + zend_ast *call_args = zend_partial_apply(callback, + zend_ast_create_znode(&value)); + if (!call_args) { + CG(active_op_array)->T--; + /* The callback is not a FCC/PFA, or is not optimizable */ + return FAILURE; + } zend_op *opline; @@ -6770,6 +6881,76 @@ static bool zend_is_pipe_optimizable_callable_name(zend_ast *ast) return true; } +static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg) +{ + if (callable_ast->kind != ZEND_AST_CALL + && callable_ast->kind != ZEND_AST_STATIC_CALL + && callable_ast->kind != ZEND_AST_METHOD_CALL) { + return NULL; + } + + zend_ast *args_ast = zend_ast_call_get_args(callable_ast); + if (!args_ast || args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { + return NULL; + } + + if (callable_ast->kind == ZEND_AST_CALL && + !zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { + return NULL; + } + + zend_ast_list *arg_list = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); + + zend_ast *first_placeholder = NULL; + bool uses_named_args = false; + + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg->kind == ZEND_AST_NAMED_ARG) { + uses_named_args = true; + arg = arg->child[1]; + } + + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (first_placeholder == NULL) { + first_placeholder = arg; + } else { + /* A PFA with multiple placeholders is unexpected in this + * context, and will usually error due to a missing argument, + * so we don't optimize those. */ + return NULL; + } + if (arg->attr == ZEND_PLACEHOLDER_VARIADIC && uses_named_args) { + /* A PFA with both a variadic placeholder and named args can not + * be optimized because this would result in a positional arg + * after a named arg: f(name: $v, ...) -> f(name: $v, pipe_arg). + * Arg placeholders ('?') are safe since they are not allowed + * after named args. */ + return NULL; + } + } + } + + ZEND_ASSERT(first_placeholder); + + zend_ast *new_arg_list = zend_ast_create_list(0, arg_list->kind); + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg == first_placeholder) { + new_arg_list = zend_ast_list_add(new_arg_list, pipe_arg); + } else if (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1] == first_placeholder) { + zend_ast *name = arg->child[0]; + new_arg_list = zend_ast_list_add(new_arg_list, + zend_ast_create(ZEND_AST_NAMED_ARG, name, pipe_arg)); + } else { + new_arg_list = zend_ast_list_add(new_arg_list, arg); + } + } + + return new_arg_list; +} + static void zend_compile_pipe(znode *result, zend_ast *ast, uint32_t type) { zend_ast *operand_ast = ast->child[0]; @@ -6794,29 +6975,35 @@ static void zend_compile_pipe(znode *result, zend_ast *ast, uint32_t type) } /* Turn the operand into a function parameter list. */ - zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&wrapped_operand_result)); + zend_ast *arg = zend_ast_create_znode(&wrapped_operand_result); zend_ast *fcall_ast; znode callable_result; + zend_ast *pfa_arg_list_ast = NULL; - /* Turn $foo |> bar(...) into bar($foo). */ - if (callable_ast->kind == ZEND_AST_CALL - && callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT - && zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { - fcall_ast = zend_ast_create(ZEND_AST_CALL, - callable_ast->child[0], arg_list_ast); - /* Turn $foo |> bar::baz(...) into bar::baz($foo). */ - } else if (callable_ast->kind == ZEND_AST_STATIC_CALL - && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); - /* Turn $foo |> $bar->baz(...) into $bar->baz($foo). */ - } else if (callable_ast->kind == ZEND_AST_METHOD_CALL - && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); + /* Turn $foo |> PFA into plain function call if possible */ + if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) { + switch (callable_ast->kind) { + case ZEND_AST_CALL: + fcall_ast = zend_ast_create(ZEND_AST_CALL, + callable_ast->child[0], pfa_arg_list_ast); + break; + case ZEND_AST_STATIC_CALL: + fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + case ZEND_AST_METHOD_CALL: + fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + default: + ZEND_UNREACHABLE(); + } /* Turn $foo |> $expr into ($expr)($foo) */ } else { + zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); zend_compile_expr(&callable_result, callable_ast); callable_ast = zend_ast_create_znode(&callable_result); fcall_ast = zend_ast_create(ZEND_AST_CALL, @@ -11808,6 +11995,13 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr) if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) { zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); } + + zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args); + if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { + // TODO: PFAs + zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); + } + ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr); switch ((*ast_ptr)->kind) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0e31332c97f0..db27bed83c5e 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -958,6 +958,7 @@ struct _zend_arena; ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type); ZEND_API zend_op_array *compile_string(zend_string *source_string, const char *filename, zend_compile_position position); ZEND_API zend_op_array *compile_filename(int type, zend_string *filename); +ZEND_API zend_op_array *zend_compile_ast(zend_ast *ast, int type, zend_string *filename); ZEND_API zend_ast *zend_compile_string_to_ast( zend_string *code, struct _zend_arena **ast_arena, zend_string *filename); ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count, ...); @@ -1119,7 +1120,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_THROW_IS_EXPR 1u -#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1 +#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0) +#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1) /* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT diff --git a/Zend/zend_cpuinfo.h b/Zend/zend_cpuinfo.h index 513dacfd08f5..855e245beafe 100644 --- a/Zend/zend_cpuinfo.h +++ b/Zend/zend_cpuinfo.h @@ -272,7 +272,10 @@ static zend_always_inline int zend_cpu_supports_avx512_vbmi(void) { #endif /* __builtin_cpu_supports has pclmul from gcc9 and clang 19 */ -#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && (!defined(__GNUC__) || (defined(__clang__) && __clang_major__ >= 19) || (ZEND_GCC_VERSION >= 9000)) +#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && (defined(__x86_64__) || defined(__i386__)) && \ + ( \ + (!defined(__GNUC__) || (defined(__clang__) && __clang_major__ >= 19) || (ZEND_GCC_VERSION >= 9000)) \ + ) ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_pclmul(void) { #ifdef PHP_HAVE_BUILTIN_CPU_INIT @@ -287,7 +290,11 @@ static inline int zend_cpu_supports_pclmul(void) { #endif /* __builtin_cpu_supports has cldemote from gcc11 and clang 19 */ -#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && ((defined(__clang__) && (__clang_major__ >= 19)) || (!defined(__clang__) && defined(__GNUC__) && (ZEND_GCC_VERSION >= 11000))) +#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && (defined(__x86_64__) || defined(__i386__)) && \ + ( \ + (defined(__clang__) && (__clang_major__ >= 19)) || \ + (!defined(__clang__) && defined(__GNUC__) && (ZEND_GCC_VERSION >= 11000)) \ + ) #define HAVE_ZEND_CPU_SUPPORTS_CLDEMOTE 1 ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_cldemote(void) { diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index d23fb647af9d..a1301b8c20b2 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -506,7 +506,7 @@ ZEND_METHOD(ErrorException, getSeverity) } \ } while (0) -static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ +static void build_trace_args(zval *arg, smart_str *str) /* {{{ */ { /* the trivial way would be to do * convert_to_string(arg); @@ -516,24 +516,21 @@ static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ ZVAL_DEREF(arg); - if (smart_str_append_zval(str, arg, EG(exception_string_param_max_len)) == SUCCESS) { - smart_str_appends(str, ", "); - } else { + if (smart_str_append_zval(str, arg, EG(exception_string_param_max_len)) != SUCCESS) { switch (Z_TYPE_P(arg)) { case IS_RESOURCE: smart_str_appends(str, "Resource id #"); smart_str_append_long(str, Z_RES_HANDLE_P(arg)); - smart_str_appends(str, ", "); break; case IS_ARRAY: - smart_str_appends(str, "Array, "); + smart_str_appends(str, "Array"); break; case IS_OBJECT: { zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg)); smart_str_appends(str, "Object("); /* cut off on NULL byte ... class@anonymous */ smart_str_appends(str, ZSTR_VAL(class_name)); - smart_str_appends(str, "), "); + smart_str_appends(str, ")"); zend_string_release_ex(class_name, 0); break; } @@ -542,7 +539,30 @@ static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ } /* }}} */ -static void _build_trace_string(smart_str *str, const HashTable *ht, uint32_t num) /* {{{ */ +static void build_trace_args_list(zval *tmp, smart_str *str) /* {{{ */ +{ + if (UNEXPECTED(Z_TYPE_P(tmp) != IS_ARRAY)) { + /* only happens w/ reflection abuse (Zend/tests/bug63762.phpt) */ + zend_error(E_WARNING, "args element is not an array"); + return; + } + + bool first = true; + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), zend_string *name, zval *arg) { + if (!first) { + smart_str_appends(str, ", "); + } + first = false; + if (name) { + smart_str_append(str, name); + smart_str_appends(str, ": "); + } + build_trace_args(arg, str); + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ + +static void build_trace_string(smart_str *str, const HashTable *ht, uint32_t num) /* {{{ */ { zval *file, *tmp; @@ -588,27 +608,40 @@ static void _build_trace_string(smart_str *str, const HashTable *ht, uint32_t nu smart_str_appendc(str, '('); tmp = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_ARGS)); if (tmp) { - if (EXPECTED(Z_TYPE_P(tmp) == IS_ARRAY)) { - size_t last_len = ZSTR_LEN(str->s); - zend_string *name; - zval *arg; - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), name, arg) { - if (name) { - smart_str_append(str, name); - smart_str_appends(str, ": "); - } - _build_trace_args(arg, str); - } ZEND_HASH_FOREACH_END(); + build_trace_args_list(tmp, str); + } + smart_str_appends(str, ")\n"); +} +/* }}} */ - if (last_len != ZSTR_LEN(str->s)) { - ZSTR_LEN(str->s) -= 2; /* remove last ', ' */ - } - } else { - zend_error(E_WARNING, "args element is not an array"); +/* {{{ Gets the function arguments printed as a string from a backtrace frame. */ +ZEND_API zend_string *zend_trace_function_args_to_string(const HashTable *frame) { + smart_str str = {0}; + + zval *tmp = zend_hash_find_known_hash(frame, ZSTR_KNOWN(ZEND_STR_ARGS)); + if (tmp) { + build_trace_args_list(tmp, &str); + } + + return smart_str_extract(&str); +} +/* }}} */ + +/* {{{ Gets the currently executing function's arguments as a string. Used by php_verror. */ +ZEND_API zend_string *zend_trace_current_function_args_string(void) { + zend_string *dynamic_params = NULL; + /* get a backtrace to snarf function args */ + zval backtrace; + zend_fetch_debug_backtrace(&backtrace, /* skip_last */ 0, /* options */ 0, /* limit */ 1); + /* can fail esp if low memory condition */ + if (Z_TYPE(backtrace) == IS_ARRAY) { + zval *first_frame = zend_hash_index_find(Z_ARRVAL(backtrace), 0); + if (first_frame) { + dynamic_params = zend_trace_function_args_to_string(Z_ARRVAL_P(first_frame)); } } - smart_str_appends(str, ")\n"); + zval_ptr_dtor(&backtrace); + return dynamic_params; } /* }}} */ @@ -624,7 +657,7 @@ ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_ continue; } - _build_trace_string(&str, Z_ARRVAL_P(frame), num++); + build_trace_string(&str, Z_ARRVAL_P(frame), num++); } ZEND_HASH_FOREACH_END(); if (include_main) { diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index f9b472598012..7ef9ef016393 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -65,6 +65,8 @@ ZEND_API zend_result zend_update_exception_properties(zend_execute_data *execute /* show an exception using zend_error(severity,...), severity should be E_ERROR */ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2); +ZEND_API zend_string *zend_trace_function_args_to_string(const HashTable *frame); +ZEND_API zend_string *zend_trace_current_function_args_string(void); ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_main); ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4253037fda52..829c379545f8 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1232,6 +1232,13 @@ static zend_always_inline bool zend_check_type( return zend_check_type_slow(type, arg, ref, is_return_type, is_internal); } +/* We can not expose zend_check_type() directly because it's inline and uses static functions */ +ZEND_API bool zend_check_type_ex( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal) +{ + return zend_check_type(type, arg, is_return_type, is_internal); +} + ZEND_API bool zend_check_user_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type) { @@ -4669,6 +4676,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4725,6 +4733,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4805,6 +4814,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4862,6 +4872,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -5571,9 +5582,10 @@ zval * ZEND_FASTCALL zend_handle_named_arg( } } else { arg = ZEND_CALL_VAR_NUM(call, arg_offset); + if (UNEXPECTED(!Z_ISUNDEF_P(arg))) { - zend_throw_error(NULL, "Named parameter $%s overwrites previous argument", - ZSTR_VAL(arg_name)); + zend_throw_error(NULL, "Named parameter $%s overwrites previous %s", + ZSTR_VAL(arg_name), Z_TYPE_P(arg) == _IS_PLACEHOLDER ? "placeholder" : "argument"); return NULL; } } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index ba48b19bcfe1..48f7e7a72530 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -109,6 +109,8 @@ ZEND_API zend_never_inline ZEND_COLD void zend_verify_never_error( ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type); +ZEND_API bool zend_check_type_ex( + const zend_type *type, zval *arg, bool is_return_type, bool is_internal); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 71e0c56a51c8..854f11376860 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -38,6 +38,7 @@ #include "zend_observer.h" #include "zend_call_stack.h" #include "zend_frameless_function.h" +#include "zend_partial.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -201,6 +202,7 @@ void init_executor(void) /* {{{ */ zend_weakrefs_init(); zend_hash_init(&EG(callable_convert_cache), 8, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&EG(partial_function_application_cache), 8, NULL, zend_partial_op_array_dtor, 0); EG(active) = 1; } @@ -418,6 +420,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1); zend_hash_clean(&EG(callable_convert_cache)); + zend_hash_clean(&EG(partial_function_application_cache)); #if ZEND_DEBUG if (!CG(unclean_shutdown)) { @@ -514,6 +517,7 @@ void shutdown_executor(void) /* {{{ */ } zend_hash_destroy(&EG(callable_convert_cache)); + zend_hash_destroy(&EG(partial_function_application_cache)); } #if ZEND_DEBUG diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8257df32e831..e94550247701 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -325,6 +325,7 @@ struct _zend_executor_globals { zend_strtod_state strtod_state; HashTable callable_convert_cache; + HashTable partial_function_application_cache; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d70d08f5f1c4..e85b4ea42250 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -188,9 +188,9 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ if (ce->constructor) { if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) { - zend_error_noreturn(E_ERROR, "Cannot override final %s::%s() with %s::%s()", - ZSTR_VAL(parent->name), ZSTR_VAL(parent->constructor->common.function_name), - ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name)); + zend_error_noreturn(E_ERROR, "Cannot override final %s::__construct() with %s::__construct()", + ZSTR_VAL(parent->name), + ZSTR_VAL(ce->name)); } return; } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 07f2d44cb5c6..f4fba2240784 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -590,6 +590,43 @@ ZEND_API zend_result open_file_for_scanning(zend_file_handle *file_handle) return SUCCESS; } +static zend_op_array *zend_compile_ast_internal(int type) +{ + ZEND_ASSERT(CG(in_compilation)); + ZEND_ASSERT(CG(ast)); + + uint32_t last_lineno = CG(zend_lineno); + zend_file_context original_file_context; + zend_oparray_context original_oparray_context; + zend_op_array *original_active_op_array = CG(active_op_array); + + zend_op_array *op_array = emalloc(sizeof(*op_array)); + init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); + CG(active_op_array) = op_array; + + /* Use heap to not waste arena memory */ + op_array->fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + + if (zend_ast_process) { + zend_ast_process(CG(ast)); + } + + zend_file_context_begin(&original_file_context); + zend_oparray_context_begin(&original_oparray_context, op_array); + zend_compile_top_stmt(CG(ast)); + CG(zend_lineno) = last_lineno; + zend_emit_final_return(type == ZEND_USER_FUNCTION); + op_array->line_start = 1; + op_array->line_end = last_lineno; + pass_two(op_array); + zend_oparray_context_end(&original_oparray_context); + zend_file_context_end(&original_file_context); + + CG(active_op_array) = original_active_op_array; + + return op_array; +} + static zend_op_array *zend_compile(zend_function_type type) { zend_op_array *op_array = NULL; @@ -600,34 +637,7 @@ static zend_op_array *zend_compile(zend_function_type type) CG(ast_arena) = zend_arena_create(1024 * 32); if (!zendparse()) { - uint32_t last_lineno = CG(zend_lineno); - zend_file_context original_file_context; - zend_oparray_context original_oparray_context; - zend_op_array *original_active_op_array = CG(active_op_array); - - op_array = emalloc(sizeof(zend_op_array)); - init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); - CG(active_op_array) = op_array; - - /* Use heap to not waste arena memory */ - op_array->fn_flags |= ZEND_ACC_HEAP_RT_CACHE; - - if (zend_ast_process) { - zend_ast_process(CG(ast)); - } - - zend_file_context_begin(&original_file_context); - zend_oparray_context_begin(&original_oparray_context, op_array); - zend_compile_top_stmt(CG(ast)); - CG(zend_lineno) = last_lineno; - zend_emit_final_return(type == ZEND_USER_FUNCTION); - op_array->line_start = 1; - op_array->line_end = last_lineno; - pass_two(op_array); - zend_oparray_context_end(&original_oparray_context); - zend_file_context_end(&original_file_context); - - CG(active_op_array) = original_active_op_array; + op_array = zend_compile_ast_internal(type); } zend_ast_destroy(CG(ast)); @@ -670,6 +680,23 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type) return op_array; } +ZEND_API zend_op_array *zend_compile_ast( + zend_ast *ast, int type, zend_string *filename) +{ + zend_string *original_compiled_filename = CG(compiled_filename); + bool original_in_compilation = CG(in_compilation); + CG(in_compilation) = true; + CG(ast) = ast; + + zend_set_compiled_filename(filename); + zend_op_array *op_array = zend_compile_ast_internal(type); + + CG(in_compilation) = original_in_compilation; + zend_restore_compiled_filename(original_compiled_filename); + + return op_array; +} + ZEND_API zend_ast *zend_compile_string_to_ast( zend_string *code, zend_arena **ast_arena, zend_string *filename) { zval code_zv; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 571aa9e92abc..e7ddd466a51a 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2176,12 +2176,16 @@ ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *c static ZEND_COLD zend_never_inline void zend_bad_constructor_call(const zend_function *constructor, const zend_class_entry *scope) /* {{{ */ { if (scope) { - zend_throw_error(NULL, "Call to %s %s::%s() from scope %s", - zend_visibility_string(constructor->common.fn_flags), ZSTR_VAL(constructor->common.scope->name), - ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name) + zend_throw_error(NULL, "Call to %s %s::__construct() from scope %s", + zend_visibility_string(constructor->common.fn_flags), + ZSTR_VAL(constructor->common.scope->name), + ZSTR_VAL(scope->name) ); } else { - zend_throw_error(NULL, "Call to %s %s::%s() from global scope", zend_visibility_string(constructor->common.fn_flags), ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name)); + zend_throw_error(NULL, "Call to %s %s::__construct() from global scope", + zend_visibility_string(constructor->common.fn_flags), + ZSTR_VAL(constructor->common.scope->name) + ); } } /* }}} */ diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index c54f4f0c2a22..a43fdcc9a48b 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -448,6 +448,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * ZEND_ASSERT(Z_TYPE(dst) == IS_LONG); return Z_LVAL(dst); } + case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c new file mode 100644 index 000000000000..a96c5349518e --- /dev/null +++ b/Zend/zend_partial.c @@ -0,0 +1,1149 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ + +/** + * Partial Function Application: + * + * A partial application is compiled to the usual sequence of function call + * opcodes (INIT_FCALL, SEND_VAR, etc), but the sequence ends with a + * CALLABLE_CONVERT_PARTIAL opcode instead of DO_FCALL, similarly to + * first class callables. Placeholders are compiled to SEND_PLACEHOLDER opcodes: + * + * $f = f($a, ?) + * + * 0001 INIT_FCALL f + * 0002 SEND_VAR CV($a) + * 0003 SEND_PLACEHOLDER + * 0004 CV($f) = CALLABLE_CONVERT_PARTIAL + * + * SEND_PLACEHOLDER sets the argument slot type to _IS_PLACEHOLDER. + * + * CALLABLE_CONVERT_PARTIAL uses the information available on the stack to + * create a Closure and return it, consuming the stack frame in the process + * like an internal function call. + * + * We create the Closure by generating the relevant AST and compiling it to an + * op_array. The op_array is cached in the Opcache SHM and inline caches. + * + * This file implements the Closure generation logic + * (see zend_partial_create(), zp_compile()). + */ + +#include "zend.h" +#include "zend_API.h" +#include "zend_arena.h" +#include "zend_ast.h" +#include "zend_compile.h" +#include "zend_closures.h" +#include "zend_attributes.h" +#include "zend_exceptions.h" +#include "ext/opcache/ZendAccelerator.h" + +#define Z_IS_PLACEHOLDER_P(p) (Z_TYPE_P(p) == _IS_PLACEHOLDER) + +static zend_always_inline bool zp_is_static_closure(const zend_function *function) { + return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)); +} + +static zend_always_inline bool zp_is_non_static_closure(const zend_function *function) { + return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == ZEND_ACC_CLOSURE); +} + +static zend_never_inline ZEND_COLD void zp_args_underflow( + const zend_function *function, uint32_t args, uint32_t expected) +{ + zend_string *symbol = get_function_or_method_name(function); + const char *limit = function->common.num_args <= function->common.required_num_args ? + "exactly" : "at least"; + + zend_argument_count_error( + "Partial application of %s() expects %s %d arguments, %d given", + ZSTR_VAL(symbol), limit, expected, args); + + zend_string_release(symbol); +} + +static zend_never_inline ZEND_COLD void zp_args_overflow( + const zend_function *function, uint32_t args, uint32_t expected) +{ + zend_string *symbol = get_function_or_method_name(function); + + zend_argument_count_error( + "Partial application of %s() expects at most %d arguments, %d given", + ZSTR_VAL(symbol), expected, args); + + zend_string_release(symbol); +} + +static zend_result zp_args_check(const zend_function *function, + uint32_t argc, const zval *argv, + const zend_array *extra_named_args, + bool uses_variadic_placeholder) { + + if (extra_named_args) { + zval *arg; + zend_string *key; + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(extra_named_args, key, arg) { + if (UNEXPECTED(Z_IS_PLACEHOLDER_P(arg))) { + zend_throw_error(NULL, + "Cannot use named placeholder for unknown or variadic parameter $%s", + ZSTR_VAL(key)); + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + } + + if (argc < function->common.required_num_args) { + if (uses_variadic_placeholder) { + /* Missing args will be turned into placeholders */ + return SUCCESS; + } + + zp_args_underflow( + function, argc, function->common.required_num_args); + return FAILURE; + } else if (argc > function->common.num_args && + !(function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zp_args_overflow(function, argc, function->common.num_args); + return FAILURE; + } + + return SUCCESS; +} + +typedef struct zp_names { + zend_string *variadic_param; + zend_string *extra_named_params; + zend_string *inner_closure; + zend_string *params[1]; +} zp_names; + +static bool zp_name_exists(const zp_names *names, uint32_t argc, const zend_string *name) +{ + for (uint32_t i = 0; i < argc; i++) { + if (names->params[i] && zend_string_equals(names->params[i], name)) { + return true; + } + } + if (names->variadic_param && zend_string_equals(names->variadic_param, name)) { + return true; + } + if (names->extra_named_params && zend_string_equals(names->extra_named_params, name)) { + return true; + } + if (names->inner_closure && zend_string_equals(names->inner_closure, name)) { + return true; + } + + return false; +} + +static void zp_names_dtor(zp_names *names, uint32_t argc) +{ + for (uint32_t i = 0; i < argc; i++) { + if (names->params[i]) { + zend_string_release(names->params[i]); + } + } + if (names->variadic_param) { + zend_string_release(names->variadic_param); + } + if (names->extra_named_params) { + zend_string_release(names->extra_named_params); + } + if (names->inner_closure) { + zend_string_release(names->inner_closure); + } +} + +static zend_string *zp_get_func_param_name(zend_function *function, uint32_t arg_offset) +{ + return zend_string_copy(function->common.arg_info[arg_offset].name); +} + +/* Assign a name for every variable that will be used in the generated closure, + * including params and used vars. */ +static zp_names *zp_assign_names(uint32_t argc, zval *argv, + zend_function *function, bool variadic_partial, + zend_array *extra_named_params) +{ + zp_names *names = zend_arena_calloc(&CG(ast_arena), + 1, offsetof(zp_names, params) + sizeof(zend_string*) * argc); + + /* Assign names for params. We never rename those. */ + for (uint32_t offset = 0; offset < MIN(argc, function->common.num_args); offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset])) { + names->params[offset] = zp_get_func_param_name(function, offset); + } + } + + /* Assign name for the variadic param. Never renamed. */ + if (variadic_partial && (function->common.fn_flags & ZEND_ACC_VARIADIC)) { + names->variadic_param = zp_get_func_param_name(function, function->common.num_args); + } + + /* Assign names for placeholders that bind to the variadic param: + * + * function f($a, ...$args) {} + * f(?, ?, ...); // The second placeholder binds into the variadic param. + * + * By default these are named $origNameN with N the offset from the + * variadic param. In case of clash we increment N until a free name is + * found. */ + for (uint32_t offset = function->common.num_args; offset < argc; offset++) { + ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC); + if (!Z_IS_PLACEHOLDER_P(&argv[offset])) { + continue; + } + zend_string *orig_name = zp_get_func_param_name(function, function->common.num_args); + zend_string *new_name; + for (uint32_t n = 0;; n++) { + new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n); + if (!zp_name_exists(names, argc, new_name)) { + break; + } + zend_string_release(new_name); + } + names->params[offset] = new_name; + zend_string_release(orig_name); + } + + /* Assign names for pre-bound params (lexical vars). + * There may be clashes, we ensure to generate unique names. */ + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) { + continue; + } + uint32_t n = 0; + zend_string *orig_name = zp_get_func_param_name(function, MIN(offset, function->common.num_args)); + zend_string *new_name = zend_string_copy(orig_name); + while (zp_name_exists(names, argc, new_name)) { + zend_string_release(new_name); + new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n); + n++; + } + names->params[offset] = new_name; + zend_string_release(orig_name); + } + + /* Assign name for $extra_named_params */ + if (extra_named_params) { + uint32_t n = 1; + zend_string *new_name = ZSTR_INIT_LITERAL("extra_named_params", 0); + while (zp_name_exists(names, argc, new_name)) { + zend_string_release(new_name); + n++; + new_name = zend_strpprintf(0, "%s%" PRIu32, "extra_named_params", n); + } + names->extra_named_params = new_name; + } + + /* Assign name for $fn */ + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + uint32_t n = 1; + zend_string *new_name = ZSTR_INIT_LITERAL("fn", 0); + while (zp_name_exists(names, argc, new_name)) { + zend_string_release(new_name); + n++; + new_name = zend_strpprintf(0, "%s%" PRIu32, "fn", n); + } + names->inner_closure = new_name; + } + + return names; +} + +static inline bool zp_is_power_of_two(uint32_t x) +{ + return (x > 0) && !(x & (x - 1)); +} + +static bool zp_is_simple_type(uint32_t type_mask) +{ + ZEND_ASSERT(!(type_mask & ~_ZEND_TYPE_MAY_BE_MASK)); + + return zp_is_power_of_two(type_mask) + || type_mask == MAY_BE_BOOL + || type_mask == MAY_BE_ANY; +} + +static zend_ast *zp_simple_type_to_ast(uint32_t type) +{ + zend_string *name; + + switch (type) { + case MAY_BE_NULL: + name = ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE); + break; + case MAY_BE_TRUE: + name = ZSTR_KNOWN(ZEND_STR_TRUE); + break; + case MAY_BE_FALSE: + name = ZSTR_KNOWN(ZEND_STR_FALSE); + break; + case MAY_BE_LONG: + name = ZSTR_KNOWN(ZEND_STR_INT); + break; + case MAY_BE_DOUBLE: + name = ZSTR_KNOWN(ZEND_STR_FLOAT); + break; + case MAY_BE_STRING: + name = ZSTR_KNOWN(ZEND_STR_STRING); + break; + case MAY_BE_BOOL: + name = ZSTR_KNOWN(ZEND_STR_BOOL); + break; + case MAY_BE_VOID: + name = ZSTR_KNOWN(ZEND_STR_VOID); + break; + case MAY_BE_NEVER: + name = ZSTR_KNOWN(ZEND_STR_NEVER); + break; + case MAY_BE_OBJECT: + name = ZSTR_KNOWN(ZEND_STR_OBJECT); + break; + case MAY_BE_ANY: + name = ZSTR_KNOWN(ZEND_STR_MIXED); + break; + case MAY_BE_CALLABLE: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); + case MAY_BE_ARRAY: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); + case MAY_BE_STATIC: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_STATIC); + default: + ZEND_UNREACHABLE(); + } + + zend_ast *ast = zend_ast_create_zval_from_str(name); + ast->attr = ZEND_NAME_NOT_FQ; + + return ast; +} + +static zend_ast *zp_type_name_to_ast(zend_string *name) +{ + zend_ast *ast = zend_ast_create_zval_from_str(name); + + if (zend_get_class_fetch_type(name) != ZEND_FETCH_CLASS_DEFAULT) { + ast->attr = ZEND_NAME_NOT_FQ; + } else { + ast->attr = ZEND_NAME_FQ; + } + + return ast; +} + +static zend_ast *zp_type_to_ast(const zend_type type) +{ + if (!ZEND_TYPE_IS_SET(type)) { + return NULL; + } + + if (ZEND_TYPE_IS_UNION(type) + || (ZEND_TYPE_IS_COMPLEX(type) && ZEND_TYPE_PURE_MASK(type)) + || (ZEND_TYPE_PURE_MASK(type) && !zp_is_simple_type(ZEND_TYPE_PURE_MASK(type)))) { + zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_UNION); + if (ZEND_TYPE_HAS_LIST(type)) { + const zend_type *type_ptr; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), type_ptr) { + type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_NAME(type)) { + zend_ast *name_ast = zp_type_name_to_ast( + zend_string_copy(ZEND_TYPE_NAME(type))); + type_ast = zend_ast_list_add(type_ast, name_ast); + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + } + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { + type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(MAY_BE_BOOL)); + type_mask &= ~MAY_BE_BOOL; + } + for (uint32_t may_be_type = 1; may_be_type < _ZEND_TYPE_MAY_BE_MASK; may_be_type <<= 1) { + if (type_mask & may_be_type) { + type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(may_be_type)); + } + } + return type_ast; + } + + if (ZEND_TYPE_IS_INTERSECTION(type)) { + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_INTERSECTION); + const zend_type *type_ptr; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), type_ptr) { + type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); + } ZEND_TYPE_LIST_FOREACH_END(); + return type_ast; + } + + if (ZEND_TYPE_HAS_NAME(type)) { + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + zend_ast *type_ast = zp_type_name_to_ast( + zend_string_copy(ZEND_TYPE_NAME(type))); + return type_ast; + } + + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + ZEND_ASSERT(zp_is_simple_type(type_mask)); + + return zp_simple_type_to_ast(type_mask); +} + +static zend_result zp_get_param_default_value(zval *result, zend_function *function, uint32_t arg_offset) +{ + ZEND_ASSERT(arg_offset < function->common.num_args); + + if (function->type == ZEND_USER_FUNCTION) { + zend_op *opline = &function->op_array.opcodes[arg_offset]; + if (EXPECTED(opline->opcode == ZEND_RECV_INIT)) { + ZVAL_COPY(result, RT_CONSTANT(opline, opline->op2)); + return SUCCESS; + } + ZEND_ASSERT(opline->opcode == ZEND_RECV); + } else { + if (function->common.fn_flags & ZEND_ACC_USER_ARG_INFO) { + goto error; + } + + const zend_arg_info *arg_info = &function->internal_function.arg_info[arg_offset]; + + if (zend_get_default_from_internal_arg_info(result, arg_info) == SUCCESS) { + return SUCCESS; + } + } + +error: + zend_argument_error_ex(function, arg_offset + 1, + zend_ce_argument_count_error, + "must be passed explicitly, because the default value is not known"); + + return FAILURE; +} + +static bool zp_arg_must_be_sent_by_ref(zend_function *function, uint32_t arg_num) +{ + if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) { + if (QUICK_ARG_MUST_BE_SENT_BY_REF(function, arg_num)) { + return true; + } + } else if (ARG_MUST_BE_SENT_BY_REF(function, arg_num)) { + return true; + } + return false; +} + +static zend_ast *zp_attribute_to_ast(zend_attribute *attribute) +{ + zend_ast *args_ast; + if (attribute->argc) { + args_ast = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); + for (uint32_t i = 0; i < attribute->argc; i++) { + zend_ast *arg_ast = zend_ast_create_zval(&attribute->args[i].value); + if (attribute->args[i].name) { + arg_ast = zend_ast_create(ZEND_AST_NAMED_ARG, + zend_ast_create_zval_from_str( + zend_string_copy(attribute->args[i].name)), + arg_ast); + } + args_ast = zend_ast_list_add(args_ast, arg_ast); + } + } else { + args_ast = NULL; + } + return zend_ast_create(ZEND_AST_ATTRIBUTE, + zend_ast_create_zval_from_str(zend_string_copy(attribute->name)), + args_ast); +} + +static zend_ast *zp_param_attributes_to_ast(zend_function *function, + uint32_t offset) +{ + zend_ast *attributes_ast = NULL; + if (!function->common.attributes) { + return NULL; + } + + /* Inherit the SensitiveParameter attribute */ + zend_attribute *attr = zend_get_parameter_attribute_str( + function->common.attributes, + "sensitiveparameter", strlen("sensitiveparameter"), offset); + if (attr) { + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, + zp_attribute_to_ast(attr)); + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, + attributes_ast); + } + + return attributes_ast; +} + +static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline) +{ + zend_string *filename = declaring_op_array->filename; + uint32_t start_lineno = declaring_opline->lineno; + + zend_string *class = zend_empty_string; + zend_string *separator = zend_empty_string; + zend_string *function = filename; + const char *parens = ""; + + if (declaring_op_array->function_name) { + if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) { + /* If the parent function is a closure, don't redundantly + * add the classname and parentheses. + */ + function = declaring_op_array->function_name; + } else { + function = declaring_op_array->function_name; + parens = "()"; + + if (declaring_op_array->scope && declaring_op_array->scope->name) { + class = declaring_op_array->scope->name; + separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM); + } + } + } + + zend_string *name = zend_strpprintf_unchecked( + 0, + "{closure:pfa:%S%S%S%s:%" PRIu32 "}", + class, + separator, + function, + parens, + start_lineno + ); + + return name; +} + +/* Generate the AST for calling the actual function */ +static zend_ast *zp_compile_forwarding_call( + zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + zp_names *var_names, bool variadic_partial, uint32_t num_args, + zend_class_entry *called_scope, zend_type return_type, + bool forward_superfluous_args, + zend_ast *stmts_ast) +{ + bool is_assert = zend_string_equals(function->common.function_name, + ZSTR_KNOWN(ZEND_STR_ASSERT)); + + zend_ast *args_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + zend_ast *call_ast = NULL; + + if (is_assert) { + /* We are going to call assert() dynamically (via call_user_func), + * otherwise assert() would print the generated AST on failure, which is + * irrelevant. */ + args_ast = zend_ast_list_add(args_ast, + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ASSERT))); + } + + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_ISUNDEF(argv[offset])) { + /* Argument was not passed. Pass its default value. */ + if (offset < function->common.required_num_args) { + /* Required param was not passed. This can happen due to named + * args. Using the same exception CE and message as + * zend_handle_undef_args(). */ + zend_argument_error_ex(function, offset + 1, + zend_ce_argument_count_error, "not passed"); + goto error; + } + zval default_value; + if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) { + ZEND_ASSERT(EG(exception)); + goto error; + } + zend_ast *default_value_ast; + if (Z_TYPE(default_value) == IS_CONSTANT_AST) { + /* Must dup AST because we are going to destroy it */ + default_value_ast = zend_ast_dup(Z_ASTVAL(default_value)); + } else { + default_value_ast = zend_ast_create_zval(&default_value); + } + args_ast = zend_ast_list_add(args_ast, default_value_ast); + } else { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->params[offset])))); + } + } + if (extra_named_params) { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->extra_named_params))))); + } + if (variadic_partial) { + if (function->common.fn_flags & ZEND_ACC_VARIADIC) { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->variadic_param))))); + } else if (forward_superfluous_args) { + /* When a '...' placeholder is used, and the underlying function is + * not variadic, superfluous arguments are forwarded. + * Add a ...array_slice(func_get_args(), n) argument, which should + * be compiled as ZEND_AST_UNPACK + ZEND_FUNC_GET_ARGS. */ + + zend_ast *func_get_args_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_GET_ARGS))); + func_get_args_name_ast->attr = ZEND_NAME_FQ; + + zend_ast *array_slice_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_ARRAY_SLICE))); + array_slice_name_ast->attr = ZEND_NAME_FQ; + + args_ast = zend_ast_list_add(args_ast, + zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_CALL, + array_slice_name_ast, + zend_ast_create_list(2, ZEND_AST_ARG_LIST, + zend_ast_create(ZEND_AST_CALL, + func_get_args_name_ast, + zend_ast_create_list(0, ZEND_AST_ARG_LIST)), + zend_ast_create_zval_from_long(num_args))))); + } + } + + if (is_assert) { + zend_ast *func_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CALL_USER_FUNC)); + func_name_ast->attr = ZEND_NAME_FQ; + call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast); + } else if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_ast *fn_ast = zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(var_names->inner_closure))); + call_ast = zend_ast_create(ZEND_AST_CALL, fn_ast, args_ast); + } else if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + zend_ast *this_ast = zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_THIS))); + zend_ast *method_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(function->common.function_name)); + call_ast = zend_ast_create(ZEND_AST_METHOD_CALL, this_ast, + method_name_ast, args_ast); + } else if (called_scope) { + zend_ast *class_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_STATIC)); + class_name_ast->attr = ZEND_NAME_NOT_FQ; + zend_ast *method_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(function->common.function_name)); + call_ast = zend_ast_create(ZEND_AST_STATIC_CALL, class_name_ast, + method_name_ast, args_ast); + } else { + zend_ast *func_name_ast = zend_ast_create_zval_from_str(zend_string_copy(function->common.function_name)); + func_name_ast->attr = ZEND_NAME_FQ; + call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast); + } + + /* Void functions can not 'return $expr' */ + if (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_VOID) { + stmts_ast = zend_ast_list_add(stmts_ast, call_ast); + } else { + zend_ast *return_ast = zend_ast_create(ZEND_AST_RETURN, call_ast); + stmts_ast = zend_ast_list_add(stmts_ast, return_ast); + } + + return stmts_ast; + +error: + zend_ast_destroy(args_ast); + zend_ast_destroy(call_ast); + return NULL; +} + +static uint32_t zp_compute_num_required(zend_function *function, + uint32_t orig_offset, uint32_t new_offset, uint32_t num_required) { + if (orig_offset < function->common.num_args) { + if (orig_offset < function->common.required_num_args) { + num_required = MAX(num_required, new_offset + 1); + } + } else { + ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC); + /* Placeholders that run into the variadic portion become + * required and make all params before them required */ + ZEND_ASSERT(orig_offset >= num_required); + num_required = new_offset + 1; + } + + return num_required; +} + +/* Compile PFA to an op_array */ +static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + zend_op_array *op_array = NULL; + + if (UNEXPECTED(function->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS)) { + zend_throw_error(NULL, "Cannot call %.*s() dynamically", + (int) ZSTR_LEN(function->common.function_name), ZSTR_VAL(function->common.function_name)); + return NULL; + } + + if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) { + ZEND_ASSERT(EG(exception)); + return NULL; + } + + zend_class_entry *called_scope; + if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + called_scope = Z_OBJCE_P(this_ptr); + } else { + called_scope = Z_CE_P(this_ptr); + } + + /* CG(ast_arena) is usually NULL, so we can't just make a snapshot */ + zend_arena *orig_ast_arena = CG(ast_arena); + CG(ast_arena) = zend_arena_create(1024 * 4); + + uint32_t orig_lineno = CG(zend_lineno); + CG(zend_lineno) = zend_get_executed_lineno(); + + uint32_t new_argc = argc; + + if (uses_variadic_placeholder) { + new_argc = MAX(new_argc, function->common.num_args); + } + + zval *tmp = zend_arena_alloc(&CG(ast_arena), new_argc * sizeof(zval)); + memcpy(tmp, argv, argc * sizeof(zval)); + argv = tmp; + + /* Compute param positions and number of required args, add implicit + * placeholders. + * + * Parameters are placed in the following order: + * - Positional placeholders + * - Then named placeholders in their syntax order + * - Then implicit placeholders added by '...' + */ + uint32_t num_params = 0; + uint32_t num_required = 0; + uint32_t *arg_to_param_offset_map = zend_arena_alloc(&CG(ast_arena), sizeof(uint32_t*) * new_argc); + { + uint32_t num_positional = 0; + + /* First, we handle explicit placeholders */ + for (uint32_t arg_offset = 0; arg_offset < argc; arg_offset++) { + if (!Z_IS_PLACEHOLDER_P(&argv[arg_offset])) { + continue; + } + + num_params++; + + zend_arg_info *arg_info = &function->common.arg_info[MIN(arg_offset, function->common.num_args)]; + zval *named_pos = named_positions ? zend_hash_find(named_positions, arg_info->name) : NULL; + uint32_t param_offset; + if (named_pos) { + /* Placeholder is sent as named arg. 'num_positional' can not + * change at this point. */ + param_offset = num_positional + Z_LVAL_P(named_pos); + } else { + /* Placeholder is sent as positional */ + param_offset = num_positional++; + } + + arg_to_param_offset_map[arg_offset] = param_offset; + } + + num_required = num_params; + + /* Handle implicit placeholders added by '...' */ + if (uses_variadic_placeholder) { + for (uint32_t arg_offset = 0; arg_offset < new_argc; arg_offset++) { + if (arg_offset < argc && !Z_ISUNDEF(argv[arg_offset])) { + continue; + } + + /* Unspecified parameters become placeholders */ + Z_TYPE_INFO(argv[arg_offset]) = _IS_PLACEHOLDER; + + num_params++; + + uint32_t param_offset = num_params - 1; + + arg_to_param_offset_map[arg_offset] = param_offset; + + num_required = zp_compute_num_required(function, + arg_offset, param_offset, num_required); + } + } + } + + argc = new_argc; + + /* Assign variable names */ + + zp_names *var_names = zp_assign_names(argc, argv, function, + uses_variadic_placeholder, extra_named_params); + + /* Generate AST */ + + zend_ast *lexical_vars_ast = zend_ast_create_list(0, ZEND_AST_CLOSURE_USES); + zend_ast *params_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + zend_ast *return_type_ast = NULL; + zend_ast *stmts_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + zend_ast *attributes_ast = NULL; + + /* Generate AST for params and lexical vars */ + { + /* The inner Closure, if any, is assumed to be the first lexical var by + * do_closure_bind(). */ + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->inner_closure)); + lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast); + } + + zend_ast **params = zend_arena_calloc(&CG(ast_arena), num_params, sizeof(zend_ast*)); + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset])) { + zend_arg_info *arg_info = &function->common.arg_info[MIN(offset, function->common.num_args)]; + + int param_flags = 0; + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + param_flags |= ZEND_PARAM_REF; + } + + uint32_t param_offset = arg_to_param_offset_map[offset]; + zend_ast *param_type_ast = zp_type_to_ast(arg_info->type); + zend_ast *default_value_ast = NULL; + if (param_offset >= num_required) { + zval default_value; + if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) { + for (uint32_t i = 0; i < num_params; i++) { + zend_ast_destroy(params[i]); + } + goto error; + } + default_value_ast = zend_ast_create_zval(&default_value); + } + + ZEND_ASSERT(offset < function->common.num_args || (function->common.fn_flags & ZEND_ACC_VARIADIC)); + + zend_ast *attributes_ast = zp_param_attributes_to_ast(function, MIN(offset, function->common.num_args)); + params[param_offset] = zend_ast_create_ex(ZEND_AST_PARAM, + param_flags, param_type_ast, + zend_ast_create_zval_from_str( + zend_string_copy(var_names->params[offset])), + default_value_ast, attributes_ast, NULL, NULL); + + } else if (!Z_ISUNDEF(argv[offset])) { + // TODO: If the pre-bound parameter is a literal, it can be a + // literal in the function body instead of a lexical var. + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->params[offset])); + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + lexical_var_ast->attr = ZEND_BIND_REF; + } + lexical_vars_ast = zend_ast_list_add( + lexical_vars_ast, lexical_var_ast); + } + } + + for (uint32_t i = 0; i < num_params; i++) { + params_ast = zend_ast_list_add(params_ast, params[i]); + } + } + + if (extra_named_params) { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->extra_named_params)); + lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast); + } + + /* If we have a variadic placeholder and the underlying function is + * variadic, add a variadic param. */ + if (uses_variadic_placeholder + && (function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zend_arg_info *arg_info = &function->common.arg_info[function->common.num_args]; + int param_flags = ZEND_PARAM_VARIADIC; + if (zp_arg_must_be_sent_by_ref(function, function->common.num_args+1)) { + param_flags |= ZEND_PARAM_REF; + } + zend_ast *param_type_ast = zp_type_to_ast(arg_info->type); + zend_ast *attributes_ast = zp_param_attributes_to_ast(function, function->common.num_args); + params_ast = zend_ast_list_add(params_ast, zend_ast_create_ex(ZEND_AST_PARAM, + param_flags, param_type_ast, + zend_ast_create_zval_from_str( + zend_string_copy(var_names->variadic_param)), + NULL, attributes_ast, NULL, NULL)); + } + + zend_type return_type = {0}; + if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + return_type = (function->common.arg_info-1)->type; + return_type_ast = zp_type_to_ast(return_type); + } + + /** + * Generate function body. + * + * If we may need to forward superflous arguments, do that conditionally, as + * it's faster: + * + * if (func_num_args() <= n) { + * // normal call + * } else { + * // call with superflous arg forwarding + * } + * + * The func_num_args() call should be compiled to a single FUNC_NUM_ARGS op. + */ + + if (uses_variadic_placeholder && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zend_ast *no_forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + zend_ast *forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + + no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + var_names, uses_variadic_placeholder, num_params, + called_scope, return_type, false, no_forwarding_ast); + + if (!no_forwarding_ast) { + ZEND_ASSERT(EG(exception)); + goto error; + } + + forwarding_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + var_names, uses_variadic_placeholder, num_params, + called_scope, return_type, true, forwarding_ast); + + if (!forwarding_ast) { + ZEND_ASSERT(EG(exception)); + zend_ast_destroy(no_forwarding_ast); + goto error; + } + + zend_ast *func_num_args_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_NUM_ARGS))); + func_num_args_name_ast->attr = ZEND_NAME_FQ; + + stmts_ast = zend_ast_list_add(stmts_ast, + zend_ast_create_list(2, ZEND_AST_IF, + zend_ast_create(ZEND_AST_IF_ELEM, + zend_ast_create_binary_op(ZEND_IS_SMALLER_OR_EQUAL, + zend_ast_create(ZEND_AST_CALL, func_num_args_name_ast, + zend_ast_create_list(0, ZEND_AST_ARG_LIST)), + zend_ast_create_zval_from_long(num_params)), + no_forwarding_ast), + zend_ast_create(ZEND_AST_IF_ELEM, + NULL, + forwarding_ast))); + } else { + stmts_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + var_names, uses_variadic_placeholder, num_params, + called_scope, return_type, false, stmts_ast); + + if (!stmts_ast) { + ZEND_ASSERT(EG(exception)); + goto error; + } + } + + /* Inherit the NoDiscard attribute */ + if (function->common.attributes) { + zend_attribute *attr = zend_get_attribute_str( + function->common.attributes, "nodiscard", strlen("nodiscard")); + if (attr) { + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, + zp_attribute_to_ast(attr)); + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, + attributes_ast); + } + } + + int closure_flags = function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE; + zend_ast *closure_ast = zend_ast_create_decl(ZEND_AST_CLOSURE, + closure_flags, CG(zend_lineno), NULL, + NULL, params_ast, lexical_vars_ast, stmts_ast, + return_type_ast, attributes_ast); + + if (Z_TYPE_P(this_ptr) != IS_OBJECT && !zp_is_non_static_closure(function)) { + ((zend_ast_decl*)closure_ast)->flags |= ZEND_ACC_STATIC; + } + +#if ZEND_DEBUG + { + const char *tmp = getenv("DUMP_PFA_AST"); + if (tmp && ZEND_ATOL(tmp)) { + zend_string *str = zend_ast_export("", closure_ast, ""); + fprintf(stderr, "PFA AST: %s\n", ZSTR_VAL(str)); + zend_string_release(str); + } + } +#endif + + zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_opline); + + op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array, + declaring_opline, function, pfa_name); + + zend_ast_destroy(closure_ast); + +clean: + zp_names_dtor(var_names, argc); + zend_arena_destroy(CG(ast_arena)); + CG(ast_arena) = orig_ast_arena; + CG(zend_lineno) = orig_lineno; + + return op_array; + +error: + zend_ast_destroy(lexical_vars_ast); + zend_ast_destroy(params_ast); + zend_ast_destroy(return_type_ast); + zend_ast_destroy(stmts_ast); + zend_ast_destroy(attributes_ast); + goto clean; +} + +static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION + ? cache_slot[0] == function + : cache_slot[0] == function->op_array.opcodes)) { + ZEND_ASSERT(!(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)); + return cache_slot[1]; + } + + const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array, + declaring_opline, function); + + if (UNEXPECTED(!op_array)) { + op_array = zp_compile(this_ptr, function, argc, argv, + extra_named_params, named_positions, declaring_op_array, declaring_opline, + cache_slot, uses_variadic_placeholder); + } + + if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) { + cache_slot[0] = function->type == ZEND_INTERNAL_FUNCTION + ? (void*)function + : (void*)function->op_array.opcodes; + cache_slot[1] = (zend_op_array*)op_array; + } + + return op_array; +} + +/* Bind pre-bound arguments as lexical vars */ +static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv, + zend_array *extra_named_params) { + + zend_arg_info *arg_infos = function->common.arg_info; + uint32_t bind_offset = 0; + + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zval var; + ZVAL_OBJ(&var, ZEND_CLOSURE_OBJECT(function)); + Z_ADDREF(var); + zend_closure_bind_var_ex(result, bind_offset, &var); + bind_offset += sizeof(Bucket); + } + + for (uint32_t offset = 0; offset < argc; offset++) { + zval *var = &argv[offset]; + if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var)) { + continue; + } + zend_arg_info *arg_info; + if (offset < function->common.num_args) { + arg_info = &arg_infos[offset]; + } else if (function->common.fn_flags & ZEND_ACC_VARIADIC) { + arg_info = &arg_infos[function->common.num_args]; + } else { + arg_info = NULL; + } + if (arg_info && ZEND_TYPE_IS_SET(arg_info->type) + && UNEXPECTED(!zend_check_type_ex(&arg_info->type, var, 0, 0))) { + zend_verify_arg_error(function, arg_info, offset+1, var); + zval_ptr_dtor(result); + ZVAL_NULL(result); + return; + } + ZEND_ASSERT(zp_arg_must_be_sent_by_ref(function, offset+1) ? Z_ISREF_P(var) : !Z_ISREF_P(var)); + zend_closure_bind_var_ex(result, bind_offset, var); + bind_offset += sizeof(Bucket); + } + + if (extra_named_params) { + zval var; + ZVAL_ARR(&var, extra_named_params); + Z_ADDREF(var); + zend_closure_bind_var_ex(result, bind_offset, &var); + } +} + +void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv, + extra_named_params, named_positions, + declaring_op_array, declaring_opline, + cache_slot, uses_variadic_placeholder); + + if (UNEXPECTED(!op_array)) { + ZEND_ASSERT(EG(exception)); + ZVAL_NULL(result); + return; + } + + zend_class_entry *called_scope; + zval object; + + if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + called_scope = Z_OBJCE_P(this_ptr); + } else { + called_scope = Z_CE_P(this_ptr); + } + + if (Z_TYPE_P(this_ptr) == IS_OBJECT && !zp_is_static_closure(function)) { + ZVAL_COPY_VALUE(&object, this_ptr); + } else { + ZVAL_UNDEF(&object); + } + + zend_create_partial_closure(result, (zend_function*)op_array, + function->common.scope, called_scope, &object, + (function->common.fn_flags & ZEND_ACC_CLOSURE) != 0); + + zp_bind(result, function, argc, argv, extra_named_params); +} + +void zend_partial_op_array_dtor(zval *pDest) +{ + destroy_op_array(Z_PTR_P(pDest)); +} diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h new file mode 100644 index 000000000000..3c47305ff543 --- /dev/null +++ b/Zend/zend_partial.h @@ -0,0 +1,35 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Arnaud Le Blanc | + +----------------------------------------------------------------------+ +*/ +#ifndef ZEND_PARTIAL_H +#define ZEND_PARTIAL_H + +#include "zend_compile.h" + +BEGIN_EXTERN_C() + +void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder); + +void zend_partial_op_array_dtor(zval *pDest); + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 3f0c9abd2596..cdfb6be8c1a9 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -646,6 +646,11 @@ default: ZEND_UNREACHABLE(); _(ZEND_STR_AUTOGLOBAL_ENV, "_ENV") \ _(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \ _(ZEND_STR_COUNT, "count") \ + _(ZEND_STR_FUNC_NUM_ARGS, "func_num_args") \ + _(ZEND_STR_FUNC_GET_ARGS, "func_get_args") \ + _(ZEND_STR_ASSERT, "assert") \ + _(ZEND_STR_CALL_USER_FUNC, "call_user_func") \ + _(ZEND_STR_ARRAY_SLICE, "array_slice") \ _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ _(ZEND_STR_DEPRECATED_CAPITALIZED, "Deprecated") \ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index cfff3b942c4b..d1cc0d793293 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -855,6 +855,8 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_OBJ_LAZY_UNINITIALIZED (1U<<31) /* Virtual proxy or uninitialized Ghost */ #define IS_OBJ_LAZY_PROXY (1U<<30) /* Virtual proxy (may be initialized) */ +#define OBJ_EXTRA_FLAG_PRIV_1 (1U<<29) /* Reserved for private use by the object itself */ +#define OBJ_EXTRA_FLAG_PRIV_2 (1U<<28) /* Reserved for private use by the object itself */ #define OBJ_EXTRA_FLAGS(obj) ((obj)->extra_flags) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 1de7a7cd4195..e2bafa106cae 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5723,6 +5723,30 @@ ZEND_VM_HELPER(zend_verify_recv_arg_type_helper, ANY, ANY, zval *op_1) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(213, ZEND_SEND_PLACEHOLDER, UNUSED, CONST|UNUSED|NUM) +{ + zval *arg; + + if (OP2_TYPE == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED) { USE_OPLINE @@ -8918,9 +8942,10 @@ ZEND_VM_HOT_HANDLER(211, ZEND_TYPE_ASSERT, CONST, ANY, NUM) zend_arg_info *arginfo = &fbc->common.arg_info[argno - 1]; if (!zend_check_type(&arginfo->type, value, /* is_return_type */ false, /* is_internal */ true)) { - const char *param_name = get_function_arg_name(fbc, argno); zend_string *expected = zend_type_to_string(arginfo->type); - zend_type_error("%s(): Argument #%d%s%s%s must be of type %s, %s given", ZSTR_VAL(fbc->common.function_name), argno, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : "", ZSTR_VAL(expected), zend_zval_value_name(value)); + zend_argument_type_error_ex(fbc, argno, + "must be of type %s, %s given", + ZSTR_VAL(expected), zend_zval_value_name(value)); zend_string_release(expected); } } @@ -9838,6 +9863,45 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NUM) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = GET_OP2_ZVAL_PTR(); + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + ZEND_VM_HANDLER(208, ZEND_JMP_FRAMELESS, CONST, JMP_ADDR, NUM|CACHE_SLOT) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5b52f1941845..f4b7bb08a3cb 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -306,6 +306,7 @@ static uint8_t zend_user_opcodes[256] = {0, }; #include "Zend/zend_vm_opcodes.h" +#include "Zend/zend_partial.h" #define SPEC_START_MASK 0x0000ffff #define SPEC_EXTRA_MASK 0xfffc0000 @@ -4272,6 +4273,45 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_R ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4416,6 +4456,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_RECV_VARIADIC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = NULL; + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -6170,9 +6249,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_T zend_arg_info *arginfo = &fbc->common.arg_info[argno - 1]; if (!zend_check_type(&arginfo->type, value, /* is_return_type */ false, /* is_internal */ true)) { - const char *param_name = get_function_arg_name(fbc, argno); zend_string *expected = zend_type_to_string(arginfo->type); - zend_type_error("%s(): Argument #%d%s%s%s must be of type %s, %s given", ZSTR_VAL(fbc->common.function_name), argno, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : "", ZSTR_VAL(expected), zend_zval_value_name(value)); + zend_argument_type_error_ex(fbc, argno, + "must be of type %s, %s given", + ZSTR_VAL(expected), zend_zval_value_name(value)); zend_string_release(expected); } } @@ -34462,6 +34542,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CHECK_FUNC_AR ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_CONST == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -37086,6 +37190,30 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_C ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_UNUSED == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -56946,6 +57074,45 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_I ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57090,6 +57257,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_VARIADIC_SPEC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = NULL; + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -58844,9 +59050,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_TYPE_A zend_arg_info *arginfo = &fbc->common.arg_info[argno - 1]; if (!zend_check_type(&arginfo->type, value, /* is_return_type */ false, /* is_internal */ true)) { - const char *param_name = get_function_arg_name(fbc, argno); zend_string *expected = zend_type_to_string(arginfo->type); - zend_type_error("%s(): Argument #%d%s%s%s must be of type %s, %s given", ZSTR_VAL(fbc->common.function_name), argno, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : "", ZSTR_VAL(expected), zend_zval_value_name(value)); + zend_argument_type_error_ex(fbc, argno, + "must be of type %s, %s given", + ZSTR_VAL(expected), zend_zval_value_name(value)); zend_string_release(expected); } } @@ -86934,6 +87141,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CHECK_FUNC_ARG_SPE ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_CONST == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -89558,6 +89789,30 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CHECK_ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_UNUSED == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + SAVE_OPLINE(); + zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + uint32_t arg_num; + arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num)); + if (UNEXPECTED(!arg)) { + HANDLE_EXCEPTION(); + } + } else { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -109243,6 +109498,16 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_TYPE_ASSERT_SPEC_CONST_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -110647,6 +110912,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_INIT_SPEC_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP): VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP) ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -110662,6 +110932,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_VARIADIC_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_VARIADIC_SPEC_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED): VM_TRACE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED) ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -113885,6 +114160,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CHECK_FUNC_ARG_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CHECK_FUNC_ARG_SPEC_UNUSED_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST): + VM_TRACE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST): VM_TRACE(ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST) ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -114065,6 +114345,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED): + VM_TRACE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_NEW_SPEC_UNUSED_UNUSED): VM_TRACE(ZEND_NEW_SPEC_UNUSED_UNUSED) ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -118181,6 +118466,16 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_NULL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -121659,6 +121954,16 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_TAILCALL_HANDLER, ZEND_RECV_NOTYPE_SPEC_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, @@ -122627,7 +122932,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3474, + 3484, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -122661,7 +122966,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3474, + 3484, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -122794,50 +123099,50 @@ void zend_vm_init(void) 2556, 2557, 2558, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, + 2559 | SPEC_RULE_OP2, + 2564 | SPEC_RULE_OP2, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, + 3484, }; #if 0 #elif (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) @@ -123030,7 +123335,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2567 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2577 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123038,7 +123343,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2592 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2602 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123046,7 +123351,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2617 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2627 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123057,17 +123362,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2642 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2652 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2667 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2677 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2692 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2702 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -123078,17 +123383,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2717 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2727 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2742 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2752 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2767 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2777 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -123099,16 +123404,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2802 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2877 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3092 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3102 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3098 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3108 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -123119,16 +123424,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2952 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3027 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3095 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3105 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3103 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3113 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -123139,12 +123444,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2802 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2877 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -123155,12 +123460,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2952 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3027 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -123168,12 +123473,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3108 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3118 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3183 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3193 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -123181,79 +123486,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3258 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3268 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3333 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3343 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_LONG) { - spec = 3420 | SPEC_RULE_OP1; + spec = 3430 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3425 | SPEC_RULE_OP1; + spec = 3435 | SPEC_RULE_OP1; } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { - spec = 3430 | SPEC_RULE_OP1; + spec = 3440 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3408 | SPEC_RULE_RETVAL; + spec = 3418 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3410 | SPEC_RULE_RETVAL; + spec = 3420 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3412 | SPEC_RULE_RETVAL; + spec = 3422 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3414 | SPEC_RULE_RETVAL; + spec = 3424 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3416; + spec = 3426; } else if (op1_info == MAY_BE_LONG) { - spec = 3417; + spec = 3427; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3418; + spec = 3428; } else if (op1_info == MAY_BE_LONG) { - spec = 3419; + spec = 3429; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2566; + spec = 2576; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2559; + spec = 2569; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2560; + spec = 2570; } break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3470; + spec = 3480; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3465 | SPEC_RULE_OP1; + spec = 3475 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3472 | SPEC_RULE_RETVAL; + spec = 3482 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -123261,22 +123566,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3435 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3445 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3471; + spec = 3481; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3460 | SPEC_RULE_OP1; + spec = 3470 | SPEC_RULE_OP1; } break; case ZEND_COUNT: if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 2561 | SPEC_RULE_OP1; + spec = 2571 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index beabe36688df..4e8f28270bae 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -1,4 +1,5 @@ #include "Zend/zend_vm_opcodes.h" +#include "Zend/zend_partial.h" {%DEFINES%} diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 6f1595195450..7ffe2c220a02 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1087,507 +1087,511 @@ _(2556, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2557, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ _(2558, ZEND_TYPE_ASSERT_SPEC_CONST) \ - _(2559, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2560, ZEND_RECV_NOTYPE_SPEC) \ - _(2562, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ - _(2565, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2566, ZEND_JMP_FORWARD_SPEC) \ - _(2572, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2574, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2576, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2577, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2578, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2579, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2581, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2559, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) \ + _(2562, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) \ + _(2564, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) \ + _(2567, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ + _(2569, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2570, ZEND_RECV_NOTYPE_SPEC) \ + _(2572, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ + _(2575, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2576, ZEND_JMP_FORWARD_SPEC) \ + _(2582, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2583, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2584, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2586, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2587, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2588, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2589, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2597, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2599, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2601, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2602, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2603, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2604, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2606, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2597, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2599, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2601, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2607, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2608, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2609, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2611, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2612, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2613, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2614, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2622, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2624, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2626, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2627, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2628, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2629, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2631, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2622, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2624, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2626, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2632, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2633, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2634, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2636, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2637, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2638, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2639, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2643, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2644, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2646, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2651, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2652, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2654, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2656, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2647, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2649, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2651, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2654, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2656, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2657, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2658, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2659, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2661, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2664, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2669, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2672, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2674, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2676, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2677, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2678, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2679, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2681, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2674, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2676, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2678, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2679, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2681, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2682, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2683, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2684, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2686, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2687, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2688, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2689, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2693, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2694, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2696, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2697, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2699, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2701, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2702, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2703, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2704, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2706, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2697, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2699, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2701, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2703, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2704, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2706, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2707, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2708, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2709, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2711, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2712, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2713, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2714, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2722, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2724, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2726, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2727, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2728, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2729, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2731, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2722, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2724, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2726, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2732, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2733, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2734, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2736, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2737, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2738, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2739, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2747, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2749, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2751, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2752, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2753, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2754, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2756, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2747, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2749, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2751, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2757, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2758, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2759, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2761, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2762, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2763, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2764, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2772, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2774, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2776, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2777, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2778, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2779, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2781, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2772, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2774, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2776, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2782, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2783, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2784, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2786, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2787, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2788, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2789, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2814, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2815, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2860, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2889, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2890, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2935, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2964, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2965, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3010, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3039, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3040, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3085, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3092, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3093, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3094, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3095, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3096, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3097, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3098, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3102, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3103, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3107, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3111, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3112, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3113, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3114, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3115, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3116, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3120, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3123, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3124, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3125, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3130, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3131, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3168, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3176, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3186, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3243, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3251, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3261, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3318, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3326, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3336, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3393, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3401, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3408, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3409, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3410, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3411, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3412, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3413, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3414, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3415, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3416, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3417, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3418, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3419, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3420, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3421, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3422, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3424, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3425, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3426, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3427, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3429, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3430, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3431, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3432, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3434, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3436, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3437, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3439, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3440, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3441, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3442, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3444, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3445, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3447, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3462, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3464, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3467, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3469, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3470, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3471, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3472, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3473, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3473+1, ZEND_NULL) + _(2797, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2799, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2801, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2862, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2863, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2867, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2868, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2869, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2870, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2907, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2908, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2937, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2938, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2942, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2943, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2944, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2945, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2982, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2983, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3012, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3013, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3017, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3018, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3019, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3020, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3057, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3058, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3087, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3088, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3092, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3093, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3094, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3095, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3102, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3103, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3104, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3105, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3106, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3107, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3108, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3112, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3113, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3117, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3123, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3124, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3125, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3126, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3130, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3131, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3132, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3133, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3134, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3154, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3160, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3161, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3162, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3178, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3179, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3183, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3184, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3185, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3186, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3190, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3191, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3192, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3207, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3208, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3229, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3235, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3236, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3237, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3253, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3254, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3258, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3259, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3260, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3261, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3265, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3266, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3267, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3282, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3283, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3284, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3304, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3310, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3311, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3312, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3328, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3329, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3333, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3334, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3335, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3336, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3340, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3341, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3342, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3357, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3358, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3359, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3379, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3385, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3386, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3387, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3403, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3404, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3408, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3409, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3410, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3411, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3415, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3416, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3417, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3418, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3419, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3420, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3421, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3422, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3423, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3424, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3425, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3426, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3427, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3428, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3429, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3430, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ + _(3431, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3432, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3434, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3435, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ + _(3436, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3437, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3439, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3440, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ + _(3441, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3442, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3444, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3447, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3450, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3451, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3452, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3454, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3465, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ + _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3467, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3469, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3472, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3474, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3477, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3479, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3480, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3481, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3482, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3483, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3483+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 0ece3e6f0c66..9846e36037b9 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[212] = { +static const char *zend_vm_opcodes_names[214] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,11 @@ static const char *zend_vm_opcodes_names[212] = { "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", "ZEND_TYPE_ASSERT", + "ZEND_CALLABLE_CONVERT_PARTIAL", + "ZEND_SEND_PLACEHOLDER", }; -static uint32_t zend_vm_opcodes_flags[212] = { +static uint32_t zend_vm_opcodes_flags[214] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -449,6 +451,8 @@ static uint32_t zend_vm_opcodes_flags[212] = { 0x01001103, 0x00000303, 0x01000003, + 0x010003a0, + 0x00001301, }; ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(uint8_t opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 92b46e6628f3..61147518d36c 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -331,7 +331,9 @@ END_EXTERN_C() #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 #define ZEND_TYPE_ASSERT 211 +#define ZEND_CALLABLE_CONVERT_PARTIAL 212 +#define ZEND_SEND_PLACEHOLDER 213 -#define ZEND_VM_LAST_OPCODE 211 +#define ZEND_VM_LAST_OPCODE 213 #endif diff --git a/build/gen_stub.php b/build/gen_stub.php index 619c4c905b60..34bbd34fd37f 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3385,6 +3385,7 @@ public function __construct( private /* readonly */ array $propertyInfos, public array $funcInfos, private readonly array $enumCaseInfos, + private readonly bool $generateCNameTable, public readonly ?string $cond, public ?int $phpVersionIdMinimumCompatibility, public readonly bool $isUndocumentable, @@ -3599,6 +3600,25 @@ public function getCDeclarations(): string $code .= "} {$cEnumName};\n"; + $caseCount = count($this->enumCaseInfos); + + if ($this->generateCNameTable && $caseCount > 0) { + $cPrefix = 'ZEND_ENUM_' . str_replace('\\', '_', $this->name->toString()); + $useGuard = $cPrefix . '_USE_NAME_TABLE'; + + $code .= "\n#define {$cPrefix}_CASE_COUNT {$caseCount}\n"; + $code .= "\n#ifdef {$useGuard}\n"; + $code .= "static const char *{$cEnumName}_case_names[{$cPrefix}_CASE_COUNT + 1] = {\n"; + + foreach ($this->enumCaseInfos as $case) { + $cName = $cPrefix . '_' . $case->name->case; + $code .= "\t[{$cName}] = \"{$case->name->case}\",\n"; + } + + $code .= "};\n"; + $code .= "#endif\n"; + } + if ($this->cond) { $code .= "#endif\n"; } @@ -5149,6 +5169,7 @@ function parseClass( $isStrictProperties = array_key_exists('strict-properties', $tagMap); $isNotSerializable = array_key_exists('not-serializable', $tagMap); $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap); + $generateCNameTable = array_key_exists('c-name-table', $tagMap); foreach ($tags as $tag) { if ($tag->name === 'alias') { $alias = $tag->getValue(); @@ -5210,6 +5231,7 @@ function parseClass( $properties, $methods, $enumCases, + $generateCNameTable, $cond, $minimumPhpVersionIdCompatibility, $isUndocumentable diff --git a/configure.ac b/configure.ac index 6c517ecc0a1e..80214f71ac79 100644 --- a/configure.ac +++ b/configure.ac @@ -1689,6 +1689,7 @@ PHP_ADD_SOURCES([main/streams], m4_normalize([ memory.c mmap.c plain_wrapper.c + stream_errors.c streams.c transports.c userspace.c @@ -1774,6 +1775,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_observer.c zend_opcode.c zend_operators.c + zend_partial.c zend_property_hooks.c zend_ptr_stack.c zend_signal.c diff --git a/docs/release-process.md b/docs/release-process.md index 7dd009676dd4..c07f328f2d8c 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -301,12 +301,27 @@ slightly different steps. We'll call attention where the steps differ. > Only release tags should have version numbers in these files that do not > end in `-dev` (e.g., `8.1.7`, `8.1.7RC1`, `8.2.0alpha1`, etc.). - Do not forget to merge up PHP-X.Y all the way to master. When resolving - the conflicts, ignore the changes from PHP-X.Y in higher branches. It - means using something like `git checkout --ours .` when on PHP.X.Y+1 or - master after the merge resulting in the conflicts. + Do not forget to merge up PHP-X.Y all the way to master. - Be sure to set up a merge driver for the NEWS file as described in + ```shell + git switch PHP-X.Y+1 # starting from your release branch + git merge PHP-X.Y + # repeat # Merge up all the way + git switch master + git merge PHP-X.Y+n # latest release branch + ``` + + When resolving the conflicts, ignore the changes from PHP-X.Y in higher + branches when on PHP.X.Y+1 or master after the merge resulting in the + conflicts. + + ```shell + git checkout --ours main/php_version.h Zend/zend.h configure.ac + git add main/php_version.h Zend/zend.h configure.ac + git merge --continue + ``` + + Be sure to set up a merge driver for the `NEWS` file as described in the [Git FAQ page on the PHP wiki][gitfaq-mandatory]. 11. Push the changes to the `php-src`. diff --git a/docs/source/core/data-structures/zend_string.rst b/docs/source/core/data-structures/zend_string.rst index 2b07611e3425..5b1d3ac0ccfb 100644 --- a/docs/source/core/data-structures/zend_string.rst +++ b/docs/source/core/data-structures/zend_string.rst @@ -29,7 +29,7 @@ in bytes, and the ``val`` field contains the actual string data. You may wonder why the ``val`` field is declared as ``char val[1]``. This is called the `struct hack`_ in C. It is used to create structs with a flexible size, namely by allowing the last element -to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the strings length, +to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the string's length, which is determined at runtime (see ``_ZSTR_STRUCT_SIZE``). When allocating the string, we append enough bytes to the allocation to hold the strings content. diff --git a/ext/curl/config.w32 b/ext/curl/config.w32 index c3c314f1009c..567699f3b748 100644 --- a/ext/curl/config.w32 +++ b/ext/curl/config.w32 @@ -14,7 +14,7 @@ if (PHP_CURL != "no") { CHECK_LIB("libssh2.lib", "curl", PHP_CURL) && CHECK_LIB("nghttp2.lib", "curl", PHP_CURL)) ) { - if (!(CHECK_HEADER_ADD_INCLUDE("brotli/decode.h", "CFLAGS_CURL") && + if (!(CHECK_HEADER("brotli/decode.h", "CFLAGS_CURL") && CHECK_LIB("brotlidec.lib;brotlidec-static.lib", "curl", PHP_CURL) && CHECK_LIB("brotlicommon.lib;brotlicommon-static.lib", "curl", PHP_CURL) )) { diff --git a/ext/curl/curl_private.h b/ext/curl/curl_private.h index 2e7b1cf41d30..77b0628ee42a 100644 --- a/ext/curl/curl_private.h +++ b/ext/curl/curl_private.h @@ -151,15 +151,11 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source); /* Consumes `zv` */ zend_long php_curl_get_long(zval *zv); -static inline php_curl *curl_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_curl, std); -} +#define curl_from_obj(obj) ZEND_CONTAINER_OF(obj, php_curl, std) #define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) -static inline php_curlsh *curl_share_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_curlsh, std); -} +#define curl_share_from_obj(obj) ZEND_CONTAINER_OF(obj, php_curlsh, std) #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 36ea47a8bca2..0e329e58f977 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -48,9 +48,7 @@ zend_class_entry *curl_multi_ce; -static inline php_curlm *curl_multi_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_curlm, std); -} +#define curl_multi_from_obj(obj) ZEND_CONTAINER_OF(obj, php_curlm, std) #define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 8d7dcd2a5ec1..f26617c15ea9 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -58,9 +58,7 @@ struct _php_date_obj { zend_object std; }; -static inline php_date_obj *php_date_obj_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_date_obj, std); -} +#define php_date_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_date_obj, std) #define Z_PHPDATE_P(zv) php_date_obj_from_obj(Z_OBJ_P((zv))) @@ -75,9 +73,7 @@ struct _php_timezone_obj { zend_object std; }; -static inline php_timezone_obj *php_timezone_obj_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_timezone_obj, std); -} +#define php_timezone_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_timezone_obj, std) #define Z_PHPTIMEZONE_P(zv) php_timezone_obj_from_obj(Z_OBJ_P((zv))) @@ -93,9 +89,7 @@ struct _php_interval_obj { zend_object std; }; -static inline php_interval_obj *php_interval_obj_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_interval_obj, std); -} +#define php_interval_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_interval_obj, std) #define Z_PHPINTERVAL_P(zv) php_interval_obj_from_obj(Z_OBJ_P((zv))) @@ -112,9 +106,7 @@ struct _php_period_obj { zend_object std; }; -static inline php_period_obj *php_period_obj_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_period_obj, std); -} +#define php_period_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_period_obj, std) #define Z_PHPPERIOD_P(zv) php_period_obj_from_obj(Z_OBJ_P((zv))) diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 29c560973b77..c0688714fe7c 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -335,10 +335,7 @@ static zend_result dba_connection_cast_object(zend_object *obj, zval *result, in return zend_std_cast_object_tostring(obj, result, type); } -static inline dba_connection *dba_connection_from_obj(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, dba_connection, std); -} +#define dba_connection_from_obj(obj) ZEND_CONTAINER_OF(obj, dba_connection, std) #define Z_DBA_CONNECTION_P(zv) dba_connection_from_obj(Z_OBJ_P(zv)) #define Z_DBA_INFO_P(zv) Z_DBA_CONNECTION_P(zv)->info diff --git a/ext/dom/CREDITS b/ext/dom/CREDITS index 106446a4730b..0c567fca9828 100644 --- a/ext/dom/CREDITS +++ b/ext/dom/CREDITS @@ -1,2 +1,2 @@ DOM -Christian Stocker, Rob Richards, Marcus Boerger, Niels Dossche +Christian Stocker, Rob Richards, Marcus Boerger, Nora Dossche diff --git a/ext/dom/xml_common.h b/ext/dom/xml_common.h index 42937cc37848..ec45fcc08342 100644 --- a/ext/dom/xml_common.h +++ b/ext/dom/xml_common.h @@ -27,9 +27,7 @@ typedef struct _dom_object { zend_object std; } dom_object; -static inline dom_object *php_dom_obj_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, dom_object, std); -} +#define php_dom_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, dom_object, std) #define Z_DOMOBJ_P(zv) php_dom_obj_from_obj(Z_OBJ_P((zv))) diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c index 4a37908612b2..a109c24062f0 100644 --- a/ext/enchant/enchant.c +++ b/ext/enchant/enchant.c @@ -48,9 +48,7 @@ typedef struct _dict_struct { zend_class_entry *enchant_broker_ce; static zend_object_handlers enchant_broker_handlers; -static inline enchant_broker *enchant_broker_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, enchant_broker, std); -} +#define enchant_broker_from_obj(obj) ZEND_CONTAINER_OF(obj, enchant_broker, std) #define Z_ENCHANT_BROKER_P(zv) enchant_broker_from_obj(Z_OBJ_P(zv)) @@ -66,9 +64,7 @@ static zend_object *enchant_broker_create_object(zend_class_entry *class_type) { zend_class_entry *enchant_dict_ce; static zend_object_handlers enchant_dict_handlers; -static inline enchant_dict *enchant_dict_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, enchant_dict, std); -} +#define enchant_dict_from_obj(obj) ZEND_CONTAINER_OF(obj, enchant_dict, std) #define Z_ENCHANT_DICT_P(zv) enchant_dict_from_obj(Z_OBJ_P(zv)) diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index 88126e9f6121..4c5c0e88c5ce 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -43,9 +43,7 @@ typedef struct _finfo_object { zend_object zo; } finfo_object; -static inline finfo_object *php_finfo_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, finfo_object, zo); -} +#define php_finfo_fetch_object(obj) ZEND_CONTAINER_OF(obj, finfo_object, zo) #define Z_FINFO_P(zv) php_finfo_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 92ab74fda575..19f83e3140be 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -144,14 +144,7 @@ static zend_function *php_gd_image_object_get_constructor(zend_object *object) return NULL; } -/** - * Returns the underlying php_gd_image_object from a zend_object - */ - -static zend_always_inline php_gd_image_object* php_gd_exgdimage_from_zobj_p(zend_object* obj) -{ - return ZEND_CONTAINER_OF(obj, php_gd_image_object, std); -} +#define php_gd_exgdimage_from_zobj_p(obj) ZEND_CONTAINER_OF(obj, php_gd_image_object, std) /** * Converts an extension GdImage instance contained within a zval into the gdImagePtr diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 71a0e9ea63f2..47c4dbc0d770 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -1783,6 +1783,9 @@ void gdImageEllipse(gdImagePtr im, int mx, int my, int w, int h, int c) a=w>>1; b=h>>1; + if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { + return; + } gdImageSetPixel(im,mx+a, my, c); gdImageSetPixel(im,mx-a, my, c); mx1 = mx-a;my1 = my; @@ -1824,7 +1827,9 @@ void gdImageFilledEllipse (gdImagePtr im, int mx, int my, int w, int h, int c) a=w>>1; b=h>>1; - + if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { + return; + } for (x = mx-a; x <= mx+a; x++) { gdImageSetPixel(im, x, my, c); } @@ -2879,12 +2884,12 @@ void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c) void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels) { - if (im->style) { - gdFree(im->style); - } if (overflow2(sizeof (int), noOfPixels)) { return; } + if (im->style) { + gdFree(im->style); + } im->style = (int *) gdMalloc(sizeof(int) * noOfPixels); memcpy(im->style, style, sizeof(int) * noOfPixels); im->styleLength = noOfPixels; diff --git a/ext/gd/libgd/gd_filter.c b/ext/gd/libgd/gd_filter.c index d567898548d2..97dea5c552cd 100644 --- a/ext/gd/libgd/gd_filter.c +++ b/ext/gd/libgd/gd_filter.c @@ -7,6 +7,7 @@ #else # include #endif +#include #include #include @@ -20,6 +21,20 @@ /* Begin filters function */ #define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel) +static int gdClampFloatToByte(float value) +{ + if (!isfinite(value)) { + return value > 0.0f ? 255 : 0; + } + if (value > 255.0f) { + return 255; + } + if (value < 0.0f) { + return 0; + } + return (int)value; +} + #ifdef _WIN32 # define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId()) #else @@ -417,6 +432,7 @@ int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, flo for ( y=0; ysy; y++) { for(x=0; xsx; x++) { + int new_ri, new_gi, new_bi; new_r = new_g = new_b = 0; pxl = f(srcback, x, y); new_a = gdImageAlpha(srcback, pxl); @@ -435,13 +451,13 @@ int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, flo new_g = (new_g/filter_div)+offset; new_b = (new_b/filter_div)+offset; - new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r); - new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g); - new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b); + new_ri = gdClampFloatToByte(new_r); + new_gi = gdClampFloatToByte(new_g); + new_bi = gdClampFloatToByte(new_b); - new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + new_pxl = gdImageColorAllocateAlpha(src, new_ri, new_gi, new_bi, new_a); if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + new_pxl = gdImageColorClosestAlpha(src, new_ri, new_gi, new_bi, new_a); } gdImageSetPixel (src, x, y, new_pxl); } diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c index f8ba9087910f..7731d47a0c7c 100644 --- a/ext/gd/libgd/gd_interpolation.c +++ b/ext/gd/libgd/gd_interpolation.c @@ -57,6 +57,7 @@ #include #include #include +#include #include "gd.h" #include "gdhelpers.h" @@ -1873,7 +1874,10 @@ int gdTransformAffineGetImage(gdImagePtr *dst, src_area = &area_full; } - gdTransformAffineBoundingBox(src_area, affine, &bbox); + if (gdTransformAffineBoundingBox(src_area, affine, &bbox) != GD_TRUE) { + *dst = NULL; + return GD_FALSE; + } *dst = gdImageCreateTrueColor(bbox.width, bbox.height); if (*dst == NULL) { @@ -2049,6 +2053,8 @@ int gdTransformAffineCopy(gdImagePtr dst, int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox) { gdPointF extent[4], min, max, point; + double width, height; + int bbox_x, bbox_y, bbox_width, bbox_height; int i; extent[0].x=0.0; @@ -2079,10 +2085,29 @@ int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPt if (max.y < extent[i].y) max.y=extent[i].y; } - bbox->x = (int) min.x; - bbox->y = (int) min.y; - bbox->width = (int) floor(max.x - min.x) - 1; - bbox->height = (int) floor(max.y - min.y); + width = floor(max.x - min.x); + height = floor(max.y - min.y); + if (!isfinite(min.x) || !isfinite(min.y) || !isfinite(width) || !isfinite(height) + || min.x <= INT_MIN || min.x > INT_MAX + || min.y <= INT_MIN || min.y > INT_MAX + || width < 1.0 || width > INT_MAX + || height < 0.0 || height > INT_MAX) { + return GD_FALSE; + } + bbox_x = (int) min.x; + bbox_y = (int) min.y; + bbox_width = (int) width - 1; + bbox_height = (int) height; + if ((bbox_x < 0 && bbox_width > INT_MAX + bbox_x) + || (bbox_x > 0 && bbox_width > INT_MAX - bbox_x) + || (bbox_y < 0 && bbox_height > INT_MAX + bbox_y) + || (bbox_y > 0 && bbox_height > INT_MAX - bbox_y)) { + return GD_FALSE; + } + bbox->x = bbox_x; + bbox->y = bbox_y; + bbox->width = bbox_width; + bbox->height = bbox_height; return GD_TRUE; } diff --git a/ext/gd/libgd/gd_security.c b/ext/gd/libgd/gd_security.c index 438a564ff17c..cbc4872d2fce 100644 --- a/ext/gd/libgd/gd_security.c +++ b/ext/gd/libgd/gd_security.c @@ -14,6 +14,7 @@ #include #include +#include #include #include "gd.h" #include "gd_errors.h" @@ -30,3 +31,20 @@ int overflow2(int a, int b) } return 0; } + +int overflowMul3(int a, int b, int c) +{ + if (a < 0 || b < 0 || c < 0) { + return 1; + } + if (a == 0 || b == 0 || c == 0) { + return 0; + } + if (a > INT_MAX / b) { + return 1; + } + if ((int64_t)a * b > INT64_MAX / c) { + return 1; + } + return 0; +} diff --git a/ext/gd/libgd/gdhelpers.h b/ext/gd/libgd/gdhelpers.h index afb45f0e67de..202ccfce7636 100644 --- a/ext/gd/libgd/gdhelpers.h +++ b/ext/gd/libgd/gdhelpers.h @@ -27,6 +27,7 @@ extern char *gd_strtok_r(char *s, char *sep, char **state); netpbm fixes by Alan Cox. */ int overflow2(int a, int b); +int overflowMul3(int a, int b, int c); #ifdef ZTS #define gdMutexDeclare(x) MUTEX_T x diff --git a/ext/gd/tests/gh19666.phpt b/ext/gd/tests/gh19666.phpt new file mode 100644 index 000000000000..ba9bf54f5661 --- /dev/null +++ b/ext/gd/tests/gh19666.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-19666 (Unexpected nan value in imageconvolution) +--EXTENSIONS-- +gd +--FILE-- + +--EXPECT-- +bool(true) +array(4) { + ["red"]=> + int(0) + ["green"]=> + int(0) + ["blue"]=> + int(0) + ["alpha"]=> + int(0) +} diff --git a/ext/gd/tests/gh19730.phpt b/ext/gd/tests/gh19730.phpt new file mode 100644 index 000000000000..5ea4a2846a9e --- /dev/null +++ b/ext/gd/tests/gh19730.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-19730 (undefined behavior in gd_interpolation.c) +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(false) diff --git a/ext/gd/tests/gh19739.phpt b/ext/gd/tests/gh19739.phpt new file mode 100644 index 000000000000..7dce387b780e --- /dev/null +++ b/ext/gd/tests/gh19739.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-19739 (integer overflow in imageellipse / imagefilledellipse) +--EXTENSIONS-- +gd +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +done diff --git a/ext/gmp/php_gmp_int.h b/ext/gmp/php_gmp_int.h index 01cf26eb3ed5..d563a43ba553 100644 --- a/ext/gmp/php_gmp_int.h +++ b/ext/gmp/php_gmp_int.h @@ -35,9 +35,7 @@ typedef struct _gmp_object { zend_object std; } gmp_object; -static inline gmp_object *php_gmp_object_from_zend_object(zend_object *zobj) { - return ZEND_CONTAINER_OF(zobj, gmp_object, std); -} +#define php_gmp_object_from_zend_object(zobj) ZEND_CONTAINER_OF(zobj, gmp_object, std) PHP_GMP_API zend_class_entry *php_gmp_class_entry(void); diff --git a/ext/intl/breakiterator/breakiterator_class.h b/ext/intl/breakiterator/breakiterator_class.h index c18f9c674866..cb4f072139ec 100644 --- a/ext/intl/breakiterator/breakiterator_class.h +++ b/ext/intl/breakiterator/breakiterator_class.h @@ -41,9 +41,7 @@ typedef struct { zend_object zo; } BreakIterator_object; -static inline BreakIterator_object *php_intl_breakiterator_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, BreakIterator_object, zo); -} +#define php_intl_breakiterator_fetch_object(obj) ZEND_CONTAINER_OF(obj, BreakIterator_object, zo) #define Z_INTL_BREAKITERATOR_P(zv) php_intl_breakiterator_fetch_object(Z_OBJ_P(zv)) #define BREAKITER_ERROR(bio) (bio)->err diff --git a/ext/intl/calendar/calendar_class.h b/ext/intl/calendar/calendar_class.h index 9f8040c81893..7c74aede9475 100644 --- a/ext/intl/calendar/calendar_class.h +++ b/ext/intl/calendar/calendar_class.h @@ -38,9 +38,7 @@ typedef struct { zend_object zo; } Calendar_object; -static inline Calendar_object *php_intl_calendar_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, Calendar_object, zo); -} +#define php_intl_calendar_fetch_object(obj) ZEND_CONTAINER_OF(obj, Calendar_object, zo) #define Z_INTL_CALENDAR_P(zv) php_intl_calendar_fetch_object(Z_OBJ_P(zv)) #define CALENDAR_ERROR(co) (co)->err diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index 03a9e6497d84..fe4749d6d62d 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -203,7 +203,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_available_locales) int32_t count; const Locale *availLocales = Calendar::getAvailableLocales(count); - array_init(return_value); + array_init_size(return_value, count); for (int i = 0; i < count; i++) { Locale locale = availLocales[i]; add_next_index_string(return_value, locale.getName()); diff --git a/ext/intl/collator/collator_class.h b/ext/intl/collator/collator_class.h index 643291ab181c..637d5dc490ae 100644 --- a/ext/intl/collator/collator_class.h +++ b/ext/intl/collator/collator_class.h @@ -47,9 +47,7 @@ typedef struct { #define COLLATOR_ERROR_CODE(co) INTL_ERROR_CODE(COLLATOR_ERROR(co)) #define COLLATOR_ERROR_CODE_P(co) &(INTL_ERROR_CODE(COLLATOR_ERROR(co))) -static inline Collator_object *php_intl_collator_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, Collator_object, zo); -} +#define php_intl_collator_fetch_object(obj) ZEND_CONTAINER_OF(obj, Collator_object, zo) #define Z_INTL_COLLATOR_P(zv) php_intl_collator_fetch_object(Z_OBJ_P(zv)) #ifdef __cplusplus diff --git a/ext/intl/common/common_enum.h b/ext/intl/common/common_enum.h index 69633064ee90..e4a8b5461df6 100644 --- a/ext/intl/common/common_enum.h +++ b/ext/intl/common/common_enum.h @@ -51,10 +51,7 @@ typedef struct { zend_object zo; } IntlIterator_object; - -static inline IntlIterator_object *php_intl_iterator_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, IntlIterator_object, zo); -} +#define php_intl_iterator_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlIterator_object, zo) #define Z_INTL_ITERATOR_P(zv) php_intl_iterator_fetch_object(Z_OBJ_P(zv)) typedef struct { diff --git a/ext/intl/dateformat/dateformat_class.h b/ext/intl/dateformat/dateformat_class.h index dc94907e9960..bef8f6dd6146 100644 --- a/ext/intl/dateformat/dateformat_class.h +++ b/ext/intl/dateformat/dateformat_class.h @@ -36,9 +36,7 @@ typedef struct { zend_object zo; } IntlDateFormatter_object; -static inline IntlDateFormatter_object *php_intl_dateformatter_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, IntlDateFormatter_object, zo); -} +#define php_intl_dateformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlDateFormatter_object, zo) #define Z_INTL_DATEFORMATTER_P(zv) php_intl_dateformatter_fetch_object(Z_OBJ_P(zv)) #ifdef __cplusplus diff --git a/ext/intl/dateformat/dateformat_parse.cpp b/ext/intl/dateformat/dateformat_parse.cpp index 667bbf98ac62..13cf56ad7d82 100644 --- a/ext/intl/dateformat/dateformat_parse.cpp +++ b/ext/intl/dateformat/dateformat_parse.cpp @@ -108,7 +108,7 @@ static void internal_parse_to_localtime(IntlDateFormatter_object *dfo, char* tex INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" ); - array_init( return_value ); + array_init_size( return_value, 9 ); /* Add entries from various fields of the obtained parsed_calendar */ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_SECOND, CALENDAR_SEC); add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_MINUTE, CALENDAR_MIN); diff --git a/ext/intl/dateformat/datepatterngenerator_class.h b/ext/intl/dateformat/datepatterngenerator_class.h index 92739d396627..c2c280e2df2c 100644 --- a/ext/intl/dateformat/datepatterngenerator_class.h +++ b/ext/intl/dateformat/datepatterngenerator_class.h @@ -36,9 +36,7 @@ typedef struct { zend_object zo; } IntlDatePatternGenerator_object; -static inline IntlDatePatternGenerator_object *php_intl_datepatterngenerator_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, IntlDatePatternGenerator_object, zo); -} +#define php_intl_datepatterngenerator_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlDatePatternGenerator_object, zo) #define Z_INTL_DATEPATTERNGENERATOR_P(zv) php_intl_datepatterngenerator_fetch_object(Z_OBJ_P(zv)) #define DTPATTERNGEN_ERROR(dtpgo) (dtpgo)->err diff --git a/ext/intl/formatter/formatter_class.h b/ext/intl/formatter/formatter_class.h index e7f4780bb3bd..44bdfae8debe 100644 --- a/ext/intl/formatter/formatter_class.h +++ b/ext/intl/formatter/formatter_class.h @@ -32,9 +32,7 @@ typedef struct { zend_object zo; } NumberFormatter_object; -static inline NumberFormatter_object *php_intl_number_format_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, NumberFormatter_object, zo); -} +#define php_intl_number_format_fetch_object(obj) ZEND_CONTAINER_OF(obj, NumberFormatter_object, zo) #define Z_INTL_NUMBERFORMATTER_P(zv) php_intl_number_format_fetch_object(Z_OBJ_P(zv)) #ifdef __cplusplus diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp index 817b3d743690..950eb81b7e15 100644 --- a/ext/intl/listformatter/listformatter_class.cpp +++ b/ext/intl/listformatter/listformatter_class.cpp @@ -131,6 +131,8 @@ PHP_METHOD(IntlListFormatter, format) Z_PARAM_ARRAY_HT(ht) ZEND_PARSE_PARAMETERS_END(); + intl_errors_reset(LISTFORMATTER_ERROR_P(obj)); + uint32_t count = zend_hash_num_elements(ht); if (count == 0) { RETURN_EMPTY_STRING(); @@ -152,7 +154,7 @@ PHP_METHOD(IntlListFormatter, format) zend_tmp_string_release(tmp_str); if (U_FAILURE(conv_status)) { - intl_error_set(nullptr, conv_status, "Failed to convert string to UTF-16"); + intl_errors_set(LISTFORMATTER_ERROR_P(obj), conv_status, "Failed to convert string to UTF-16"); RETURN_FALSE; } @@ -165,14 +167,14 @@ PHP_METHOD(IntlListFormatter, format) LISTFORMATTER_OBJECT(obj)->format(items.get(), count, result, status); if (U_FAILURE(status)) { - intl_error_set(nullptr, status, "Failed to format list"); + intl_errors_set(LISTFORMATTER_ERROR_P(obj), status, "Failed to format list"); RETURN_FALSE; } zend_string *ret = intl_charFromString(result, &status); if (!ret) { - intl_error_set(nullptr, status, "Failed to convert result to UTF-8"); + intl_errors_set(LISTFORMATTER_ERROR_P(obj), status, "Failed to convert result to UTF-8"); RETURN_FALSE; } diff --git a/ext/intl/listformatter/listformatter_class.h b/ext/intl/listformatter/listformatter_class.h index 80b35f55b388..7e7fd29207fc 100644 --- a/ext/intl/listformatter/listformatter_class.h +++ b/ext/intl/listformatter/listformatter_class.h @@ -42,9 +42,7 @@ typedef struct { zend_object zo; } ListFormatter_object; -static inline ListFormatter_object *php_intl_listformatter_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, ListFormatter_object, zo); -} +#define php_intl_listformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, ListFormatter_object, zo) #define Z_INTL_LISTFORMATTER_P(zv) php_intl_listformatter_fetch_object(Z_OBJ_P(zv)) #define LISTFORMATTER_ERROR(lfo) (lfo)->lf_data.error diff --git a/ext/intl/locale/locale_methods.cpp b/ext/intl/locale/locale_methods.cpp index e325e14e0d49..1ee32a2f094e 100644 --- a/ext/intl/locale/locale_methods.cpp +++ b/ext/intl/locale/locale_methods.cpp @@ -457,7 +457,7 @@ static zend_string* get_icu_value_internal( const char* loc_name , const char* t efree( mod_loc_name); } - tag_value->len = strlen(tag_value->val); + tag_value->len = buflen; return tag_value; } /* }}} */ @@ -736,7 +736,7 @@ U_CFUNC PHP_FUNCTION( locale_get_keywords ) Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); - INTL_CHECK_LOCALE_LEN(strlen(loc_name)); + INTL_CHECK_LOCALE_LEN(loc_name_len); if(loc_name_len == 0) { loc_name = (char *)intl_locale_get_default(); @@ -1127,7 +1127,7 @@ U_CFUNC PHP_FUNCTION(locale_parse) Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); - INTL_CHECK_LOCALE_LEN(strlen(loc_name)); + INTL_CHECK_LOCALE_LEN(loc_name_len); if(loc_name_len == 0) { loc_name = (char *)intl_locale_get_default(); @@ -1318,7 +1318,7 @@ U_CFUNC PHP_FUNCTION(locale_filter_matches) if( token && (token==cur_lang_tag) ){ /* check if the char. after match is SEPARATOR */ - chrcheck = token + (strlen(cur_loc_range)); + chrcheck = token + can_loc_range->len; if( isIDSeparator(*chrcheck) || isKeywordSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ efree( cur_lang_tag ); efree( cur_loc_range ); @@ -1350,14 +1350,14 @@ U_CFUNC PHP_FUNCTION(locale_filter_matches) } /* end of if isCanonical */ else{ /* Convert to lower case for case-insensitive comparison */ - cur_lang_tag = reinterpret_cast(ecalloc( 1, strlen(lang_tag ) + 1)); + cur_lang_tag = reinterpret_cast(ecalloc(1, lang_tag_len + 1)); result = strToMatch( lang_tag , cur_lang_tag); if( result == 0) { efree( cur_lang_tag ); RETURN_FALSE; } - cur_loc_range = reinterpret_cast(ecalloc( 1, strlen(loc_range ) + 1)); + cur_loc_range = reinterpret_cast(ecalloc(1, loc_range_len + 1)); result = strToMatch( loc_range , cur_loc_range ); if( result == 0) { efree( cur_lang_tag ); @@ -1370,7 +1370,7 @@ U_CFUNC PHP_FUNCTION(locale_filter_matches) if( token && (token==cur_lang_tag) ){ /* check if the char. after match is SEPARATOR */ - chrcheck = token + (strlen(cur_loc_range)); + chrcheck = token + loc_range_len; if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ efree( cur_lang_tag ); efree( cur_loc_range ); diff --git a/ext/intl/msgformat/msgformat_class.h b/ext/intl/msgformat/msgformat_class.h index a066b38e1d13..849c45c14729 100644 --- a/ext/intl/msgformat/msgformat_class.h +++ b/ext/intl/msgformat/msgformat_class.h @@ -30,10 +30,7 @@ typedef struct { zend_object zo; } MessageFormatter_object; - -static inline MessageFormatter_object *php_intl_messageformatter_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, MessageFormatter_object, zo); -} +#define php_intl_messageformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, MessageFormatter_object, zo) #define Z_INTL_MESSAGEFORMATTER_P(zv) php_intl_messageformatter_fetch_object(Z_OBJ_P(zv)) void msgformat_register_class( void ); diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index f504ee50abcb..12e05af57219 100644 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -649,7 +649,7 @@ U_CFUNC void umsg_parse_helper(UMessageFormat *fmt, int *count, zval **args, UCh case Formattable::kInt64: aInt64 = fargs[i].getInt64(); - if(aInt64 > ZEND_LONG_MAX || aInt64 < -ZEND_LONG_MAX) { + if(aInt64 > ZEND_LONG_MAX || aInt64 < ZEND_LONG_MIN) { ZVAL_DOUBLE(&(*args)[i], (double)aInt64); } else { ZVAL_LONG(&(*args)[i], (zend_long)aInt64); diff --git a/ext/intl/rangeformatter/rangeformatter_class.h b/ext/intl/rangeformatter/rangeformatter_class.h index 31b31d784282..494a6d09a05f 100644 --- a/ext/intl/rangeformatter/rangeformatter_class.h +++ b/ext/intl/rangeformatter/rangeformatter_class.h @@ -36,9 +36,7 @@ typedef struct { zend_object zo; } IntlNumberRangeFormatter_object; -static inline IntlNumberRangeFormatter_object *php_intl_numberrangeformatter_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, IntlNumberRangeFormatter_object, zo); -} +#define php_intl_numberrangeformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlNumberRangeFormatter_object, zo) #define Z_INTL_RANGEFORMATTER_P(zv) php_intl_numberrangeformatter_fetch_object(Z_OBJ_P(zv)) diff --git a/ext/intl/spoofchecker/spoofchecker_class.h b/ext/intl/spoofchecker/spoofchecker_class.h index c3da53583b3a..8871ca2a5b24 100644 --- a/ext/intl/spoofchecker/spoofchecker_class.h +++ b/ext/intl/spoofchecker/spoofchecker_class.h @@ -42,9 +42,7 @@ typedef struct { zend_object zo; } Spoofchecker_object; -static inline Spoofchecker_object *php_intl_spoofchecker_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, Spoofchecker_object, zo); -} +#define php_intl_spoofchecker_fetch_object(obj) ZEND_CONTAINER_OF(obj, Spoofchecker_object, zo) #define Z_INTL_SPOOFCHECKER_P(zv) php_intl_spoofchecker_fetch_object((Z_OBJ_P(zv))) #define SPOOFCHECKER_ERROR(co) (co)->err diff --git a/ext/intl/tests/listformatter/listformatter_get_error.phpt b/ext/intl/tests/listformatter/listformatter_get_error.phpt new file mode 100644 index 000000000000..a2a23b3473d2 --- /dev/null +++ b/ext/intl/tests/listformatter/listformatter_get_error.phpt @@ -0,0 +1,25 @@ +--TEST-- +IntlListFormatter getErrorCode()/getErrorMessage() reflect format() failures +--EXTENSIONS-- +intl +--FILE-- +format(["\x80"])); +var_dump($formatter->getErrorCode() === U_INVALID_CHAR_FOUND); +var_dump($formatter->getErrorMessage()); + +var_dump($formatter->format(['a', 'b'])); +var_dump($formatter->getErrorCode() === U_ZERO_ERROR); +var_dump($formatter->getErrorMessage()); + +?> +--EXPECT-- +bool(false) +bool(true) +string(85) "IntlListFormatter::format(): Failed to convert string to UTF-16: U_INVALID_CHAR_FOUND" +string(7) "a and b" +bool(true) +string(12) "U_ZERO_ERROR" diff --git a/ext/intl/tests/msgfmt_parse_int64_min_64.phpt b/ext/intl/tests/msgfmt_parse_int64_min_64.phpt new file mode 100644 index 000000000000..2d003667813c --- /dev/null +++ b/ext/intl/tests/msgfmt_parse_int64_min_64.phpt @@ -0,0 +1,26 @@ +--TEST-- +MessageFormatter::parse() with PHP_INT_MIN on 64-bit platform +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +parse('-9,223,372,036,854,775,808'); +var_dump($parsed); + +$parsed = MessageFormatter::parseMessage('en_US', '{0,number,integer}', '-9,223,372,036,854,775,808'); +var_dump($parsed); + +?> +--EXPECT-- +array(1) { + [0]=> + int(-9223372036854775808) +} +array(1) { + [0]=> + int(-9223372036854775808) +} diff --git a/ext/intl/timezone/timezone_class.h b/ext/intl/timezone/timezone_class.h index a8a05395b406..cb74709c965a 100644 --- a/ext/intl/timezone/timezone_class.h +++ b/ext/intl/timezone/timezone_class.h @@ -46,9 +46,7 @@ typedef struct { zend_object zo; } TimeZone_object; -static inline TimeZone_object *php_intl_timezone_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, TimeZone_object, zo); -} +#define php_intl_timezone_fetch_object(obj) ZEND_CONTAINER_OF(obj, TimeZone_object, zo) #define Z_INTL_TIMEZONE_P(zv) php_intl_timezone_fetch_object(Z_OBJ_P(zv)) #define TIMEZONE_ERROR(to) (to)->err diff --git a/ext/intl/transliterator/transliterator_class.h b/ext/intl/transliterator/transliterator_class.h index e94758996e55..1bdcd60eadc5 100644 --- a/ext/intl/transliterator/transliterator_class.h +++ b/ext/intl/transliterator/transliterator_class.h @@ -38,9 +38,7 @@ typedef struct { zend_object zo; } Transliterator_object; -static inline Transliterator_object *php_intl_transliterator_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, Transliterator_object, zo); -} +#define php_intl_transliterator_fetch_object(obj) ZEND_CONTAINER_OF(obj, Transliterator_object, zo) #define Z_INTL_TRANSLITERATOR_P(zv) php_intl_transliterator_fetch_object(Z_OBJ_P(zv)) #define TRANSLITERATOR_FORWARD UTRANS_FORWARD diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 77322d2de59f..da0c4b216ffe 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -100,9 +100,7 @@ ZEND_TSRMLS_CACHE_DEFINE() ZEND_GET_MODULE(ldap) #endif -static inline ldap_linkdata *ldap_link_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, ldap_linkdata, std); -} +#define ldap_link_from_obj(obj) ZEND_CONTAINER_OF(obj, ldap_linkdata, std) #define Z_LDAP_LINK_P(zv) ldap_link_from_obj(Z_OBJ_P(zv)) @@ -147,9 +145,7 @@ static void ldap_link_free_obj(zend_object *obj) zend_object_std_dtor(&ld->std); } -static inline ldap_resultdata *ldap_result_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, ldap_resultdata, std); -} +#define ldap_result_from_obj(obj) ZEND_CONTAINER_OF(obj, ldap_resultdata, std) #define Z_LDAP_RESULT_P(zv) ldap_result_from_obj(Z_OBJ_P(zv)) @@ -184,9 +180,7 @@ static void ldap_result_free_obj(zend_object *obj) zend_object_std_dtor(&result->std); } -static inline ldap_result_entry *ldap_result_entry_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, ldap_result_entry, std); -} +#define ldap_result_entry_from_obj(obj) ZEND_CONTAINER_OF(obj, ldap_result_entry, std) #define Z_LDAP_RESULT_ENTRY_P(zv) ldap_result_entry_from_obj(Z_OBJ_P(zv)) diff --git a/ext/lexbor/config.m4 b/ext/lexbor/config.m4 index 21fabcd0ddb0..a75f490e77cc 100644 --- a/ext/lexbor/config.m4 +++ b/ext/lexbor/config.m4 @@ -1,4 +1,4 @@ -PHP_LEXBOR_CFLAGS="-I@ext_srcdir@/" +PHP_LEXBOR_CFLAGS="-Wno-unknown-warning-option -Wno-unterminated-string-initialization -I@ext_srcdir@/" LEXBOR_DIR="lexbor" AC_DEFINE([HAVE_LEXBOR], [1], [Define to 1 if the PHP extension 'lexbor' is available.]) diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index 5a1143469d1a..a5b323f2df09 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -860,7 +860,7 @@ lxb_url_is_url_codepoint(lxb_codepoint_t cp) return lxb_url_codepoint_alphanumeric[(lxb_char_t) cp] != 0xFF; } -lxb_inline bool +bool lxb_url_is_special(const lxb_url_t *url) { return url->scheme.type != LXB_URL_SCHEMEL_TYPE__UNKNOWN; diff --git a/ext/lexbor/lexbor/url/url.h b/ext/lexbor/lexbor/url/url.h index 4ed3f32aa646..6cc6f1081c8a 100644 --- a/ext/lexbor/lexbor/url/url.h +++ b/ext/lexbor/lexbor/url/url.h @@ -763,6 +763,15 @@ LXB_API lxb_status_t lxb_url_search_params_serialize(lxb_url_search_params_t *search_params, lexbor_callback_f cb, void *ctx); +/** + * Returns whether the URL is special. + * + * @param[in] lxb_url_t *. Cannot be NULL. + * @return true if URL is special, false otherwise. + */ +LXB_API bool +lxb_url_is_special(const lxb_url_t *url); + /* * Inline functions. */ diff --git a/ext/lexbor/patches/0007-Add-Is_Special_Url_Support.patch b/ext/lexbor/patches/0007-Add-Is_Special_Url_Support.patch new file mode 100644 index 000000000000..6f5e126336d0 --- /dev/null +++ b/ext/lexbor/patches/0007-Add-Is_Special_Url_Support.patch @@ -0,0 +1,44 @@ +From 9181fce509ab9b37c02994545f3971687433e770 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= +Date: Sun, 17 May 2026 22:17:14 +0200 +Subject: [PATCH] Add lxb_url_is_special() to the public API (#362) + +As https://wiki.php.net/rfc/uri_followup#uri_type_detection relies on this information. +--- + source/lexbor/url/url.c | 2 +- + source/lexbor/url/url.h | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/source/lexbor/url/url.c b/source/lexbor/url/url.c +index 5a114346..a5b323f2 100644 +--- a/source/lexbor/url/url.c ++++ b/source/lexbor/url/url.c +@@ -860,7 +860,7 @@ lxb_url_is_url_codepoint(lxb_codepoint_t cp) + return lxb_url_codepoint_alphanumeric[(lxb_char_t) cp] != 0xFF; + } + +-lxb_inline bool ++bool + lxb_url_is_special(const lxb_url_t *url) + { + return url->scheme.type != LXB_URL_SCHEMEL_TYPE__UNKNOWN; +diff --git a/source/lexbor/url/url.h b/source/lexbor/url/url.h +index 4ed3f32a..6cc6f108 100644 +--- a/source/lexbor/url/url.h ++++ b/source/lexbor/url/url.h +@@ -763,6 +763,15 @@ LXB_API lxb_status_t + lxb_url_search_params_serialize(lxb_url_search_params_t *search_params, + lexbor_callback_f cb, void *ctx); + ++/** ++ * Returns whether the URL is special. ++ * ++ * @param[in] lxb_url_t *. Cannot be NULL. ++ * @return true if URL is special, false otherwise. ++ */ ++LXB_API bool ++lxb_url_is_special(const lxb_url_t *url); ++ + /* + * Inline functions. + */ \ No newline at end of file diff --git a/ext/libxml/tests/bug61367-read_2.phpt b/ext/libxml/tests/bug61367-read_2.phpt index f4b0f300293c..ec4ce827336b 100644 --- a/ext/libxml/tests/bug61367-read_2.phpt +++ b/ext/libxml/tests/bug61367-read_2.phpt @@ -5,6 +5,7 @@ dom --SKIPIF-- = 2.9.12 only'); +if (preg_match('/[^\x00-\x7F]/', __DIR__)) die('skip path contains non-ASCII characters that libxml URI parser rejects'); ?> --INI-- open_basedir=. @@ -58,6 +59,6 @@ bool(true) int(4) bool(true) -%s: DOMDocument::loadXML(): %Sfailed to load %s +%s: DOMDocument::loadXML(): %s Warning: Attempt to read property "nodeValue" on null in %s on line %d diff --git a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt index fb71c0c8d756..be386154bb20 100644 --- a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +++ b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt @@ -6,6 +6,7 @@ dom --SKIPIF-- = 2.9.12 only'); +if (preg_match('/[^\x00-\x7F]/', __DIR__)) die('skip path contains non-ASCII characters that libxml URI parser rejects'); ?> --FILE-- --FILE-- connection +#define odbc_result_from_obj(obj) ZEND_CONTAINER_OF(obj, odbc_result, std) #define Z_ODBC_RESULT_P(zv) odbc_result_from_obj(Z_OBJ_P(zv)) static void odbc_insert_new_result(odbc_connection *connection, zval *result) @@ -85,10 +85,7 @@ static void odbc_insert_new_result(odbc_connection *connection, zval *result) Z_ADDREF_P(result); } -static inline odbc_link *odbc_link_from_obj(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, odbc_link, std); -} +#define odbc_link_from_obj(obj) ZEND_CONTAINER_OF(obj, odbc_link, std) static int _close_pconn_with_res(zval *zv, void *p) { @@ -202,11 +199,6 @@ static void odbc_connection_free_obj(zend_object *obj) zend_object_std_dtor(&link->std); } -static inline odbc_result *odbc_result_from_obj(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, odbc_result, std); -} - static zend_object *odbc_result_create_object(zend_class_entry *class_type) { odbc_result *intern = zend_object_alloc(sizeof(odbc_result), class_type); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 3d005b3835a7..52fe71ccca1d 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -24,7 +24,9 @@ #include "zend_compile.h" #include "ZendAccelerator.h" #include "zend_modules.h" +#include "zend_operators.h" #include "zend_persist.h" +#include "zend_portability.h" #include "zend_shared_alloc.h" #include "zend_accelerator_module.h" #include "zend_accelerator_blacklist.h" @@ -48,6 +50,7 @@ #include "zend_system_id.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/basic_functions.h" +#include "zend_vm_opcodes.h" #ifdef ZEND_WIN32 # include "ext/hash/php_hash.h" @@ -2006,6 +2009,211 @@ static bool check_persistent_script_access(const zend_persistent_script *persist } } +static const char hexchars[] = "0123456789abcdef"; + +static char *zend_accel_uintptr_hex(char *dest, uintptr_t n) +{ + do { + *dest++ = hexchars[n & 0xf]; + n >>= 4; + } while (n); + + return dest; +} + +/* Prevents collisions with real scripts, as we don't cache paths prefixed with + * a scheme, except file:// and phar://. */ +#define PFA_KEY_PREFIX "pfa://" + +static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline, + const zend_function *called_function) +{ + const size_t max_key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2); + zend_string *key = zend_string_alloc(max_key_len, 0); + char *dest = ZSTR_VAL(key); + + dest = zend_mempcpy(ZSTR_VAL(key), PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX)); + dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_opline); + *dest++ = ':'; + + const void *ptr; + if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE) + && called_function->type == ZEND_USER_FUNCTION) { + /* Can not use 'called_function' as part of the key, as it's an inner + * pointer to a Closure, which may be freed. Use its opcodes instead. + * zend_accel_compile_pfa() ensures to extend the lifetime of opcodes + * in this case. */ + ptr = called_function->op_array.opcodes; + } else { + ptr = called_function; + } + dest = zend_accel_uintptr_hex(dest, (uintptr_t)ptr); + + *dest = '\0'; + ZSTR_LEN(key) = dest - ZSTR_VAL(key); + + return key; +} + +const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, const zend_function *called_function) +{ + zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + zend_op_array *op_array = NULL; + + /* A PFA is SHM-cacheable if the declaring_op_array and called_function are + * cached. */ + if (ZCG(accelerator_enabled) + && !file_cache_only + && !declaring_op_array->refcount + && (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) { + zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key); + if (persistent_script) { + op_array = persistent_script->script.main_op_array.dynamic_func_defs[0]; + if (persistent_script->num_warnings) { + zend_emit_recorded_errors_ex(persistent_script->num_warnings, + persistent_script->warnings); + } + } + } else { + op_array = zend_hash_find_ptr(&EG(partial_function_application_cache), key); + } + + zend_string_release(key); + + return op_array; +} + +zend_op_array *zend_accel_compile_pfa(zend_ast *ast, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function, + zend_string *pfa_func_name) +{ + zend_begin_record_errors(); + zend_op_array *op_array; + + uint32_t orig_compiler_options = CG(compiler_options); + + zend_try { + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER; +#ifdef ZEND_WIN32 + /* On Windows, don't compile with internal classes. Shm may be attached from different + * processes with internal classes living in different addresses. */ + CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; +#endif + + op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_op_array->filename); + + ZEND_ASSERT(op_array->num_dynamic_func_defs == 1); + + CG(compiler_options) = orig_compiler_options; + } zend_catch { + op_array = NULL; + CG(compiler_options) = orig_compiler_options; + EG(record_errors) = false; + zend_free_recorded_errors(); + zend_bailout(); + } zend_end_try(); + + if (!op_array) { + zend_emit_recorded_errors(); + zend_free_recorded_errors(); + return NULL; + } + + ZEND_ASSERT(op_array->num_dynamic_func_defs == 1); + + zend_string_release(op_array->dynamic_func_defs[0]->function_name); + op_array->dynamic_func_defs[0]->function_name = pfa_func_name; + + zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + + /* Cache op_array only if the declaring op_array and the called function + * are cached */ + if (!ZCG(accelerator_enabled) + || file_cache_only + || declaring_op_array->refcount + || (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount) + || (ZCSG(restart_in_progress) && accel_restart_is_active()) + || (!ZCG(counted) && accel_activate_add() == FAILURE)) { + zend_op_array *script_op_array = op_array; + zend_op_array *op_array = script_op_array->dynamic_func_defs[0]; + GC_ADDREF(op_array->function_name); + (*op_array->refcount)++; + destroy_op_array(script_op_array); + efree(script_op_array); + + if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE) + && called_function->type == ZEND_USER_FUNCTION + && called_function->op_array.refcount) { + /* Extend the lifetime of the called opcodes if + * the called function is a closure. + * See comment in zend_accel_pfa_key(). */ + zend_op_array *copy = zend_arena_alloc(&CG(arena), sizeof(zend_function)); + memcpy(copy, called_function, sizeof(zend_op_array)); + zend_string_addref(copy->function_name); + (*copy->refcount)++; + /* Reference the copy in op_array->dynamic_func_defs so that it's + * destroyed when op_array is destroyed. */ + ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs); + op_array->dynamic_func_defs = emalloc(sizeof(zend_op_array*)); + op_array->dynamic_func_defs[0] = copy; + op_array->num_dynamic_func_defs = 1; + } + + zend_hash_add_new_ptr(&EG(partial_function_application_cache), key, op_array); + zend_string_release(key); + + zend_emit_recorded_errors(); + zend_free_recorded_errors(); + + return op_array; + } + + zend_persistent_script *new_persistent_script = create_persistent_script(); + new_persistent_script->script.main_op_array = *op_array; + efree_size(op_array, sizeof(zend_op_array)); + new_persistent_script->script.filename = key; + + if (ZCG(accel_directives).record_warnings) { + new_persistent_script->num_warnings = EG(errors).size; + new_persistent_script->warnings = EG(errors).errors; + } + + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + bool from_shared_memory; + /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */ + bool orig_gc_state = gc_enable(false); + char *orig_file_cache = ZCG(accel_directives).file_cache; + /* Disable file_cache temporarily, as we can't guarantee consistency. */ + ZCG(accel_directives).file_cache = false; + new_persistent_script = cache_script_in_shared_memory(new_persistent_script, NULL, &from_shared_memory); + ZCG(accel_directives).file_cache = orig_file_cache; + gc_enable(orig_gc_state); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + /* We may have switched to an existing persistent script that was persisted in + * the meantime. Make sure to use its warnings if available. */ + if (ZCG(accel_directives).record_warnings) { + EG(record_errors) = false; + zend_emit_recorded_errors_ex(new_persistent_script->num_warnings, new_persistent_script->warnings); + } else { + zend_emit_recorded_errors(); + } + zend_free_recorded_errors(); + + return new_persistent_script->script.main_op_array.dynamic_func_defs[0]; +} + /* zend_compile() replacement */ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) { diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 91642e288d31..ef7eea433c09 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -334,6 +334,16 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str); uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name); +const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function); + +zend_op_array *zend_accel_compile_pfa(zend_ast *ast, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function, + zend_string *pfa_func_name); + END_EXTERN_C() /* memory write protection */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index fbbfab6b243c..54198562e8ef 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -302,6 +302,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: return 0; case ZEND_SEND_VAL: case ZEND_SEND_VAR: @@ -387,6 +388,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: end = opline; if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) { /* INIT_FCALL and DO_FCALL in different BasicBlocks */ @@ -866,6 +868,7 @@ static bool zend_jit_dec_call_level(uint8_t opcode) case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: return true; default: return false; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index cf43d3ad840f..a5ed82f65b5a 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10112,7 +10112,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen } bool may_have_extra_named_params = - opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS && + (opline->extended_value & ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS) && (!func || func->common.fn_flags & ZEND_ACC_VARIADIC); if (!jit->reuse_ip) { diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 271d923598d9..65d83e8db55b 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1057,7 +1057,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, TRACE_RECORD(ZEND_JIT_TRACE_DO_ICALL, 0, func); } } else if (opline->opcode == ZEND_INCLUDE_OR_EVAL - || opline->opcode == ZEND_CALLABLE_CONVERT) { + || opline->opcode == ZEND_CALLABLE_CONVERT + || opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { /* TODO: Support tracing JIT for ZEND_CALLABLE_CONVERT. */ stop = ZEND_JIT_TRACE_STOP_INTERPRETER; break; diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c85ca073f446..65e63265dfc6 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -169,7 +169,76 @@ static void php_openssl_pkey_free_obj(zend_object *object) zend_object_std_dtor(&key_object->std); } -/* OpenSSLSession class */ +/* Openssl\Psk class */ + +zend_class_entry *php_openssl_psk_ce; + +static zend_object_handlers php_openssl_psk_object_handlers; + +bool php_openssl_is_psk_ce(zval *val) +{ + return Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_psk_ce; +} + +zend_string *php_openssl_psk_get_psk(zval *psk_zv) +{ + zval rv; + zval *prop = zend_read_property(php_openssl_psk_ce, Z_OBJ_P(psk_zv), ZEND_STRL("psk"), 0, &rv); + if (UNEXPECTED(Z_TYPE_P(prop) != IS_STRING)) { + return NULL; + } + return Z_STR_P(prop); +} + +zend_string *php_openssl_psk_get_identity(zval *psk_zv) +{ + zval rv; + zval *prop = zend_read_property(php_openssl_psk_ce, Z_OBJ_P(psk_zv), + ZEND_STRL("identity"), 0, &rv); + if (Z_TYPE_P(prop) == IS_NULL) { + return NULL; + } + if (UNEXPECTED(Z_TYPE_P(prop) != IS_STRING)) { + return NULL; + } + return Z_STR_P(prop); +} + +PHP_METHOD(Openssl_Psk, __construct) +{ + zend_string *psk; + zend_string *identity = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(psk) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(identity) + ZEND_PARSE_PARAMETERS_END(); + + if (ZSTR_LEN(psk) == 0) { + zend_argument_value_error(1, "must not be empty"); + RETURN_THROWS(); + } + if (ZSTR_LEN(psk) > PHP_OPENSSL_PSK_MAX_PSK_LEN) { + zend_argument_value_error(1, "must not exceed %d bytes", PHP_OPENSSL_PSK_MAX_PSK_LEN); + RETURN_THROWS(); + } + if (identity != NULL && ZSTR_LEN(identity) > PHP_OPENSSL_PSK_MAX_IDENTITY_LEN) { + zend_argument_value_error(2, "must not exceed %d bytes", PHP_OPENSSL_PSK_MAX_IDENTITY_LEN); + RETURN_THROWS(); + } + + zend_update_property_str(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("psk"), psk); + + if (identity != NULL) { + zend_update_property_str(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), + ZEND_STRL("identity"), identity); + } else { + zend_update_property_null(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("identity")); + } +} + +/* Openssl\Session class */ zend_class_entry *php_openssl_session_ce; @@ -716,6 +785,11 @@ PHP_MINIT_FUNCTION(openssl) php_openssl_pkey_object_handlers.clone_obj = NULL; php_openssl_pkey_object_handlers.compare = zend_objects_not_comparable; + php_openssl_psk_ce = register_class_Openssl_Psk(); + php_openssl_psk_ce->default_object_handlers = &php_openssl_psk_object_handlers; + + memcpy(&php_openssl_psk_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + php_openssl_session_ce = register_class_Openssl_Session(); php_openssl_session_ce->create_object = php_openssl_session_create_object; php_openssl_session_ce->default_object_handlers = &php_openssl_session_object_handlers; diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index 86dcc8f4f556..6080ac323903 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -8,6 +8,27 @@ class OpensslException extends Exception { } + /** + * @strict-properties + */ + final class Psk + { + /** + * @cvalue PHP_OPENSSL_PSK_MAX_PSK_LEN + */ + public const int MAX_PSK_LEN = UNKNOWN; + + /** + * @cvalue PHP_OPENSSL_PSK_MAX_IDENTITY_LEN + */ + public const int MAX_IDENTITY_LEN = UNKNOWN; + + public readonly string $psk; + public readonly ?string $identity; + + public function __construct(string $psk, ?string $identity = null) {} + } + /** * @strict-properties */ diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 851ba2e913ba..caf47a256e78 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit openssl.stub.php instead. - * Stub hash: c0b746f3a9fff06533a7682a35f44f5df951d12f */ + * Stub hash: 4d38e81a2f73bb6dd4bbe7a3e0b8ba86600654e2 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -406,6 +406,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_password_verify, 0, 3, _ ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Openssl_Psk___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, psk, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, identity, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session_export, 0, 0, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, IS_LONG, 0, "OPENSSL_ENCODING_PEM") ZEND_END_ARG_INFO() @@ -506,6 +511,7 @@ ZEND_FUNCTION(openssl_get_cert_locations); ZEND_FUNCTION(openssl_password_hash); ZEND_FUNCTION(openssl_password_verify); #endif +ZEND_METHOD(Openssl_Psk, __construct); ZEND_METHOD(Openssl_Session, export); ZEND_METHOD(Openssl_Session, import); ZEND_METHOD(Openssl_Session, isResumable); @@ -592,6 +598,11 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; +static const zend_function_entry class_Openssl_Psk_methods[] = { + ZEND_ME(Openssl_Psk, __construct, arginfo_class_Openssl_Psk___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_Openssl_Session_methods[] = { ZEND_ME(Openssl_Session, export, arginfo_class_Openssl_Session_export, ZEND_ACC_PUBLIC) ZEND_ME(Openssl_Session, import, arginfo_class_Openssl_Session_import, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -820,6 +831,40 @@ static zend_class_entry *register_class_Openssl_OpensslException(zend_class_entr return class_entry; } +static zend_class_entry *register_class_Openssl_Psk(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Openssl", "Psk", class_Openssl_Psk_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + zval const_MAX_PSK_LEN_value; + ZVAL_LONG(&const_MAX_PSK_LEN_value, PHP_OPENSSL_PSK_MAX_PSK_LEN); + zend_string *const_MAX_PSK_LEN_name = zend_string_init_interned("MAX_PSK_LEN", sizeof("MAX_PSK_LEN") - 1, true); + zend_declare_typed_class_constant(class_entry, const_MAX_PSK_LEN_name, &const_MAX_PSK_LEN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(const_MAX_PSK_LEN_name, true); + + zval const_MAX_IDENTITY_LEN_value; + ZVAL_LONG(&const_MAX_IDENTITY_LEN_value, PHP_OPENSSL_PSK_MAX_IDENTITY_LEN); + zend_string *const_MAX_IDENTITY_LEN_name = zend_string_init_interned("MAX_IDENTITY_LEN", sizeof("MAX_IDENTITY_LEN") - 1, true); + zend_declare_typed_class_constant(class_entry, const_MAX_IDENTITY_LEN_name, &const_MAX_IDENTITY_LEN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(const_MAX_IDENTITY_LEN_name, true); + + zval property_psk_default_value; + ZVAL_UNDEF(&property_psk_default_value); + zend_string *property_psk_name = zend_string_init("psk", sizeof("psk") - 1, true); + zend_declare_typed_property(class_entry, property_psk_name, &property_psk_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_psk_name, true); + + zval property_identity_default_value; + ZVAL_UNDEF(&property_identity_default_value); + zend_string *property_identity_name = zend_string_init("identity", sizeof("identity") - 1, true); + zend_declare_typed_property(class_entry, property_identity_name, &property_identity_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release_ex(property_identity_name, true); + + return class_entry; +} + static zend_class_entry *register_class_Openssl_Session(void) { zend_class_entry ce, *class_entry; diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h index ed47d8850848..20a04bbcd1a0 100644 --- a/ext/openssl/php_openssl.h +++ b/ext/openssl/php_openssl.h @@ -163,9 +163,7 @@ typedef struct _php_openssl_certificate_object { extern zend_class_entry *php_openssl_certificate_ce; -static inline php_openssl_certificate_object *php_openssl_certificate_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_openssl_certificate_object, std); -} +#define php_openssl_certificate_from_obj(obj) ZEND_CONTAINER_OF(obj, php_openssl_certificate_object, std) #define Z_OPENSSL_CERTIFICATE_P(zv) php_openssl_certificate_from_obj(Z_OBJ_P(zv)) @@ -178,9 +176,7 @@ typedef struct _php_openssl_x509_request_object { zend_object std; } php_openssl_request_object; -static inline php_openssl_request_object *php_openssl_request_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_openssl_request_object, std); -} +#define php_openssl_request_from_obj(obj) ZEND_CONTAINER_OF(obj, php_openssl_request_object, std) #define Z_OPENSSL_REQUEST_P(zv) php_openssl_request_from_obj(Z_OBJ_P(zv)) @@ -194,16 +190,26 @@ typedef struct _php_openssl_pkey_object { zend_object std; } php_openssl_pkey_object; -static inline php_openssl_pkey_object *php_openssl_pkey_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_openssl_pkey_object, std); -} +#define php_openssl_pkey_from_obj(obj) ZEND_CONTAINER_OF(obj, php_openssl_pkey_object, std) #define Z_OPENSSL_PKEY_P(zv) php_openssl_pkey_from_obj(Z_OBJ_P(zv)) bool php_openssl_is_pkey_ce(zval *val); void php_openssl_pkey_object_init(zval *zv, EVP_PKEY *pkey, bool is_private); -/* OpenSSLSession class */ +/* Openssl\Psk class */ + +/* Matches OpenSSL's PSK_MAX_PSK_LEN and PSK_MAX_IDENTITY_LEN */ +#define PHP_OPENSSL_PSK_MAX_PSK_LEN 256 +#define PHP_OPENSSL_PSK_MAX_IDENTITY_LEN 128 + +extern zend_class_entry *php_openssl_psk_ce; + +bool php_openssl_is_psk_ce(zval *val); +zend_string *php_openssl_psk_get_psk(zval *psk_zv); +zend_string *php_openssl_psk_get_identity(zval *psk_zv); + +/* Openssl\Session class */ #include diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc index f0336fdd3921..c5db41d48417 100644 --- a/ext/openssl/tests/ServerClientTestCase.inc +++ b/ext/openssl/tests/ServerClientTestCase.inc @@ -100,7 +100,8 @@ class ServerClientTestCase $ini = php_ini_loaded_file(); $cmd = sprintf( '%s %s "%s" %s', - PHP_BINARY, $ini ? "-n -c $ini" : "", + // XXX: TEST_PHP_EXTRA_ARGS for run-test values won't work here? + PHP_BINARY, $ini ? "-n -c $ini -d error_include_args=0" : "", __FILE__, WORKER_ARGV_VALUE ); diff --git a/ext/openssl/tests/gh22081.phpt b/ext/openssl/tests/gh22081.phpt new file mode 100644 index 000000000000..17f74be7584c --- /dev/null +++ b/ext/openssl/tests/gh22081.phpt @@ -0,0 +1,76 @@ +--TEST-- +GH-22081: server reneg limit not reallocated across non-blocking retries +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + /* Plain TCP listener so the TLS handshake is driven manually below. */ + $server = stream_socket_server('tcp://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Complete each handshake in non-blocking mode so that php_openssl_enable_crypto() is + * re-entered across multiple WANT_READ/WANT_WRITE rounds per connection. */ + for ($i = 0; $i < 5; $i++) { + $conn = @stream_socket_accept($server, 30); + if (!$conn) { + continue; + } + stream_set_blocking($conn, false); + do { + $r = stream_socket_enable_crypto($conn, true, STREAM_CRYPTO_METHOD_TLS_SERVER); + } while ($r === 0); + + if ($r === true) { + fwrite($conn, "ok $i\n"); + } + fclose($conn); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ]]); + + for ($i = 0; $i < 5; $i++) { + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client) { + echo trim(fgets($client)) . "\n"; + fclose($client); + } + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('gh22081-server-reneg-nonblocking', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +ok 0 +ok 1 +ok 2 +ok 3 +ok 4 + diff --git a/ext/openssl/tests/tls_psk_callback_not_callable.phpt b/ext/openssl/tests/tls_psk_callback_not_callable.phpt new file mode 100644 index 000000000000..29382e2a13ab --- /dev/null +++ b/ext/openssl/tests/tls_psk_callback_not_callable.phpt @@ -0,0 +1,46 @@ +--TEST-- +TLS PSK callback option must be a valid callable +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => 'php_openssl_no_such_function', + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (TypeError $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECTF-- +caught: psk_client_cb must be a valid callback, %s + diff --git a/ext/openssl/tests/tls_psk_callback_wrong_type.phpt b/ext/openssl/tests/tls_psk_callback_wrong_type.phpt new file mode 100644 index 000000000000..78d950f19974 --- /dev/null +++ b/ext/openssl/tests/tls_psk_callback_wrong_type.phpt @@ -0,0 +1,48 @@ +--TEST-- +TLS PSK client callback returning wrong type raises TypeError +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream) { + /* Returning a string instead of Openssl\Psk|null. */ + return "not a Psk object"; + }, + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (TypeError $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +caught: PSK callback must return Openssl\Psk or null diff --git a/ext/openssl/tests/tls_psk_client_callback_null.phpt b/ext/openssl/tests/tls_psk_client_callback_null.phpt new file mode 100644 index 000000000000..92d970908c8e --- /dev/null +++ b/ext/openssl/tests/tls_psk_client_callback_null.phpt @@ -0,0 +1,45 @@ +--TEST-- +TLS 1.2 PSK: client callback returning null aborts handshake (no shared cipher) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("doesnotmatter", null); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + /* Reject: no PSK to use, no other ciphers offered. */ + return null; + }, + ]]); + $client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) + diff --git a/ext/openssl/tests/tls_psk_client_callback_throws.phpt b/ext/openssl/tests/tls_psk_client_callback_throws.phpt new file mode 100644 index 000000000000..13b2d71476c2 --- /dev/null +++ b/ext/openssl/tests/tls_psk_client_callback_throws.phpt @@ -0,0 +1,48 @@ +--TEST-- +TLS PSK client callback throwing exception propagates to stream_socket_client caller +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + throw new RuntimeException('callback boom'); + }, + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (RuntimeException $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +caught: callback boom + diff --git a/ext/openssl/tests/tls_psk_client_no_identity.phpt b/ext/openssl/tests/tls_psk_client_no_identity.phpt new file mode 100644 index 000000000000..bfc8d078ceff --- /dev/null +++ b/ext/openssl/tests/tls_psk_client_no_identity.phpt @@ -0,0 +1,49 @@ +--TEST-- +TLS PSK client callback must return Openssl\Psk with a non-null identity +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + /* No identity supplied. */ + return new Openssl\Psk(str_repeat("\x42", 16)); + }, + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (ValueError $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +caught: Client PSK callback must return Openssl\Psk with a non-null identity + diff --git a/ext/openssl/tests/tls_psk_mismatch.phpt b/ext/openssl/tests/tls_psk_mismatch.phpt new file mode 100644 index 000000000000..8fa90d115c5a --- /dev/null +++ b/ext/openssl/tests/tls_psk_mismatch.phpt @@ -0,0 +1,44 @@ +--TEST-- +TLS 1.2 PSK: mismatching key material aborts handshake +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x11", 16), $identity); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x22", 16), 'id'); + }, + ]]); + $client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) + diff --git a/ext/openssl/tests/tls_psk_tls12_basic.phpt b/ext/openssl/tests/tls_psk_tls12_basic.phpt new file mode 100644 index 000000000000..2f2a558c4b40 --- /dev/null +++ b/ext/openssl/tests/tls_psk_tls12_basic.phpt @@ -0,0 +1,62 @@ +--TEST-- +TLS 1.2 PSK basic client/server round-trip +--EXTENSIONS-- +openssl +--SKIPIF-- += 1.1.1 required"); +?> +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + if ($identity === 'client_id') { + return new Openssl\Psk("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff"); + } + return null; + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + $client = stream_socket_accept($server, 30); + fwrite($client, "hello-from-server"); + fread($client, 1024); + fclose($client); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'verify_peer_name' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk( + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + 'client_id' + ); + }, + ]]); + $client = stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 30, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client !== false); + var_dump(fread($client, 1024)); + fwrite($client, "hello-from-client"); + /* Verify PSK was used (no peer cert) */ + $params = stream_context_get_params($client); + var_dump(isset($params['options']['ssl']['peer_certificate'])); + fclose($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(true) +string(17) "hello-from-server" +bool(false) diff --git a/ext/openssl/tests/tls_psk_tls13_basic.phpt b/ext/openssl/tests/tls_psk_tls13_basic.phpt new file mode 100644 index 000000000000..a7642624252a --- /dev/null +++ b/ext/openssl/tests/tls_psk_tls13_basic.phpt @@ -0,0 +1,55 @@ +--TEST-- +TLS 1.3 PSK basic client/server round-trip +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + if ($identity === 'tls13-client') { + return new Openssl\Psk(str_repeat("\x42", 32)); + } + return null; + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + $client = stream_socket_accept($server, 30); + fwrite($client, "tls13-psk-ok"); + fclose($client); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + 'verify_peer' => false, + 'verify_peer_name' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x42", 32), 'tls13-client'); + }, + ]]); + $client = stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 30, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client !== false); + var_dump(fread($client, 1024)); + /* TLS 1.3 PSK: no peer cert */ + $params = stream_context_get_params($client); + var_dump(isset($params['options']['ssl']['peer_certificate'])); + fclose($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(true) +string(12) "tls13-psk-ok" +bool(false) diff --git a/ext/openssl/tests/tls_psk_tls13_unknown_identity.phpt b/ext/openssl/tests/tls_psk_tls13_unknown_identity.phpt new file mode 100644 index 000000000000..830c02449dc9 --- /dev/null +++ b/ext/openssl/tests/tls_psk_tls13_unknown_identity.phpt @@ -0,0 +1,44 @@ +--TEST-- +TLS 1.3 PSK: server rejects unknown identity (no cert fallback) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return null; + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + 'verify_peer' => false, + 'verify_peer_name' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x42", 32), 'unknown-id'); + }, + ]]); + $client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) + diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index ed72ca49677f..307cc3489c3c 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -176,6 +176,17 @@ typedef struct _php_openssl_alpn_ctx_t { } php_openssl_alpn_ctx; #endif +/* TLS 1.3 PSK ciphersuite IDs */ +static const unsigned char php_openssl_tls13_aes128gcmsha256_id[] = { 0x13, 0x01 }; +static const unsigned char php_openssl_tls13_aes256gcmsha384_id[] = { 0x13, 0x02 }; + +/* Holds PSK callbacks */ +typedef struct _php_openssl_psk_callbacks_t { + int refcount; + zval client_cb; + zval server_cb; +} php_openssl_psk_callbacks_t; + /* Holds session callback */ typedef struct _php_openssl_session_callbacks_t { int refcount; @@ -204,6 +215,11 @@ typedef struct _php_openssl_netstream_data_t { php_openssl_alpn_ctx alpn_ctx; #endif php_openssl_session_callbacks_t *session_callbacks; + php_openssl_psk_callbacks_t *psk_callbacks; + /* Identity buffer for TLS 1.3 client PSK whose lifetime outlives the + * psk_use_session_cb call but OpenSSL doesn't free it, so we own it. */ + unsigned char *psk_identity_buf; + size_t psk_identity_len; char *url_name; unsigned state_set:1; unsigned _spare:31; @@ -1565,6 +1581,407 @@ static int php_openssl_get_ctx_stream_data_index(void) return ctx_data_index; } +/** + * Build a SSL_SESSION suitable for use as an external PSK in TLS 1.3. + */ +static SSL_SESSION *php_openssl_psk_build_session(SSL *ssl, + const unsigned char *psk, size_t psk_len, const EVP_MD *md) +{ + const SSL_CIPHER *cipher; + + cipher = SSL_CIPHER_find(ssl, php_openssl_tls13_aes128gcmsha256_id); + if (cipher == NULL) { + return NULL; + } + + if (md != NULL && SSL_CIPHER_get_handshake_digest(cipher) != md) { + /* Fallback to SHA384 if SHA256 doesn't match */ + cipher = SSL_CIPHER_find(ssl, php_openssl_tls13_aes256gcmsha384_id); + if (cipher == NULL || SSL_CIPHER_get_handshake_digest(cipher) != md) { + return NULL; + } + } + + SSL_SESSION *sess = SSL_SESSION_new(); + if (sess == NULL) { + return NULL; + } + + if (!SSL_SESSION_set1_master_key(sess, psk, psk_len) + || !SSL_SESSION_set_cipher(sess, cipher) + || !SSL_SESSION_set_protocol_version(sess, TLS1_3_VERSION)) { + SSL_SESSION_free(sess); + return NULL; + } + + return sess; +} + + +/** + * Invoke a user PHP callback (psk_client_cb or psk_server_cb). + */ +static zend_result php_openssl_call_psk_cb(php_stream *stream, zval *cb, + const unsigned char *identity, size_t identity_len, + zval *result) +{ + zval args[2]; + zval retval; + int argc; + + ZVAL_RES(&args[0], stream->res); + + if (identity != NULL) { + ZVAL_STRINGL(&args[1], (const char *)identity, identity_len); + argc = 2; + } else { + argc = 1; + } + + ZVAL_UNDEF(&retval); + call_user_function(NULL, NULL, cb, &retval, argc, args); + + if (identity != NULL) { + zval_ptr_dtor(&args[1]); + } + + if (EG(exception)) { + ZVAL_UNDEF(result); + return FAILURE; + } + + if (Z_TYPE(retval) == IS_NULL) { + ZVAL_NULL(result); + return SUCCESS; + } + + if (!php_openssl_is_psk_ce(&retval)) { + zval_ptr_dtor(&retval); + zend_type_error("PSK callback must return Openssl\\Psk or null"); + ZVAL_UNDEF(result); + return FAILURE; + } + + ZVAL_COPY_VALUE(result, &retval); + return SUCCESS; +} + +#ifndef OPENSSL_NO_PSK +/* TLS 1.2 (and below) PSK callbacks. */ + +static unsigned int php_openssl_psk_client_cb(SSL *ssl, const char *hint, + char *identity_out, unsigned int max_identity_len, + unsigned char *psk_out, unsigned int max_psk_len) +{ + (void)hint; /* identity hint deliberately not exposed as it is useless */ + (void)max_identity_len; + (void)max_psk_len; + + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 0; + } + + php_openssl_netstream_data_t *sslsock = + (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || Z_ISUNDEF(sslsock->psk_callbacks->client_cb)) { + return 0; + } + + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->client_cb, + NULL, 0, &result) != SUCCESS) { + return 0; + } + + if (Z_TYPE(result) == IS_NULL) { + return 0; + } + + zend_string *psk_str = php_openssl_psk_get_psk(&result); + zend_string *identity_str = php_openssl_psk_get_identity(&result); + + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; + } + + if (identity_str == NULL) { + zval_ptr_dtor(&result); + zend_value_error("Client PSK callback must return Openssl\\Psk with a non-null identity"); + return 0; + } + + memcpy(identity_out, ZSTR_VAL(identity_str), ZSTR_LEN(identity_str)); + identity_out[ZSTR_LEN(identity_str)] = '\0'; + memcpy(psk_out, ZSTR_VAL(psk_str), ZSTR_LEN(psk_str)); + + unsigned int psk_len = (unsigned int)ZSTR_LEN(psk_str); + zval_ptr_dtor(&result); + return psk_len; +} + +static unsigned int php_openssl_psk_server_cb(SSL *ssl, const char *identity, + unsigned char *psk_out, unsigned int max_psk_len) +{ + (void)max_psk_len; + + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 0; + } + + php_openssl_netstream_data_t *sslsock = + (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || Z_ISUNDEF(sslsock->psk_callbacks->server_cb)) { + return 0; + } + + if (SSL_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + if (identity == NULL) { + return 0; + } + + size_t identity_len = strlen(identity); + + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->server_cb, + (const unsigned char *)identity, identity_len, &result) != SUCCESS) { + return 0; + } + + if (Z_TYPE(result) == IS_NULL) { + return 0; + } + + zend_string *psk_str = php_openssl_psk_get_psk(&result); + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; + } + + memcpy(psk_out, ZSTR_VAL(psk_str), ZSTR_LEN(psk_str)); + unsigned int psk_len = (unsigned int)ZSTR_LEN(psk_str); + + zval_ptr_dtor(&result); + return psk_len; +} +#endif /* OPENSSL_NO_PSK */ + +/* TLS 1.3 PSK callbacks */ + +static int php_openssl_psk_use_session_cb(SSL *ssl, const EVP_MD *md, + const unsigned char **id, size_t *idlen, SSL_SESSION **sess) +{ + *id = NULL; + *idlen = 0; + *sess = NULL; + + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 1; + } + + php_openssl_netstream_data_t *sslsock = + (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || Z_ISUNDEF(sslsock->psk_callbacks->client_cb)) { + return 1; + } + + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->client_cb, + NULL, 0, &result) != SUCCESS) { + return 0; + } + + if (Z_TYPE(result) == IS_NULL) { + return 1; /* user rejected, continue without PSK */ + } + + zend_string *psk_str = php_openssl_psk_get_psk(&result); + zend_string *identity_str = php_openssl_psk_get_identity(&result); + + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; + } + + if (identity_str == NULL) { + zval_ptr_dtor(&result); + zend_value_error("Client PSK callback must return Openssl\\Psk with a non-null identity"); + return 0; + } + + SSL_SESSION *session = php_openssl_psk_build_session(ssl, + (const unsigned char *)ZSTR_VAL(psk_str), ZSTR_LEN(psk_str), md); + if (session == NULL) { + zval_ptr_dtor(&result); + if (md != NULL) { + /* Could not satisfy the server's chosen digest */ + return 1; + } + return 0; + } + + /* Identity buffer must outlive this callback. */ + if (sslsock->psk_identity_buf == NULL) { + sslsock->psk_identity_buf = emalloc(PHP_OPENSSL_PSK_MAX_IDENTITY_LEN); + } + memcpy(sslsock->psk_identity_buf, ZSTR_VAL(identity_str), ZSTR_LEN(identity_str)); + sslsock->psk_identity_len = ZSTR_LEN(identity_str); + + *id = sslsock->psk_identity_buf; + *idlen = sslsock->psk_identity_len; + *sess = session; + + zval_ptr_dtor(&result); + return 1; +} + +static int php_openssl_psk_find_session_cb(SSL *ssl, const unsigned char *identity, + size_t identity_len, SSL_SESSION **sess) +{ + *sess = NULL; + + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 1; + } + + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || Z_ISUNDEF(sslsock->psk_callbacks->server_cb)) { + return 1; + } + + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->server_cb, + identity, identity_len, &result) != SUCCESS) { + return 0; + } + + if (Z_TYPE(result) == IS_NULL) { + return 1; /* identity unknown - let handshake fall through */ + } + + zend_string *psk_str = php_openssl_psk_get_psk(&result); + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; + } + + SSL_SESSION *session = php_openssl_psk_build_session(ssl, + (const unsigned char *)ZSTR_VAL(psk_str), ZSTR_LEN(psk_str), NULL); + + zval_ptr_dtor(&result); + + if (session == NULL) { + return 0; + } + + *sess = session; + return 1; +} + +/* PSK setup */ + +static zend_result php_openssl_validate_and_allocate_psk_callback( + php_openssl_netstream_data_t *sslsock, zval *callable, + const char *callback_name, bool is_persistent) +{ + if (is_persistent) { + php_error_docref(NULL, E_WARNING, + "%s is not supported for persistent streams", callback_name); + return FAILURE; + } + + char *is_callable_error = NULL; + if (!zend_is_callable_ex(callable, NULL, 0, NULL, NULL, &is_callable_error)) { + if (is_callable_error) { + zend_type_error("%s must be a valid callback, %s", + callback_name, is_callable_error); + efree(is_callable_error); + } else { + zend_type_error("%s must be a valid callback", callback_name); + } + return FAILURE; + } + + if (!sslsock->psk_callbacks) { + sslsock->psk_callbacks = (php_openssl_psk_callbacks_t *)pemalloc( + sizeof(php_openssl_psk_callbacks_t), is_persistent); + ZVAL_UNDEF(&sslsock->psk_callbacks->client_cb); + ZVAL_UNDEF(&sslsock->psk_callbacks->server_cb); + sslsock->psk_callbacks->refcount = 1; + } + + return SUCCESS; +} + +static zend_result php_openssl_setup_client_psk(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + + if (!GET_VER_OPT("psk_client_cb")) { + return SUCCESS; + } + + if (FAILURE == php_openssl_validate_and_allocate_psk_callback( + sslsock, val, "psk_client_cb", php_stream_is_persistent(stream))) { + return FAILURE; + } + + ZVAL_COPY(&sslsock->psk_callbacks->client_cb, val); + +#ifndef OPENSSL_NO_PSK + SSL_CTX_set_psk_client_callback(sslsock->ctx, php_openssl_psk_client_cb); +#endif + SSL_CTX_set_psk_use_session_callback(sslsock->ctx, php_openssl_psk_use_session_cb); + + return SUCCESS; +} + +static zend_result php_openssl_setup_server_psk(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + + if (!GET_VER_OPT("psk_server_cb")) { + return SUCCESS; + } + + if (FAILURE == php_openssl_validate_and_allocate_psk_callback( + sslsock, val, "psk_server_cb", php_stream_is_persistent(stream))) { + return FAILURE; + } + + ZVAL_COPY(&sslsock->psk_callbacks->server_cb, val); + +#ifndef OPENSSL_NO_PSK + SSL_CTX_set_psk_server_callback(sslsock->ctx, php_openssl_psk_server_cb); +#endif + SSL_CTX_set_psk_find_session_callback(sslsock->ctx, php_openssl_psk_find_session_cb); + + if (!GET_VER_OPT("session_id_context")) { + static const unsigned char default_psk_sid_ctx[] = "PHP_PSK"; + SSL_CTX_set_session_id_context(sslsock->ctx, default_psk_sid_ctx, + sizeof(default_psk_sid_ctx) - 1); + } + + return SUCCESS; +} + /** * OpenSSL new session callback - called when a new session is established */ @@ -2069,16 +2486,21 @@ static zend_result php_openssl_create_server_ctx(php_stream *stream, SSL_CTX_set_min_proto_version(sslsock->ctx, php_openssl_get_min_proto_version(method_flags)); SSL_CTX_set_max_proto_version(sslsock->ctx, php_openssl_get_max_proto_version(method_flags)); + if (sslsock->is_client) { - /* Setup client session resumption */ if (FAILURE == php_openssl_setup_client_session(stream, sslsock)) { return FAILURE; } + if (FAILURE == php_openssl_setup_client_psk(stream, sslsock)) { + return FAILURE; + } } else if (PHP_STREAM_CONTEXT(stream)) { if (FAILURE == php_openssl_setup_server_session(stream, sslsock)) { return FAILURE; } - /* Original server-specific setup */ + if (FAILURE == php_openssl_setup_server_psk(stream, sslsock)) { + return FAILURE; + } if (FAILURE == php_openssl_set_server_specific_opts(stream, sslsock->ctx)) { return FAILURE; } @@ -2131,6 +2553,10 @@ static zend_result php_openssl_setup_crypto(php_stream *stream, parent_sslsock->session_callbacks->refcount++; sslsock->session_callbacks = parent_sslsock->session_callbacks; } + if (parent_sslsock->psk_callbacks) { + parent_sslsock->psk_callbacks->refcount++; + sslsock->psk_callbacks = parent_sslsock->psk_callbacks; + } sslsock->ssl_handle = SSL_new(sslsock->ctx); if (!sslsock->ssl_handle) { @@ -2262,28 +2688,23 @@ static int php_openssl_enable_crypto(php_stream *stream, struct timeval start_time, *timeout; bool blocked = sslsock->s.is_blocked, has_timeout = false; - if (sslsock->is_client) { - /* Set session data for client */ - if ( php_openssl_apply_client_session_data(stream, sslsock)) { - return FAILURE; - } -#ifdef HAVE_TLS_SNI - php_openssl_enable_client_sni(stream, sslsock); -#endif - } else { - php_openssl_init_server_reneg_limit(stream, sslsock); - } - + if (!sslsock->state_set) { #ifdef PHP_OPENSSL_TLS_DEBUG - BIO *b_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); - SSL_set_msg_callback(sslsock->ssl_handle, SSL_trace); - SSL_set_msg_callback_arg(sslsock->ssl_handle, b_out); + BIO *b_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); + SSL_set_msg_callback(sslsock->ssl_handle, SSL_trace); + SSL_set_msg_callback_arg(sslsock->ssl_handle, b_out); #endif - - if (!sslsock->state_set) { if (sslsock->is_client) { + /* Set session data for client */ + if (php_openssl_apply_client_session_data(stream, sslsock) == FAILURE) { + return -1; + } +#ifdef HAVE_TLS_SNI + php_openssl_enable_client_sni(stream, sslsock); +#endif SSL_set_connect_state(sslsock->ssl_handle); } else { + php_openssl_init_server_reneg_limit(stream, sslsock); SSL_set_accept_state(sslsock->ssl_handle); } sslsock->state_set = 1; @@ -2671,6 +3092,16 @@ static int php_openssl_sockop_close(php_stream *stream, int close_handle) /* {{{ pefree(sslsock->session_callbacks, php_stream_is_persistent(stream)); } + if (sslsock->psk_callbacks && --sslsock->psk_callbacks->refcount == 0) { + zval_ptr_dtor(&sslsock->psk_callbacks->client_cb); + zval_ptr_dtor(&sslsock->psk_callbacks->server_cb); + pefree(sslsock->psk_callbacks, php_stream_is_persistent(stream)); + } + + if (sslsock->psk_identity_buf) { + efree(sslsock->psk_identity_buf); + } + pefree(sslsock, php_stream_is_persistent(stream)); return 0; diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index 74449d565901..479a1b5436c9 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -521,9 +521,7 @@ static inline pdo_dbh_t *php_pdo_dbh_fetch_inner(zend_object *obj) { return (pdo_dbh_t *)((ZEND_CONTAINER_OF(obj, pdo_dbh_object_t, std))->inner); } -static inline pdo_dbh_object_t *php_pdo_dbh_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, pdo_dbh_object_t, std); -} +#define php_pdo_dbh_fetch_object(obj) ZEND_CONTAINER_OF(obj, pdo_dbh_object_t, std) #define Z_PDO_DBH_P(zv) php_pdo_dbh_fetch_inner(Z_OBJ_P((zv))) #define Z_PDO_OBJECT_P(zv) php_pdo_dbh_fetch_object(Z_OBJ_P((zv))) @@ -637,11 +635,7 @@ struct _pdo_stmt_t { zend_object std; }; - - -static inline pdo_stmt_t *php_pdo_stmt_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, pdo_stmt_t, std); -} +#define php_pdo_stmt_fetch_object(obj) ZEND_CONTAINER_OF(obj, pdo_stmt_t, std) #define Z_PDO_STMT_P(zv) php_pdo_stmt_fetch_object(Z_OBJ_P((zv))) @@ -650,9 +644,7 @@ struct _pdo_row_t { zend_object std; }; -static inline pdo_row_t *php_pdo_row_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, pdo_row_t, std); -} +#define php_pdo_row_fetch_object(obj) ZEND_CONTAINER_OF(obj, pdo_row_t, std) struct _pdo_scanner_t { const char *ptr, *cur, *tok, *end; diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 8efd43ed3a8b..0ea766c12b42 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -151,9 +151,7 @@ static int le_plink; static zend_class_entry *pgsql_link_ce, *pgsql_result_ce, *pgsql_lob_ce; static zend_object_handlers pgsql_link_object_handlers, pgsql_result_object_handlers, pgsql_lob_object_handlers; -static inline pgsql_link_handle *pgsql_link_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, pgsql_link_handle, std); -} +#define pgsql_link_from_obj(obj) ZEND_CONTAINER_OF(obj, pgsql_link_handle, std) #define Z_PGSQL_LINK_P(zv) pgsql_link_from_obj(Z_OBJ_P(zv)) @@ -207,9 +205,7 @@ static void pgsql_link_free_obj(zend_object *obj) zend_object_std_dtor(&link->std); } -static inline pgsql_result_handle *pgsql_result_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, pgsql_result_handle, std); -} +#define pgsql_result_from_obj(obj) ZEND_CONTAINER_OF(obj, pgsql_result_handle, std) #define Z_PGSQL_RESULT_P(zv) pgsql_result_from_obj(Z_OBJ_P(zv)) @@ -244,9 +240,7 @@ static void pgsql_result_free_obj(zend_object *obj) zend_object_std_dtor(&pg_result->std); } -static inline pgLofp *pgsql_lob_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, pgLofp, std); -} +#define pgsql_lob_from_obj(obj) ZEND_CONTAINER_OF(obj, pgLofp, std) #define Z_PGSQL_LOB_P(zv) pgsql_lob_from_obj(Z_OBJ_P(zv)) diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 6c356c04e5e1..b1c8c4747cc6 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -247,27 +247,32 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, { char *error; - php_url *resource = phar_parse_url(wrapper, path, mode, options); - if (!resource) { - php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path); + php_url *resource = phar_parse_url(wrapper, context, path, mode, options); + if (resource == NULL) { + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar url \"%s\" is unknown", path); return NULL; } /* we must have at the very least phar://alias.phar/ */ if (!resource->scheme || !resource->host || !resource->path) { if (resource->host && !resource->path) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidPath, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + path, ZSTR_VAL(resource->host)); php_url_free(resource); return NULL; } php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar url \"%s\"", path); return NULL; } @@ -276,10 +281,11 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, const phar_archive_data *phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error); if (!phar) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); } php_url_free(resource); return NULL; @@ -344,7 +350,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo /* pre-readonly check, we need to know if this is a data phar */ zend_string *arch = phar_split_fname(url_from, strlen(url_from), NULL, 2, 2); if (!arch) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } @@ -353,30 +360,35 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo zend_string_release_ex(arch, false); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", write operations disabled", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: cannot create directory \"%s\", write operations disabled", url_from); return 0; } - if ((resource = phar_parse_url(wrapper, url_from, "w", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url_from, "w", options)) == NULL) { return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", url_from); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url_from); return 0; } phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error); if (!phar) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -389,13 +401,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo zend_string_efree(e->filename); efree(e); } - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists, + "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -403,13 +419,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, true)) { /* entry exists as a file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists, + "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -437,9 +457,10 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo entry.flags = PHAR_ENT_PERM_DEF_DIR; entry.old_flags = PHAR_ENT_PERM_DEF_DIR; - void *had_been_added = zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info)); - if (!had_been_added) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname)); + if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) { + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", + ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname)); zend_string_efree(entry.filename); return 0; } @@ -447,7 +468,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname), error); zend_hash_del(&phar->manifest, entry.filename); efree(error); return 0; @@ -468,7 +491,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* pre-readonly check, we need to know if this is a data phar */ zend_string *arch = phar_split_fname(url, strlen(url), NULL, 2, 2); if (!arch) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } @@ -477,11 +501,12 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_release_ex(arch, false); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: cannot rmdir directory \"%s\", write operations disabled", url); return 0; } - php_url *resource = phar_parse_url(wrapper, url, "w", options); + php_url *resource = phar_parse_url(wrapper, context, url, "w", options); if (!resource) { return 0; } @@ -489,19 +514,23 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url); return 0; } phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error); if (!phar) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -512,10 +541,14 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_entry_info *entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true); if (!entry) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); } php_url_free(resource); return 0; @@ -529,7 +562,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -545,7 +579,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -566,7 +601,9 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry->filename), ZSTR_VAL(phar->fname), error); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry->filename), ZSTR_VAL(phar->fname), error); php_url_free(resource); efree(error); return 0; diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h index 41899f88a992..179150229e3e 100644 --- a/ext/phar/dirstream.h +++ b/ext/phar/dirstream.h @@ -22,7 +22,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options #ifdef PHAR_DIRSTREAM #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); /* directory handlers */ static ssize_t phar_dir_write(php_stream *stream, const char *buf, size_t count); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 772f9c2769a5..3fee251d2589 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -55,7 +55,8 @@ const php_stream_wrapper php_stream_phar_wrapper = { /** * Open a phar file for streams API */ -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options) /* {{{ */ +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, + const char *filename, const char *mode, int options) { php_url *resource; char *error; @@ -65,7 +66,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (mode[0] == 'a') { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: open mode append not supported"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "phar error: open mode append not supported"); } return NULL; } @@ -76,9 +78,13 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (!arch) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch_error && !entry) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch_error); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidPath, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + filename, arch_error); + arch = NULL; } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url or non-existent phar \"%s\"", filename); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url or non-existent phar \"%s\"", filename); } } return NULL; @@ -109,7 +115,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); return NULL; @@ -118,7 +125,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, NULL, options, OpenFailed, "%s", error); } efree(error); } @@ -129,7 +136,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, OpenFailed, "%s", error); } efree(error); } @@ -141,7 +148,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); } efree(error); } @@ -151,7 +158,6 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } return resource; } -/* }}} */ /** * used for fopen('phar://...') and company @@ -166,7 +172,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *fpf; zval *pzoption, *metadata; - php_url *resource = phar_parse_url(wrapper, path, mode, options); + php_url *resource = phar_parse_url(wrapper, context, path, mode, options); if (!resource) { return NULL; } @@ -174,13 +180,15 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", path); return NULL; } @@ -191,10 +199,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { if (NULL == (idata = phar_get_or_create_entry_data(resource->host, internal_file, strlen(internal_file), mode, 0, &error, true, time(NULL)))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, CreateFailed, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, CreateFailed, + "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -232,7 +241,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* retrieve the stub */ phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL); if (!phar) { - php_stream_wrapper_log_error(wrapper, options, "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidFormat, + "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -252,7 +262,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (stream == NULL) { stream = phar_open_archive_fp(phar); if (UNEXPECTED(!stream)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, OpenFailed, + "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -289,10 +300,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if ((FAILURE == phar_get_entry_data(&idata, resource->host, internal_file, strlen(internal_file), "r", 0, &error, false)) || !idata) { idata_error: if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -310,7 +322,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* check length, crc32 */ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2) != SUCCESS) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, ArchivingFailed, "%s", error); efree(error); phar_entry_delref(idata); efree(internal_file); @@ -441,7 +453,9 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); if (count != php_stream_write(data->fp, buf, count)) { - php_stream_wrapper_log_error(stream->wrapper, stream->flags, "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", count, ZSTR_VAL(data->internal_file->filename), ZSTR_VAL(data->phar->fname)); + php_stream_warn(stream, WriteFailed, + "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", + count, ZSTR_VAL(data->internal_file->filename), ZSTR_VAL(data->phar->fname)); return -1; } data->position = php_stream_tell(data->fp) - data->zero; @@ -468,7 +482,7 @@ static int phar_stream_flush(php_stream *stream) /* {{{ */ data->internal_file->timestamp = time(0); ret = phar_flush(data->phar, &error); if (error) { - php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS, "%s", error); + php_stream_warn(stream, FlushFailed, "%s", error); efree(error); } return ret; @@ -557,7 +571,7 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, const char *url, int f char *internal_file; size_t internal_file_len; - php_url *resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET); + php_url *resource = phar_parse_url(wrapper, context, url, "r", flags|PHP_STREAM_URL_STAT_QUIET); if (!resource) { return FAILURE; } @@ -660,22 +674,25 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int size_t internal_file_len; phar_entry_data *idata; - php_url *resource = phar_parse_url(wrapper, url, "rb", options); + php_url *resource = phar_parse_url(wrapper, context, url, "rb", options); if (!resource) { - php_stream_wrapper_log_error(wrapper, options, "phar error: unlink failed"); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "phar error: unlink failed"); return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url); return 0; } @@ -684,7 +701,8 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int const phar_archive_data *pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), resource->host); if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: write operations disabled by the php.ini setting phar.readonly"); return 0; } @@ -694,10 +712,12 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int if (FAILURE == phar_get_entry_data(&idata, resource->host, internal_file, internal_file_len, "r", 0, &error, true)) { /* constraints of fp refcount were not met */ if (error) { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed: %s", url, error); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "unlink of \"%s\" failed: %s", url, error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed, file does not exist", url); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "unlink of \"%s\" failed, file does not exist", url); } efree(internal_file); php_url_free(resource); @@ -705,7 +725,9 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int } if (idata->internal_file->fp_refcount > 1) { /* more than just our fp resource is open for this file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", + internal_file, ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); phar_entry_delref(idata); @@ -715,7 +737,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int efree(internal_file); phar_entry_remove(idata, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, "%s", error); efree(error); } return 1; @@ -730,23 +752,28 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from error = NULL; - php_url *resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET); + php_url *resource_from = phar_parse_url(wrapper, context, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET); if (!resource_from) { - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_from); return 0; } phar_archive_data *pfrom = phar_get_archive(ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, NULL); if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, Readonly, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } - php_url *resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET); + php_url *resource_to = phar_parse_url(wrapper, context, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET); if (!resource_to) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_to); return 0; } @@ -754,14 +781,17 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (PHAR_G(readonly) && (!pto || !pto->is_data)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, Readonly, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } if (!zend_string_equals(resource_from->host, resource_to->host)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", + url_from, url_to); return 0; } @@ -769,28 +799,36 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (!resource_from->scheme || !resource_from->host || !resource_from->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_from); return 0; } if (!resource_to->scheme || !resource_to->host || !resource_to->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_to); return 0; } if (!zend_string_equals_literal_ci(resource_from->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_from); return 0; } if (!zend_string_equals_literal_ci(resource_to->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_to); return 0; } @@ -798,7 +836,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (!phar) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } @@ -806,7 +845,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", + url_from, url_to); return 0; } @@ -818,7 +859,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (entry->is_deleted) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, NotFound, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", + url_from, url_to); return 0; } /* transfer all data over to the new entry */ @@ -839,7 +882,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (FAILURE == phar_copy_entry_fp(source, entry, &error)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); zend_hash_del(&phar->manifest, entry->filename); return 0; @@ -853,7 +897,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from /* file does not exist */ php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, NotFound, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", + url_from, url_to); return 0; } @@ -932,7 +978,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (error) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } diff --git a/ext/phar/stream.h b/ext/phar/stream.h index cf40e3129c69..ebac093402b1 100644 --- a/ext/phar/stream.h +++ b/ext/phar/stream.h @@ -18,7 +18,7 @@ BEGIN_EXTERN_C() #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); ZEND_ATTRIBUTE_NONNULL void phar_entry_remove(phar_entry_data *idata, char **error); static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC); diff --git a/ext/phar/util.c b/ext/phar/util.c index e95b3ab7574b..9906728a00f7 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -1326,18 +1326,18 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, si static const char hexChars[] = "0123456789ABCDEF"; -static int phar_hex_str(const char *digest, size_t digest_len, char **signature) /* {{{ */ +static size_t phar_hex_str(const char *digest, size_t digest_len, char **signature) /* {{{ */ { - int pos = -1; + size_t pos = 0; size_t len = 0; *signature = (char*)safe_pemalloc(digest_len, 2, 1, PHAR_G(persist)); for (; len < digest_len; ++len) { - (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] >> 4]; - (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] & 0x0F]; + (*signature)[pos++] = hexChars[((const unsigned char *)digest)[len] >> 4]; + (*signature)[pos++] = hexChars[((const unsigned char *)digest)[len] & 0x0F]; } - (*signature)[++pos] = '\0'; + (*signature)[pos] = '\0'; return pos; } /* }}} */ @@ -1363,7 +1363,7 @@ ZEND_ATTRIBUTE_NONNULL static bool phar_call_openssl_verify( php_stream_rewind(fp); zend_string *str = php_stream_copy_to_mem(fp, (size_t) end, false); /* No content thus signing must fail */ - if (UNEXPECTED(str == NULL)) { + if (UNEXPECTED(str == NULL || (size_t)end != ZSTR_LEN(str))) { return false; } diff --git a/ext/random/php_random.h b/ext/random/php_random.h index 6e3df1317a4f..f5db3403ec18 100644 --- a/ext/random/php_random.h +++ b/ext/random/php_random.h @@ -125,13 +125,8 @@ extern PHPAPI zend_class_entry *random_ce_Random_Randomizer; extern PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary; -static inline php_random_engine *php_random_engine_from_obj(zend_object *object) { - return ZEND_CONTAINER_OF(object, php_random_engine, std); -} - -static inline php_random_randomizer *php_random_randomizer_from_obj(zend_object *object) { - return ZEND_CONTAINER_OF(object, php_random_randomizer, std); -} +#define php_random_engine_from_obj(object) ZEND_CONTAINER_OF(object, php_random_engine, std) +#define php_random_randomizer_from_obj(object) ZEND_CONTAINER_OF(object, php_random_randomizer, std) # define Z_RANDOM_ENGINE_P(zval) php_random_engine_from_obj(Z_OBJ_P(zval)) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 50bcf4cb79c1..af565ed53a32 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -174,9 +174,7 @@ typedef struct { zend_object zo; } reflection_object; -static inline reflection_object *reflection_object_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, reflection_object, zo); -} +#define reflection_object_from_obj(obj) ZEND_CONTAINER_OF(obj, reflection_object, zo) #define Z_REFLECTION_P(zv) reflection_object_from_obj(Z_OBJ_P((zv))) /* }}} */ diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 9d1210705096..9ef87f42b225 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -17,5 +17,10 @@ Directory RoundingMode SortDirection StreamBucket +StreamError +StreamErrorCode +StreamErrorMode +StreamErrorStore +StreamException __PHP_Incomplete_Class php_user_filter diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c index 8721e4333ce8..c3ed217443f4 100644 --- a/ext/shmop/shmop.c +++ b/ext/shmop/shmop.c @@ -68,10 +68,7 @@ typedef struct php_shmop zend_class_entry *shmop_ce; static zend_object_handlers shmop_object_handlers; -static inline php_shmop *shmop_from_obj(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, php_shmop, std); -} +#define shmop_from_obj(obj) ZEND_CONTAINER_OF(obj, php_shmop, std) #define Z_SHMOP_P(zv) shmop_from_obj(Z_OBJ_P(zv)) diff --git a/ext/simplexml/php_simplexml_exports.h b/ext/simplexml/php_simplexml_exports.h index 8e07bbf19f2e..fb940f2fbc77 100644 --- a/ext/simplexml/php_simplexml_exports.h +++ b/ext/simplexml/php_simplexml_exports.h @@ -35,10 +35,7 @@ PHP_SXE_API zend_object *sxe_object_new(zend_class_entry *ce); -static inline php_sxe_object *php_sxe_fetch_object(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, php_sxe_object, zo); -} -/* }}} */ +#define php_sxe_fetch_object(obj) ZEND_CONTAINER_OF(obj, php_sxe_object, zo) #define Z_SXEOBJ_P(zv) php_sxe_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/snmp/php_snmp.h b/ext/snmp/php_snmp.h index 40c5bebb0c04..d889eb37ab95 100644 --- a/ext/snmp/php_snmp.h +++ b/ext/snmp/php_snmp.h @@ -56,9 +56,7 @@ typedef struct _php_snmp_object { zend_object zo; } php_snmp_object; -static inline php_snmp_object *php_snmp_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_snmp_object, zo); -} +#define php_snmp_fetch_object(obj) ZEND_CONTAINER_OF(obj, php_snmp_object, zo) #define Z_SNMP_P(zv) php_snmp_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index ce36dae11aec..58dbdfed6c05 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -258,10 +258,7 @@ typedef struct soap_url_object { zend_object std; } soap_url_object; -static inline soap_url_object *soap_url_object_fetch(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, soap_url_object, std); -} +#define soap_url_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_url_object, std) #define Z_SOAP_URL_P(zv) soap_url_object_fetch(Z_OBJ_P(zv)) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index da5e10ac3258..2c2c5b7fa986 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -205,13 +205,8 @@ typedef struct { zend_object std; } soap_client_object; -static inline soap_client_object *soap_client_object_fetch(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, soap_client_object, std); -} - -static inline soap_server_object *soap_server_object_fetch(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, soap_server_object, std); -} +#define soap_client_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_client_object, std) +#define soap_server_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_server_object, std) static zend_object *soap_client_object_create(zend_class_entry *ce) { @@ -286,10 +281,7 @@ static zend_result soap_url_cast_object(zend_object *obj, zval *result, int type return zend_std_cast_object_tostring(obj, result, type); } -static inline soap_sdl_object *soap_sdl_object_fetch(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, soap_sdl_object, std); -} +#define soap_sdl_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_sdl_object, std) #define Z_SOAP_SDL_P(zv) soap_sdl_object_fetch(Z_OBJ_P(zv)) diff --git a/ext/sockets/php_sockets.h b/ext/sockets/php_sockets.h index bc8d4617f7f0..4d047885dbaa 100644 --- a/ext/sockets/php_sockets.h +++ b/ext/sockets/php_sockets.h @@ -75,9 +75,7 @@ typedef struct { extern PHP_SOCKETS_API zend_class_entry *socket_ce; -static inline php_socket *socket_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_socket, std); -} +#define socket_from_obj(obj) ZEND_CONTAINER_OF(obj, php_socket, std) #define Z_SOCKET_P(zv) socket_from_obj(Z_OBJ_P(zv)) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 7b87a2a2f9bd..7be7554d4123 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -180,9 +180,7 @@ typedef struct { zend_class_entry *address_info_ce; static zend_object_handlers address_info_object_handlers; -static inline php_addrinfo *address_info_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_addrinfo, std); -} +#define address_info_from_obj(obj) ZEND_CONTAINER_OF(obj, php_addrinfo, std) #define Z_ADDRESS_INFO_P(zv) address_info_from_obj(Z_OBJ_P(zv)) @@ -852,18 +850,17 @@ PHP_FUNCTION(socket_listen) PHP_FUNCTION(socket_close) { zval *arg1; - php_socket *php_socket; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce) ZEND_PARSE_PARAMETERS_END(); - php_socket = Z_SOCKET_P(arg1); - ENSURE_SOCKET_VALID(php_socket); + php_socket *socket = Z_SOCKET_P(arg1); + ENSURE_SOCKET_VALID(socket); - if (!Z_ISUNDEF(php_socket->zstream)) { + if (!Z_ISUNDEF(socket->zstream)) { php_stream *stream = NULL; - php_stream_from_zval_no_verify(stream, &php_socket->zstream); + php_stream_from_zval_no_verify(stream, &socket->zstream); if (stream != NULL) { /* close & destroy stream, incl. removing it from the rsrc list; * resource stored in php_sock->zstream will become invalid */ @@ -872,13 +869,13 @@ PHP_FUNCTION(socket_close) (stream->is_persistent?PHP_STREAM_FREE_CLOSE_PERSISTENT:0)); } } else { - if (!IS_INVALID_SOCKET(php_socket)) { - close(php_socket->bsd_socket); + if (!IS_INVALID_SOCKET(socket)) { + close(socket->bsd_socket); } } - ZVAL_UNDEF(&php_socket->zstream); - php_socket->bsd_socket = -1; + ZVAL_UNDEF(&socket->zstream); + socket->bsd_socket = -1; } /* }}} */ diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 0105af776137..a3df888a6102 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -51,10 +51,7 @@ typedef struct _spl_array_object { zend_object std; } spl_array_object; -static inline spl_array_object *spl_array_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_array_object, std); -} -/* }}} */ +#define spl_array_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_array_object, std) #define Z_SPLARRAY_P(zv) spl_array_from_obj(Z_OBJ_P((zv))) @@ -1482,9 +1479,9 @@ PHP_METHOD(ArrayObject, __unserialize) RETURN_THROWS(); } - if (!instanceof_function(ce, zend_ce_iterator)) { + if (!instanceof_function(ce, spl_ce_ArrayIterator)) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, - "Cannot deserialize ArrayObject with iterator class '%s'; this class does not implement the Iterator interface", + "Cannot deserialize ArrayObject with iterator class '%s'; this class is not derived from ArrayIterator", ZSTR_VAL(Z_STR_P(iterator_class_zv))); RETURN_THROWS(); } diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 95fc5e25be4b..f662bb8a1dbd 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -48,11 +48,7 @@ PHPAPI zend_class_entry *spl_ce_GlobIterator; PHPAPI zend_class_entry *spl_ce_SplFileObject; PHPAPI zend_class_entry *spl_ce_SplTempFileObject; -/* Object helper */ -static inline spl_filesystem_object *spl_filesystem_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_filesystem_object, std); -} -/* }}} */ +#define spl_filesystem_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_filesystem_object, std) /* define an overloaded iterator structure */ typedef struct { diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 39d2592b79a9..77e8b0d4e7da 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -85,10 +85,7 @@ struct _spl_dllist_it { int flags; }; -static inline spl_dllist_object *spl_dllist_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_dllist_object, std); -} -/* }}} */ +#define spl_dllist_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_dllist_object, std) #define Z_SPLDLLIST_P(zv) spl_dllist_from_obj(Z_OBJ_P((zv))) diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index cfaf209a56e1..d2eae52e3c0c 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -53,10 +53,7 @@ typedef struct _spl_fixedarray_it { zend_long current; } spl_fixedarray_it; -static spl_fixedarray_object *spl_fixed_array_from_obj(zend_object *obj) -{ - return ZEND_CONTAINER_OF(obj, spl_fixedarray_object, std); -} +#define spl_fixed_array_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_fixedarray_object, std) #define Z_SPLFIXEDARRAY_P(zv) spl_fixed_array_from_obj(Z_OBJ_P((zv))) diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index cce07f4d91f3..1073836aa53b 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -70,10 +70,7 @@ typedef struct _spl_pqueue_elem { zval priority; } spl_pqueue_elem; -static inline spl_heap_object *spl_heap_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_heap_object, std); -} -/* }}} */ +#define spl_heap_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_heap_object, std) #define Z_SPLHEAP_P(zv) spl_heap_from_obj(Z_OBJ_P((zv))) diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index bc4bfd759400..86652ae4f5cc 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -136,16 +136,11 @@ typedef struct _spl_dual_it_object { static zend_object_handlers spl_handlers_rec_it_it; static zend_object_handlers spl_handlers_dual_it; -static inline spl_recursive_it_object *spl_recursive_it_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_recursive_it_object, std); -} -/* }}} */ +#define spl_recursive_it_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_recursive_it_object, std) #define Z_SPLRECURSIVE_IT_P(zv) spl_recursive_it_from_obj(Z_OBJ_P((zv))) -static inline spl_dual_it_object *spl_dual_it_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_dual_it_object, std); -} /* }}} */ +#define spl_dual_it_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_dual_it_object, std) #define Z_SPLDUAL_IT_P(zv) spl_dual_it_from_obj(Z_OBJ_P((zv))) diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 7d565714afd6..f897ab1350cc 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -64,10 +64,7 @@ typedef struct _spl_SplObjectStorageElement { zval inf; } spl_SplObjectStorageElement; /* }}} */ -static inline spl_SplObjectStorage *spl_object_storage_from_obj(zend_object *obj) /* {{{ */ { - return ZEND_CONTAINER_OF(obj, spl_SplObjectStorage, std); -} -/* }}} */ +#define spl_object_storage_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_SplObjectStorage, std) #define Z_SPLOBJSTORAGE_P(zv) spl_object_storage_from_obj(Z_OBJ_P((zv))) diff --git a/ext/spl/tests/GH-22047.phpt b/ext/spl/tests/GH-22047.phpt new file mode 100644 index 000000000000..b01fbfd633c0 --- /dev/null +++ b/ext/spl/tests/GH-22047.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-22047: ArrayObject invalid iterator class in serialized payload +--CREDITS-- +Igor Sak-Sakovskiy (Positive Technologies) +--FILE-- + $v) { + echo "should not reach here\n"; + } +} catch (UnexpectedValueException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +Cannot deserialize ArrayObject with iterator class 'GlobIterator'; this class is not derived from ArrayIterator diff --git a/ext/spl/tests/unserialize_errors.phpt b/ext/spl/tests/unserialize_errors.phpt index 1138b5c8cd54..64356923ae29 100644 --- a/ext/spl/tests/unserialize_errors.phpt +++ b/ext/spl/tests/unserialize_errors.phpt @@ -144,7 +144,7 @@ Incomplete or ill-typed serialization data Passed variable is not an array or object Incomplete or ill-typed serialization data Cannot deserialize ArrayObject with iterator class 'NonExistent'; no such class exists -Cannot deserialize ArrayObject with iterator class 'Existent'; this class does not implement the Iterator interface +Cannot deserialize ArrayObject with iterator class 'Existent'; this class is not derived from ArrayIterator ArrayIterator: Incomplete or ill-typed serialization data Incomplete or ill-typed serialization data diff --git a/ext/sqlite3/php_sqlite3_structs.h b/ext/sqlite3/php_sqlite3_structs.h index 8012def8ea65..e8488028240e 100644 --- a/ext/sqlite3/php_sqlite3_structs.h +++ b/ext/sqlite3/php_sqlite3_structs.h @@ -72,9 +72,7 @@ typedef struct _php_sqlite3_db_object { zend_object zo; } php_sqlite3_db_object; -static inline php_sqlite3_db_object *php_sqlite3_db_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_sqlite3_db_object, zo); -} +#define php_sqlite3_db_from_obj(obj) ZEND_CONTAINER_OF(obj, php_sqlite3_db_object, zo) #define Z_SQLITE3_DB_P(zv) php_sqlite3_db_from_obj(Z_OBJ_P((zv))) @@ -101,9 +99,7 @@ struct _php_sqlite3_result_object { zend_object zo; }; -static inline php_sqlite3_result *php_sqlite3_result_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_sqlite3_result, zo); -} +#define php_sqlite3_result_from_obj(obj) ZEND_CONTAINER_OF(obj, php_sqlite3_result, zo) #define Z_SQLITE3_RESULT_P(zv) php_sqlite3_result_from_obj(Z_OBJ_P((zv))) @@ -119,9 +115,7 @@ struct _php_sqlite3_stmt_object { zend_object zo; }; -static inline php_sqlite3_stmt *php_sqlite3_stmt_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_sqlite3_stmt, zo); -} +#define php_sqlite3_stmt_from_obj(obj) ZEND_CONTAINER_OF(obj, php_sqlite3_stmt, zo) #define Z_SQLITE3_STMT_P(zv) php_sqlite3_stmt_from_obj(Z_OBJ_P((zv))) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 29ed6ddf14e9..0c1e2f8222a6 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -335,6 +335,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(exec) + BASIC_MINIT_SUBMODULE(stream_errors) BASIC_MINIT_SUBMODULE(user_streams) php_register_url_stream_wrapper("php", &php_stream_php_wrapper); @@ -1955,6 +1956,12 @@ PHP_FUNCTION(ini_get_all) add_assoc_null(&option, "local_value"); } + if (ini_entry->def->value) { + add_assoc_stringl(&option, "builtin_default_value", ini_entry->def->value, ini_entry->def->value_length); + } else { + add_assoc_null(&option, "builtin_default_value"); + } + add_assoc_long(&option, "access", ini_entry->modifiable); zend_symtable_update(Z_ARRVAL_P(return_value), ini_entry->name, &option); diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 1999c9b92be1..7f7e0de0f65f 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3385,7 +3385,10 @@ function soundex(string $string): string {} /* streamsfuncs.c */ -function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null): int|false {} +/** + * @param resource|null $context + */ +function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null, $context = null): int|false {} /** * @return resource @@ -3488,17 +3491,19 @@ function stream_socket_shutdown($stream, int $mode): bool {} #ifdef HAVE_SOCKETPAIR /** + * @param resource|null $context * @return array|false * @refcount 1 */ -function stream_socket_pair(int $domain, int $type, int $protocol): array|false {} +function stream_socket_pair(int $domain, int $type, int $protocol, $context = null): array|false {} #endif /** * @param resource $from * @param resource $to + * @param resource|null $context */ -function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0): int|false {} +function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0, $context = null): int|false {} /** * @param resource $stream @@ -3558,14 +3563,24 @@ function stream_resolve_include_path(string $filename): string|false {} */ function stream_get_wrappers(): array {} +/** + * @return array + */ +function stream_last_errors(): array {} + +function stream_clear_errors(): void {} + /** * @return array * @refcount 1 */ function stream_get_transports(): array {} -/** @param resource|string $stream */ -function stream_is_local($stream): bool {} +/** + * @param resource|string $stream + * @param resource|null $context + */ +function stream_is_local($stream, $context = null): bool {} /** @param resource $stream */ function stream_isatty($stream): bool {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index e51a837ffa4d..a17efc6961dc 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f + * Stub hash: d997420d0668e5b6ac26c43a8f86cff64891aced * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) @@ -1823,6 +1823,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_select, 0, 4, MAY_BE_LONG ZEND_ARG_TYPE_INFO(1, except, IS_ARRAY, 1) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microseconds, IS_LONG, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_context_create, 0, 0, 0) @@ -1937,6 +1938,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_socket_pair, 0, 3, MAY_BE ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, protocol, IS_LONG, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() #endif @@ -1945,6 +1947,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_copy_to_stream, 0, 2, MAY ZEND_ARG_INFO(0, to) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "0") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_get_contents, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) @@ -1987,9 +1990,16 @@ ZEND_END_ARG_INFO() #define arginfo_stream_get_wrappers arginfo_ob_list_handlers +#define arginfo_stream_last_errors arginfo_ob_list_handlers + +#define arginfo_stream_clear_errors arginfo_flush + #define arginfo_stream_get_transports arginfo_ob_list_handlers -#define arginfo_stream_is_local arginfo_rewind +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_is_local, 0, 1, _IS_BOOL, 0) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") +ZEND_END_ARG_INFO() #define arginfo_stream_isatty arginfo_rewind @@ -2829,6 +2839,8 @@ ZEND_FUNCTION(stream_get_meta_data); ZEND_FUNCTION(stream_get_line); ZEND_FUNCTION(stream_resolve_include_path); ZEND_FUNCTION(stream_get_wrappers); +ZEND_FUNCTION(stream_last_errors); +ZEND_FUNCTION(stream_clear_errors); ZEND_FUNCTION(stream_get_transports); ZEND_FUNCTION(stream_is_local); ZEND_FUNCTION(stream_isatty); @@ -3445,6 +3457,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(stream_get_line, arginfo_stream_get_line) ZEND_FE(stream_resolve_include_path, arginfo_stream_resolve_include_path) ZEND_FE(stream_get_wrappers, arginfo_stream_get_wrappers) + ZEND_FE(stream_last_errors, arginfo_stream_last_errors) + ZEND_FE(stream_clear_errors, arginfo_stream_clear_errors) ZEND_FE(stream_get_transports, arginfo_stream_get_transports) ZEND_FE(stream_is_local, arginfo_stream_is_local) ZEND_FE(stream_isatty, arginfo_stream_isatty) diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index b3eb25c5d988..067959c8b5f6 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f */ + * Stub hash: d997420d0668e5b6ac26c43a8f86cff64891aced */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H -#define ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_d997420d0668e5b6ac26c43a8f86cff64891aced_H +#define ZEND_BASIC_FUNCTIONS_DECL_d997420d0668e5b6ac26c43a8f86cff64891aced_H typedef enum zend_enum_SortDirection { ZEND_ENUM_SortDirection_Ascending = 1, @@ -20,4 +20,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_d997420d0668e5b6ac26c43a8f86cff64891aced_H */ diff --git a/ext/standard/credits_ext.h b/ext/standard/credits_ext.h index 509135a27e3e..b22a3f090c43 100644 --- a/ext/standard/credits_ext.h +++ b/ext/standard/credits_ext.h @@ -19,7 +19,7 @@ CREDIT_LINE("cURL", "Sterling Hughes"); CREDIT_LINE("Date/Time Support", "Derick Rethans"); CREDIT_LINE("DB-LIB (MS SQL, Sybase)", "Wez Furlong, Frank M. Kromann, Adam Baratz"); CREDIT_LINE("DBA", "Sascha Schumann, Marcus Boerger"); -CREDIT_LINE("DOM", "Christian Stocker, Rob Richards, Marcus Boerger, Niels Dossche"); +CREDIT_LINE("DOM", "Christian Stocker, Rob Richards, Marcus Boerger, Nora Dossche"); CREDIT_LINE("enchant", "Pierre-Alain Joye, Ilia Alshanetsky"); CREDIT_LINE("EXIF", "Rasmus Lerdorf, Marcus Boerger"); CREDIT_LINE("FFI", "Dmitry Stogov"); @@ -69,7 +69,7 @@ CREDIT_LINE("System V Semaphores", "Tom May"); CREDIT_LINE("System V Shared Memory", "Christian Cartus"); CREDIT_LINE("tidy", "John Coggeshall, Ilia Alshanetsky"); CREDIT_LINE("tokenizer", "Andrei Zmievski, Johannes Schlueter"); -CREDIT_LINE("uri", "Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Niels Dossche, Nicolas Grekas"); +CREDIT_LINE("uri", "Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Nora Dossche, Nicolas Grekas"); CREDIT_LINE("XML", "Stig Bakken, Thies C. Arntzen, Sterling Hughes"); CREDIT_LINE("XMLReader", "Rob Richards"); CREDIT_LINE("XMLWriter", "Rob Richards, Pierre-Alain Joye"); diff --git a/ext/standard/file.c b/ext/standard/file.c index ba5b26f6d222..db0fc45385dd 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -221,7 +221,9 @@ PHP_FUNCTION(flock) Z_PARAM_ZVAL(wouldblock) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); php_flock_common(stream, operation, 2, wouldblock, return_value); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -257,6 +259,8 @@ PHP_FUNCTION(get_meta_tags) RETURN_FALSE; } + php_stream_error_operation_begin(); + array_init(return_value); tok_last = TOK_EOF; @@ -368,6 +372,8 @@ PHP_FUNCTION(get_meta_tags) if (value) efree(value); if (name) efree(name); php_stream_close(md.stream); + + php_stream_error_operation_end_for_stream(md.stream); } /* }}} */ @@ -402,12 +408,13 @@ PHP_FUNCTION(file_get_contents) RETURN_THROWS(); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); - stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (!stream) { + php_stream_error_operation_end(context); RETURN_FALSE; } @@ -418,8 +425,9 @@ PHP_FUNCTION(file_get_contents) } if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) { - php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); php_stream_close(stream); + php_stream_error_operation_end(context); + php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); RETURN_FALSE; } @@ -430,6 +438,7 @@ PHP_FUNCTION(file_get_contents) } php_stream_close(stream); + php_stream_error_operation_end(context); } /* }}} */ @@ -459,6 +468,7 @@ PHP_FUNCTION(file_put_contents) php_stream_from_zval(srcstream, data); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_FILE_APPEND) { @@ -477,11 +487,13 @@ PHP_FUNCTION(file_put_contents) stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (stream == NULL) { + php_stream_error_operation_end(context); RETURN_FALSE; } if ((flags & LOCK_EX) && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) { php_stream_close(stream); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Exclusive locks are not supported for this stream"); RETURN_FALSE; } @@ -564,6 +576,7 @@ PHP_FUNCTION(file_put_contents) break; } php_stream_close(stream); + php_stream_error_operation_end(context); if (numbytes < 0) { RETURN_FALSE; @@ -609,10 +622,12 @@ PHP_FUNCTION(file) include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES); skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES; + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (!stream) { + php_stream_error_operation_end(context); RETURN_FALSE; } @@ -666,6 +681,7 @@ PHP_FUNCTION(file) } php_stream_close(stream); + php_stream_error_operation_end(context); } /* }}} */ @@ -705,7 +721,9 @@ PHP_FUNCTION(tmpfile) ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_operation_begin(); stream = php_stream_fopen_tmpfile(); + php_stream_error_operation_end_for_stream(stream); if (stream) { php_stream_to_zval(stream, return_value); @@ -733,9 +751,11 @@ PHP_FUNCTION(fopen) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); stream = php_stream_open_wrapper_ex(filename, mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); + php_stream_error_operation_end(context); if (stream == NULL) { RETURN_FALSE; @@ -759,9 +779,11 @@ PHPAPI PHP_FUNCTION(fclose) RETURN_FALSE; } + php_stream_error_operation_begin(); php_stream_free(stream, PHP_STREAM_FREE_KEEP_RSRC | (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE)); + php_stream_error_operation_end_for_stream(stream); RETURN_TRUE; } @@ -809,6 +831,7 @@ PHP_FUNCTION(popen) RETURN_FALSE; } + php_stream_error_operation_begin(); stream = php_stream_fopen_from_pipe(fp, mode); if (stream == NULL) { @@ -817,6 +840,7 @@ PHP_FUNCTION(popen) } else { php_stream_to_zval(stream, return_value); } + php_stream_error_operation_end_for_stream(stream); efree(posix_mode); } @@ -831,9 +855,11 @@ PHP_FUNCTION(pclose) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); FG(pclose_wait) = 1; zend_list_close(stream->res); FG(pclose_wait) = 0; + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(FG(pclose_ret)); } /* }}} */ @@ -847,11 +873,13 @@ PHPAPI PHP_FUNCTION(feof) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (php_stream_eof(stream)) { - RETURN_TRUE; + RETVAL_TRUE; } else { - RETURN_FALSE; + RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -871,9 +899,11 @@ PHPAPI PHP_FUNCTION(fgets) Z_PARAM_LONG_OR_NULL(len, len_is_null) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (len_is_null) { /* ask streams to give us a buffer of an appropriate size */ buf = php_stream_get_line(stream, NULL, 0, &line_len); + php_stream_error_operation_end_for_stream(stream); if (buf == NULL) { RETURN_FALSE; } @@ -887,7 +917,9 @@ PHPAPI PHP_FUNCTION(fgets) } str = zend_string_alloc(len, 0); - if (php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len) == NULL) { + buf = php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len); + php_stream_error_operation_end_for_stream(stream); + if (buf == NULL) { zend_string_efree(str); RETURN_FALSE; } @@ -912,7 +944,9 @@ PHPAPI PHP_FUNCTION(fgetc) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); int result = php_stream_getc(stream); + php_stream_error_operation_end_for_stream(stream); if (result == EOF) { RETVAL_FALSE; @@ -931,7 +965,6 @@ PHP_FUNCTION(fscanf) zval *file_handle; char *buf, *format; size_t len; - void *what; ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_RESOURCE(file_handle) @@ -939,16 +972,18 @@ PHP_FUNCTION(fscanf) Z_PARAM_VARIADIC('*', args, argc) ZEND_PARSE_PARAMETERS_END(); - what = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream()); + php_stream *stream = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream()); - /* we can't do a ZEND_VERIFY_RESOURCE(what), otherwise we end up + /* we can't do a ZEND_VERIFY_RESOURCE(stream), otherwise we end up * with a leak if we have an invalid filehandle. This needs changing * if the code behind ZEND_VERIFY_RESOURCE changed. - cc */ - if (!what) { + if (!stream) { RETURN_THROWS(); } - buf = php_stream_get_line((php_stream *) what, NULL, 0, &len); + php_stream_error_operation_begin(); + buf = php_stream_get_line(stream, NULL, 0, &len); + php_stream_error_operation_end_for_stream(stream); if (buf == NULL) { RETURN_FALSE; } @@ -994,7 +1029,9 @@ PHPAPI PHP_FUNCTION(fwrite) RETURN_LONG(0); } + php_stream_error_operation_begin(); ret = php_stream_write(stream, input, num_bytes); + php_stream_error_operation_end_for_stream(stream); if (ret < 0) { RETURN_FALSE; } @@ -1013,8 +1050,9 @@ PHPAPI PHP_FUNCTION(fflush) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); ret = php_stream_flush(stream); - + php_stream_error_operation_end_for_stream(stream); RETURN_BOOL(!ret); } /* }}} */ @@ -1022,13 +1060,17 @@ PHPAPI PHP_FUNCTION(fflush) /* {{{ Rewind the position of a file pointer */ PHPAPI PHP_FUNCTION(rewind) { + int ret; php_stream *stream; ZEND_PARSE_PARAMETERS_START(1, 1) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(-1 != php_stream_rewind(stream)); + php_stream_error_operation_begin(); + ret = php_stream_rewind(stream); + php_stream_error_operation_end_for_stream(stream); + RETURN_BOOL(-1 != ret); } /* }}} */ @@ -1042,7 +1084,9 @@ PHPAPI PHP_FUNCTION(ftell) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); ret = php_stream_tell(stream); + php_stream_error_operation_end_for_stream(stream); if (ret == -1) { RETURN_FALSE; } @@ -1063,7 +1107,9 @@ PHPAPI PHP_FUNCTION(fseek) Z_PARAM_LONG(whence) ZEND_PARSE_PARAMETERS_END(); - RETURN_LONG(php_stream_seek(stream, offset, (int) whence)); + php_stream_error_operation_begin(); + RETVAL_LONG(php_stream_seek(stream, offset, (int) whence)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1087,7 +1133,9 @@ PHP_FUNCTION(mkdir) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1107,7 +1155,9 @@ PHP_FUNCTION(rmdir) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1131,14 +1181,17 @@ PHP_FUNCTION(readfile) context = php_stream_context_from_zval(zcontext, 0); + php_stream_error_operation_begin(); stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (stream) { size = php_stream_passthru(stream); php_stream_close(stream); - RETURN_LONG(size); + RETVAL_LONG(size); + } else { + RETVAL_FALSE; } + php_stream_error_operation_end(context); - RETURN_FALSE; } /* }}} */ @@ -1180,7 +1233,9 @@ PHPAPI PHP_FUNCTION(fpassthru) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); size = php_stream_passthru(stream); + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(size); } /* }}} */ @@ -1201,26 +1256,31 @@ PHP_FUNCTION(rename) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + wrapper = php_stream_locate_url_wrapper(old_name, NULL, 0); if (!wrapper || !wrapper->wops) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper"); RETURN_FALSE; } if (!wrapper->wops->rename) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "%s wrapper does not support renaming", wrapper->wops->label ? wrapper->wops->label : "Source"); RETURN_FALSE; } if (wrapper != php_stream_locate_url_wrapper(new_name, NULL, 0)) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Cannot rename a file across wrapper types"); RETURN_FALSE; } - context = php_stream_context_from_zval(zcontext, 0); - - RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context)); + RETVAL_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1239,20 +1299,24 @@ PHP_FUNCTION(unlink) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); wrapper = php_stream_locate_url_wrapper(filename, NULL, 0); if (!wrapper || !wrapper->wops) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper"); RETURN_FALSE; } if (!wrapper->wops->unlink) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper"); RETURN_FALSE; } - RETURN_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context)); + RETVAL_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1264,12 +1328,15 @@ PHP_FUNCTION(fsync) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (!php_stream_sync_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); RETURN_FALSE; } - RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); + RETVAL_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); + php_stream_error_operation_end_for_stream(stream); } PHP_FUNCTION(fdatasync) @@ -1280,12 +1347,15 @@ PHP_FUNCTION(fdatasync) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (!php_stream_sync_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); RETURN_FALSE; } - RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); + RETVAL_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); + php_stream_error_operation_end_for_stream(stream); } /* {{{ Truncate file to 'size' length */ @@ -1304,12 +1374,16 @@ PHP_FUNCTION(ftruncate) RETURN_THROWS(); } + php_stream_error_operation_begin(); + if (!php_stream_truncate_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't truncate this stream!"); RETURN_FALSE; } - RETURN_BOOL(0 == php_stream_truncate_set_size(stream, size)); + RETVAL_BOOL(0 == php_stream_truncate_set_size(stream, size)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ PHPAPI void php_fstat(php_stream *stream, zval *return_value) @@ -1393,7 +1467,9 @@ PHP_FUNCTION(fstat) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); php_fstat(stream, return_value); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1412,13 +1488,16 @@ PHP_FUNCTION(copy) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (php_stream_locate_url_wrapper(source, NULL, 0) == &php_plain_files_wrapper && php_check_open_basedir(source)) { + php_stream_error_operation_end(context); RETURN_FALSE; } - context = php_stream_context_from_zval(zcontext, 0); - - RETURN_BOOL(php_copy_file_ctx(source, target, 0, context) == SUCCESS); + RETVAL_BOOL(php_copy_file_ctx(source, target, 0, context) == SUCCESS); + php_stream_error_operation_end(context); } /* }}} */ @@ -1543,7 +1622,9 @@ PHPAPI PHP_FUNCTION(fread) RETURN_THROWS(); } + php_stream_error_operation_begin(); str = php_stream_read_to_str(stream, len); + php_stream_error_operation_end_for_stream(stream); if (!str) { RETURN_FALSE; } @@ -1662,7 +1743,9 @@ PHP_FUNCTION(fputcsv) RETURN_THROWS(); } + php_stream_error_operation_begin(); ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str); + php_stream_error_operation_end_for_stream(stream); if (ret < 0) { RETURN_FALSE; } @@ -1795,19 +1878,23 @@ PHP_FUNCTION(fgetcsv) RETURN_THROWS(); } + php_stream_error_operation_begin(); if (len < 0) { if ((buf = php_stream_get_line(stream, NULL, 0, &buf_len)) == NULL) { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } else { buf = emalloc(len + 1); if (php_stream_get_line(stream, buf, len + 1, &buf_len) == NULL) { efree(buf); + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } HashTable *values = php_fgetcsv(stream, delimiter, enclosure, escape_char, buf_len, buf); + php_stream_error_operation_end_for_stream(stream); if (values == NULL) { values = php_bc_fgetcsv_empty_line(); } diff --git a/ext/standard/file.h b/ext/standard/file.h index 3c6160fd4bb1..ac218169599c 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -32,6 +32,7 @@ PHPAPI PHP_FUNCTION(ftell); PHPAPI PHP_FUNCTION(fseek); PHPAPI PHP_FUNCTION(fpassthru); +PHP_MINIT_FUNCTION(stream_errors); PHP_MINIT_FUNCTION(user_streams); PHPAPI zend_result php_copy_file(const char *src, const char *dest); @@ -98,7 +99,8 @@ typedef struct { php_stream_context *default_context; HashTable *stream_wrappers; /* per-request copy of url_stream_wrappers_hash */ HashTable *stream_filters; /* per-request copy of stream_filters_hash */ - HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ + HashTable *wrapper_logged_errors; /* key: wrapper address; value: linked list of error entries */ + php_stream_error_state stream_error_state; int pclose_wait; #ifdef HAVE_GETHOSTBYNAME_R struct hostent tmp_host_info; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index e1f3a88d2497..f99ae5e4b4e1 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -106,7 +106,9 @@ static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper, php_stream * /* For write modes close data stream first to signal EOF to server */ result = GET_FTP_RESULT(controlstream); if (result != 226 && result != 250) { - php_error_docref(NULL, E_WARNING, "FTP server error %d:%s", result, tmp_line); + php_stream_wrapper_warn(wrapper, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + ProtocolError, + "FTP server error %d:%s", result, tmp_line); ret = EOF; } } @@ -184,7 +186,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { - php_stream_wrapper_log_error(wrapper, options, "Server doesn't support FTPS."); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Server doesn't support FTPS."); goto connect_errexit; } else { /* we must reuse the old SSL session id */ @@ -203,7 +206,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; goto connect_errexit; @@ -234,7 +238,7 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \ while (s < e) { \ if (iscntrl((unsigned char)*s)) { \ - php_stream_wrapper_log_error(wrapper, options, err_msg, val); \ + php_stream_wrapper_log_warn(wrapper, context, options, AuthFailed, err_msg, val); \ goto connect_errexit; \ } \ s++; \ @@ -431,7 +435,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (strpbrk(mode, "wa+")) { if (read_write) { - php_stream_wrapper_log_error(wrapper, options, "FTP does not support simultaneous read/write connections"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "FTP does not support simultaneous read/write connections"); return NULL; } if (strchr(mode, 'a')) { @@ -442,7 +447,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (!read_write) { /* No mode specified? */ - php_stream_wrapper_log_error(wrapper, options, "Unknown file open mode"); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidMode, + "Unknown file open mode"); return NULL; } @@ -453,7 +459,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC); } else { /* ftp proxy is read-only */ - php_stream_wrapper_log_error(wrapper, options, "FTP proxy may only be used in read mode"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "FTP proxy may only be used in read mode"); return NULL; } } @@ -505,7 +512,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa goto errexit; } } else { - php_stream_wrapper_log_error(wrapper, options, "Remote file already exists and overwrite context option not specified"); + php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists, + "Remote file already exists and overwrite context option not specified"); errno = EEXIST; goto errexit; } @@ -529,7 +537,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval)); result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); + php_stream_wrapper_log_warn(wrapper, context, options, ResumptionFailed, + "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } } @@ -574,7 +583,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; tmp_line[0]='\0'; @@ -596,10 +606,12 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_close(stream); } if (tmp_line[0] != '\0') - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "FTP server reports %s", tmp_line); if (error_message) { - php_stream_wrapper_log_error(wrapper, options, "Failed to set up data channel: %s", ZSTR_VAL(error_message)); + php_stream_wrapper_log_warn(wrapper, context, options, NetworkSendFailed, + "Failed to set up data channel: %s", ZSTR_VAL(error_message)); zend_string_release(error_message); } return NULL; @@ -744,7 +756,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; goto opendir_errexit; @@ -768,7 +781,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch php_stream_close(stream); } if (tmp_line[0] != '\0') { - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "FTP server reports %s", tmp_line); } return NULL; } @@ -907,14 +921,16 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); } goto unlink_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); } goto unlink_errexit; } @@ -925,7 +941,8 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Deleting file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, UnlinkFailed, + "Error Deleting file: %s", tmp_line); } goto unlink_errexit; } @@ -989,7 +1006,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, context, NULL, NULL, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", ZSTR_VAL(resource_from->host)); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", ZSTR_VAL(resource_from->host)); } goto rename_errexit; } @@ -1000,7 +1018,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1011,7 +1030,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1044,14 +1064,16 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); } goto mkdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); } goto mkdir_errexit; } @@ -1092,7 +1114,8 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, MkdirFailed, + "%s", tmp_line); } break; } @@ -1136,14 +1159,16 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); } goto rmdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); } goto rmdir_errexit; } @@ -1153,7 +1178,8 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, RmdirFailed, + "%s", tmp_line); } goto rmdir_errexit; } diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index f3172180bed9..9e3db6716048 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -243,7 +243,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w while (last_header_name < last_header_value) { if (*last_header_name == ' ' || *last_header_name == '\t') { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid response format (space in header name)!"); zend_string_efree(last_header_line_str); return NULL; @@ -261,7 +261,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w } else { /* There is no colon which means invalid response so error. */ header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid response format (no colon in header line)!"); zend_string_efree(last_header_line_str); return NULL; @@ -285,7 +285,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w size_t last_header_value_len = strlen(last_header_value); if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP Location header size is over the limit of %d bytes", HTTP_HEADER_MAX_LOCATION_SIZE); zend_string_efree(last_header_line_str); @@ -386,7 +386,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, tmp_line[0] = '\0'; if (redirect_max < 1) { - php_stream_wrapper_log_error(wrapper, options, "Redirection limit reached, aborting"); + php_stream_wrapper_log_warn(wrapper, context, options, RedirectLimit, + "Redirection limit reached, aborting"); return NULL; } @@ -419,7 +420,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, /* Normal http request (possibly with proxy) */ if (strpbrk(mode, "awx+")) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper does not support writeable connections"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "HTTP wrapper does not support writeable connections"); php_uri_struct_free(resource); return NULL; } @@ -448,7 +450,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters"); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "HTTP wrapper full URI path does not allow CR or LF characters"); php_uri_struct_free(resource); zend_string_release(transport_string); return NULL; @@ -463,7 +466,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, #endif if (d > timeoutmax) { - php_stream_wrapper_log_error(wrapper, options, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidParam, + "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); zend_string_release(transport_string); php_uri_struct_free(resource); return NULL; @@ -493,7 +497,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (errstr) { - php_stream_wrapper_log_error(wrapper, options, "%s", ZSTR_VAL(errstr)); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "%s", ZSTR_VAL(errstr)); zend_string_release_ex(errstr, 0); errstr = NULL; } @@ -547,8 +552,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (reset_ssl_peer_name) { php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); } - - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -573,7 +578,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -755,14 +761,14 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, smart_str scratch = {0}; /* decode the strings first */ - php_url_decode(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user)); + ZSTR_LEN(resource->user) = php_url_decode(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user)); smart_str_append(&scratch, resource->user); smart_str_appendc(&scratch, ':'); /* Note: password is optional! */ if (resource->password) { - php_url_decode(ZSTR_VAL(resource->password), ZSTR_LEN(resource->password)); + ZSTR_LEN(resource->password) = php_url_decode(ZSTR_VAL(resource->password), ZSTR_LEN(resource->password)); smart_str_append(&scratch, resource->password); } @@ -830,7 +836,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, ua[ua_len] = 0; smart_str_appendl(&req_buf, ua, ua_len); } else { - php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header"); + php_stream_wrapper_warn_nt(wrapper, context, options, InvalidHeader, + "Cannot construct User-agent header"); } efree(ua); } @@ -869,7 +876,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (!(have_header & HTTP_HEADER_TYPE)) { smart_str_appends(&req_buf, "Content-Type: application/x-www-form-urlencoded\r\n"); - php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded"); + php_stream_wrapper_notice(wrapper, context, options, InvalidHeader, + "Content-type not specified assuming application/x-www-form-urlencoded"); } smart_str_appends(&req_buf, "\r\n"); smart_str_append(&req_buf, Z_STR_P(tmpzval)); @@ -956,7 +964,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } else { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed!"); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "HTTP request failed!"); goto out; } } @@ -974,7 +983,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (http_header_line[1] != '\n') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid header name (cannot start with CR character)!"); goto out; } @@ -1005,7 +1014,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (*http_header_line == ' ' || *http_header_line == '\t') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid response format (folding header at the start)!"); goto out; } @@ -1101,7 +1110,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, php_uri_struct_free(resource); /* check for invalid redirection URLs */ if ((resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "Invalid redirect URL! %s", new_path); efree(new_path); goto out; } @@ -1113,7 +1123,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \ + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, \ + "Invalid redirect URL! %s", new_path); \ efree(new_path); \ goto out; \ } \ @@ -1139,7 +1150,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, --redirect_max, new_flags, response_header STREAMS_CC); efree(new_path); } else { - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "HTTP request failed! %s", tmp_line); } } out: diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 89c2d9c49b0c..3f062bf2ea1e 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -156,14 +156,18 @@ static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, i if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->readfilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + CreateFailed, + "Unable to create filter (%s)", p); } } if (write_chain) { if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->writefilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + CreateFailed, + "Unable to create filter (%s)", p); } } p = php_strtok_r(NULL, "|", &token); @@ -217,7 +221,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -236,7 +242,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcasecmp(path, "stdin")) { if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -295,14 +303,18 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (strcmp(sapi_module.name, "cli")) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Direct access to file descriptors is only available from command-line PHP"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "Direct access to file descriptors is only available from command-line PHP"); } return NULL; } if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -310,7 +322,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c start = &path[3]; fildes_ori = ZEND_STRTOL(start, &end, 10); if (end == start || *end != '\0') { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "php://fd/ stream must be specified in the form php://fd/"); return NULL; } @@ -322,14 +335,16 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c #endif if (fildes_ori < 0 || fildes_ori >= dtablesize) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidParam, "The file descriptors must be non-negative numbers smaller than %d", dtablesize); return NULL; } fd = dup((int)fildes_ori); if (fd == -1) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + DupFailed, "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: " "[%d]: %s", fildes_ori, errno, strerror(errno)); return NULL; @@ -378,7 +393,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c return stream; } else { /* invalid php://thingy */ - php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified"); + php_stream_wrapper_warn(wrapper, context, options, + InvalidUrl, "Invalid php:// URL specified"); return NULL; } diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 68f79783c6f8..3609ee1f3f24 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -48,11 +48,15 @@ PHP_FUNCTION(stream_socket_pair) zend_long domain, type, protocol; php_stream *s1, *s2; php_socket_t pair[2]; + zval *zcontext = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(3, 3) + ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_LONG(domain) Z_PARAM_LONG(type) Z_PARAM_LONG(protocol) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (0 != socketpair((int)domain, (int)type, (int)protocol, pair)) { @@ -62,10 +66,14 @@ PHP_FUNCTION(stream_socket_pair) RETURN_FALSE; } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + s1 = php_stream_sock_open_from_socket(pair[0], 0); if (s1 == NULL) { close(pair[0]); close(pair[1]); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair"); RETURN_FALSE; } @@ -73,6 +81,7 @@ PHP_FUNCTION(stream_socket_pair) if (s2 == NULL) { php_stream_free(s1, PHP_STREAM_FREE_CLOSE); close(pair[1]); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair"); RETURN_FALSE; } @@ -86,6 +95,8 @@ PHP_FUNCTION(stream_socket_pair) add_next_index_resource(return_value, s1->res); add_next_index_resource(return_value, s2->res); + + php_stream_error_operation_end(context); } /* }}} */ #endif @@ -124,6 +135,7 @@ PHP_FUNCTION(stream_socket_client) RETURN_THROWS(); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_STREAM_CLIENT_PERSISTENT) { @@ -158,6 +170,7 @@ PHP_FUNCTION(stream_socket_client) (flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0), hashkey, tv_pointer, context, &errstr, &err); + php_stream_error_operation_end(context); if (stream == NULL) { /* host might contain binary characters */ @@ -215,6 +228,7 @@ PHP_FUNCTION(stream_socket_server) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (zerrno) { @@ -228,6 +242,8 @@ PHP_FUNCTION(stream_socket_server) STREAM_XPORT_SERVER | (int)flags, NULL, NULL, context, &errstr, &err); + php_stream_error_operation_end(context); + if (stream == NULL) { php_error_docref(NULL, E_WARNING, "Unable to connect to %s (%s)", host, errstr == NULL ? "Unknown error" : ZSTR_VAL(errstr)); } @@ -293,6 +309,8 @@ PHP_FUNCTION(stream_socket_accept) tv_pointer = &tv; } + php_stream_error_operation_begin(); + if (0 == php_stream_xport_accept(stream, &clistream, zpeername ? &peername : NULL, NULL, NULL, @@ -311,6 +329,8 @@ PHP_FUNCTION(stream_socket_accept) RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); + if (errstr) { zend_string_release_ex(errstr, 0); } @@ -329,10 +349,11 @@ PHP_FUNCTION(stream_socket_get_name) Z_PARAM_BOOL(want_peer) ZEND_PARSE_PARAMETERS_END(); - if (0 != php_stream_xport_get_name(stream, want_peer, - &name, - NULL, NULL - ) || !name) { + php_stream_error_operation_begin(); + int ret = php_stream_xport_get_name(stream, want_peer, &name, NULL, NULL); + php_stream_error_operation_end_for_stream(stream); + + if (0 != ret || !name) { RETURN_FALSE; } @@ -363,15 +384,18 @@ PHP_FUNCTION(stream_socket_sendto) Z_PARAM_STRING(target_addr, target_addr_len) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (target_addr_len) { /* parse the address */ if (FAILURE == php_network_parse_network_address_with_port(target_addr, target_addr_len, (struct sockaddr*)&sa, &sl)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Failed to parse `%s' into a valid network address", target_addr); RETURN_FALSE; } } - RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl)); + RETVAL_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -405,9 +429,10 @@ PHP_FUNCTION(stream_socket_recvfrom) read_buf = zend_string_alloc(to_read, 0); + php_stream_error_operation_begin(); recvd = php_stream_xport_recvfrom(stream, ZSTR_VAL(read_buf), to_read, (int)flags, NULL, NULL, - zremote ? &remote_addr : NULL - ); + zremote ? &remote_addr : NULL); + php_stream_error_operation_end_for_stream(stream); if (recvd >= 0) { if (zremote && remote_addr) { @@ -445,6 +470,8 @@ PHP_FUNCTION(stream_get_contents) RETURN_THROWS(); } + php_stream_error_operation_begin(); + if (desiredpos >= 0) { int seek_res = 0; zend_off_t position; @@ -459,6 +486,7 @@ PHP_FUNCTION(stream_get_contents) } if (seek_res != 0) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", desiredpos); RETURN_FALSE; @@ -466,10 +494,11 @@ PHP_FUNCTION(stream_get_contents) } if ((contents = php_stream_copy_to_mem(stream, maxlen, 0))) { - RETURN_STR(contents); + RETVAL_STR(contents); } else { - RETURN_EMPTY_STRING(); + RETVAL_EMPTY_STRING(); } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -480,28 +509,37 @@ PHP_FUNCTION(stream_copy_to_stream) zend_long maxlen, pos = 0; bool maxlen_is_null = 1; size_t len; + zval *zcontext = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(2, 4) + ZEND_PARSE_PARAMETERS_START(2, 5) PHP_Z_PARAM_STREAM(src) PHP_Z_PARAM_STREAM(dest) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null) Z_PARAM_LONG(pos) + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (maxlen_is_null) { maxlen = PHP_STREAM_COPY_ALL; } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", pos); RETURN_FALSE; } if (php_stream_copy_to_stream_ex(src, dest, maxlen, &len) != SUCCESS) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + RETVAL_LONG(len); } - RETURN_LONG(len); + php_stream_error_operation_end(context); } /* }}} */ @@ -516,11 +554,13 @@ PHP_FUNCTION(stream_get_meta_data) array_init(return_value); + php_stream_error_operation_begin(); if (!php_stream_populate_meta_data(stream, return_value)) { add_assoc_bool(return_value, "timed_out", 0); add_assoc_bool(return_value, "blocked", 1); add_assoc_bool(return_value, "eof", php_stream_eof(stream)); } + php_stream_error_operation_end_for_stream(stream); if (!Z_ISUNDEF(stream->wrapperdata)) { Z_ADDREF_P(&stream->wrapperdata); @@ -592,6 +632,18 @@ PHP_FUNCTION(stream_get_wrappers) } /* }}} */ +PHP_FUNCTION(stream_last_errors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_get_last(return_value); +} + +PHP_FUNCTION(stream_clear_errors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_clear_stored(); +} + /* {{{ stream_select related functions */ static int stream_array_to_fd_set(const HashTable *stream_array, fd_set *fds, php_socket_t *max_fd) { @@ -724,7 +776,7 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) /* {{{ Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */ PHP_FUNCTION(stream_select) { - zval *r_array, *w_array, *e_array; + zval *r_array, *w_array, *e_array, *zcontext = NULL; struct timeval tv, *tv_p = NULL; fd_set rfds, wfds, efds; php_socket_t max_fd = 0; @@ -733,20 +785,25 @@ PHP_FUNCTION(stream_select) bool secnull; bool usecnull = 1; int set_count, max_set_count = 0; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(4, 5) + ZEND_PARSE_PARAMETERS_START(4, 6) Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) Z_PARAM_LONG_OR_NULL(sec, secnull) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(usec, usecnull) + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (r_array != NULL) { set_count = stream_array_to_fd_set(Z_ARR_P(r_array), &rfds, &max_fd); if (set_count > max_set_count) @@ -769,6 +826,7 @@ PHP_FUNCTION(stream_select) } if (!sets) { + php_stream_error_operation_end(context); zend_value_error("No stream arrays were passed"); RETURN_THROWS(); } @@ -779,6 +837,7 @@ PHP_FUNCTION(stream_select) if (secnull && !usecnull) { if (usec != 0) { + php_stream_error_operation_end(context); zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null"); RETURN_THROWS(); } @@ -787,9 +846,11 @@ PHP_FUNCTION(stream_select) /* If seconds is not set to null, build the timeval, else we wait indefinitely */ if (!secnull) { if (sec < 0) { + php_stream_error_operation_end(context); zend_argument_value_error(4, "must be greater than or equal to 0"); RETURN_THROWS(); } else if (usec < 0) { + php_stream_error_operation_end(context); zend_argument_value_error(5, "must be greater than or equal to 0"); RETURN_THROWS(); } @@ -806,6 +867,7 @@ PHP_FUNCTION(stream_select) if (r_array != NULL) { retval = stream_array_emulate_read_fd_set(r_array); if (retval > 0) { + php_stream_error_operation_end(context); if (w_array != NULL) { zval_ptr_dtor(w_array); ZVAL_EMPTY_ARRAY(w_array); @@ -819,6 +881,7 @@ PHP_FUNCTION(stream_select) } retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p); + php_stream_error_operation_end(context); if (retval == -1) { php_error_docref(NULL, E_WARNING, "Unable to select [%d]: %s (max_fd=" PHP_SOCKET_FMT ")", @@ -1145,6 +1208,24 @@ PHP_FUNCTION(stream_context_get_default) } /* }}} */ +/* Check if options contain stream error handling settings */ +static bool php_stream_context_options_has_error_settings(const HashTable *options) +{ + zval *stream_options = zend_hash_str_find(options, ZEND_STRL("stream")); + if (!stream_options) { + return false; + } + + ZVAL_DEREF(stream_options); + if (Z_TYPE_P(stream_options) != IS_ARRAY) { + return false; + } + + return zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_mode")) + || zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_store")) + || zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_handler")); +} + /* {{{ Set default file/stream context, returns the context as a resource */ PHP_FUNCTION(stream_context_set_default) { @@ -1160,6 +1241,11 @@ PHP_FUNCTION(stream_context_set_default) } context = FG(default_context); + if (php_stream_context_options_has_error_settings(options)) { + zend_value_error("Stream error handling options cannot be set on the default context"); + RETURN_THROWS(); + } + if (parse_context_options(context, options) == FAILURE) { RETURN_THROWS(); } @@ -1339,11 +1425,13 @@ PHP_FUNCTION(stream_get_line) max_length = PHP_SOCK_CHUNK_SIZE; } + php_stream_error_operation_begin(); if ((buf = php_stream_get_record(stream, max_length, str, str_len))) { - RETURN_STR(buf); + RETVAL_STR(buf); } else { - RETURN_FALSE; + RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1359,7 +1447,9 @@ PHP_FUNCTION(stream_set_blocking) Z_PARAM_BOOL(block) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); + php_stream_error_operation_begin(); + RETVAL_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1400,7 +1490,9 @@ PHP_FUNCTION(stream_set_timeout) } #endif - RETURN_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); + php_stream_error_operation_begin(); + RETVAL_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); + php_stream_error_operation_end_for_stream(stream); } #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */ /* }}} */ @@ -1420,12 +1512,14 @@ PHP_FUNCTION(stream_set_write_buffer) buff = arg2; + php_stream_error_operation_begin(); /* if buff is 0 then set to non-buffered */ if (buff == 0) { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); } else { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff); } + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret == 0 ? 0 : EOF); } @@ -1456,7 +1550,9 @@ PHP_FUNCTION(stream_set_chunk_size) RETURN_THROWS(); } + php_stream_error_operation_begin(); ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL); + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret > 0 ? (zend_long)ret : (zend_long)EOF); } @@ -1477,12 +1573,14 @@ PHP_FUNCTION(stream_set_read_buffer) buff = arg2; + php_stream_error_operation_begin(); /* if buff is 0 then set to non-buffered */ if (buff == 0) { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); } else { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff); } + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret == 0 ? 0 : EOF); } @@ -1504,11 +1602,14 @@ PHP_FUNCTION(stream_socket_enable_crypto) PHP_Z_PARAM_STREAM_OR_NULL(sessstream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + if (enable) { if (cryptokindnull) { zval *val; if (!GET_CTX_OPT(stream, "ssl", "crypto_method", val)) { + php_stream_error_operation_end_for_stream(stream); zend_argument_value_error(3, "must be specified when enabling encryption"); RETURN_THROWS(); } @@ -1517,11 +1618,13 @@ PHP_FUNCTION(stream_socket_enable_crypto) } if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream) < 0) { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } ret = php_stream_xport_crypto_enable(stream, enable); + php_stream_error_operation_end_for_stream(stream); switch (ret) { case -1: RETURN_FALSE; @@ -1557,12 +1660,15 @@ PHP_FUNCTION(stream_resolve_include_path) /* {{{ */ PHP_FUNCTION(stream_is_local) { - zval *zstream; + zval *zstream, *zcontext = NULL; php_stream *stream = NULL; php_stream_wrapper *wrapper = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(zstream) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (Z_TYPE_P(zstream) == IS_RESOURCE) { @@ -1573,7 +1679,10 @@ PHP_FUNCTION(stream_is_local) RETURN_THROWS(); } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0); + php_stream_error_operation_end(context); } RETURN_BOOL(wrapper && wrapper->is_url == 0); @@ -1589,7 +1698,9 @@ PHP_FUNCTION(stream_supports_lock) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(php_stream_supports_lock(stream)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_supports_lock(stream)); + php_stream_error_operation_end_for_stream(stream); } /* {{{ Check if a stream is a TTY. */ @@ -1602,6 +1713,8 @@ PHP_FUNCTION(stream_isatty) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + /* get the fd. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. @@ -1611,8 +1724,10 @@ PHP_FUNCTION(stream_isatty) } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0); } else { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } + php_stream_error_operation_end_for_stream(stream); #ifdef PHP_WIN32 /* Check if the Windows standard handle is redirected to file */ @@ -1642,6 +1757,8 @@ PHP_FUNCTION(sapi_windows_vt100_support) Z_PARAM_BOOL_OR_NULL(enable, enable_is_null) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + /* get the fd. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. @@ -1651,6 +1768,7 @@ PHP_FUNCTION(sapi_windows_vt100_support) } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0); } else { + php_stream_error_operation_end_for_stream(stream); if (!enable_is_null) { php_error_docref( NULL, @@ -1660,6 +1778,7 @@ PHP_FUNCTION(sapi_windows_vt100_support) } RETURN_FALSE; } + php_stream_error_operation_end_for_stream(stream); /* Check if the file descriptor is a console */ if (!php_win32_console_fileno_is_console(fileno)) { @@ -1699,7 +1818,9 @@ PHP_FUNCTION(stream_socket_shutdown) RETURN_THROWS(); } - RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ #endif diff --git a/ext/standard/tests/array/array_map_foreach_optimization_006.phpt b/ext/standard/tests/array/array_map_foreach_optimization_006.phpt new file mode 100644 index 000000000000..e21e3e0a9a8b --- /dev/null +++ b/ext/standard/tests/array/array_map_foreach_optimization_006.phpt @@ -0,0 +1,84 @@ +--TEST-- +array_map(): foreach optimization - PFA +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 INIT_FCALL 2 %d string("range") +0001 SEND_VAL int(1) 1 +0002 SEND_VAL int(10) 2 +0003 T2 = DO_ICALL +0004 ASSIGN CV0($array) T2 +0005 TYPE_ASSERT 131079 string("array_map") CV0($array) +0006 T2 = INIT_ARRAY 0 (packed) NEXT +0007 V3 = FE_RESET_R CV0($array) 0015 +0008 T5 = FE_FETCH_R V3 T4 0015 +0009 INIT_FCALL 2 %d string("plusn") +0010 SEND_VAL T4 1 +0011 SEND_VAL int(2) 2 +0012 T4 = DO_UCALL +0013 T2 = ADD_ARRAY_ELEMENT T4 T5 +0014 JMP 0008 +0015 FE_FREE V3 +0016 ASSIGN CV1($foo) T2 +0017 INIT_FCALL 1 %d string("var_dump") +0018 SEND_VAR CV1($foo) 1 +0019 DO_ICALL +0020 RETURN int(1) +LIVE RANGES: + 2: 0007 - 0016 (tmp/var) + 3: 0008 - 0015 (loop) + 4: 0009 - 0010 (tmp/var) + 5: 0009 - 0013 (tmp/var) + +plusn: + ; (lines=4, args=2, vars=2, tmps=1) + ; (after optimizer) + ; %s +0000 CV0($x) = RECV 1 +0001 CV1($n) = RECV 2 +0002 T2 = ADD CV0($x) CV1($n) +0003 RETURN T2 +array(10) { + [0]=> + int(3) + [1]=> + int(4) + [2]=> + int(5) + [3]=> + int(6) + [4]=> + int(7) + [5]=> + int(8) + [6]=> + int(9) + [7]=> + int(10) + [8]=> + int(11) + [9]=> + int(12) +} diff --git a/ext/standard/tests/array/array_map_foreach_optimization_007.phpt b/ext/standard/tests/array/array_map_foreach_optimization_007.phpt new file mode 100644 index 000000000000..67654b9e7640 --- /dev/null +++ b/ext/standard/tests/array/array_map_foreach_optimization_007.phpt @@ -0,0 +1,109 @@ +--TEST-- +array_map(): foreach optimization - unoptimizable PFA +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.opt_debug_level=0x20000 +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=%d, args=0, vars=%d, tmps=%d) + ; (after optimizer) + ; %s +0000 INIT_FCALL 2 %d string("range") +0001 SEND_VAL int(1) 1 +0002 SEND_VAL int(10) 2 +0003 T2 = DO_ICALL +0004 ASSIGN CV0($array) T2 +0005 INIT_FCALL 2 %d string("array_map") +0006 INIT_FCALL 0 %d string("plusn") +0007 SEND_VAL int(2) string("n") +0008 T2 = CALLABLE_CONVERT_PARTIAL 2 +0009 SEND_VAL T2 1 +0010 SEND_VAR CV0($array) 2 +0011 T2 = DO_ICALL +0012 ASSIGN CV1($foo) T2 +0013 INIT_FCALL 1 %d string("var_dump") +0014 SEND_VAR CV1($foo) 1 +0015 DO_ICALL +0016 RETURN int(1) + +plusn: + ; (lines=4, args=2, vars=2, tmps=1) + ; (after optimizer) + ; %s +0000 CV0($x) = RECV 1 +0001 CV1($n) = RECV 2 +0002 T2 = ADD CV0($x) CV1($n) +0003 RETURN T2 + +$_main: + ; (lines=4, args=0, vars=1, tmps=1) + ; (after optimizer) + ; %s:1-9 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($n) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:pfa:%s:9}: + ; (lines=18, args=1, vars=2, tmps=2) + ; (after optimizer) + ; %s:9-9 +0000 CV0($x) = RECV 1 +0001 BIND_STATIC CV1($n) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("plusn") +0006 SEND_VAR CV0($x) 1 +0007 SEND_VAR CV1($n) 2 +0008 T2 = DO_UCALL +0009 RETURN T2 +0010 INIT_FCALL 2 %d string("plusn") +0011 SEND_VAR CV0($x) 1 +0012 SEND_VAR CV1($n) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 T2 = DO_UCALL +0017 RETURN T2 +array(10) { + [0]=> + int(3) + [1]=> + int(4) + [2]=> + int(5) + [3]=> + int(6) + [4]=> + int(7) + [5]=> + int(8) + [6]=> + int(9) + [7]=> + int(10) + [8]=> + int(11) + [9]=> + int(12) +} diff --git a/ext/standard/tests/file/php_fd_wrapper_03.phpt b/ext/standard/tests/file/php_fd_wrapper_03.phpt index 991c497f5e19..a19d1f5acd94 100644 --- a/ext/standard/tests/file/php_fd_wrapper_03.phpt +++ b/ext/standard/tests/file/php_fd_wrapper_03.phpt @@ -10,7 +10,6 @@ fopen("php://fd/1/", "w"); echo "\nDone.\n"; ?> --EXPECTF-- -Warning: fopen(): Invalid php:// URL specified in %s on line %d Warning: fopen(php://fd): Failed to open stream: operation failed in %s on line 2 diff --git a/ext/standard/tests/general_functions/ini_get_all.phpt b/ext/standard/tests/general_functions/ini_get_all.phpt index 2b71b474a013..c54b39ac3325 100644 --- a/ext/standard/tests/general_functions/ini_get_all.phpt +++ b/ext/standard/tests/general_functions/ini_get_all.phpt @@ -33,29 +33,35 @@ array(0) { } array(3) { ["pcre.backtrack_limit"]=> - array(3) { + array(4) { ["global_value"]=> string(7) "1000000" ["local_value"]=> string(7) "1000000" + ["builtin_default_value"]=> + string(7) "1000000" ["access"]=> int(7) } ["pcre.jit"]=> - array(3) { + array(4) { ["global_value"]=> string(1) "1" ["local_value"]=> string(1) "1" + ["builtin_default_value"]=> + string(1) "1" ["access"]=> int(7) } ["pcre.recursion_limit"]=> - array(3) { + array(4) { ["global_value"]=> string(6) "100000" ["local_value"]=> string(6) "100000" + ["builtin_default_value"]=> + string(6) "100000" ["access"]=> int(7) } diff --git a/ext/standard/tests/general_functions/ini_get_all_builtin_default_value.phpt b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value.phpt new file mode 100644 index 000000000000..82de8781caa7 --- /dev/null +++ b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value.phpt @@ -0,0 +1,33 @@ +--TEST-- +ini_get_all() exposes the built-in default value independent of configuration and runtime changes +--INI-- +precision=8 +--FILE-- + +--EXPECT-- +string(1) "8" +string(1) "8" +string(2) "14" +string(1) "8" +string(1) "3" +string(2) "14" +Done diff --git a/ext/standard/tests/general_functions/ini_get_all_builtin_default_value_null.phpt b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value_null.phpt new file mode 100644 index 000000000000..efd86eb947e4 --- /dev/null +++ b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value_null.phpt @@ -0,0 +1,33 @@ +--TEST-- +ini_get_all() reports a null built-in default value for a directive that has no compiled-in default +--INI-- +error_append_string=FOO +--FILE-- + +--EXPECT-- +string(3) "FOO" +string(3) "FOO" +NULL +string(3) "FOO" +string(3) "BAR" +NULL +Done diff --git a/ext/standard/tests/http/gh22171.phpt b/ext/standard/tests/http/gh22171.phpt new file mode 100644 index 000000000000..0c1aa150f60f --- /dev/null +++ b/ext/standard/tests/http/gh22171.phpt @@ -0,0 +1,47 @@ +--TEST-- +GH-22171 (http(s) stream wrapper sends a corrupted Authorization header for percent-encoded userinfo) +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- + $pid, 'uri' => $uri] = http_server($responses, $output); + + $url = preg_replace('(^http://)', 'http://' . $userinfo . '@', $uri); + file_get_contents($url); + + fseek($output, 0, SEEK_SET); + $output = stream_get_contents($output); + + http_server_kill($pid); + + if (preg_match('(^Authorization:\s*Basic\s+(\S+))mi', $output, $m)) { + $decoded = base64_decode($m[1]); + } else { + $decoded = ''; + } + + echo "=== {$label} ===", PHP_EOL; + echo " decoded : ", addcslashes($decoded, "\0..\37"), PHP_EOL; + echo " result : ", ($decoded === $expected ? "OK" : "CORRUPT"), PHP_EOL; +} + +probe('user only', '%76%6f%72%74%66%75', 'vortfu:'); +probe('user + password', '%76%6f%72%74%66%75:%70%61%73%73%77%6f%72%64', 'vortfu:password'); +?> +--EXPECT-- +=== user only === + decoded : vortfu: + result : OK +=== user + password === + decoded : vortfu:password + result : OK diff --git a/ext/standard/tests/streams/gh14506.phpt b/ext/standard/tests/streams/gh14506.phpt index f83eba4f1ff3..3d82350221d5 100644 --- a/ext/standard/tests/streams/gh14506.phpt +++ b/ext/standard/tests/streams/gh14506.phpt @@ -86,10 +86,10 @@ Warning: fclose(): cannot close the provided stream, as it must not be manually Warning: fclose(): cannot close the provided stream, as it must not be manually closed in %s on line %d -Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d - Warning: fclose(): cannot close the provided stream, as it must not be manually closed in %s on line %d +Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d + Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d No stream arrays were passed fclose(): Argument #1 ($stream) must be an open stream resource diff --git a/ext/standard/tests/streams/stream_errors_error_has_code.phpt b/ext/standard/tests/streams/stream_errors_error_has_code.phpt new file mode 100644 index 000000000000..6cd6b70411a6 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_error_has_code.phpt @@ -0,0 +1,30 @@ +--TEST-- +Stream errors - multiple operations stored +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::All, + ] +]); + +// First operation +$stream1 = fopen('php://nonexistent1', 'r', false, $context); +$error1 = stream_last_errors()[0]; + +// Second operation +$stream2 = fopen('php://nonexistent2', 'r', false, $context); +$error2 = stream_last_errors()[0]; + +// Should get the most recent error (second operation) +if ($error2) { + echo "Got most recent error\n"; + echo "Param contains 'nonexistent2': " . (strpos($error2->param ?? '', 'nonexistent2') !== false ? 'yes' : 'no') . "\n"; +} + +?> +--EXPECT-- +Got most recent error +Param contains 'nonexistent2': yes diff --git a/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt new file mode 100644 index 000000000000..37f5d39bfa75 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt @@ -0,0 +1,31 @@ +--TEST-- +Stream errors - exception mode for terminal errors +--FILE-- + [ + 'error_mode' => StreamErrorMode::Exception, + ] +]); + +try { + $stream = fopen('php://nonexistent', 'r', false, $context); +} catch (StreamException $e) { + echo "Caught: " . $e->getMessage() . "\n"; + echo "Code: " . $e->getCode() . "\n"; + + $errors = $e->getErrors(); + if (!empty($errors)) { + $error = $errors[0]; + echo "Wrapper: " . $error->wrapperName . "\n"; + echo "Error code name: " . $error->code->name . "\n"; + } +} + +?> +--EXPECTF-- +Caught: Failed to open stream: operation failed +Code: 20 +Wrapper: PHP +Error code name: OpenFailed diff --git a/ext/standard/tests/streams/stream_errors_invalid_types.phpt b/ext/standard/tests/streams/stream_errors_invalid_types.phpt new file mode 100644 index 000000000000..1b3ab5e8931e --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_invalid_types.phpt @@ -0,0 +1,37 @@ +--TEST-- +Stream errors - invalid enum type throws TypeError +--FILE-- + [ + 'error_mode' => 'invalid', + ] + ]); + + fopen('php://nonexistent', 'r', false, $context); +} catch (TypeError $e) { + echo "Caught TypeError for error_mode\n"; +} + +try { + $context = stream_context_create([ + 'stream' => [ + 'error_store' => 123, + ] + ]); + + fopen('php://nonexistent', 'r', false, $context); +} catch (TypeError $e) { + echo "Caught TypeError for error_store\n"; +} + +?> +--EXPECTF-- + +Warning: fopen(php://nonexistent): Failed to open stream: operation failed in %s on line %d +Caught TypeError for error_mode + +Warning: fopen(php://nonexistent): Failed to open stream: operation failed in %s on line %d +Caught TypeError for error_store diff --git a/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt new file mode 100644 index 000000000000..49c18616dad3 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt @@ -0,0 +1,149 @@ +--TEST-- +Stream errors - silent mode with mixed error stores +--FILE-- +position += $count; + return str_repeat('x', $count + 10); + } + + public function stream_eof() { + return $this->position >= 100; + } + + public function stream_stat() { + return []; + } +} + +stream_wrapper_register('test', 'TestStream'); + +function stream_test_errors($title, $contextOptions) { + stream_clear_errors(); + $context = stream_context_create($contextOptions); + $stream = fopen('test://foo', 'r', false, $context); + try { + echo $title . "\n"; + $readin = fopen('php://stdin', 'r', false, $context); + $data = fread($stream, 10); + + $read = [$readin, $stream]; + $write = NULL; + $except = NULL; + stream_select($read, $write, $except, 0, 0, $context); + } catch (StreamException $e) { + echo 'EXCEPTION: ' . $e->getMessage() . "\n"; + } + + $errors = stream_last_errors(); + if ($errors) { + $first = $errors[0]; + echo "Error details:\n"; + echo "- Message: $first->message\n"; + echo "- Code: " . $first->code->name . "\n"; + echo "- Wrapper: $first->wrapperName\n"; + echo "- Terminating: " . ($first->terminating ? 'yes' : 'no') . "\n"; + echo "- Count: " . count($errors) . "\n"; + + foreach ($errors as $idx => $error) { + echo " [$idx] " . $error->code->name . ": " . $error->message . "\n"; + } + } else { + echo "No errors stored\n"; + } + echo "\n"; +} + +stream_test_errors('ALL', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::All, + ] +]); + +stream_test_errors('NON TERMINATING', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::NonTerminating, + ] +]); + +stream_test_errors('TERMINATING', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::Terminating, + ] +]); + +stream_test_errors('AUTO EXCEPTION', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Exception, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +stream_test_errors('AUTO ERROR', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Error, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +?> +--EXPECTF-- +ALL +Error details: +- Message: TestStream::stream_cast is not implemented! +- Code: NotImplemented +- Wrapper: user-space +- Terminating: yes +- Count: 2 + [0] NotImplemented: TestStream::stream_cast is not implemented! + [1] CastNotSupported: Cannot represent a stream of type user-space as a select()able descriptor + +NON TERMINATING +Error details: +- Message: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost +- Code: UserspaceInvalidReturn +- Wrapper: user-space +- Terminating: no +- Count: 1 + [0] UserspaceInvalidReturn: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost + +TERMINATING +Error details: +- Message: TestStream::stream_cast is not implemented! +- Code: NotImplemented +- Wrapper: user-space +- Terminating: yes +- Count: 2 + [0] NotImplemented: TestStream::stream_cast is not implemented! + [1] CastNotSupported: Cannot represent a stream of type user-space as a select()able descriptor + +AUTO EXCEPTION +EXCEPTION: TestStream::stream_cast is not implemented! +Error details: +- Message: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost +- Code: UserspaceInvalidReturn +- Wrapper: user-space +- Terminating: no +- Count: 1 + [0] UserspaceInvalidReturn: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost + +AUTO ERROR + +Warning: fread(): TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost in %s on line %d + +Warning: stream_select(): TestStream::stream_cast is not implemented! in %s on line %d + +Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d +No errors stored diff --git a/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt new file mode 100644 index 000000000000..2575a8a795f9 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt @@ -0,0 +1,49 @@ +--TEST-- +Stream errors - error_store AUTO mode behavior +--FILE-- + [ + 'error_mode' => StreamErrorMode::Error, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +@fopen('php://nonexistent', 'r', false, $context1); +$errors1 = stream_last_errors(); +echo "ERROR mode AUTO: " . (!empty($errors1) ? "has error" : "no error") . "\n"; + +// AUTO with EXCEPTION mode should store NON_TERM +$context2 = stream_context_create([ + 'stream' => [ + 'error_mode' => StreamErrorMode::Exception, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +try { + fopen('php://nonexistent2', 'r', false, $context2); +} catch (StreamException $e) {} + +$errors2 = stream_last_errors(); +echo "EXCEPTION mode AUTO: " . (!empty($errors2) ? "has error" : "no error") . "\n"; + +// AUTO with SILENT mode should store ALL +$context3 = stream_context_create([ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +fopen('php://nonexistent3', 'r', false, $context3); +$errors3 = stream_last_errors(); +echo "SILENT mode AUTO: " . (!empty($errors3) ? "has error" : "no error") . "\n"; + +?> +--EXPECTF-- +ERROR mode AUTO: no error +EXCEPTION mode AUTO: %s +SILENT mode AUTO: has error diff --git a/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt b/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt new file mode 100644 index 000000000000..46fc0f7fd34b --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt @@ -0,0 +1,18 @@ +--TEST-- +Stream errors - prohibit setting error mode in default context +--FILE-- + [ + 'error_mode' => StreamErrorMode::Exception, + ] + ]); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Stream error handling options cannot be set on the default context diff --git a/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt new file mode 100644 index 000000000000..456575affc3a --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt @@ -0,0 +1,37 @@ +--TEST-- +Stream errors - custom error handler +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_handler' => function(array $errors) use (&$handler_called) { + $handler_called = true; + echo "Handler called\n"; + foreach ($errors as $error) { + echo "Wrapper: " . $error->wrapperName . "\n"; + echo "Code: " . $error->code->name . "\n"; + echo "Message: " . $error->message . "\n"; + echo "Param: " . ($error->param ?? 'null') . "\n"; + echo "Terminating: " . ($error->terminating ? 'yes' : 'no') . "\n"; + } + } + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($handler_called); + +?> +--EXPECT-- +Handler called +Wrapper: PHP +Code: OpenFailed +Message: Failed to open stream: operation failed +Param: php://nonexistent +Terminating: yes +bool(true) diff --git a/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt new file mode 100644 index 000000000000..9e356216b0dd --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt @@ -0,0 +1,29 @@ +--TEST-- +Stream errors - silent mode with error storage +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); +var_dump($stream); + +$errors = stream_last_errors(); +foreach ($errors as $error) { + echo "Has error: yes\n"; + echo "Error code: " . $error->code->name . "\n"; + echo "Error wrapper: " . $error->wrapperName . "\n"; + echo "Error message: " . $error->message . "\n"; +} + +?> +--EXPECT-- +bool(false) +Has error: yes +Error code: OpenFailed +Error wrapper: PHP +Error message: Failed to open stream: operation failed diff --git a/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt new file mode 100644 index 000000000000..8155ce60bfee --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error_store NONE option +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::None, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +$errors = stream_last_errors(); +echo "Has error: " . (!empty($error) ? "yes" : "no") . "\n"; + +?> +--EXPECT-- +Has error: no diff --git a/ext/standard/tests/streams/stream_errors_standard_error.phpt b/ext/standard/tests/streams/stream_errors_standard_error.phpt new file mode 100644 index 000000000000..42de8cc3b17d --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_standard_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error mode with standard error reporting +--FILE-- + [ + 'error_mode' => StreamErrorMode::Error, + ] +]); + +// This will trigger a warning +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($stream); + +?> +--EXPECTF-- +Warning: fopen(php://nonexistent): Failed to open stream: %s in %s on line %d +bool(false) diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c index 16b4ffec50ee..0c2b3dcf183b 100644 --- a/ext/sysvmsg/sysvmsg.c +++ b/ext/sysvmsg/sysvmsg.c @@ -65,9 +65,7 @@ ZEND_GET_MODULE(sysvmsg) zend_class_entry *sysvmsg_queue_ce; static zend_object_handlers sysvmsg_queue_object_handlers; -static inline sysvmsg_queue_t *sysvmsg_queue_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, sysvmsg_queue_t, std); -} +#define sysvmsg_queue_from_obj(obj) ZEND_CONTAINER_OF(obj, sysvmsg_queue_t, std) #define Z_SYSVMSG_QUEUE_P(zv) sysvmsg_queue_from_obj(Z_OBJ_P(zv)) diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c index 9d3990d0bdc0..e506bd5bb37c 100644 --- a/ext/sysvsem/sysvsem.c +++ b/ext/sysvsem/sysvsem.c @@ -82,9 +82,7 @@ ZEND_GET_MODULE(sysvsem) zend_class_entry *sysvsem_ce; static zend_object_handlers sysvsem_object_handlers; -static inline sysvsem_sem *sysvsem_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, sysvsem_sem, std); -} +#define sysvsem_from_obj(obj) ZEND_CONTAINER_OF(obj, sysvsem_sem, std) #define Z_SYSVSEM_P(zv) sysvsem_from_obj(Z_OBJ_P(zv)) diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c index 96324e67c44d..59ca49a974e6 100644 --- a/ext/sysvshm/sysvshm.c +++ b/ext/sysvshm/sysvshm.c @@ -33,9 +33,7 @@ zend_class_entry *sysvshm_ce; static zend_object_handlers sysvshm_object_handlers; -static inline sysvshm_shm *sysvshm_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, sysvshm_shm, std); -} +#define sysvshm_from_obj(obj) ZEND_CONTAINER_OF(obj, sysvshm_shm, std) #define Z_SYSVSHM_P(zv) sysvshm_from_obj(Z_OBJ_P(zv)) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index cff4ad703418..98e1c05a720f 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -104,9 +104,7 @@ struct _PHPTidyObj { zend_object std; }; -static inline PHPTidyObj *php_tidy_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, PHPTidyObj, std); -} +#define php_tidy_fetch_object(obj) ZEND_CONTAINER_OF(obj, PHPTidyObj, std) #define Z_TIDY_P(zv) php_tidy_fetch_object(Z_OBJ_P((zv))) /* }}} */ diff --git a/ext/uri/CREDITS b/ext/uri/CREDITS index fc6ce83fec88..0bbea2af21d2 100644 --- a/ext/uri/CREDITS +++ b/ext/uri/CREDITS @@ -1,2 +1,2 @@ uri -Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Niels Dossche, Nicolas Grekas +Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Nora Dossche, Nicolas Grekas diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index b7c4bfbd2246..2bed937a7377 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -2,7 +2,7 @@ EXTENSION("uri", "php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_wha AC_DEFINE("URI_ENABLE_ANSI", 1, "Define to 1 for enabling ANSI support of uriparser.") AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uriparser.") -ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); +ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD /D LEXBOR_STATIC"); ADD_EXTENSION_DEP('uri', 'lexbor'); ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c \ diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 4cf6e970b07e..58f34a370151 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -31,11 +31,14 @@ #include "uriparser/Uri.h" zend_class_entry *php_uri_ce_rfc3986_uri; +zend_class_entry *php_uri_ce_rfc3986_uri_type; +zend_class_entry *php_uri_ce_rfc3986_uri_host_type; zend_class_entry *php_uri_ce_whatwg_url; zend_class_entry *php_uri_ce_comparison_mode; zend_class_entry *php_uri_ce_exception; zend_class_entry *php_uri_ce_error; zend_class_entry *php_uri_ce_invalid_uri_exception; +zend_class_entry *php_uri_ce_whatwg_url_host_type; zend_class_entry *php_uri_ce_whatwg_invalid_url_exception; zend_class_entry *php_uri_ce_whatwg_url_validation_error_type; zend_class_entry *php_uri_ce_whatwg_url_validation_error; @@ -508,6 +511,16 @@ PHP_METHOD(Uri_WhatWg_Url, __construct) create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } +PHP_METHOD(Uri_Rfc3986_Uri, getUriType) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + php_uri_parser_rfc3986_uri_type_read(uri_object->uri, return_value); +} + PHP_METHOD(Uri_Rfc3986_Uri, getScheme) { php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_SCHEME, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_ASCII); @@ -611,6 +624,16 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawHost) php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST, PHP_URI_COMPONENT_READ_MODE_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, getHostType) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + php_uri_parser_rfc3986_host_type_read(uri_object->uri, return_value); +} + PHP_METHOD(Uri_Rfc3986_Uri, withHost) { php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST); @@ -883,6 +906,16 @@ PHP_METHOD(Uri_WhatWg_Url, withScheme) php_uri_property_write_str_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_SCHEME); } +PHP_METHOD(Uri_WhatWg_Url, isSpecialScheme) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + RETVAL_BOOL(php_uri_parser_whatwg_is_special(uri_object->uri)); +} + PHP_METHOD(Uri_WhatWg_Url, withUsername) { php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_USERNAME); @@ -903,6 +936,16 @@ PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE); } +PHP_METHOD(Uri_WhatWg_Url, getHostType) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + php_uri_parser_whatwg_host_type_read(uri_object->uri, return_value); +} + PHP_METHOD(Uri_WhatWg_Url, getFragment) { php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_FRAGMENT, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE); @@ -1078,6 +1121,9 @@ static PHP_MINIT_FUNCTION(uri) object_handlers_rfc3986_uri.free_obj = php_uri_object_handler_free; object_handlers_rfc3986_uri.clone_obj = php_uri_object_handler_clone; + php_uri_ce_rfc3986_uri_type = register_class_Uri_Rfc3986_UriType(); + php_uri_ce_rfc3986_uri_host_type = register_class_Uri_Rfc3986_UriHostType(); + php_uri_ce_whatwg_url = register_class_Uri_WhatWg_Url(); php_uri_ce_whatwg_url->create_object = php_uri_object_create_whatwg; php_uri_ce_whatwg_url->default_object_handlers = &object_handlers_whatwg_uri; @@ -1091,6 +1137,7 @@ static PHP_MINIT_FUNCTION(uri) php_uri_ce_error = register_class_Uri_UriError(zend_ce_error); php_uri_ce_invalid_uri_exception = register_class_Uri_InvalidUriException(php_uri_ce_exception); php_uri_ce_whatwg_invalid_url_exception = register_class_Uri_WhatWg_InvalidUrlException(php_uri_ce_invalid_uri_exception); + php_uri_ce_whatwg_url_host_type = register_class_Uri_WhatWg_UrlHostType(); php_uri_ce_whatwg_url_validation_error = register_class_Uri_WhatWg_UrlValidationError(); php_uri_ce_whatwg_url_validation_error_type = register_class_Uri_WhatWg_UrlValidationErrorType(); diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 6d4b2c3517a5..b0b83fcf83ec 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -29,6 +29,22 @@ enum UriComparisonMode } namespace Uri\Rfc3986 { + enum UriType + { + case AbsolutePathReference; + case RelativePathReference; + case NetworkPathReference; + case Uri; + } + + enum UriHostType + { + case IPv4; + case IPv6; + case IPvFuture; + case RegisteredName; + } + /** @strict-properties */ final readonly class Uri { @@ -36,6 +52,8 @@ public static function parse(string $uri, ?\Uri\Rfc3986\Uri $baseUrl = null): ?s public function __construct(string $uri, ?\Uri\Rfc3986\Uri $baseUrl = null) {} + public function getUriType(): ?\Uri\Rfc3986\UriType {} + public function getScheme(): ?string {} public function getRawScheme(): ?string {} @@ -60,6 +78,8 @@ public function getHost(): ?string {} public function getRawHost(): ?string {} + public function getHostType(): ?\Uri\Rfc3986\UriHostType {} + public function withHost(?string $host): static {} public function getPort(): ?int {} @@ -152,6 +172,15 @@ enum UrlValidationErrorType public function __construct(string $context, \Uri\WhatWg\UrlValidationErrorType $type, bool $failure) {} } + enum UrlHostType + { + case IPv4; + case IPv6; + case Domain; + case Opaque; + case Empty; + } + /** @strict-properties */ final readonly class Url { @@ -165,6 +194,8 @@ public function getScheme(): string {} public function withScheme(string $scheme): static {} + public function isSpecialScheme(): bool {} + /** @implementation-alias Uri\Rfc3986\Uri::getUsername */ public function getUsername(): ?string {} @@ -179,6 +210,8 @@ public function getAsciiHost(): ?string {} public function getUnicodeHost(): ?string {} + public function getHostType(): ?\Uri\WhatWg\UrlHostType {} + /** @implementation-alias Uri\Rfc3986\Uri::withHost */ public function withHost(?string $host): static {} diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 18d7f4adf783..0fb464ee74aa 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: 3c228f4227e7543be5c12c99074789d92c27ab99 + * Stub hash: a3b4696ac001d537cc34b818715c7eb382c17c5b * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_parse, 0, 1, IS_STATIC, 1) @@ -12,6 +12,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_Rfc3986_Uri___construct, 0, 0, 1) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\Rfc3986\\\125ri, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getUriType, 0, 0, Uri\\Rfc3986\\\125riType, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getScheme, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -41,6 +44,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawHost arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getHostType, 0, 0, Uri\\Rfc3986\\\125riHostType, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withHost, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -130,6 +136,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_isSpecialScheme, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_WhatWg_Url_getUsername arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withUsername, 0, 1, IS_STATIC, 0) @@ -146,6 +155,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_WhatWg_Url_getHostType, 0, 0, Uri\\WhatWg\\\125rlHostType, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_WhatWg_Url_withHost arginfo_class_Uri_Rfc3986_Uri_withHost #define arginfo_class_Uri_WhatWg_Url_getPort arginfo_class_Uri_Rfc3986_Uri_getPort @@ -186,6 +198,7 @@ ZEND_END_ARG_INFO() ZEND_METHOD(Uri_Rfc3986_Uri, parse); ZEND_METHOD(Uri_Rfc3986_Uri, __construct); +ZEND_METHOD(Uri_Rfc3986_Uri, getUriType); ZEND_METHOD(Uri_Rfc3986_Uri, getScheme); ZEND_METHOD(Uri_Rfc3986_Uri, getRawScheme); ZEND_METHOD(Uri_Rfc3986_Uri, withScheme); @@ -198,6 +211,7 @@ ZEND_METHOD(Uri_Rfc3986_Uri, getPassword); ZEND_METHOD(Uri_Rfc3986_Uri, getRawPassword); ZEND_METHOD(Uri_Rfc3986_Uri, getHost); ZEND_METHOD(Uri_Rfc3986_Uri, getRawHost); +ZEND_METHOD(Uri_Rfc3986_Uri, getHostType); ZEND_METHOD(Uri_Rfc3986_Uri, withHost); ZEND_METHOD(Uri_Rfc3986_Uri, getPort); ZEND_METHOD(Uri_Rfc3986_Uri, withPort); @@ -223,10 +237,12 @@ ZEND_METHOD(Uri_WhatWg_Url, parse); ZEND_METHOD(Uri_WhatWg_Url, __construct); ZEND_METHOD(Uri_WhatWg_Url, getScheme); ZEND_METHOD(Uri_WhatWg_Url, withScheme); +ZEND_METHOD(Uri_WhatWg_Url, isSpecialScheme); ZEND_METHOD(Uri_WhatWg_Url, withUsername); ZEND_METHOD(Uri_WhatWg_Url, withPassword); ZEND_METHOD(Uri_WhatWg_Url, getAsciiHost); ZEND_METHOD(Uri_WhatWg_Url, getUnicodeHost); +ZEND_METHOD(Uri_WhatWg_Url, getHostType); ZEND_METHOD(Uri_WhatWg_Url, equals); ZEND_METHOD(Uri_WhatWg_Url, toAsciiString); ZEND_METHOD(Uri_WhatWg_Url, toUnicodeString); @@ -238,6 +254,7 @@ ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { ZEND_ME(Uri_Rfc3986_Uri, parse, arginfo_class_Uri_Rfc3986_Uri_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(Uri_Rfc3986_Uri, __construct, arginfo_class_Uri_Rfc3986_Uri___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getUriType, arginfo_class_Uri_Rfc3986_Uri_getUriType, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getScheme, arginfo_class_Uri_Rfc3986_Uri_getScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawScheme, arginfo_class_Uri_Rfc3986_Uri_getRawScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, withScheme, arginfo_class_Uri_Rfc3986_Uri_withScheme, ZEND_ACC_PUBLIC) @@ -250,6 +267,7 @@ static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { ZEND_ME(Uri_Rfc3986_Uri, getRawPassword, arginfo_class_Uri_Rfc3986_Uri_getRawPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getHost, arginfo_class_Uri_Rfc3986_Uri_getHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawHost, arginfo_class_Uri_Rfc3986_Uri_getRawHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getHostType, arginfo_class_Uri_Rfc3986_Uri_getHostType, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, withHost, arginfo_class_Uri_Rfc3986_Uri_withHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getPort, arginfo_class_Uri_Rfc3986_Uri_getPort, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, withPort, arginfo_class_Uri_Rfc3986_Uri_withPort, ZEND_ACC_PUBLIC) @@ -287,12 +305,14 @@ static const zend_function_entry class_Uri_WhatWg_Url_methods[] = { ZEND_ME(Uri_WhatWg_Url, __construct, arginfo_class_Uri_WhatWg_Url___construct, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getScheme, arginfo_class_Uri_WhatWg_Url_getScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, withScheme, arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, isSpecialScheme, arginfo_class_Uri_WhatWg_Url_isSpecialScheme, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("getUsername", zim_Uri_Rfc3986_Uri_getUsername, arginfo_class_Uri_WhatWg_Url_getUsername, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withUsername, arginfo_class_Uri_WhatWg_Url_withUsername, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("getPassword", zim_Uri_Rfc3986_Uri_getPassword, arginfo_class_Uri_WhatWg_Url_getPassword, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withPassword, arginfo_class_Uri_WhatWg_Url_withPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getAsciiHost, arginfo_class_Uri_WhatWg_Url_getAsciiHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getUnicodeHost, arginfo_class_Uri_WhatWg_Url_getUnicodeHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getHostType, arginfo_class_Uri_WhatWg_Url_getHostType, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("withHost", zim_Uri_Rfc3986_Uri_withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getPort", zim_Uri_Rfc3986_Uri_getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("withPort", zim_Uri_Rfc3986_Uri_withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC, NULL, NULL) @@ -353,6 +373,36 @@ static zend_class_entry *register_class_Uri_UriComparisonMode(void) return class_entry; } +static zend_class_entry *register_class_Uri_Rfc3986_UriType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\Rfc3986\\UriType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "AbsolutePathReference", NULL); + + zend_enum_add_case_cstr(class_entry, "RelativePathReference", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkPathReference", NULL); + + zend_enum_add_case_cstr(class_entry, "Uri", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_Uri_Rfc3986_UriHostType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\Rfc3986\\UriHostType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "IPv4", NULL); + + zend_enum_add_case_cstr(class_entry, "IPv6", NULL); + + zend_enum_add_case_cstr(class_entry, "IPvFuture", NULL); + + zend_enum_add_case_cstr(class_entry, "RegisteredName", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Uri_Rfc3986_Uri(void) { zend_class_entry ce, *class_entry; @@ -474,6 +524,23 @@ static zend_class_entry *register_class_Uri_WhatWg_UrlValidationError(void) return class_entry; } +static zend_class_entry *register_class_Uri_WhatWg_UrlHostType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\WhatWg\\UrlHostType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "IPv4", NULL); + + zend_enum_add_case_cstr(class_entry, "IPv6", NULL); + + zend_enum_add_case_cstr(class_entry, "Domain", NULL); + + zend_enum_add_case_cstr(class_entry, "Opaque", NULL); + + zend_enum_add_case_cstr(class_entry, "Empty", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Uri_WhatWg_Url(void) { zend_class_entry ce, *class_entry; diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index 65e9cff89a15..f83327690dd4 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -18,6 +18,8 @@ #include "php_uri_decl.h" extern zend_class_entry *php_uri_ce_rfc3986_uri; +extern zend_class_entry *php_uri_ce_rfc3986_uri_type; +extern zend_class_entry *php_uri_ce_rfc3986_uri_host_type; extern zend_class_entry *php_uri_ce_whatwg_url; extern zend_class_entry *php_uri_ce_comparison_mode; extern zend_class_entry *php_uri_ce_exception; @@ -26,6 +28,7 @@ extern zend_class_entry *php_uri_ce_invalid_uri_exception; extern zend_class_entry *php_uri_ce_whatwg_invalid_url_exception; extern zend_class_entry *php_uri_ce_whatwg_url_validation_error_type; extern zend_class_entry *php_uri_ce_whatwg_url_validation_error; +extern zend_class_entry *php_uri_ce_whatwg_url_host_type; typedef enum php_uri_recomposition_mode { PHP_URI_RECOMPOSITION_MODE_RAW_ASCII, @@ -146,9 +149,7 @@ typedef struct php_uri_object { zend_object std; } php_uri_object; -static inline php_uri_object *php_uri_object_from_obj(zend_object *object) { - return ZEND_CONTAINER_OF(object, php_uri_object, std); -} +#define php_uri_object_from_obj(object) ZEND_CONTAINER_OF(object, php_uri_object, std) #define Z_URI_OBJECT_P(zv) php_uri_object_from_obj(Z_OBJ_P((zv))) diff --git a/ext/uri/php_uri_decl.h b/ext/uri/php_uri_decl.h index 3c069f3abe6d..d1fd58d04b2c 100644 --- a/ext/uri/php_uri_decl.h +++ b/ext/uri/php_uri_decl.h @@ -1,14 +1,28 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: 3c228f4227e7543be5c12c99074789d92c27ab99 */ + * Stub hash: a3b4696ac001d537cc34b818715c7eb382c17c5b */ -#ifndef ZEND_PHP_URI_DECL_3c228f4227e7543be5c12c99074789d92c27ab99_H -#define ZEND_PHP_URI_DECL_3c228f4227e7543be5c12c99074789d92c27ab99_H +#ifndef ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H +#define ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H typedef enum zend_enum_Uri_UriComparisonMode { ZEND_ENUM_Uri_UriComparisonMode_IncludeFragment = 1, ZEND_ENUM_Uri_UriComparisonMode_ExcludeFragment = 2, } zend_enum_Uri_UriComparisonMode; +typedef enum zend_enum_Uri_Rfc3986_UriType { + ZEND_ENUM_Uri_Rfc3986_UriType_AbsolutePathReference = 1, + ZEND_ENUM_Uri_Rfc3986_UriType_RelativePathReference = 2, + ZEND_ENUM_Uri_Rfc3986_UriType_NetworkPathReference = 3, + ZEND_ENUM_Uri_Rfc3986_UriType_Uri = 4, +} zend_enum_Uri_Rfc3986_UriType; + +typedef enum zend_enum_Uri_Rfc3986_UriHostType { + ZEND_ENUM_Uri_Rfc3986_UriHostType_IPv4 = 1, + ZEND_ENUM_Uri_Rfc3986_UriHostType_IPv6 = 2, + ZEND_ENUM_Uri_Rfc3986_UriHostType_IPvFuture = 3, + ZEND_ENUM_Uri_Rfc3986_UriHostType_RegisteredName = 4, +} zend_enum_Uri_Rfc3986_UriHostType; + typedef enum zend_enum_Uri_WhatWg_UrlValidationErrorType { ZEND_ENUM_Uri_WhatWg_UrlValidationErrorType_DomainToAscii = 1, ZEND_ENUM_Uri_WhatWg_UrlValidationErrorType_DomainToUnicode = 2, @@ -41,4 +55,12 @@ typedef enum zend_enum_Uri_WhatWg_UrlValidationErrorType { ZEND_ENUM_Uri_WhatWg_UrlValidationErrorType_FileInvalidWindowsDriveLetterHost = 29, } zend_enum_Uri_WhatWg_UrlValidationErrorType; -#endif /* ZEND_PHP_URI_DECL_3c228f4227e7543be5c12c99074789d92c27ab99_H */ +typedef enum zend_enum_Uri_WhatWg_UrlHostType { + ZEND_ENUM_Uri_WhatWg_UrlHostType_IPv4 = 1, + ZEND_ENUM_Uri_WhatWg_UrlHostType_IPv6 = 2, + ZEND_ENUM_Uri_WhatWg_UrlHostType_Domain = 3, + ZEND_ENUM_Uri_WhatWg_UrlHostType_Opaque = 4, + ZEND_ENUM_Uri_WhatWg_UrlHostType_Empty = 5, +} zend_enum_Uri_WhatWg_UrlHostType; + +#endif /* ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H */ diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt new file mode 100644 index 000000000000..0ac7ea3f7efd --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - IP future +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::IPvFuture) diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt new file mode 100644 index 000000000000..1c0c4448b953 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - IPv4 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::IPv4) diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt new file mode 100644 index 000000000000..5f3d826b412a --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - IPv6 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::IPv6) diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt new file mode 100644 index 000000000000..3a0e0205befe --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - none +--FILE-- +getHostType()); + +?> +--EXPECT-- +NULL diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt new file mode 100644 index 000000000000..31d4aca36c82 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - registered name +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::RegisteredName) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_absolute_path_reference.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_absolute_path_reference.phpt new file mode 100644 index 000000000000..1cddc15fa33c --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_absolute_path_reference.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - Absolute path reference +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::AbsolutePathReference) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_network_path_reference.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_network_path_reference.phpt new file mode 100644 index 000000000000..d7debc5ba66c --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_network_path_reference.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - Network path reference +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::NetworkPathReference) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_relative_path_reference.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_relative_path_reference.phpt new file mode 100644 index 000000000000..6cb51405f925 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_relative_path_reference.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - Relative path reference +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::RelativePathReference) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_uri_basic.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_basic.phpt new file mode 100644 index 000000000000..262300103f9c --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_basic.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - URI +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::Uri) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_uri_empty_authority.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_empty_authority.phpt new file mode 100644 index 000000000000..ad8f2f27a8c6 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_empty_authority.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - URI with empty authority +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::Uri) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_uri_no_authority.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_no_authority.phpt new file mode 100644 index 000000000000..e266502ce90e --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_no_authority.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - URI without authority +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::Uri) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt b/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt new file mode 100644 index 000000000000..21fc961e5541 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - registered name +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::Domain) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt b/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt new file mode 100644 index 000000000000..41351350fb61 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - empty +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::Empty) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt b/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt new file mode 100644 index 000000000000..f2f63caabb5e --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - IPv4 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::IPv4) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt b/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt new file mode 100644 index 000000000000..ce5bb00b93d5 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - IPv6 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::IPv6) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_none.phpt b/ext/uri/tests/whatwg/getters/host_type_success_none.phpt new file mode 100644 index 000000000000..07d54412a23c --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_none.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - none +--FILE-- +getHostType()); + +?> +--EXPECT-- +NULL diff --git a/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt b/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt new file mode 100644 index 000000000000..844cba82d512 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - opaque +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::Opaque) diff --git a/ext/uri/tests/whatwg/getters/is_special_scheme_success_false.phpt b/ext/uri/tests/whatwg/getters/is_special_scheme_success_false.phpt new file mode 100644 index 000000000000..9bbb85755843 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/is_special_scheme_success_false.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url::isSpecialScheme() - success - not special +--FILE-- +isSpecialScheme()); + +?> +--EXPECT-- +bool(false) diff --git a/ext/uri/tests/whatwg/getters/is_special_scheme_success_true.phpt b/ext/uri/tests/whatwg/getters/is_special_scheme_success_true.phpt new file mode 100644 index 000000000000..1e19641b5600 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/is_special_scheme_success_true.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test Uri\WhatWg\Url::isSpecialScheme() - success - special +--FILE-- +isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("https://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("ws://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("wss://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("ftp://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("file://example.com"); +var_dump($url->isSpecialScheme()); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index 172d7f08f144..4e2c5656aa77 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -15,6 +15,7 @@ #include "php.h" #include "uri_parser_rfc3986.h" #include "php_uri_common.h" +#include "Zend/zend_enum.h" #include "Zend/zend_smart_str.h" #include "Zend/zend_exceptions.h" @@ -23,6 +24,7 @@ struct php_uri_parser_rfc3986_uris { UriUriA uri; UriUriA normalized_uri; + unsigned int normalization_mask; bool normalized_uri_initialized; }; @@ -83,12 +85,21 @@ ZEND_ATTRIBUTE_NONNULL static void copy_uri(UriUriA *new_uriparser_uri, const Ur ZEND_ATTRIBUTE_NONNULL static UriUriA *get_normalized_uri(php_uri_parser_rfc3986_uris *uriparser_uris) { if (!uriparser_uris->normalized_uri_initialized) { - copy_uri(&uriparser_uris->normalized_uri, &uriparser_uris->uri); - int result = uriNormalizeSyntaxExMmA(&uriparser_uris->normalized_uri, (unsigned int)-1, mm); - ZEND_ASSERT(result == URI_SUCCESS); + int mask_result = uriNormalizeSyntaxMaskRequiredExA(&uriparser_uris->uri, &uriparser_uris->normalization_mask); + ZEND_ASSERT(mask_result == URI_SUCCESS); + + if (uriparser_uris->normalization_mask != URI_NORMALIZED) { + copy_uri(&uriparser_uris->normalized_uri, &uriparser_uris->uri); + int result = uriNormalizeSyntaxExMmA(&uriparser_uris->normalized_uri, uriparser_uris->normalization_mask, mm); + ZEND_ASSERT(result == URI_SUCCESS); + } uriparser_uris->normalized_uri_initialized = true; } + if (uriparser_uris->normalization_mask == URI_NORMALIZED) { + return &uriparser_uris->uri; + } + return &uriparser_uris->normalized_uri; } @@ -110,6 +121,25 @@ ZEND_ATTRIBUTE_NONNULL static UriUriA *get_uri_for_writing(php_uri_parser_rfc398 return &uriparser_uris->uri; } +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval) +{ + const UriUriA *uriparser_uri = get_uri_for_reading(uri, PHP_URI_COMPONENT_READ_MODE_RAW); + + const char *type; + + if (has_text_range(&uriparser_uri->scheme)) { + type = "Uri"; + } else if (has_text_range(&uriparser_uri->hostText)) { + type = "NetworkPathReference"; + } else if (uriparser_uri->absolutePath) { + type = "AbsolutePathReference"; + } else { + type = "RelativePathReference"; + } + + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_rfc3986_uri_type, type)); +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -251,6 +281,30 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(void return SUCCESS; } +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval) +{ + const UriUriA *uriparser_uri = get_uri_for_reading(uri, PHP_URI_COMPONENT_READ_MODE_RAW); + + if (!has_text_range(&uriparser_uri->hostText)) { + ZVAL_NULL(retval); + return; + } + + const char *type; + + if (uriparser_uri->hostData.ip4 != NULL) { + type = "IPv4"; + } else if (uriparser_uri->hostData.ip6 != NULL) { + type = "IPv6"; + } else if (has_text_range(&uriparser_uri->hostData.ipFuture)) { + type = "IPvFuture"; + } else { + type = "RegisteredName"; + } + + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_rfc3986_uri_host_type, type)); +} + static zend_result php_uri_parser_rfc3986_host_write(void *uri, zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); diff --git a/ext/uri/uri_parser_rfc3986.h b/ext/uri/uri_parser_rfc3986.h index 21f938c370ad..633dd72062f2 100644 --- a/ext/uri/uri_parser_rfc3986.h +++ b/ext/uri/uri_parser_rfc3986.h @@ -21,6 +21,9 @@ PHPAPI extern const php_uri_parser php_uri_parser_rfc3986; typedef struct php_uri_parser_rfc3986_uris php_uri_parser_rfc3986_uris; +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval); +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval); + zend_result php_uri_parser_rfc3986_userinfo_read(php_uri_parser_rfc3986_uris *uri, php_uri_component_read_mode read_mode, zval *retval); zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *uri, zval *value, zval *errors); diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index d140357c18ed..f4e148704004 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -273,6 +273,11 @@ static zend_result php_uri_parser_whatwg_scheme_write(void *uri, zval *value, zv return SUCCESS; } +ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_whatwg_is_special(const lxb_url_t *lexbor_uri) +{ + return lxb_url_is_special(lexbor_uri); +} + /* 4.2. URL miscellaneous: A URL includes credentials if its username or password is not the empty string. */ static bool includes_credentials(const lxb_url_t *lexbor_uri) { @@ -381,6 +386,31 @@ static zend_result php_uri_parser_whatwg_host_read(void *uri, php_uri_component_ return SUCCESS; } +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t *lexbor_uri, zval *retval) +{ + switch (lexbor_uri->host.type) { + case LXB_URL_HOST_TYPE_IPV4: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "IPv4")); + return; + case LXB_URL_HOST_TYPE_IPV6: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "IPv6")); + return; + case LXB_URL_HOST_TYPE_DOMAIN: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Domain")); + return; + case LXB_URL_HOST_TYPE_EMPTY: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Empty")); + return; + case LXB_URL_HOST_TYPE_OPAQUE: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Opaque")); + return; + case LXB_URL_HOST_TYPE__UNDEF: + ZVAL_NULL(retval); + return; + default: ZEND_UNREACHABLE(); + } +} + static zend_result php_uri_parser_whatwg_host_write(void *uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; diff --git a/ext/uri/uri_parser_whatwg.h b/ext/uri/uri_parser_whatwg.h index 3e9e2824e42b..f714ee483680 100644 --- a/ext/uri/uri_parser_whatwg.h +++ b/ext/uri/uri_parser_whatwg.h @@ -20,6 +20,9 @@ PHPAPI extern const php_uri_parser php_uri_parser_whatwg; +ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_whatwg_is_special(const lxb_url_t *lexbor_uri); +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t *lexbor_uri, zval *retval); + lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent); PHP_RINIT_FUNCTION(uri_parser_whatwg); diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 5bc502f2ef76..d1bb4bbda23f 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -295,9 +295,7 @@ static void xml_xmlchar_zval(const XML_Char *s, int len, const XML_Char *encodin } /* }}} */ -static inline xml_parser *xml_parser_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, xml_parser, std); -} +#define xml_parser_from_obj(obj) ZEND_CONTAINER_OF(obj, xml_parser, std) #define Z_XMLPARSER_P(zv) xml_parser_from_obj(Z_OBJ_P(zv)) diff --git a/ext/xmlreader/php_xmlreader.h b/ext/xmlreader/php_xmlreader.h index 161d5eddc0c9..d33938f40506 100644 --- a/ext/xmlreader/php_xmlreader.h +++ b/ext/xmlreader/php_xmlreader.h @@ -44,9 +44,7 @@ typedef struct _xmlreader_object { zend_object std; } xmlreader_object; -static inline xmlreader_object *php_xmlreader_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, xmlreader_object, std); -} +#define php_xmlreader_fetch_object(obj) ZEND_CONTAINER_OF(obj, xmlreader_object, std) #define Z_XMLREADER_P(zv) php_xmlreader_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/xmlreader/tests/012.phpt b/ext/xmlreader/tests/012.phpt index b10421d2624a..e00f37b82667 100644 --- a/ext/xmlreader/tests/012.phpt +++ b/ext/xmlreader/tests/012.phpt @@ -2,6 +2,10 @@ XMLReader: accessing empty and non existing attributes --EXTENSIONS-- xmlreader +--SKIPIF-- + --FILE-- +--EXPECTF-- +Notice: Only variable references should be returned by reference in %s on line %d diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index a8289a41a304..cb2389ca4370 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -573,8 +573,12 @@ static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */ } /* }}} */ -/* Close and free the zip_t */ -static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */ +/* Close and free the zip_t. If the archive was opened as a string, the + * final contents of the archive will be assigned to *out_str and that + * string will afterwards be owned by the caller. + * + * If out_str is NULL, the final string contents, if any, will be discarded. */ +static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ */ { struct zip *intern = obj->za; bool success = false; @@ -606,7 +610,19 @@ static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */ obj->filename_len = 0; } + if (obj->out_str) { + if (out_str) { + *out_str = obj->out_str; + } else { + zend_string_release(obj->out_str); + } + obj->out_str = NULL; + } else { + ZEND_ASSERT(!out_str); + } + obj->za = NULL; + obj->from_string = false; return success; } /* }}} */ @@ -1060,7 +1076,7 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */ { ze_zip_object * intern = php_zip_fetch_object(object); - php_zipobj_close(intern); + php_zipobj_close(intern, NULL); #ifdef HAVE_PROGRESS_CALLBACK /* if not properly called by libzip */ @@ -1467,7 +1483,7 @@ PHP_METHOD(ZipArchive, open) } /* If we already have an opened zip, free it */ - php_zipobj_close(ze_obj); + php_zipobj_close(ze_obj, NULL); /* open for write without option to empty the archive */ if ((flags & (ZIP_TRUNCATE | ZIP_RDONLY)) == 0) { @@ -1491,28 +1507,34 @@ PHP_METHOD(ZipArchive, open) ze_obj->filename = resolved_path; ze_obj->filename_len = strlen(resolved_path); ze_obj->za = intern; + ze_obj->from_string = false; RETURN_TRUE; } /* }}} */ -/* {{{ Create new read-only zip using given string */ +/* {{{ Create new zip from a string, or a create an empty zip to be saved to a string */ PHP_METHOD(ZipArchive, openString) { - zend_string *buffer; + zend_string *buffer = NULL; + zend_long flags = 0; zval *self = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &buffer) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &buffer, &flags) == FAILURE) { RETURN_THROWS(); } + if (!buffer) { + buffer = ZSTR_EMPTY_ALLOC(); + } + ze_zip_object *ze_obj = Z_ZIP_P(self); - php_zipobj_close(ze_obj); + php_zipobj_close(ze_obj, NULL); zip_error_t err; zip_error_init(&err); - zip_source_t * zip_source = php_zip_create_string_source(buffer, NULL, &err); + zip_source_t * zip_source = php_zip_create_string_source(buffer, &ze_obj->out_str, &err); if (!zip_source) { ze_obj->err_zip = zip_error_code_zip(&err); @@ -1521,7 +1543,7 @@ PHP_METHOD(ZipArchive, openString) RETURN_LONG(ze_obj->err_zip); } - struct zip *intern = zip_open_from_source(zip_source, ZIP_RDONLY, &err); + struct zip *intern = zip_open_from_source(zip_source, flags, &err); if (!intern) { ze_obj->err_zip = zip_error_code_zip(&err); ze_obj->err_sys = zip_error_code_system(&err); @@ -1530,6 +1552,7 @@ PHP_METHOD(ZipArchive, openString) RETURN_LONG(ze_obj->err_zip); } + ze_obj->from_string = true; ze_obj->za = intern; zip_error_fini(&err); RETURN_TRUE; @@ -1568,7 +1591,34 @@ PHP_METHOD(ZipArchive, close) ZIP_FROM_OBJECT(intern, self); - RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self))); + RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self), NULL)); +} +/* }}} */ + +/* {{{ close the zip archive and get the result as a string */ +PHP_METHOD(ZipArchive, closeString) +{ + struct zip *intern; + zval *self = ZEND_THIS; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZIP_FROM_OBJECT(intern, self); + + if (!Z_ZIP_P(self)->from_string) { + zend_throw_error(NULL, "ZipArchive::closeString can only be called on " + "an archive opened with ZipArchive::openString"); + RETURN_THROWS(); + } + + zend_string *ret = NULL; + bool success = php_zipobj_close(Z_ZIP_P(self), &ret); + ZEND_ASSERT(ret); + if (success) { + RETURN_STR(ret); + } + zend_string_release(ret); + RETURN_FALSE; } /* }}} */ diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index cdd72ba36367..9e57a54de1cc 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -69,6 +69,8 @@ typedef struct _ze_zip_object { HashTable *prop_handler; char *filename; size_t filename_len; + zend_string *out_str; + bool from_string; zip_int64_t last_id; int err_zip; int err_sys; @@ -81,9 +83,7 @@ typedef struct _ze_zip_object { zend_object zo; } ze_zip_object; -static inline ze_zip_object *php_zip_fetch_object(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, ze_zip_object, zo); -} +#define php_zip_fetch_object(obj) ZEND_CONTAINER_OF(obj, ze_zip_object, zo) #define Z_ZIP_P(zv) php_zip_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/zip/php_zip.stub.php b/ext/zip/php_zip.stub.php index 19ea67e07fba..49dd19e53553 100644 --- a/ext/zip/php_zip.stub.php +++ b/ext/zip/php_zip.stub.php @@ -646,7 +646,7 @@ class ZipArchive implements Countable /** @tentative-return-type */ public function open(string $filename, int $flags = 0): bool|int {} - public function openString(string $data): bool|int {} + public function openString(string $data = '', int $flags = 0): bool|int {} /** * @tentative-return-type @@ -656,6 +656,8 @@ public function setPassword(#[\SensitiveParameter] string $password): bool {} /** @tentative-return-type */ public function close(): bool {} + public function closeString(): string|false {} + /** @tentative-return-type */ public function count(): int {} diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index ae2569400efe..faa6feb1cb1e 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_zip.stub.php instead. - * Stub hash: bf6706496639628a3287d0026f68f57ecebc4a55 */ + * Stub hash: d623efdfe5ac46f07aebf8fb120050c818f3d793 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -45,8 +45,9 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_open, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_openString, 0, 1, MAY_BE_BOOL|MAY_BE_LONG) - ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_openString, 0, 0, MAY_BE_BOOL|MAY_BE_LONG) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, "\'\'") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_setPassword, 0, 1, _IS_BOOL, 0) @@ -56,6 +57,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_close, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_closeString, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -320,6 +324,7 @@ ZEND_METHOD(ZipArchive, open); ZEND_METHOD(ZipArchive, openString); ZEND_METHOD(ZipArchive, setPassword); ZEND_METHOD(ZipArchive, close); +ZEND_METHOD(ZipArchive, closeString); ZEND_METHOD(ZipArchive, count); ZEND_METHOD(ZipArchive, getStatusString); ZEND_METHOD(ZipArchive, clearError); @@ -399,6 +404,7 @@ static const zend_function_entry class_ZipArchive_methods[] = { ZEND_ME(ZipArchive, openString, arginfo_class_ZipArchive_openString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, setPassword, arginfo_class_ZipArchive_setPassword, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, close, arginfo_class_ZipArchive_close, ZEND_ACC_PUBLIC) + ZEND_ME(ZipArchive, closeString, arginfo_class_ZipArchive_closeString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, count, arginfo_class_ZipArchive_count, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, getStatusString, arginfo_class_ZipArchive_getStatusString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, clearError, arginfo_class_ZipArchive_clearError, ZEND_ACC_PUBLIC) diff --git a/ext/zip/tests/ZipArchive_closeString_basic.phpt b/ext/zip/tests/ZipArchive_closeString_basic.phpt new file mode 100644 index 000000000000..852a7ebf53f3 --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +ZipArchive::closeString() basic +--EXTENSIONS-- +zip +--FILE-- +openString(); +$zip->addFromString('test1', '1'); +$zip->addFromString('test2', '2'); +$contents = $zip->closeString(); +echo $contents ? "OK\n" : "FAILED\n"; + +$zip = new ZipArchive(); +$zip->openString($contents); +var_dump($zip->getFromName('test1')); +var_dump($zip->getFromName('test2')); +var_dump($zip->getFromName('nonexistent')); + +?> +--EXPECT-- +OK +string(1) "1" +string(1) "2" +bool(false) diff --git a/ext/zip/tests/ZipArchive_closeString_error.phpt b/ext/zip/tests/ZipArchive_closeString_error.phpt new file mode 100644 index 000000000000..d5dca14a97fc --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_error.phpt @@ -0,0 +1,47 @@ +--TEST-- +ZipArchive::closeString() error cases +--EXTENSIONS-- +zip +--FILE-- +openString(); +var_dump($zip->open(__DIR__ . '/test.zip')); +try { + $zip->closeString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +echo "2.\n"; +$zip = new ZipArchive(); +$zip->openString('...'); +echo $zip->getStatusString() . "\n"; +try { + $zip->closeString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +echo "3.\n"; +$zip = new ZipArchive(); +$zip->openString(file_get_contents(__DIR__ . '/test.zip')); +echo gettype($zip->closeString()) . "\n"; +try { + $zip->closeString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +1. +bool(true) +ZipArchive::closeString can only be called on an archive opened with ZipArchive::openString +2. +Not a zip archive +Invalid or uninitialized Zip object +3. +string +Invalid or uninitialized Zip object diff --git a/ext/zip/tests/ZipArchive_closeString_false.phpt b/ext/zip/tests/ZipArchive_closeString_false.phpt new file mode 100644 index 000000000000..8a7d942527f6 --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_false.phpt @@ -0,0 +1,22 @@ +--TEST-- +ZipArchive::closeString() false return +--EXTENSIONS-- +zip +--FILE-- +openString($input)); +$zip->setCompressionIndex(0, ZipArchive::CM_DEFLATE); +var_dump($zip->closeString()); +echo $zip->getStatusString() . "\n"; +?> +--EXPECTREGEX-- +bool\(true\) + +Warning: ZipArchive::closeString\(\): (Zip archive inconsistent|Unexpected length of data).* +bool\(false\) +(Zip archive inconsistent|Unexpected length of data) diff --git a/ext/zip/tests/ZipArchive_closeString_variation.phpt b/ext/zip/tests/ZipArchive_closeString_variation.phpt new file mode 100644 index 000000000000..a1922ebd8f1f --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_variation.phpt @@ -0,0 +1,39 @@ +--TEST-- +ZipArchive::closeString() variations +--EXTENSIONS-- +zip +--FILE-- +openString(); +var_dump($zip->closeString()); +echo $zip->getStatusString() . "\n"; + +echo "2. Update existing archive\n"; +$input = file_get_contents(__DIR__ . '/test.zip'); +$zip = new ZipArchive(); +$zip->openString($input); +$zip->addFromString('entry1.txt', ''); +$result = $zip->closeString(); +echo gettype($result) . "\n"; +var_dump($input !== $result); + +echo "3. Unchanged existing archive\n"; +$zip = new ZipArchive(); +$zip->openString($input); +$result = $zip->closeString(); +echo gettype($result) . "\n"; +var_dump($input === $result); + +?> +--EXPECT-- +1. Empty archive creation +string(0) "" +No error +2. Update existing archive +string +bool(true) +3. Unchanged existing archive +string +bool(true) diff --git a/ext/zip/tests/ZipArchive_openString.phpt b/ext/zip/tests/ZipArchive_openString.phpt index f787b4a84933..2a11505fcf69 100644 --- a/ext/zip/tests/ZipArchive_openString.phpt +++ b/ext/zip/tests/ZipArchive_openString.phpt @@ -4,8 +4,10 @@ ZipArchive::openString() method zip --FILE-- openString(file_get_contents(__DIR__."/test_procedural.zip")); +$zip->openString($input, ZipArchive::RDONLY); for ($i = 0; $i < $zip->numFiles; $i++) { $stat = $zip->statIndex($i); @@ -17,8 +19,22 @@ var_dump($zip->addFromString("foobar/baz", "baz")); var_dump($zip->addEmptyDir("blub")); var_dump($zip->close()); + +echo "2. CREATE and EXCL flags\n"; +$zip = new ZipArchive(); +var_dump($zip->openString($input, ZipArchive::CREATE)); +var_dump($zip->openString($input, ZipArchive::EXCL)); +echo $zip->getStatusString() . "\n"; + +echo "3. CHECKCONS flag\n"; +$inconsistent = file_get_contents(__DIR__ . '/checkcons.zip'); +$zip = new ZipArchive(); +var_dump($zip->openString($inconsistent)); +var_dump($zip->openString($inconsistent, ZipArchive::CHECKCONS)); + ?> --EXPECTF-- +1. Open read-only and read directory foo bar foobar/ @@ -26,3 +42,10 @@ foobar/baz bool(false) bool(false) bool(true) +2. CREATE and EXCL flags +bool(true) +int(10) +File already exists +3. CHECKCONS flag +bool(true) +int(%d) diff --git a/ext/zip/tests/checkcons.zip b/ext/zip/tests/checkcons.zip new file mode 100644 index 000000000000..50bcea128a46 Binary files /dev/null and b/ext/zip/tests/checkcons.zip differ diff --git a/ext/zip/tests/wrong-file-size.zip b/ext/zip/tests/wrong-file-size.zip new file mode 100644 index 000000000000..fc9fa1a434c7 Binary files /dev/null and b/ext/zip/tests/wrong-file-size.zip differ diff --git a/ext/zip/zip_source.c b/ext/zip/zip_source.c index 48b35862b903..03fa50867d56 100644 --- a/ext/zip/zip_source.c +++ b/ext/zip/zip_source.c @@ -155,6 +155,7 @@ static zip_int64_t php_zip_string_cb(void *userdata, void *data, zip_uint64_t le ctx->in_str = ctx->out_str; ctx->out_str = ZSTR_EMPTY_ALLOC(); if (ctx->dest) { + zend_string_release(*(ctx->dest)); *(ctx->dest) = zend_string_copy(ctx->in_str); } return 0; @@ -200,5 +201,10 @@ zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest ctx->out_str = ZSTR_EMPTY_ALLOC(); ctx->dest = dest; ctx->mtime = time(NULL); + + if (dest) { + *dest = zend_string_copy(str); + } + return zip_source_function_create(php_zip_string_cb, (void*)ctx, err); } diff --git a/ext/zlib/tests/bug55544-win.phpt b/ext/zlib/tests/bug55544-win.phpt index 11116da28b6d..02a1ac837000 100644 Binary files a/ext/zlib/tests/bug55544-win.phpt and b/ext/zlib/tests/bug55544-win.phpt differ diff --git a/ext/zlib/tests/data/func.inc b/ext/zlib/tests/data/func.inc deleted file mode 100644 index 73cfc4e06ffc..000000000000 --- a/ext/zlib/tests/data/func.inc +++ /dev/null @@ -1,18 +0,0 @@ - (\d+\.\d+\.\d+),s', $info, $match)) { - // $version = $match[1]; - if (preg_match(',zlib(?!.*libXML).*Compiled Version (=> |getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +deflate_init(): Argument #2 ($options) the value for option "level" must be of type int, null given diff --git a/ext/zlib/tests/gzencode_variation2-win32.phpt b/ext/zlib/tests/gzencode_variation2-win32.phpt index 2cc1f982089d..183d67f359b8 100644 --- a/ext/zlib/tests/gzencode_variation2-win32.phpt +++ b/ext/zlib/tests/gzencode_variation2-win32.phpt @@ -7,9 +7,8 @@ zlib if( substr(PHP_OS, 0, 3) != "WIN" ) { die("skip.. only for Windows"); } -include 'data/func.inc'; -if (version_compare(get_zlib_version(), "1.2.11") < 0) { - die("skip - at least zlib 1.2.11 required, got " . get_zlib_version()); +if (version_compare(ZLIB_VERSION, "1.2.11") < 0) { + die("skip - at least zlib 1.2.11 required, got " . ZLIB_VERSION); } ?> --FILE-- diff --git a/ext/zlib/tests/gzgetc_basic.phpt b/ext/zlib/tests/gzgetc_basic.phpt index ce366e4b6b31..3540332a740e 100644 --- a/ext/zlib/tests/gzgetc_basic.phpt +++ b/ext/zlib/tests/gzgetc_basic.phpt @@ -4,8 +4,7 @@ Test function gzgetc() by calling it with its expected arguments zlib 1.2.5 zlib --SKIPIF-- 0) { +if (version_compare(ZLIB_VERSION, '1.2.5') > 0) { die('skip - only for zlib <= 1.2.5'); } ?> diff --git a/ext/zlib/tests/gzgetc_basic_1.phpt b/ext/zlib/tests/gzgetc_basic_1.phpt index cf6da96e5612..82d7c1941327 100644 --- a/ext/zlib/tests/gzgetc_basic_1.phpt +++ b/ext/zlib/tests/gzgetc_basic_1.phpt @@ -4,9 +4,7 @@ Test function gzgetc() by calling it with its expected arguments zlib 1.2.7 zlib --SKIPIF-- = 1.2.7'); } ?> diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 50527ceffa3a..cc2bfd38cc3e 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -47,9 +47,7 @@ ZEND_DECLARE_MODULE_GLOBALS(zlib) zend_class_entry *inflate_context_ce; static zend_object_handlers inflate_context_object_handlers; -static inline php_zlib_context *inflate_context_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_zlib_context, std); -} +#define inflate_context_from_obj(obj) ZEND_CONTAINER_OF(obj, php_zlib_context, std) #define Z_INFLATE_CONTEXT_P(zv) inflate_context_from_obj(Z_OBJ_P(zv)) @@ -85,9 +83,7 @@ static void inflate_context_free_obj(zend_object *object) zend_class_entry *deflate_context_ce; static zend_object_handlers deflate_context_object_handlers; -static inline php_zlib_context *deflate_context_from_obj(zend_object *obj) { - return ZEND_CONTAINER_OF(obj, php_zlib_context, std); -} +#define deflate_context_from_obj(obj) ZEND_CONTAINER_OF(obj, php_zlib_context, std) #define Z_DEFLATE_CONTEXT_P(zv) deflate_context_from_obj(Z_OBJ_P(zv)) @@ -929,6 +925,7 @@ PHP_FUNCTION(inflate_init) } if (inflateInit2(&ctx->Z, encoding) != Z_OK) { + efree(dict); zval_ptr_dtor(return_value); php_error_docref(NULL, E_WARNING, "Failed allocating zlib.inflate context"); RETURN_FALSE; @@ -1174,6 +1171,7 @@ PHP_FUNCTION(deflate_init) } if (deflateInit2(&ctx->Z, level, Z_DEFLATED, encoding, memory, strategy) != Z_OK) { + efree(dict); zval_ptr_dtor(return_value); php_error_docref(NULL, E_WARNING, "Failed allocating zlib.deflate context"); RETURN_FALSE; diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h index f92c7d6fd443..62bebca9b715 100644 --- a/main/fopen_wrappers.h +++ b/main/fopen_wrappers.h @@ -28,9 +28,6 @@ PHPAPI int php_check_open_basedir(const char *path); PHPAPI int php_check_open_basedir_ex(const char *path, int warn); PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path); -/* OPENBASEDIR_CHECKPATH(filename) to ease merge between 6.x and 5.x */ -#define OPENBASEDIR_CHECKPATH(filename) php_check_open_basedir(filename) - PHPAPI zend_string *php_resolve_path(const char *filename, size_t filename_len, const char *path); PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, zend_string **opened_path); diff --git a/main/getopt.c b/main/getopt.c index 5f3a0085cdac..210c32152415 100644 --- a/main/getopt.c +++ b/main/getopt.c @@ -18,15 +18,14 @@ #include #include "php_getopt.h" -#define OPTERRCOLON (1) -#define OPTERRNF (2) -#define OPTERRARG (3) +#define OPTERRCOLON 1 +#define OPTERRNF 2 +#define OPTERRARG 3 // Print error message to stderr and return -2 to distinguish it from '?' command line option. static int php_opt_error(int argc, char * const *argv, int optint, int optchr, int err, int show_err) /* {{{ */ { - if (show_err) - { + if (show_err) { fprintf(stderr, "Error in argument %d, char %d: ", optint, optchr+1); switch(err) { @@ -61,7 +60,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char *optarg = NULL; } - if(prev_optarg && prev_optarg != optarg) { + if (prev_optarg && prev_optarg != optarg) { /* reset the state */ optchr = 0; dash = 0; @@ -69,20 +68,18 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char prev_optarg = optarg; if (*optind >= argc) { - return(EOF); + return EOF; } if (!dash) { - if ((argv[*optind][0] != '-')) { - return(EOF); - } else { - if (!argv[*optind][1]) - { - /* - * use to specify stdin. Need to let pgm process this and - * the following args - */ - return(EOF); - } + if (argv[*optind][0] != '-') { + return EOF; + } + if (!argv[*optind][1]) { + /* + * use to specify stdin. Need to let pgm process this and + * the following args + */ + return EOF; } } if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) { @@ -92,7 +89,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char /* '--' indicates end of args if not followed by a known long option name */ if (argv[*optind][2] == '\0') { (*optind)++; - return(EOF); + return EOF; } arg_start = 2; @@ -109,7 +106,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char php_optidx++; if (opts[php_optidx].opt_char == '-') { (*optind)++; - return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err)); + return php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err); } else if (opts[php_optidx].opt_name && !strncmp(&argv[*optind][2], opts[php_optidx].opt_name, arg_end) && arg_end == strlen(opts[php_optidx].opt_name)) { break; } @@ -127,7 +124,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char if (argv[*optind][optchr] == ':') { dash = 0; (*optind)++; - return (php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err)); + return php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err); } arg_start = 1 + optchr; } @@ -145,7 +142,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char optchr++; arg_start++; } - return(php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err)); + return php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err); } else if (argv[*optind][optchr] == opts[php_optidx].opt_char) { break; } @@ -160,7 +157,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char if (*optind == argc) { /* Was the value required or is it optional? */ if (opts[php_optidx].need_param == 1) { - return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err)); + return php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err); } /* Optional value is not supported with - style */ } else if (opts[php_optidx].need_param == 1) { @@ -178,8 +175,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char } else { /* multiple options specified as one (exclude long opts) */ if (arg_start >= 2 && !((argv[*optind][0] == '-') && (argv[*optind][1] == '-'))) { - if (!argv[*optind][optchr+1]) - { + if (!argv[*optind][optchr+1]) { dash = 0; (*optind)++; } else { @@ -191,6 +187,6 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char return opts[php_optidx].opt_char; } assert(0); - return(0); /* never reached */ + return 0; /* never reached */ } /* }}} */ diff --git a/main/main.c b/main/main.c index cc3f1cae2586..6bda55ac8746 100644 --- a/main/main.c +++ b/main/main.c @@ -62,6 +62,7 @@ #include "win32/php_registry.h" #include "ext/standard/flock_compat.h" #endif +#include "Zend/zend_builtin_functions.h" #include "Zend/zend_exceptions.h" #if PHP_SIGCHILD @@ -801,6 +802,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY_EX("display_errors", "1", PHP_INI_ALL, OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals, display_errors_mode) STD_PHP_INI_BOOLEAN("display_startup_errors", "1", PHP_INI_ALL, OnUpdateBool, display_startup_errors, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("enable_dl", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_dl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("error_include_args", "0", PHP_INI_ALL, OnUpdateBool, error_include_args, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_globals, core_globals) STD_PHP_INI_ENTRY("docref_root", "", PHP_INI_ALL, OnUpdateString, docref_root, php_core_globals, core_globals) STD_PHP_INI_ENTRY("docref_ext", "", PHP_INI_ALL, OnUpdateString, docref_ext, php_core_globals, core_globals) @@ -1132,7 +1134,14 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ /* if we still have memory then format the origin */ if (is_function) { - origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, params); + zend_string *dynamic_params = NULL; + if (PG(error_include_args)) { + dynamic_params = zend_trace_current_function_args_string(); + } + origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, dynamic_params ? ZSTR_VAL(dynamic_params) : params); + if (dynamic_params) { + zend_string_release(dynamic_params); + } } else { origin_len = strlen(function); origin = estrndup(function, origin_len); diff --git a/main/php_globals.h b/main/php_globals.h index f6f57e0045c8..8a032e9edb13 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -59,6 +59,7 @@ struct _php_core_globals { uint8_t display_errors; bool display_startup_errors; + bool error_include_args; bool log_errors; bool ignore_repeated_errors; bool ignore_repeated_source; diff --git a/main/php_streams.h b/main/php_streams.h index d248de7a8168..7622a7295af3 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -246,6 +246,8 @@ struct _php_stream { #endif struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */ + + zend_llist *error_list; }; /* php_stream */ #define PHP_STREAM_CONTEXT(stream) \ @@ -537,6 +539,7 @@ PHPAPI ssize_t _php_stream_passthru(php_stream * src STREAMS_DC); #define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC) END_EXTERN_C() +#include "streams/php_stream_errors.h" #include "streams/php_stream_transport.h" #include "streams/php_stream_plain_wrapper.h" #include "streams/php_stream_glob_wrapper.h" @@ -640,10 +643,6 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf); #define php_stream_open_wrapper(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC) #define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC) - -/* pushes an error message onto the stack for a wrapper instance */ -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); - typedef enum { PHP_STREAM_UNCHANGED = 0, /* orig stream was seekable anyway */ PHP_STREAM_RELEASED = 1, /* newstream should be used; origstream is no longer valid */ diff --git a/main/streams/cast.c b/main/streams/cast.c index 10c93cbb3ffb..f480f3cd59a6 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -187,7 +187,6 @@ void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *resul result[res_curs] = '\0'; } /* }}} */ - /* {{{ php_stream_cast */ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) { @@ -257,7 +256,7 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, b) no memory -> lets bail */ - php_error_docref(NULL, E_ERROR, "fopencookie failed"); + php_stream_fatal(stream, CastFailed, "fopencookie failed"); return FAILURE; #endif @@ -297,7 +296,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, if (php_stream_is_filtered(stream) && castas != PHP_STREAM_AS_FD_FOR_SELECT) { if (show_err) { - php_error_docref(NULL, E_WARNING, "Cannot cast a filtered stream on this system"); + php_stream_warn(stream, CastNotSupported, + "Cannot cast a filtered stream on this system"); } return FAILURE; } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS) { @@ -313,7 +313,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, "select()able descriptor" }; - php_error_docref(NULL, E_WARNING, "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); + php_stream_warn(stream, CastNotSupported, + "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); } return FAILURE; @@ -328,7 +329,9 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, * will be accessing the stream. Emit a warning so that the end-user will * know that they should try something else */ - php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", (zend_long)(stream->writepos - stream->readpos)); + php_stream_warn_nt(stream, BufferedDataLost, + ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", + (zend_long)(stream->writepos - stream->readpos)); } if (castas == PHP_STREAM_AS_STDIO && ret) { diff --git a/main/streams/filter.c b/main/streams/filter.c index 3a19f5ce918a..35dbef455a30 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -391,7 +391,8 @@ PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, p php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } - php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data"); + php_stream_warn(stream, FilterFailed, + "Filter failed to process pre-buffered data"); return FAILURE; case PSFS_FEED_ME: /* We don't actually need data yet, diff --git a/main/streams/memory.c b/main/streams/memory.c index 44336f0e3e2b..af54c46dd9ad 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -362,7 +362,9 @@ static ssize_t php_stream_temp_write(php_stream *stream, const char *buf, size_t zend_string *membuf = php_stream_memory_get_buffer(ts->innerstream); php_stream *file = php_stream_fopen_temporary_file(ts->tmpdir, "php", NULL); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file, Check permissions in temporary files directory."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + PermissionDenied, + "Unable to create temporary file, Check permissions in temporary files directory."); return 0; } php_stream_write(file, ZSTR_VAL(membuf), ZSTR_LEN(membuf)); @@ -489,7 +491,8 @@ static int php_stream_temp_cast(php_stream *stream, int castas, void **ret) file = php_stream_fopen_tmpfile(); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + PermissionDenied, "Unable to create temporary file."); return FAILURE; } @@ -639,7 +642,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if ((comma = (char *) memchr(path, ',', dlen)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: no comma in URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: no comma in URL"); return NULL; } @@ -651,7 +655,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con sep = memchr(path, '/', mlen); if (!semi && !sep) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: illegal media type"); return NULL; } @@ -666,7 +671,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con path += plen; } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: illegal media type"); return NULL; } /* get parameters and potentially ';base64' */ @@ -679,7 +685,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) { /* must be error since parameters are only allowed after mediatype and we have no '=' sign */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal parameter"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidParam, "rfc2397: illegal parameter"); return NULL; } base64 = 1; @@ -699,7 +706,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if (mlen) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: illegal URL"); return NULL; } } else { @@ -715,7 +723,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con base64_comma = php_base64_decode_ex((const unsigned char *)comma, dlen, 1); if (!base64_comma) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: unable to decode"); + php_stream_wrapper_log_warn(wrapper, context, options, + DecodingFailed, "rfc2397: unable to decode"); return NULL; } comma = ZSTR_VAL(base64_comma); diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h new file mode 100644 index 000000000000..c55c06e37f25 --- /dev/null +++ b/main/streams/php_stream_errors.h @@ -0,0 +1,239 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + +#ifndef PHP_STREAM_ERRORS_H +#define PHP_STREAM_ERRORS_H + +#include "php.h" +#include "php_streams.h" +#include "stream_errors_decl.h" + +BEGIN_EXTERN_C() + +/* Error mode context options (internal C constants) */ +#define PHP_STREAM_ERROR_MODE_ERROR 0 +#define PHP_STREAM_ERROR_MODE_EXCEPTION 1 +#define PHP_STREAM_ERROR_MODE_SILENT 2 + +/* Error store context options (internal C constants) */ +#define PHP_STREAM_ERROR_STORE_AUTO 0 +#define PHP_STREAM_ERROR_STORE_NONE 1 +#define PHP_STREAM_ERROR_STORE_NON_TERM 2 +#define PHP_STREAM_ERROR_STORE_TERMINAL 3 +#define PHP_STREAM_ERROR_STORE_ALL 4 + +/* Maximum operation nesting depth */ +#define PHP_STREAM_ERROR_MAX_DEPTH 1000 +/* Operations pool size to prevent extra allocations */ +#define PHP_STREAM_ERROR_OPERATION_POOL_SIZE 8 + +/* Shorthand for error code enum values */ +#define PHP_STREAM_EC(name) ZEND_ENUM_StreamErrorCode_##name + +/* Error code range boundaries (case_id based, ranges are [start, end)) */ +#define STREAM_EC_IO_START PHP_STREAM_EC(ReadFailed) +#define STREAM_EC_IO_END PHP_STREAM_EC(Disabled) +#define STREAM_EC_FS_START PHP_STREAM_EC(Disabled) +#define STREAM_EC_FS_END PHP_STREAM_EC(NotImplemented) +#define STREAM_EC_WRAPPER_START PHP_STREAM_EC(NotImplemented) +#define STREAM_EC_WRAPPER_END PHP_STREAM_EC(FilterNotFound) +#define STREAM_EC_FILTER_START PHP_STREAM_EC(FilterNotFound) +#define STREAM_EC_FILTER_END PHP_STREAM_EC(CastFailed) +#define STREAM_EC_CAST_START PHP_STREAM_EC(CastFailed) +#define STREAM_EC_CAST_END PHP_STREAM_EC(NetworkSendFailed) +#define STREAM_EC_NETWORK_START PHP_STREAM_EC(NetworkSendFailed) +#define STREAM_EC_NETWORK_END PHP_STREAM_EC(ArchivingFailed) +#define STREAM_EC_ENCODING_START PHP_STREAM_EC(ArchivingFailed) +#define STREAM_EC_ENCODING_END PHP_STREAM_EC(AllocationFailed) +#define STREAM_EC_RESOURCE_START PHP_STREAM_EC(AllocationFailed) +#define STREAM_EC_RESOURCE_END PHP_STREAM_EC(LockFailed) +#define STREAM_EC_LOCK_START PHP_STREAM_EC(LockFailed) +#define STREAM_EC_LOCK_END PHP_STREAM_EC(UserspaceNotImplemented) +#define STREAM_EC_USERSPACE_START PHP_STREAM_EC(UserspaceNotImplemented) +#define STREAM_EC_USERSPACE_END (PHP_STREAM_EC(UserspaceCallFailed) + 1) + +/* Wrapper name for PHP errors */ +#define PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME ":na" +#define PHP_STREAM_ERROR_WRAPPER_NAME(_wrapper) \ + (_wrapper ? _wrapper->wops->label : PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME) + +/* Error entry in chain (internal linked list) */ +typedef struct _php_stream_error_entry { + zend_string *message; + zend_enum_StreamErrorCode code; + char *wrapper_name; + char *param; + char *docref; + int severity; + bool terminating; + struct _php_stream_error_entry *next; +} php_stream_error_entry; + +/* Active error operation */ +typedef struct _php_stream_error_operation { + php_stream_error_entry *first_error; + php_stream_error_entry *last_error; + uint32_t error_count; +} php_stream_error_operation; + +/* Stored completed operation */ +typedef struct _php_stream_stored_error { + php_stream_error_entry *first_error; + uint32_t error_count; + struct _php_stream_stored_error *next; +} php_stream_stored_error; + +typedef struct { + php_stream_error_operation *current_operation; + uint32_t operation_depth; + php_stream_stored_error *stored_errors; + uint32_t stored_count; + php_stream_error_operation operation_pool[PHP_STREAM_ERROR_OPERATION_POOL_SIZE]; + php_stream_error_operation *overflow_operations; + uint32_t overflow_capacity; +} php_stream_error_state; + +/* Error operation management */ +PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void); +PHPAPI void php_stream_error_operation_end(php_stream_context *context); +PHPAPI void php_stream_error_operation_end_for_stream(php_stream *stream); +PHPAPI void php_stream_error_operation_abort(void); + +/* State cleanup function */ +PHPAPI void php_stream_error_state_cleanup(void); + +/* Retrieve last stored errors as array of StreamError objects */ +PHPAPI void php_stream_error_get_last(zval *return_value); + +/* Clear all stored errors */ +PHPAPI void php_stream_error_clear_stored(void); + +/* Wrapper error reporting functions */ +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *param1, const char *param2, + const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 10, 11); + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); + +/* Legacy wrapper error log functions */ +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption); + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption); + +PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name); +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); + +/* Convenience macros - code argument is the bare case name (e.g. RenameFailed) */ +#define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_name(wrapper_name, context, options, code, ...) \ + php_stream_wrapper_error_with_name( \ + wrapper_name, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_NOTICE, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param( \ + wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param_nt(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param( \ + wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2( \ + wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), param1, param2, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2_nt(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2( \ + wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), param1, param2, __VA_ARGS__) + +#define php_stream_warn(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, true, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_warn_nt(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, false, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_warn_docref(stream, docref, code, ...) \ + php_stream_error(stream, docref, E_WARNING, true, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_notice(stream, code, ...) \ + php_stream_error(stream, NULL, E_NOTICE, false, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_fatal(stream, code, ...) \ + php_stream_error(stream, NULL, E_ERROR, true, PHP_STREAM_EC(code), __VA_ARGS__) + +/* Legacy log variants */ +#define php_stream_wrapper_log_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, true, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_log_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_log_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_NOTICE, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +END_EXTERN_C() + +#endif /* PHP_STREAM_ERRORS_H */ diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 69258158fbe3..9335ab3fdb6c 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -392,7 +392,8 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun } if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, WriteFailed, + "Write of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } @@ -470,7 +471,8 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) } else { if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, ReadFailed, + "Read of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } @@ -619,7 +621,8 @@ static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, ze assert(data != NULL); if (!data->is_seekable) { - php_error_docref(NULL, E_WARNING, "Cannot seek on this stream"); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + SeekNotSupported, "Cannot seek on this stream"); return -1; } @@ -1156,7 +1159,8 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen char *persistent_id = NULL; if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { - php_stream_wrapper_log_error(&php_plain_files_wrapper, options, "`%s' is not a valid mode for fopen", mode); + php_stream_wrapper_log_warn(&php_plain_files_wrapper, NULL, options, + InvalidMode, "`%s' is not a valid mode for fopen", mode); return NULL; } @@ -1309,8 +1313,9 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, if (ret == -1) { if (options & REPORT_ERRORS) { char errstr[256]; - php_error_docref1(NULL, url, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + UnlinkFailed, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1377,20 +1382,22 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f * access to the file in the meantime. */ if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, + !success, PHP_STREAM_EC(ChownFailed), url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } if (success) { if (VCWD_CHMOD(url_to, sb.st_mode)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, + !success, PHP_STREAM_EC(ChownFailed), url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } # endif @@ -1398,12 +1405,14 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f VCWD_UNLINK(url_from); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, StatFailed, + url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, CopyFailed, + url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } # if !defined(ZTS) && !defined(TSRM_WIN32) umask(oldmask); @@ -1416,8 +1425,9 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_docref2_from_error(GetLastError(), url_from, url_to); #else - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2(wrapper, context, options, + RenameFailed, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif return 0; } @@ -1441,7 +1451,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(dir, (mode_t)mode); if (ret < 0 && (options & REPORT_ERRORS)) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_stream_wrapper_warn(wrapper, context, options, + MkdirFailed, "%s", strerror(errno)); return 0; } @@ -1450,7 +1461,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i char buf[MAXPATHLEN]; if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND)) { - php_error_docref(NULL, E_WARNING, "Invalid path"); + php_stream_wrapper_warn(wrapper, context, options, + InvalidPath, "Invalid path"); return 0; } @@ -1502,7 +1514,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(buf, (mode_t) mode); if (ret < 0 && errno != EEXIST) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + MkdirFailed, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1522,7 +1536,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i /* issue a warning to client when the last directory was created failed */ if (ret < 0) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + MkdirFailed, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1544,13 +1560,17 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, i char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + NotFound, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif if (VCWD_RMDIR(url) < 0) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + RmdirFailed, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1573,7 +1593,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + NotFound, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif @@ -1592,7 +1614,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url if (VCWD_ACCESS(url, F_OK) != 0) { FILE *file = VCWD_FOPEN(url, "w"); if (file == NULL) { - php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + PermissionDenied, url, + "Unable to create file %s because %s", url, php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1606,7 +1630,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_OWNER: if(option == PHP_STREAM_META_OWNER_NAME) { if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + MetaFailed, url, + "Unable to find uid for %s", (char *)value); return 0; } } else { @@ -1618,7 +1644,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_GROUP_NAME: if(option == PHP_STREAM_META_GROUP_NAME) { if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + MetaFailed, url, + "Unable to find gid for %s", (char *)value); return 0; } } else { @@ -1636,8 +1664,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url return 0; } if (ret == -1) { - php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + MetaFailed, url, + "Operation failed: %s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } php_clear_stat_cache(0, NULL, 0); @@ -1730,7 +1759,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char *(cwd+3) = '\0'; if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + PathTooLong, + "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); } efree(cwd); @@ -1785,7 +1816,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char goto stream_skip; } if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + PathTooLong, + "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); } if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) { diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c new file mode 100644 index 000000000000..c4a2f74db8a9 --- /dev/null +++ b/main/streams/stream_errors.c @@ -0,0 +1,917 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + +#define ZEND_ENUM_StreamErrorCode_USE_NAME_TABLE +#include "php.h" +#include "php_globals.h" +#include "php_streams.h" +#include "php_stream_errors.h" +#include "zend_enum.h" +#include "zend_exceptions.h" +#include "ext/standard/file.h" +#include "stream_errors_arginfo.h" + +/* Class entries */ +static zend_class_entry *php_ce_stream_error_code; +static zend_class_entry *php_ce_stream_error_mode; +static zend_class_entry *php_ce_stream_error_store; +static zend_class_entry *php_ce_stream_error; +static zend_class_entry *php_ce_stream_exception; + +/* Forward declarations */ +static void php_stream_error_entry_free(php_stream_error_entry *entry); + +/* Helper to create a single StreamError object from an entry */ +static void php_stream_error_create_object(zval *zv, php_stream_error_entry *entry) +{ + object_init_ex(zv, php_ce_stream_error); + + const char *case_name = NULL; + if (entry->code > 0 && entry->code <= ZEND_ENUM_StreamErrorCode_CASE_COUNT) { + case_name = zend_enum_StreamErrorCode_case_names[entry->code]; + } + if (!case_name) { + case_name = "Generic"; + } + + zend_object *enum_obj = zend_enum_get_case_cstr(php_ce_stream_error_code, case_name); + ZEND_ASSERT(enum_obj != NULL); + + zval code_enum; + ZVAL_OBJ_COPY(&code_enum, enum_obj); + + zend_update_property(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("code"), &code_enum); + zval_ptr_dtor(&code_enum); + + zend_update_property_str( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("message"), entry->message); + + zend_update_property_string(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("wrapperName"), + entry->wrapper_name ? entry->wrapper_name : ""); + + zend_update_property_long( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("severity"), entry->severity); + + zend_update_property_bool( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("terminating"), entry->terminating); + + if (entry->param) { + zend_update_property_string( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("param"), entry->param); + } else { + zend_update_property_null(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("param")); + } +} + +/* Create array of StreamError objects from error chain */ +PHPAPI void php_stream_error_create_array(zval *zv, php_stream_error_entry *first) +{ + array_init(zv); + + php_stream_error_entry *entry = first; + while (entry) { + zval error_obj; + php_stream_error_create_object(&error_obj, entry); + zend_hash_next_index_insert_new(Z_ARRVAL_P(zv), &error_obj); + entry = entry->next; + } +} + +/* Context option helpers */ + +static int php_stream_auto_decide_error_store_mode(int error_mode) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: + return PHP_STREAM_ERROR_STORE_NONE; + case PHP_STREAM_ERROR_MODE_EXCEPTION: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case PHP_STREAM_ERROR_MODE_SILENT: + return PHP_STREAM_ERROR_STORE_ALL; + default: + return PHP_STREAM_ERROR_STORE_NONE; + } +} + +static int php_stream_get_error_mode(php_stream_context *context) +{ + if (!context) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + zval *option = php_stream_context_get_option(context, "stream", "error_mode"); + if (!option) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + if (Z_TYPE_P(option) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(option), php_ce_stream_error_mode)) { + zend_type_error("stream context option 'error_mode' must be of type StreamErrorMode"); + return PHP_STREAM_ERROR_MODE_ERROR; + } + + switch ((zend_enum_StreamErrorMode) zend_enum_fetch_case_id(Z_OBJ_P(option))) { + case ZEND_ENUM_StreamErrorMode_Error: + return PHP_STREAM_ERROR_MODE_ERROR; + case ZEND_ENUM_StreamErrorMode_Exception: + return PHP_STREAM_ERROR_MODE_EXCEPTION; + case ZEND_ENUM_StreamErrorMode_Silent: + return PHP_STREAM_ERROR_MODE_SILENT; + } + + return PHP_STREAM_ERROR_MODE_ERROR; +} + +static int php_stream_get_error_store_mode(php_stream_context *context, int error_mode) +{ + if (!context) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + zval *option = php_stream_context_get_option(context, "stream", "error_store"); + if (!option) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + if (Z_TYPE_P(option) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(option), php_ce_stream_error_store)) { + zend_type_error("stream context option 'error_store' must be of type StreamErrorStore"); + return php_stream_auto_decide_error_store_mode(error_mode); + } + + switch ((zend_enum_StreamErrorStore) zend_enum_fetch_case_id(Z_OBJ_P(option))) { + case ZEND_ENUM_StreamErrorStore_Auto: + return php_stream_auto_decide_error_store_mode(error_mode); + case ZEND_ENUM_StreamErrorStore_None: + return PHP_STREAM_ERROR_STORE_NONE; + case ZEND_ENUM_StreamErrorStore_NonTerminating: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case ZEND_ENUM_StreamErrorStore_Terminating: + return PHP_STREAM_ERROR_STORE_TERMINAL; + case ZEND_ENUM_StreamErrorStore_All: + return PHP_STREAM_ERROR_STORE_ALL; + } + + return php_stream_auto_decide_error_store_mode(error_mode); +} + +/* Helper functions */ + +static bool php_stream_has_terminating_error(php_stream_error_operation *op) +{ + php_stream_error_entry *entry = op->first_error; + while (entry) { + if (entry->terminating) { + return true; + } + entry = entry->next; + } + return false; +} + +static inline php_stream_error_operation *php_stream_get_operation_at_depth(uint32_t depth) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (depth < PHP_STREAM_ERROR_OPERATION_POOL_SIZE) { + return &state->operation_pool[depth]; + } else { + uint32_t overflow_index = depth - PHP_STREAM_ERROR_OPERATION_POOL_SIZE; + ZEND_ASSERT(overflow_index < state->overflow_capacity); + return &state->overflow_operations[overflow_index]; + } +} + +static inline php_stream_error_operation *php_stream_get_parent_operation(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (state->operation_depth <= 1) { + return NULL; + } + + return php_stream_get_operation_at_depth(state->operation_depth - 2); +} + +/* Clean up functions */ + +static void php_stream_error_entry_free(php_stream_error_entry *entry) +{ + while (entry) { + php_stream_error_entry *next = entry->next; + zend_string_release(entry->message); + efree(entry->wrapper_name); + efree(entry->param); + efree(entry); + entry = next; + } +} + +PHPAPI void php_stream_error_state_cleanup(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + while (state->current_operation) { + php_stream_error_operation *op = state->current_operation; + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + php_stream_error_entry_free(op->first_error); + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; + } + + php_stream_stored_error *stored = state->stored_errors; + while (stored) { + php_stream_stored_error *next = stored->next; + php_stream_error_entry_free(stored->first_error); + efree(stored); + stored = next; + } + + state->stored_errors = NULL; + state->stored_count = 0; + state->operation_depth = 0; + + if (state->overflow_operations) { + efree(state->overflow_operations); + state->overflow_operations = NULL; + state->overflow_capacity = 0; + } +} + +PHPAPI void php_stream_error_get_last(zval *return_value) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (!state->stored_errors) { + ZVAL_EMPTY_ARRAY(return_value); + return; + } + + php_stream_error_create_array(return_value, state->stored_errors->first_error); +} + +PHPAPI void php_stream_error_clear_stored(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + php_stream_stored_error *stored = state->stored_errors; + while (stored) { + php_stream_stored_error *next = stored->next; + php_stream_error_entry_free(stored->first_error); + efree(stored); + stored = next; + } + + state->stored_errors = NULL; + state->stored_count = 0; +} + +/* Error operation stack management */ + +PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (state->operation_depth >= PHP_STREAM_ERROR_MAX_DEPTH) { + php_error_docref(NULL, E_WARNING, + "Stream error operation depth exceeded (%u), possible infinite recursion", + state->operation_depth); + return NULL; + } + + php_stream_error_operation *op; + + if (state->operation_depth < PHP_STREAM_ERROR_OPERATION_POOL_SIZE) { + op = &state->operation_pool[state->operation_depth]; + } else { + uint32_t overflow_index = state->operation_depth - PHP_STREAM_ERROR_OPERATION_POOL_SIZE; + + if (overflow_index >= state->overflow_capacity) { + uint32_t new_capacity + = state->overflow_capacity == 0 ? 8 : state->overflow_capacity * 2; + php_stream_error_operation *new_overflow = erealloc( + state->overflow_operations, sizeof(php_stream_error_operation) * new_capacity); + state->overflow_operations = new_overflow; + state->overflow_capacity = new_capacity; + } + + op = &state->overflow_operations[overflow_index]; + } + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; + + state->current_operation = op; + state->operation_depth++; + + return op; +} + +static void php_stream_error_add(zend_enum_StreamErrorCode code, const char *wrapper_name, + zend_string *message, const char *docref, char *param, int severity, bool terminating) +{ + php_stream_error_operation *op = FG(stream_error_state).current_operation; + ZEND_ASSERT(op != NULL); + + php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->param = param; + entry->docref = docref ? estrdup(docref) : NULL; + entry->severity = severity; + entry->terminating = terminating; + entry->next = NULL; + + if (op->last_error) { + op->last_error->next = entry; + } else { + op->first_error = entry; + } + op->last_error = entry; + op->error_count++; +} + +/* Error reporting */ + +static void php_stream_call_error_handler(zval *handler, zval *errors_array) +{ + zend_fcall_info_cache fcc; + char *is_callable_error = NULL; + + if (!zend_is_callable_ex(handler, NULL, 0, NULL, &fcc, &is_callable_error)) { + if (is_callable_error) { + zend_type_error("stream error handler must be a valid callback, %s", is_callable_error); + efree(is_callable_error); + } + return; + } + + zend_call_known_fcc(&fcc, NULL, 1, errors_array, NULL); +} + +static void php_stream_throw_exception_with_errors(php_stream_error_operation *op) +{ + if (!op->first_error) { + return; + } + + zval ex; + object_init_ex(&ex, php_ce_stream_exception); + + /* Set message from first error */ + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("message"), + ZSTR_VAL(op->first_error->message)); + + /* Set code from first error */ + zend_update_property_long(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("code"), + (zend_long) op->first_error->code); + + /* Build errors array and set it */ + zval errors_array; + php_stream_error_create_array(&errors_array, op->first_error); + zend_update_property(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("errors"), &errors_array); + zval_ptr_dtor(&errors_array); + + zend_throw_exception_object(&ex); +} + +static void php_stream_report_errors(php_stream_context *context, php_stream_error_operation *op, + int error_mode, bool is_terminating) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: { + php_stream_error_entry *entry = op->first_error; + while (entry) { + if (entry->param) { + php_error_docref1(entry->docref, entry->param, entry->severity, "%s", + ZSTR_VAL(entry->message)); + } else { + php_error_docref( + entry->docref, entry->severity, "%s", ZSTR_VAL(entry->message)); + } + entry = entry->next; + } + break; + } + + case PHP_STREAM_ERROR_MODE_EXCEPTION: { + if (is_terminating) { + php_stream_throw_exception_with_errors(op); + } + break; + } + + case PHP_STREAM_ERROR_MODE_SILENT: + break; + } + + /* Call user error handler if set */ + zval *handler + = context ? php_stream_context_get_option(context, "stream", "error_handler") : NULL; + + if (handler) { + zval errors_array; + php_stream_error_create_array(&errors_array, op->first_error); + + php_stream_call_error_handler(handler, &errors_array); + + zval_ptr_dtor(&errors_array); + } +} + +/* Error storage */ + +PHPAPI void php_stream_error_operation_end(php_stream_context *context) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + if (op->error_count > 0) { + if (context == NULL) { + context = FG(default_context); + } + + int error_mode = php_stream_get_error_mode(context); + int store_mode = php_stream_get_error_store_mode(context, error_mode); + + bool is_terminating = php_stream_has_terminating_error(op); + + php_stream_report_errors(context, op, error_mode, is_terminating); + + if (store_mode == PHP_STREAM_ERROR_STORE_NONE) { + php_stream_error_entry_free(op->first_error); + op->first_error = NULL; + } else { + php_stream_error_entry *entry = op->first_error; + php_stream_error_entry *prev = NULL; + php_stream_error_entry *to_store_first = NULL; + php_stream_error_entry *to_store_last = NULL; + uint32_t to_store_count = 0; + php_stream_error_entry *remaining_first = NULL; + + while (entry) { + php_stream_error_entry *next = entry->next; + bool should_store = false; + + if (store_mode == PHP_STREAM_ERROR_STORE_ALL) { + should_store = true; + } else if (store_mode == PHP_STREAM_ERROR_STORE_NON_TERM && !entry->terminating) { + should_store = true; + } else if (store_mode == PHP_STREAM_ERROR_STORE_TERMINAL && entry->terminating) { + should_store = true; + } + + if (should_store) { + entry->next = NULL; + if (to_store_last) { + to_store_last->next = entry; + } else { + to_store_first = entry; + } + to_store_last = entry; + to_store_count++; + } else { + entry->next = NULL; + if (prev) { + prev->next = entry; + } else { + remaining_first = entry; + } + prev = entry; + } + + entry = next; + } + + if (to_store_first) { + php_stream_stored_error *stored = emalloc(sizeof(php_stream_stored_error)); + stored->first_error = to_store_first; + stored->error_count = to_store_count; + stored->next = state->stored_errors; + + state->stored_errors = stored; + state->stored_count++; + } + + if (remaining_first) { + php_stream_error_entry_free(remaining_first); + } + + op->first_error = NULL; + } + } + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; +} + +PHPAPI void php_stream_error_operation_end_for_stream(php_stream *stream) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + if (op->error_count == 0) { + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + op->first_error = NULL; + op->last_error = NULL; + return; + } + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + php_stream_error_operation_end(context); +} + +PHPAPI void php_stream_error_operation_abort(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + php_stream_error_entry_free(op->first_error); + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; +} + +/* Wrapper error reporting */ + +static void php_stream_wrapper_error_internal(const char *wrapper_name, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, char *param, zend_string *message) +{ + bool implicit_operation = (FG(stream_error_state).current_operation == NULL); + if (implicit_operation) { + php_stream_error_operation_begin(); + } + + php_stream_error_add(code, wrapper_name, message, docref, param, severity, terminating); + + if (implicit_operation) { + php_stream_error_operation_end(context); + } +} + +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + php_stream_wrapper_error_internal( + wrapper_name, context, docref, options, severity, terminating, code, NULL, message); +} + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal( + wrapper_name, context, docref, options, severity, terminating, code, NULL, message); +} + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + char *param_copy = param ? estrdup(param) : NULL; + + php_stream_wrapper_error_internal(wrapper_name, context, docref, options, severity, terminating, + code, param_copy, message); +} + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *param1, const char *param2, + const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + char *combined_param; + spprintf(&combined_param, 0, "%s,%s", param1, param2); + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal(wrapper_name, context, docref, options, severity, terminating, + code, combined_param, message); +} + +/* Stream error reporting */ + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = stream->wrapper ? stream->wrapper->wops->label : "stream"; + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + + php_stream_wrapper_error_internal(wrapper_name, context, docref, REPORT_ERRORS, severity, + terminating, code, NULL, message); +} + +/* Legacy wrapper error logging */ + +static void php_stream_error_entry_dtor_legacy(void *error) +{ + php_stream_error_entry *entry = *(php_stream_error_entry **) error; + zend_string_release(entry->message); + efree(entry->wrapper_name); + efree(entry->param); + efree(entry->docref); + efree(entry); +} + +static void php_stream_error_list_dtor(zval *item) +{ + zend_llist *list = (zend_llist *) Z_PTR_P(item); + zend_llist_destroy(list); + efree(list); +} + +static void php_stream_wrapper_log_store_error(zend_string *message, zend_enum_StreamErrorCode code, + const char *wrapper_name, const char *param, int severity, bool terminating) +{ + char *param_copy = param ? estrdup(param) : NULL; + + php_stream_error_entry *entry = ecalloc(1, sizeof(php_stream_error_entry)); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->param = param_copy; + entry->severity = severity; + entry->terminating = terminating; + + if (!FG(wrapper_logged_errors)) { + ALLOC_HASHTABLE(FG(wrapper_logged_errors)); + zend_hash_init(FG(wrapper_logged_errors), 8, NULL, php_stream_error_list_dtor, 0); + } + + zend_llist *list + = zend_hash_str_find_ptr(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + + if (!list) { + zend_llist new_list; + zend_llist_init( + &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor_legacy, 0); + list = zend_hash_str_update_mem(FG(wrapper_logged_errors), wrapper_name, + strlen(wrapper_name), &new_list, sizeof(new_list)); + } + + zend_llist_add_element(list, &entry); +} + +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, char *param, const char *fmt, va_list args) +{ + zend_string *message = vstrpprintf(0, fmt, args); + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + if (options & REPORT_ERRORS) { + php_stream_wrapper_error_internal( + wrapper_name, context, NULL, options, severity, terminating, code, param, message); + } else { + php_stream_wrapper_log_store_error( + message, code, wrapper_name, param, severity, terminating); + } +} + +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminating, code, NULL, fmt, args); + va_end(args); +} + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + char *param_copy = param ? estrdup(param) : NULL; + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminating, code, param_copy, fmt, args); + va_end(args); +} + +static zend_llist *php_stream_get_wrapper_errors_list(const char *wrapper_name) +{ + if (!FG(wrapper_logged_errors)) { + return NULL; + } + return (zend_llist *) zend_hash_str_find_ptr( + FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); +} + +PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption) +{ + char *msg; + char errstr[256]; + int free_msg = 0; + + if (EG(exception)) { + return; + } + + char *tmp = estrdup(path); + if (strcmp(wrapper_name, PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME)) { + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name); + if (err_list) { + size_t l = 0; + int brlen; + int i; + int count = (int) zend_llist_count(err_list); + const char *br; + php_stream_error_entry **err_entry_p; + zend_llist_position pos; + + if (PG(html_errors)) { + brlen = 7; + br = "
\n"; + } else { + brlen = 1; + br = "\n"; + } + + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + l += ZSTR_LEN((*err_entry_p)->message); + if (i < count - 1) { + l += brlen; + } + } + msg = emalloc(l + 1); + msg[0] = '\0'; + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + strcat(msg, ZSTR_VAL((*err_entry_p)->message)); + if (i < count - 1) { + strcat(msg, br); + } + } + + free_msg = 1; + } else { + if (!strcmp(wrapper_name, php_plain_files_wrapper.wops->label)) { + msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); + } else { + msg = "operation failed"; + } + } + } else { + msg = "no suitable wrapper could be found"; + } + + php_strip_url_passwd(tmp); + + zend_string *message = strpprintf(0, "%s: %s", caption, msg); + + php_stream_wrapper_error_internal(wrapper_name, context, NULL, REPORT_ERRORS, E_WARNING, true, + code, tmp, message); + + if (free_msg) { + efree(msg); + } +} + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption) +{ + if (wrapper) { + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + php_stream_display_wrapper_name_errors(wrapper_name, context, code, path, caption); + } +} + +PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name) +{ + if (FG(wrapper_logged_errors)) { + zend_hash_str_del(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + } +} + +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) +{ + if (wrapper) { + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + php_stream_tidy_wrapper_name_error_log(wrapper_name); + } +} + +/* StreamException methods */ + +PHP_METHOD(StreamException, getErrors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *errors = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), 1, NULL); + + RETURN_COPY(errors); +} + +/* Module init */ + +PHP_MINIT_FUNCTION(stream_errors) +{ + php_ce_stream_error_code = register_class_StreamErrorCode(); + php_ce_stream_error_mode = register_class_StreamErrorMode(); + php_ce_stream_error_store = register_class_StreamErrorStore(); + + php_ce_stream_error = register_class_StreamError(); + php_ce_stream_exception = register_class_StreamException(zend_ce_exception); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(stream_errors) +{ + return SUCCESS; +} diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php new file mode 100644 index 000000000000..2c56932c4850 --- /dev/null +++ b/main/streams/stream_errors.stub.php @@ -0,0 +1,143 @@ + */ + private array $errors = []; + + /** @return array */ + public function getErrors(): array {} +} diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h new file mode 100644 index 000000000000..3a10e9bc5a40 --- /dev/null +++ b/main/streams/stream_errors_arginfo.h @@ -0,0 +1,259 @@ +/* This is a generated file, edit stream_errors.stub.php instead. + * Stub hash: 3e9ee6f0fdd8ecf3ded82728487a9e774137036a + * Has decl header: yes */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getErrors, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +static ZEND_METHOD(StreamException, getErrors); + +static const zend_function_entry class_StreamException_methods[] = { + ZEND_ME(StreamException, getErrors, arginfo_class_StreamException_getErrors, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_StreamErrorCode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorCode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "None", NULL); + + zend_enum_add_case_cstr(class_entry, "Generic", NULL); + + zend_enum_add_case_cstr(class_entry, "ReadFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WriteFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SeekFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SeekNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "FlushFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TruncateFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ConnectFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "BindFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ListenFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "NotWritable", NULL); + + zend_enum_add_case_cstr(class_entry, "NotReadable", NULL); + + zend_enum_add_case_cstr(class_entry, "Disabled", NULL); + + zend_enum_add_case_cstr(class_entry, "NotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "PermissionDenied", NULL); + + zend_enum_add_case_cstr(class_entry, "AlreadyExists", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidPath", NULL); + + zend_enum_add_case_cstr(class_entry, "PathTooLong", NULL); + + zend_enum_add_case_cstr(class_entry, "OpenFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CreateFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "DupFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "UnlinkFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "RenameFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "MkdirFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "RmdirFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "StatFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "MetaFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ChmodFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ChownFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CopyFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TouchFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidMode", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidMeta", NULL); + + zend_enum_add_case_cstr(class_entry, "ModeNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "Readonly", NULL); + + zend_enum_add_case_cstr(class_entry, "RecursionDetected", NULL); + + zend_enum_add_case_cstr(class_entry, "NotImplemented", NULL); + + zend_enum_add_case_cstr(class_entry, "NoOpener", NULL); + + zend_enum_add_case_cstr(class_entry, "PersistentNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperNotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperDisabled", NULL); + + zend_enum_add_case_cstr(class_entry, "ProtocolUnsupported", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperRegistrationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperUnregistrationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperRestorationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "FilterNotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "FilterFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CastFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CastNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "MakeSeekableFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "BufferedDataLost", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkSendFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkRecvFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SslNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "ResumptionFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SocketPathTooLong", NULL); + + zend_enum_add_case_cstr(class_entry, "OobNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "ProtocolError", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidUrl", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidResponse", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidHeader", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidParam", NULL); + + zend_enum_add_case_cstr(class_entry, "RedirectLimit", NULL); + + zend_enum_add_case_cstr(class_entry, "AuthFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ArchivingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "EncodingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "DecodingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidFormat", NULL); + + zend_enum_add_case_cstr(class_entry, "AllocationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TemporaryFileFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "LockFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "LockNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceNotImplemented", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceInvalidReturn", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceCallFailed", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamErrorMode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorMode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Error", NULL); + + zend_enum_add_case_cstr(class_entry, "Exception", NULL); + + zend_enum_add_case_cstr(class_entry, "Silent", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamErrorStore(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorStore", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Auto", NULL); + + zend_enum_add_case_cstr(class_entry, "None", NULL); + + zend_enum_add_case_cstr(class_entry, "NonTerminating", NULL); + + zend_enum_add_case_cstr(class_entry, "Terminating", NULL); + + zend_enum_add_case_cstr(class_entry, "All", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamError(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamError", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_READONLY_CLASS); + + zval property_code_default_value; + ZVAL_UNDEF(&property_code_default_value); + zend_string *property_code_class_StreamErrorCode = zend_string_init("StreamErrorCode", sizeof("StreamErrorCode")-1, 1); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CODE), &property_code_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_code_class_StreamErrorCode, 0, 0)); + + zval property_message_default_value; + ZVAL_UNDEF(&property_message_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zval property_wrapperName_default_value; + ZVAL_UNDEF(&property_wrapperName_default_value); + zend_string *property_wrapperName_name = zend_string_init("wrapperName", sizeof("wrapperName") - 1, true); + zend_declare_typed_property(class_entry, property_wrapperName_name, &property_wrapperName_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_wrapperName_name, true); + + zval property_severity_default_value; + ZVAL_UNDEF(&property_severity_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SEVERITY), &property_severity_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + + zval property_terminating_default_value; + ZVAL_UNDEF(&property_terminating_default_value); + zend_string *property_terminating_name = zend_string_init("terminating", sizeof("terminating") - 1, true); + zend_declare_typed_property(class_entry, property_terminating_name, &property_terminating_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release_ex(property_terminating_name, true); + + zval property_param_default_value; + ZVAL_UNDEF(&property_param_default_value); + zend_string *property_param_name = zend_string_init("param", sizeof("param") - 1, true); + zend_declare_typed_property(class_entry, property_param_name, &property_param_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release_ex(property_param_name, true); + + return class_entry; +} + +static zend_class_entry *register_class_StreamException(zend_class_entry *class_entry_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamException", class_StreamException_methods); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Exception, 0); + + zval property_errors_default_value; + ZVAL_EMPTY_ARRAY(&property_errors_default_value); + zend_string *property_errors_name = zend_string_init("errors", sizeof("errors") - 1, true); + zend_declare_typed_property(class_entry, property_errors_name, &property_errors_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release_ex(property_errors_name, true); + + return class_entry; +} diff --git a/main/streams/stream_errors_decl.h b/main/streams/stream_errors_decl.h new file mode 100644 index 000000000000..4748768195f1 --- /dev/null +++ b/main/streams/stream_errors_decl.h @@ -0,0 +1,183 @@ +/* This is a generated file, edit stream_errors.stub.php instead. + * Stub hash: 3e9ee6f0fdd8ecf3ded82728487a9e774137036a */ + +#ifndef ZEND_STREAM_ERRORS_DECL_3e9ee6f0fdd8ecf3ded82728487a9e774137036a_H +#define ZEND_STREAM_ERRORS_DECL_3e9ee6f0fdd8ecf3ded82728487a9e774137036a_H + +typedef enum zend_enum_StreamErrorCode { + ZEND_ENUM_StreamErrorCode_None = 1, + ZEND_ENUM_StreamErrorCode_Generic = 2, + ZEND_ENUM_StreamErrorCode_ReadFailed = 3, + ZEND_ENUM_StreamErrorCode_WriteFailed = 4, + ZEND_ENUM_StreamErrorCode_SeekFailed = 5, + ZEND_ENUM_StreamErrorCode_SeekNotSupported = 6, + ZEND_ENUM_StreamErrorCode_FlushFailed = 7, + ZEND_ENUM_StreamErrorCode_TruncateFailed = 8, + ZEND_ENUM_StreamErrorCode_ConnectFailed = 9, + ZEND_ENUM_StreamErrorCode_BindFailed = 10, + ZEND_ENUM_StreamErrorCode_ListenFailed = 11, + ZEND_ENUM_StreamErrorCode_NotWritable = 12, + ZEND_ENUM_StreamErrorCode_NotReadable = 13, + ZEND_ENUM_StreamErrorCode_Disabled = 14, + ZEND_ENUM_StreamErrorCode_NotFound = 15, + ZEND_ENUM_StreamErrorCode_PermissionDenied = 16, + ZEND_ENUM_StreamErrorCode_AlreadyExists = 17, + ZEND_ENUM_StreamErrorCode_InvalidPath = 18, + ZEND_ENUM_StreamErrorCode_PathTooLong = 19, + ZEND_ENUM_StreamErrorCode_OpenFailed = 20, + ZEND_ENUM_StreamErrorCode_CreateFailed = 21, + ZEND_ENUM_StreamErrorCode_DupFailed = 22, + ZEND_ENUM_StreamErrorCode_UnlinkFailed = 23, + ZEND_ENUM_StreamErrorCode_RenameFailed = 24, + ZEND_ENUM_StreamErrorCode_MkdirFailed = 25, + ZEND_ENUM_StreamErrorCode_RmdirFailed = 26, + ZEND_ENUM_StreamErrorCode_StatFailed = 27, + ZEND_ENUM_StreamErrorCode_MetaFailed = 28, + ZEND_ENUM_StreamErrorCode_ChmodFailed = 29, + ZEND_ENUM_StreamErrorCode_ChownFailed = 30, + ZEND_ENUM_StreamErrorCode_CopyFailed = 31, + ZEND_ENUM_StreamErrorCode_TouchFailed = 32, + ZEND_ENUM_StreamErrorCode_InvalidMode = 33, + ZEND_ENUM_StreamErrorCode_InvalidMeta = 34, + ZEND_ENUM_StreamErrorCode_ModeNotSupported = 35, + ZEND_ENUM_StreamErrorCode_Readonly = 36, + ZEND_ENUM_StreamErrorCode_RecursionDetected = 37, + ZEND_ENUM_StreamErrorCode_NotImplemented = 38, + ZEND_ENUM_StreamErrorCode_NoOpener = 39, + ZEND_ENUM_StreamErrorCode_PersistentNotSupported = 40, + ZEND_ENUM_StreamErrorCode_WrapperNotFound = 41, + ZEND_ENUM_StreamErrorCode_WrapperDisabled = 42, + ZEND_ENUM_StreamErrorCode_ProtocolUnsupported = 43, + ZEND_ENUM_StreamErrorCode_WrapperRegistrationFailed = 44, + ZEND_ENUM_StreamErrorCode_WrapperUnregistrationFailed = 45, + ZEND_ENUM_StreamErrorCode_WrapperRestorationFailed = 46, + ZEND_ENUM_StreamErrorCode_FilterNotFound = 47, + ZEND_ENUM_StreamErrorCode_FilterFailed = 48, + ZEND_ENUM_StreamErrorCode_CastFailed = 49, + ZEND_ENUM_StreamErrorCode_CastNotSupported = 50, + ZEND_ENUM_StreamErrorCode_MakeSeekableFailed = 51, + ZEND_ENUM_StreamErrorCode_BufferedDataLost = 52, + ZEND_ENUM_StreamErrorCode_NetworkSendFailed = 53, + ZEND_ENUM_StreamErrorCode_NetworkRecvFailed = 54, + ZEND_ENUM_StreamErrorCode_SslNotSupported = 55, + ZEND_ENUM_StreamErrorCode_ResumptionFailed = 56, + ZEND_ENUM_StreamErrorCode_SocketPathTooLong = 57, + ZEND_ENUM_StreamErrorCode_OobNotSupported = 58, + ZEND_ENUM_StreamErrorCode_ProtocolError = 59, + ZEND_ENUM_StreamErrorCode_InvalidUrl = 60, + ZEND_ENUM_StreamErrorCode_InvalidResponse = 61, + ZEND_ENUM_StreamErrorCode_InvalidHeader = 62, + ZEND_ENUM_StreamErrorCode_InvalidParam = 63, + ZEND_ENUM_StreamErrorCode_RedirectLimit = 64, + ZEND_ENUM_StreamErrorCode_AuthFailed = 65, + ZEND_ENUM_StreamErrorCode_ArchivingFailed = 66, + ZEND_ENUM_StreamErrorCode_EncodingFailed = 67, + ZEND_ENUM_StreamErrorCode_DecodingFailed = 68, + ZEND_ENUM_StreamErrorCode_InvalidFormat = 69, + ZEND_ENUM_StreamErrorCode_AllocationFailed = 70, + ZEND_ENUM_StreamErrorCode_TemporaryFileFailed = 71, + ZEND_ENUM_StreamErrorCode_LockFailed = 72, + ZEND_ENUM_StreamErrorCode_LockNotSupported = 73, + ZEND_ENUM_StreamErrorCode_UserspaceNotImplemented = 74, + ZEND_ENUM_StreamErrorCode_UserspaceInvalidReturn = 75, + ZEND_ENUM_StreamErrorCode_UserspaceCallFailed = 76, +} zend_enum_StreamErrorCode; + +#define ZEND_ENUM_StreamErrorCode_CASE_COUNT 76 + +#ifdef ZEND_ENUM_StreamErrorCode_USE_NAME_TABLE +static const char *zend_enum_StreamErrorCode_case_names[ZEND_ENUM_StreamErrorCode_CASE_COUNT + 1] = { + [ZEND_ENUM_StreamErrorCode_None] = "None", + [ZEND_ENUM_StreamErrorCode_Generic] = "Generic", + [ZEND_ENUM_StreamErrorCode_ReadFailed] = "ReadFailed", + [ZEND_ENUM_StreamErrorCode_WriteFailed] = "WriteFailed", + [ZEND_ENUM_StreamErrorCode_SeekFailed] = "SeekFailed", + [ZEND_ENUM_StreamErrorCode_SeekNotSupported] = "SeekNotSupported", + [ZEND_ENUM_StreamErrorCode_FlushFailed] = "FlushFailed", + [ZEND_ENUM_StreamErrorCode_TruncateFailed] = "TruncateFailed", + [ZEND_ENUM_StreamErrorCode_ConnectFailed] = "ConnectFailed", + [ZEND_ENUM_StreamErrorCode_BindFailed] = "BindFailed", + [ZEND_ENUM_StreamErrorCode_ListenFailed] = "ListenFailed", + [ZEND_ENUM_StreamErrorCode_NotWritable] = "NotWritable", + [ZEND_ENUM_StreamErrorCode_NotReadable] = "NotReadable", + [ZEND_ENUM_StreamErrorCode_Disabled] = "Disabled", + [ZEND_ENUM_StreamErrorCode_NotFound] = "NotFound", + [ZEND_ENUM_StreamErrorCode_PermissionDenied] = "PermissionDenied", + [ZEND_ENUM_StreamErrorCode_AlreadyExists] = "AlreadyExists", + [ZEND_ENUM_StreamErrorCode_InvalidPath] = "InvalidPath", + [ZEND_ENUM_StreamErrorCode_PathTooLong] = "PathTooLong", + [ZEND_ENUM_StreamErrorCode_OpenFailed] = "OpenFailed", + [ZEND_ENUM_StreamErrorCode_CreateFailed] = "CreateFailed", + [ZEND_ENUM_StreamErrorCode_DupFailed] = "DupFailed", + [ZEND_ENUM_StreamErrorCode_UnlinkFailed] = "UnlinkFailed", + [ZEND_ENUM_StreamErrorCode_RenameFailed] = "RenameFailed", + [ZEND_ENUM_StreamErrorCode_MkdirFailed] = "MkdirFailed", + [ZEND_ENUM_StreamErrorCode_RmdirFailed] = "RmdirFailed", + [ZEND_ENUM_StreamErrorCode_StatFailed] = "StatFailed", + [ZEND_ENUM_StreamErrorCode_MetaFailed] = "MetaFailed", + [ZEND_ENUM_StreamErrorCode_ChmodFailed] = "ChmodFailed", + [ZEND_ENUM_StreamErrorCode_ChownFailed] = "ChownFailed", + [ZEND_ENUM_StreamErrorCode_CopyFailed] = "CopyFailed", + [ZEND_ENUM_StreamErrorCode_TouchFailed] = "TouchFailed", + [ZEND_ENUM_StreamErrorCode_InvalidMode] = "InvalidMode", + [ZEND_ENUM_StreamErrorCode_InvalidMeta] = "InvalidMeta", + [ZEND_ENUM_StreamErrorCode_ModeNotSupported] = "ModeNotSupported", + [ZEND_ENUM_StreamErrorCode_Readonly] = "Readonly", + [ZEND_ENUM_StreamErrorCode_RecursionDetected] = "RecursionDetected", + [ZEND_ENUM_StreamErrorCode_NotImplemented] = "NotImplemented", + [ZEND_ENUM_StreamErrorCode_NoOpener] = "NoOpener", + [ZEND_ENUM_StreamErrorCode_PersistentNotSupported] = "PersistentNotSupported", + [ZEND_ENUM_StreamErrorCode_WrapperNotFound] = "WrapperNotFound", + [ZEND_ENUM_StreamErrorCode_WrapperDisabled] = "WrapperDisabled", + [ZEND_ENUM_StreamErrorCode_ProtocolUnsupported] = "ProtocolUnsupported", + [ZEND_ENUM_StreamErrorCode_WrapperRegistrationFailed] = "WrapperRegistrationFailed", + [ZEND_ENUM_StreamErrorCode_WrapperUnregistrationFailed] = "WrapperUnregistrationFailed", + [ZEND_ENUM_StreamErrorCode_WrapperRestorationFailed] = "WrapperRestorationFailed", + [ZEND_ENUM_StreamErrorCode_FilterNotFound] = "FilterNotFound", + [ZEND_ENUM_StreamErrorCode_FilterFailed] = "FilterFailed", + [ZEND_ENUM_StreamErrorCode_CastFailed] = "CastFailed", + [ZEND_ENUM_StreamErrorCode_CastNotSupported] = "CastNotSupported", + [ZEND_ENUM_StreamErrorCode_MakeSeekableFailed] = "MakeSeekableFailed", + [ZEND_ENUM_StreamErrorCode_BufferedDataLost] = "BufferedDataLost", + [ZEND_ENUM_StreamErrorCode_NetworkSendFailed] = "NetworkSendFailed", + [ZEND_ENUM_StreamErrorCode_NetworkRecvFailed] = "NetworkRecvFailed", + [ZEND_ENUM_StreamErrorCode_SslNotSupported] = "SslNotSupported", + [ZEND_ENUM_StreamErrorCode_ResumptionFailed] = "ResumptionFailed", + [ZEND_ENUM_StreamErrorCode_SocketPathTooLong] = "SocketPathTooLong", + [ZEND_ENUM_StreamErrorCode_OobNotSupported] = "OobNotSupported", + [ZEND_ENUM_StreamErrorCode_ProtocolError] = "ProtocolError", + [ZEND_ENUM_StreamErrorCode_InvalidUrl] = "InvalidUrl", + [ZEND_ENUM_StreamErrorCode_InvalidResponse] = "InvalidResponse", + [ZEND_ENUM_StreamErrorCode_InvalidHeader] = "InvalidHeader", + [ZEND_ENUM_StreamErrorCode_InvalidParam] = "InvalidParam", + [ZEND_ENUM_StreamErrorCode_RedirectLimit] = "RedirectLimit", + [ZEND_ENUM_StreamErrorCode_AuthFailed] = "AuthFailed", + [ZEND_ENUM_StreamErrorCode_ArchivingFailed] = "ArchivingFailed", + [ZEND_ENUM_StreamErrorCode_EncodingFailed] = "EncodingFailed", + [ZEND_ENUM_StreamErrorCode_DecodingFailed] = "DecodingFailed", + [ZEND_ENUM_StreamErrorCode_InvalidFormat] = "InvalidFormat", + [ZEND_ENUM_StreamErrorCode_AllocationFailed] = "AllocationFailed", + [ZEND_ENUM_StreamErrorCode_TemporaryFileFailed] = "TemporaryFileFailed", + [ZEND_ENUM_StreamErrorCode_LockFailed] = "LockFailed", + [ZEND_ENUM_StreamErrorCode_LockNotSupported] = "LockNotSupported", + [ZEND_ENUM_StreamErrorCode_UserspaceNotImplemented] = "UserspaceNotImplemented", + [ZEND_ENUM_StreamErrorCode_UserspaceInvalidReturn] = "UserspaceInvalidReturn", + [ZEND_ENUM_StreamErrorCode_UserspaceCallFailed] = "UserspaceCallFailed", +}; +#endif + +typedef enum zend_enum_StreamErrorMode { + ZEND_ENUM_StreamErrorMode_Error = 1, + ZEND_ENUM_StreamErrorMode_Exception = 2, + ZEND_ENUM_StreamErrorMode_Silent = 3, +} zend_enum_StreamErrorMode; + +typedef enum zend_enum_StreamErrorStore { + ZEND_ENUM_StreamErrorStore_Auto = 1, + ZEND_ENUM_StreamErrorStore_None = 2, + ZEND_ENUM_StreamErrorStore_NonTerminating = 3, + ZEND_ENUM_StreamErrorStore_Terminating = 4, + ZEND_ENUM_StreamErrorStore_All = 5, +} zend_enum_StreamErrorStore; + +#endif /* ZEND_STREAM_ERRORS_DECL_3e9ee6f0fdd8ecf3ded82728487a9e774137036a_H */ diff --git a/main/streams/streams.c b/main/streams/streams.c index e638c52159a6..715bbcfe0371 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -138,141 +138,6 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream * return PHP_STREAM_PERSISTENT_NOT_EXIST; } -/* }}} */ - -static zend_llist *php_get_wrapper_errors_list(php_stream_wrapper *wrapper) -{ - if (!FG(wrapper_errors)) { - return NULL; - } else { - return (zend_llist*) zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } -} - -/* {{{ wrapper error reporting */ -static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption) -{ - char *tmp; - char *msg; - char errstr[256]; - int free_msg = 0; - - if (EG(exception)) { - /* Don't emit additional warnings if an exception has already been thrown. */ - return; - } - - tmp = estrdup(path); - if (wrapper) { - zend_llist *err_list = php_get_wrapper_errors_list(wrapper); - if (err_list) { - size_t l = 0; - int brlen; - int i; - int count = (int)zend_llist_count(err_list); - const char *br; - const char **err_buf_p; - zend_llist_position pos; - - if (PG(html_errors)) { - brlen = 7; - br = "
\n"; - } else { - brlen = 1; - br = "\n"; - } - - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - l += strlen(*err_buf_p); - if (i < count - 1) { - l += brlen; - } - } - msg = emalloc(l + 1); - msg[0] = '\0'; - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - strcat(msg, *err_buf_p); - if (i < count - 1) { - strcat(msg, br); - } - } - - free_msg = 1; - } else { - if (wrapper == &php_plain_files_wrapper) { - msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); - } else { - msg = "operation failed"; - } - } - } else { - msg = "no suitable wrapper could be found"; - } - - php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg); - efree(tmp); - if (free_msg) { - efree(msg); - } -} - -static void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) -{ - if (wrapper && FG(wrapper_errors)) { - zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } -} - -static void wrapper_error_dtor(void *error) -{ - efree(*(char**)error); -} - -static void wrapper_list_dtor(zval *item) { - zend_llist *list = (zend_llist*)Z_PTR_P(item); - zend_llist_destroy(list); - efree(list); -} - -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) -{ - va_list args; - char *buffer = NULL; - - va_start(args, fmt); - vspprintf(&buffer, 0, fmt, args); - va_end(args); - - if ((options & REPORT_ERRORS) || wrapper == NULL) { - php_error_docref(NULL, E_WARNING, "%s", buffer); - efree(buffer); - } else { - zend_llist *list = NULL; - if (!FG(wrapper_errors)) { - ALLOC_HASHTABLE(FG(wrapper_errors)); - zend_hash_init(FG(wrapper_errors), 8, NULL, wrapper_list_dtor, 0); - } else { - list = zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } - - if (!list) { - zend_llist new_list; - zend_llist_init(&new_list, sizeof(buffer), wrapper_error_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_errors), (const char*)&wrapper, - sizeof(wrapper), &new_list, sizeof(new_list)); - } - - /* append to linked list */ - zend_llist_add_element(list, &buffer); - } -} - - /* }}} */ /* allocate a new stream for a particular ops */ @@ -511,6 +376,11 @@ fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remov ZVAL_UNDEF(&stream->wrapperdata); } + if (stream->error_list) { + zend_llist_destroy(stream->error_list); + pefree(stream->error_list, stream->is_persistent); + } + if (stream->readbuf) { pefree(stream->readbuf, stream->is_persistent); stream->readbuf = NULL; @@ -1318,7 +1188,7 @@ PHPAPI ssize_t _php_stream_write(php_stream *stream, const char *buf, size_t cou ZEND_ASSERT(buf != NULL); if (stream->ops->write == NULL) { - php_error_docref(NULL, E_NOTICE, "Stream is not writable"); + php_stream_notice(stream, NotWritable, "Stream is not writable"); return (ssize_t) -1; } @@ -1513,7 +1383,8 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) return 0; } - php_error_docref(NULL, E_WARNING, "Stream does not support seeking"); + php_stream_warn(stream, SeekNotSupported, + "Stream does not support seeking"); return -1; } @@ -1937,11 +1808,13 @@ void php_shutdown_stream_hashes(void) FG(stream_filters) = NULL; } - if (FG(wrapper_errors)) { - zend_hash_destroy(FG(wrapper_errors)); - efree(FG(wrapper_errors)); - FG(wrapper_errors) = NULL; + if (FG(wrapper_logged_errors)) { + zend_hash_destroy(FG(wrapper_logged_errors)); + efree(FG(wrapper_logged_errors)); + FG(wrapper_logged_errors) = NULL; } + + php_stream_error_state_cleanup(); } zend_result php_init_stream_wrappers(int module_number) @@ -2108,7 +1981,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const if (!localhost && path[n+3] != '\0' && path[n+3] != '/') { #endif if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Remote host file access not supported, %s", path); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + ProtocolUnsupported, + "Remote host file access not supported, %s", path); } return NULL; } @@ -2147,7 +2022,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const } if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "file:// wrapper is disabled in the server configuration"); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + Disabled, + "file:// wrapper is disabled in the server configuration"); } return NULL; } @@ -2245,10 +2122,12 @@ PHPAPI php_stream *_php_stream_opendir(const char *path, int options, stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR; } } else if (wrapper) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, "not implemented"); + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + NoOpener, "not implemented"); } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open directory"); + php_stream_display_wrapper_errors(wrapper, context, PHP_STREAM_EC(OpenFailed), + path, "Failed to open directory"); } php_stream_tidy_wrapper_error_log(wrapper); @@ -2315,16 +2194,25 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options); if ((options & STREAM_USE_URL) && (!wrapper || !wrapper->is_url)) { - php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + if (wrapper) { + php_stream_wrapper_warn(wrapper, context, options, + ProtocolUnsupported, + "This function may only be used against URLs"); + } else { + php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + } if (resolved_path) { zend_string_release_ex(resolved_path, 0); } return NULL; } + /* wrapper name needs to be stored as wrapper can be removed in opener (user stream) */ + char *wrapper_name = pestrdup(PHP_STREAM_ERROR_WRAPPER_NAME(wrapper), persistent); if (wrapper) { if (!wrapper->wops->stream_opener) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + NoOpener, "wrapper does not support stream open"); } else { stream = wrapper->wops->stream_opener(wrapper, @@ -2335,7 +2223,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod /* if the caller asked for a persistent stream but the wrapper did not * return one, force an error here */ if (stream && persistent && !stream->is_persistent) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + PersistentNotSupported, "wrapper does not support persistent streams"); php_stream_close(stream); stream = NULL; @@ -2359,6 +2248,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; #endif + if (stream->ctx == NULL && context != NULL && !persistent) { + php_stream_context_set(stream, context); + } } if (stream != NULL && (options & STREAM_MUST_SEEK)) { @@ -2371,6 +2263,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (resolved_path) { zend_string_release_ex(resolved_path, 0); } + pefree(wrapper_name, persistent); return stream; case PHP_STREAM_RELEASED: if (newstream->orig_path) { @@ -2380,6 +2273,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (resolved_path) { zend_string_release_ex(resolved_path, 0); } + pefree(wrapper_name, persistent); return newstream; default: php_stream_close(stream); @@ -2387,8 +2281,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (options & REPORT_ERRORS) { char *tmp = estrdup(path); php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "could not make seekable - %s", - tmp); + php_stream_wrapper_warn_param(wrapper, context, options, + SeekNotSupported, tmp, + "could not make seekable - %s", tmp); efree(tmp); options &= ~REPORT_ERRORS; @@ -2406,13 +2301,15 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open stream"); + php_stream_display_wrapper_name_errors(wrapper_name, context, PHP_STREAM_EC(OpenFailed), + path, "Failed to open stream"); if (opened_path && *opened_path) { zend_string_release_ex(*opened_path, 0); *opened_path = NULL; } } - php_stream_tidy_wrapper_error_log(wrapper); + php_stream_tidy_wrapper_name_error_log(wrapper_name); + pefree(wrapper_name, persistent); if (resolved_path) { zend_string_release_ex(resolved_path, 0); } diff --git a/main/streams/transports.c b/main/streams/transports.c index 014e435cfb05..3231670dd9a3 100644 --- a/main/streams/transports.c +++ b/main/streams/transports.c @@ -37,13 +37,13 @@ PHPAPI int php_stream_xport_unregister(const char *protocol) return zend_hash_str_del(&xport_hash, protocol, strlen(protocol)); } -#define ERR_REPORT(out_err, fmt, arg) \ +#define ERR_REPORT(code, out_err, fmt, arg) \ if (out_err) { *out_err = strpprintf(0, fmt, arg); } \ - else { php_error_docref(NULL, E_WARNING, fmt, arg); } + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, arg); } -#define ERR_RETURN(out_err, local_err, fmt) \ +#define ERR_RETURN(code, out_err, local_err, fmt) \ if (out_err) { *out_err = local_err; } \ - else { php_error_docref(NULL, E_WARNING, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ if (local_err) { zend_string_release_ex(local_err, 0); local_err = NULL; } \ } @@ -114,7 +114,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in n = sizeof(wrapper_name) - 1; PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); - ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", + ERR_REPORT(WrapperNotFound, error_string, + "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); return NULL; @@ -123,7 +124,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in if (factory == NULL) { /* should never happen */ - php_error_docref(NULL, E_WARNING, "Could not find a factory !?"); + php_stream_wrapper_warn(NULL, context, REPORT_ERRORS, + WrapperNotFound, "Could not find a factory !?"); return NULL; } @@ -144,7 +146,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0, timeout, &error_text, error_code)) { - ERR_RETURN(error_string, error_text, "connect() failed: %s"); + ERR_RETURN(ConnectFailed, error_string, error_text, "connect() failed: %s"); failed = true; } @@ -154,7 +156,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in /* server */ if (flags & STREAM_XPORT_BIND) { if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) { - ERR_RETURN(error_string, error_text, "bind() failed: %s"); + ERR_RETURN(BindFailed, error_string, error_text, "bind() failed: %s"); failed = true; } else if (flags & STREAM_XPORT_LISTEN) { zval *zbacklog = NULL; @@ -165,7 +167,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in } if (0 != php_stream_xport_listen(stream, backlog, &error_text)) { - ERR_RETURN(error_string, error_text, "listen() failed: %s"); + ERR_RETURN(ListenFailed, error_string, error_text, "listen() failed: %s"); failed = true; } } @@ -368,7 +370,8 @@ PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_cr return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", SslNotSupported, + "This stream does not support SSL/crypto"); return ret; } @@ -388,7 +391,8 @@ PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate) return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", SslNotSupported, + "This stream does not support SSL/crypto"); return ret; } @@ -410,7 +414,8 @@ PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t bufle } if (stream->readfilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot peek or fetch OOB data from a filtered stream"); + php_stream_warn(stream, FilterFailed, + "Cannot peek or fetch OOB data from a filtered stream"); return -1; } @@ -480,7 +485,8 @@ PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t b oob = (flags & STREAM_OOB) == STREAM_OOB; if ((oob || addr) && stream->writefilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot write OOB data, or data to a targeted address on a filtered stream"); + php_stream_warn(stream, FilterFailed, + "Cannot write OOB data, or data to a targeted address on a filtered stream"); return -1; } diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 335ef3aa4f27..bf6ffa500963 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -291,7 +291,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + RecursionDetected, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -332,8 +333,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options,NotImplemented, + "\"%s::" USERSTREAM_OPEN "\" is not implemented", ZSTR_VAL(us->wrapper->ce->name)); zval_ptr_dtor(&args[3]); goto end; } @@ -355,8 +356,9 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + UserspaceCallFailed, + "\"%s::" USERSTREAM_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -392,7 +394,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + RecursionDetected, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -417,8 +420,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, NotImplemented, + "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", + ZSTR_VAL(us->wrapper->ce->name)); goto end; } /* Exception occurred in call */ @@ -433,8 +437,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + UserspaceCallFailed, + "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -477,10 +482,15 @@ PHP_FUNCTION(stream_wrapper_register) /* We failed. But why? */ if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol)) { - php_error_docref(NULL, E_WARNING, "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + WrapperRegistrationFailed, + "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); } else { /* Hash doesn't exist so it must have been an invalid protocol scheme */ - php_error_docref(NULL, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + WrapperRegistrationFailed, + "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", + ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); } zend_list_delete(rsrc); @@ -500,7 +510,9 @@ PHP_FUNCTION(stream_wrapper_unregister) php_stream_wrapper *wrapper = zend_hash_find_ptr(php_stream_get_url_stream_wrappers_hash(), protocol); if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) { /* We failed */ - php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + WrapperUnregistrationFailed, + "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -528,13 +540,17 @@ PHP_FUNCTION(stream_wrapper_restore) global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global(); if ((wrapper = zend_hash_find_ptr(global_wrapper_hash, protocol)) == NULL) { - php_error_docref(NULL, E_WARNING, "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_warn_name(user_stream_wops.label, NULL, REPORT_ERRORS, + WrapperNotFound, + "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); RETURN_FALSE; } wrapper_hash = php_stream_get_url_stream_wrappers_hash(); if (wrapper_hash == global_wrapper_hash || zend_hash_find_ptr(wrapper_hash, protocol) == wrapper) { - php_error_docref(NULL, E_NOTICE, "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_notice(wrapper, NULL, REPORT_ERRORS, + WrapperRestorationFailed, + "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); RETURN_TRUE; } @@ -542,7 +558,9 @@ PHP_FUNCTION(stream_wrapper_restore) php_unregister_url_stream_wrapper_volatile(protocol); if (php_register_url_stream_wrapper_volatile(protocol, wrapper) == FAILURE) { - php_error_docref(NULL, E_WARNING, "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + WrapperRestorationFailed, + "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -570,8 +588,8 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_WRITE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE; @@ -591,7 +609,9 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ /* don't allow strange buffer overruns due to bogus return */ if (didwrite > 0 && didwrite > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", + php_stream_warn_nt(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" + ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count); didwrite = count; @@ -622,8 +642,8 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count } if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_READ " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); goto err; } @@ -639,8 +659,12 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count didread = Z_STRLEN(retval); if (didread > 0) { if (didread > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost", - ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), (zend_long)didread, (zend_long)count); + php_stream_warn_nt(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT + " bytes more data than requested (" ZEND_LONG_FMT " read, " + ZEND_LONG_FMT " max) - excess data will be lost", + ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), + (zend_long)didread, (zend_long)count); didread = count; } memcpy(buf, Z_STRVAL(retval), didread); @@ -656,7 +680,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, NotImplemented, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); stream->eof = 1; @@ -772,7 +796,8 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when *newoffs = Z_LVAL(retval); ret = 0; } else if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); ret = -1; } else { ret = -1; @@ -836,8 +861,8 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_STAT " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -855,7 +880,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) return ret; } -static int user_stream_set_check_liveliness(const php_userstream_data_t *us) +static int user_stream_set_check_liveliness(php_stream *stream, const php_userstream_data_t *us) { zval retval; @@ -864,7 +889,7 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, NotImplemented, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -875,15 +900,15 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) { return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; } else { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_EOF " value must be of type bool, %s given", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_EOF " value must be of type bool, %s given", ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_locking(const php_userstream_data_t *us, int value) +static int user_stream_set_locking(php_stream *stream, const php_userstream_data_t *us, int value) { zval retval; zval zlock; @@ -918,9 +943,8 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) /* lock support test (TODO: more check) */ return PHP_STREAM_OPTION_RETURN_OK; } - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_LOCK " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_LOCK " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -932,14 +956,15 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) } // TODO: ext/standard/tests/file/userstreams_004.phpt returns null implicitly for function // Should this warn or not? And should this be considered an error? - //php_error_docref(NULL, E_WARNING, - // "%s::" USERSTREAM_LOCK " value must be of type bool, %s given", + //php_stream_warn(stream, UserspaceInvalidReturn, + // "%s::" USERSTREAM_LOCK " value must be of type bool, %s given", // ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_NOTIMPL; } -static int user_stream_set_truncation(const php_userstream_data_t *us, int value, void *ptrparam) { +static int user_stream_set_truncation(php_stream *stream, const php_userstream_data_t *us, + int value, void *ptrparam) { zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_TRUNCATE, false); if (value == PHP_STREAM_TRUNCATE_SUPPORTED) { @@ -967,9 +992,8 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_TRUNCATE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -978,15 +1002,16 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) { return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; } else { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given", ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_option(const php_userstream_data_t *us, int option, int value, void *ptrparam) +static int user_stream_set_option(php_stream *stream, const php_userstream_data_t *us, int option, + int value, void *ptrparam) { zval args[3]; ZVAL_LONG(&args[0], option); @@ -1011,7 +1036,7 @@ static int user_stream_set_option(const php_userstream_data_t *us, int option, i zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, NotImplemented, "%s::" USERSTREAM_SET_OPTION " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -1036,19 +1061,19 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value switch (option) { case PHP_STREAM_OPTION_CHECK_LIVENESS: - return user_stream_set_check_liveliness(us); + return user_stream_set_check_liveliness(stream, us); case PHP_STREAM_OPTION_LOCKING: - return user_stream_set_locking(us, value); + return user_stream_set_locking(stream, us, value); case PHP_STREAM_OPTION_TRUNCATE_API: - return user_stream_set_truncation(us, value, ptrparam); + return user_stream_set_truncation(stream, us, value, ptrparam); case PHP_STREAM_OPTION_READ_BUFFER: case PHP_STREAM_OPTION_WRITE_BUFFER: case PHP_STREAM_OPTION_READ_TIMEOUT: case PHP_STREAM_OPTION_BLOCKING: - return user_stream_set_option(us, option, value, ptrparam); + return user_stream_set_option(stream, us, option, value, ptrparam); default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; @@ -1081,7 +1106,8 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1119,7 +1145,8 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1157,7 +1184,8 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1194,7 +1222,8 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1233,7 +1262,9 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i ZVAL_STRING(&args[2], value); break; default: - php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, + InvalidMeta, + "Unknown option %d for " USERSTREAM_METADATA, option); return ret; } @@ -1256,7 +1287,8 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1294,8 +1326,8 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!", - ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_STATURL " is not implemented!", ZSTR_VAL(uwrap->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(zretval))) { @@ -1330,8 +1362,9 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_DIR_READ " is not implemented!", + ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -1416,7 +1449,8 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) if (UNEXPECTED(call_result == FAILURE)) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!", + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_CAST " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } goto out; @@ -1430,14 +1464,16 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) php_stream_from_zval_no_verify(intstream, &retval); if (!intstream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_CAST " must return a stream resource", ZSTR_VAL(us->wrapper->ce->name)); } break; } if (intstream == stream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_CAST " must not return itself", ZSTR_VAL(us->wrapper->ce->name)); } intstream = NULL; diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index f2b2bb54ec6e..d18b0e901957 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -114,9 +114,8 @@ static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t coun if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { estr = php_socket_strerror(err, NULL, 0); - php_error_docref(NULL, E_NOTICE, - "Send of %zu bytes failed with errno=%d %s", - count, err, estr); + php_stream_warn(stream, NetworkSendFailed, + "Send of %zu bytes failed with errno=%d %s", count, err, estr); efree(estr); } } @@ -452,8 +451,7 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void xparam->inputs.addrlen); if (xparam->outputs.returncode == -1) { char *err = php_socket_strerror(php_socket_errno(), NULL, 0); - php_error_docref(NULL, E_WARNING, - "%s\n", err); + php_stream_warn(stream, NetworkSendFailed, "%s", err); efree(err); } return PHP_STREAM_OPTION_RETURN_OK; @@ -593,7 +591,8 @@ static const php_stream_ops php_stream_unixdg_socket_ops = { /* network socket operations */ #ifdef AF_UNIX -static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr) +static inline int parse_unix_address(php_stream *stream, php_stream_xport_param *xparam, + struct sockaddr_un *unix_addr) { memset(unix_addr, 0, sizeof(*unix_addr)); unix_addr->sun_family = AF_UNIX; @@ -612,9 +611,9 @@ static inline int parse_unix_address(php_stream_xport_param *xparam, struct sock * BUT, to get into this branch of code, the name is too long, * so we don't care. */ xparam->inputs.namelen = max_length; - php_error_docref(NULL, E_NOTICE, - "socket path exceeded the maximum allowed length of %lu bytes " - "and was truncated", max_length); + php_stream_notice(stream, InvalidPath, + "socket path exceeded the maximum allowed length of %lu bytes and was truncated", + max_length); } memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen); @@ -695,7 +694,7 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t * return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); int result = bind(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) offsetof(struct sockaddr_un, sun_path) + xparam->inputs.namelen); @@ -831,7 +830,7 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_ return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); ret = php_network_connect_socket(sock->socket, (const struct sockaddr *)&unix_addr, (socklen_t) offsetof(struct sockaddr_un, sun_path) + xparam->inputs.namelen, diff --git a/php.ini-development b/php.ini-development index 78ae50708d5a..afabe74ba0e4 100644 --- a/php.ini-development +++ b/php.ini-development @@ -611,6 +611,12 @@ ignore_repeated_source = Off ; Production Value: On ;fatal_error_backtraces = On +; This directive controls whether PHP will print the actual arguments of a +; function upon an error. If this is off (or there was an error fetching the +; arguments), the function providing the error may optionally provide some +; additional information after the problem function's name. +;error_include_args = Off + ;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;; diff --git a/php.ini-production b/php.ini-production index eb6880fe75d6..04a7b699dadd 100644 --- a/php.ini-production +++ b/php.ini-production @@ -613,6 +613,12 @@ ignore_repeated_source = Off ; Production Value: On ;fatal_error_backtraces = On +; This directive controls whether PHP will print the actual arguments of a +; function upon an error. If this is off (or there was an error fetching the +; arguments), the function providing the error may optionally provide some +; additional information after the problem function's name. +;error_include_args = Off + ;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;; diff --git a/run-tests.php b/run-tests.php index c08d07cdd7c1..f5c7be8b4f45 100755 --- a/run-tests.php +++ b/run-tests.php @@ -273,6 +273,7 @@ function main(): void 'fatal_error_backtraces=Off', 'display_errors=1', 'display_startup_errors=1', + 'error_include_args=0', 'log_errors=0', 'html_errors=0', 'track_errors=0', diff --git a/sapi/cli/tests/php_cli_server.inc b/sapi/cli/tests/php_cli_server.inc index 3022022f894e..3ad6ced5cb44 100644 --- a/sapi/cli/tests/php_cli_server.inc +++ b/sapi/cli/tests/php_cli_server.inc @@ -24,7 +24,8 @@ function php_cli_server_start( file_put_contents($doc_root . '/' . ($router ?: 'index.php'), ''); } - $cmd = [$php_executable, '-t', $doc_root, '-n', ...$cmd_args, '-S', 'localhost:0']; + // XXX: This should ideally use the same INI overrides as run-tests + $cmd = [$php_executable, '-d', 'error_include_args=0', '-t', $doc_root, '-n', ...$cmd_args, '-S', 'localhost:0']; if (!is_null($router)) { $cmd[] = $router; } diff --git a/win32/build/config.w32 b/win32/build/config.w32 index aefcfb5f8247..7029258b7be2 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -241,7 +241,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ - zend_lazy_objects.c zend_autoload.c"); + zend_lazy_objects.c zend_autoload.c zend_partial.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({ @@ -298,7 +298,7 @@ AC_DEFINE('HAVE_STRNLEN', 1); AC_DEFINE('ZEND_CHECK_STACK_LIMIT', 1) -ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c \ +ADD_SOURCES("main/streams", "streams.c stream_errors.c cast.c memory.c filter.c plain_wrapper.c \ userspace.c transports.c xp_socket.c mmap.c glob_wrapper.c"); ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");