diff --git a/.circleci/config.yml b/.circleci/config.yml
index 346dbbb4366e..eec1b4b1434c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,7 +4,7 @@ jobs:
arm:
resource_class: arm.medium
docker:
- - image: cimg/base:current-22.04
+ - image: cimg/base:current-24.04
- image: mysql:8.4
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: true
@@ -60,7 +60,7 @@ jobs:
libreadline-dev \
libldap2-dev \
libsodium-dev \
- libargon2-0-dev \
+ libargon2-dev \
libmm-dev \
libsnmp-dev \
snmpd \
@@ -78,7 +78,7 @@ jobs:
libqdbm-dev \
libjpeg-dev \
libpng-dev \
- libfreetype6-dev
+ libfreetype-dev
- run:
name: ./configure
command: |
diff --git a/.gitattributes b/.gitattributes
index 8dea3f8bbafb..74fd9f995e8d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -21,6 +21,7 @@
# Collapse generated files within git and pull request diff.
**/*_arginfo.h linguist-generated -diff
+**/*_decl.h linguist-generated -diff
/main/debug_gdb_scripts.c linguist-generated -diff
/Zend/zend_vm_execute.h linguist-generated -diff
/Zend/zend_vm_handlers.h linguist-generated -diff
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 53bd4df5a1ca..296835e02e0d 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -26,14 +26,15 @@ body:
attributes:
label: PHP Version
description: |
- Please run PHP with the `-v` flag (e.g. `php -v`, `php8.3 -v`, `php-fpm -v` or similar) and provide the full output of that command. If executing that command is not possible, please provide the full version number as given in PHPInfo.
+ Please run PHP with the `-v` flag (e.g. `php -v`, `php8.5 -v`, `php-fpm -v` or similar) and provide the full output of that command. If executing that command is not possible, please provide the full version number as given in PHPInfo.
Please make sure that the used PHP version [is a supported version](https://www.php.net/supported-versions.php).
placeholder: |
- PHP 8.3.19 (cli) (built: Mar 13 2025 17:44:40) (NTS)
- Copyright (c) The PHP Group
- Zend Engine v4.3.19, Copyright (c) Zend Technologies
- with Zend OPcache v8.3.19, Copyright (c), by Zend Technologies
+ PHP 8.5.2 (cli) (built: Jan 21 2026 17:35:28) (NTS)
+ Copyright © The PHP Group and Contributors
+ Built by Ubuntu
+ Zend Engine v4.5.2, Copyright © Zend by Perforce
+ with Zend OPcache v8.5.2, Copyright ©, Zend by Perforce
render: plain
validations:
required: true
diff --git a/.github/actions/brew/action.yml b/.github/actions/brew/action.yml
index f90a66f239de..db43010f396f 100644
--- a/.github/actions/brew/action.yml
+++ b/.github/actions/brew/action.yml
@@ -33,4 +33,6 @@ runs:
t1lib \
libxml2 \
libjpeg \
- libxslt
+ libxslt \
+ openssl@4
+ brew link --force --overwrite openssl@4
diff --git a/.github/actions/configure-macos/action.yml b/.github/actions/configure-macos/action.yml
index f0af959d2102..b9739a790530 100644
--- a/.github/actions/configure-macos/action.yml
+++ b/.github/actions/configure-macos/action.yml
@@ -11,7 +11,7 @@ runs:
set -x
BREW_OPT="$(brew --prefix)"/opt
export PATH="$BREW_OPT/bison/bin:$PATH"
- export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/openssl/lib/pkgconfig"
+ export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/openssl@4/lib/pkgconfig"
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/curl/lib/pkgconfig"
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/libffi/lib/pkgconfig"
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$BREW_OPT/libxml2/lib/pkgconfig"
diff --git a/.github/actions/configure-unit-tests/action.yml b/.github/actions/configure-unit-tests/action.yml
new file mode 100644
index 000000000000..ef8239b7111c
--- /dev/null
+++ b/.github/actions/configure-unit-tests/action.yml
@@ -0,0 +1,10 @@
+name: ./configure (unit tests)
+description: Configure PHP with minimal settings for unit testing
+runs:
+ using: composite
+ steps:
+ - shell: bash
+ run: |
+ set -x
+ ./buildconf --force
+ ./configure --disable-all --enable-embed=static
diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml
index 3b6d0c4e8617..197362d9f52b 100644
--- a/.github/actions/freebsd/action.yml
+++ b/.github/actions/freebsd/action.yml
@@ -46,7 +46,7 @@ runs:
pkgconf \
webp \
libavif \
- `#sqlite3` \
+ sqlite3 \
curl \
$OPCACHE_TLS_TESTS_DEPS
@@ -57,9 +57,7 @@ runs:
--enable-debug \
--enable-option-checking=fatal \
--enable-fpm \
- `#--with-pdo-sqlite` \
- --without-sqlite3 \
- --without-pdo-sqlite \
+ --with-pdo-sqlite \
--without-pear \
--with-bz2 \
--with-avif \
diff --git a/.github/actions/setup-mssql/action.yml b/.github/actions/setup-mssql/action.yml
index dd372a5637aa..cbd220d0b29e 100644
--- a/.github/actions/setup-mssql/action.yml
+++ b/.github/actions/setup-mssql/action.yml
@@ -11,4 +11,4 @@ runs:
-p 1433:1433 \
--name sql1 \
-h sql1 \
- -d mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04
+ -d mcr.microsoft.com/mssql/server:2025-latest
diff --git a/.github/actions/verify-generated-files/action.yml b/.github/actions/verify-generated-files/action.yml
index 5228105f2590..79c49dbfcfff 100644
--- a/.github/actions/verify-generated-files/action.yml
+++ b/.github/actions/verify-generated-files/action.yml
@@ -12,5 +12,5 @@ runs:
Zend/zend_vm_gen.php
ext/tokenizer/tokenizer_data_gen.php
build/gen_stub.php -f --generate-optimizer-info --verify
- # Use the -a flag for a bug in git 2.46.0, which doesn't consider changed -diff files.
- git add . -N && git diff -a --exit-code
+ ext/phar/makestub.php
+ .github/scripts/test-directory-unchanged.sh .
diff --git a/.github/labeler.yml b/.github/labeler.yml
index b9f0f36e147d..e9731352c8cc 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -20,6 +20,18 @@
- scripts/**/*
- win32/build/**/*
+"Category: CI":
+ - changed-files:
+ - any-glob-to-any-file:
+ - .circleci/*
+ - .github/actions/**/*
+ - .github/scripts/**/*
+ - .github/workflows/*
+ - .github/CODEOWNERS
+ - .github/labeler.yml
+ - .github/setup_hmailserver.php
+ - .github/matrix.php
+
"Extension: bcmath":
- changed-files:
- any-glob-to-any-file:
diff --git a/.github/matrix.php b/.github/matrix.php
index 7970442d7050..eefb27e5ba53 100644
--- a/.github/matrix.php
+++ b/.github/matrix.php
@@ -127,7 +127,19 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re
$test_arm = version_compare($php_version, '8.4', '>=');
$jobs['MACOS']['matrix'] = $all_variations
? ['arch' => $test_arm ? ['X64', 'ARM64'] : ['X64'], 'debug' => [true, false], 'zts' => [true, false]]
- : ['include' => [['arch' => $test_arm ? 'ARM64' : 'X64', 'debug' => true, 'zts' => false]]];
+ : ['include' => [['arch' => $test_arm ? 'ARM64' : 'X64', 'debug' => true, 'zts' => false, 'jit' => true]]];
+ if ($all_variations) {
+ // Set the jit variable on X64 jobs
+ $jobs['MACOS']['matrix']['include'][] = ['arch' => 'X64', 'jit' => true];
+ if ($test_arm) {
+ // Set the jit variable on ARM64 NTS jobs
+ $jobs['MACOS']['matrix']['include'][] = ['arch' => 'ARM64', 'zts' => false, 'jit' => true];
+ // Set the jit variable on ARM64 ZTS jobs on 8.6+
+ if (version_compare($php_version, '8.6', '>=')) {
+ $jobs['MACOS']['matrix']['include'][] = ['arch' => 'ARM64', 'zts' => true, 'jit' => true];
+ }
+ }
+ }
$jobs['MACOS']['config']['arm64_version'] = version_compare($php_version, '8.4', '>=') ? '15' : '14';
}
if ($all_jobs || $test_msan) {
diff --git a/.github/scripts/download-bundled/.gitignore b/.github/scripts/download-bundled/.gitignore
new file mode 100644
index 000000000000..69f1bf453095
--- /dev/null
+++ b/.github/scripts/download-bundled/.gitignore
@@ -0,0 +1 @@
+!*.patch
diff --git a/.github/scripts/download-bundled/boost-context.sh b/.github/scripts/download-bundled/boost-context.sh
new file mode 100755
index 000000000000..02febff73c3f
--- /dev/null
+++ b/.github/scripts/download-bundled/boost-context.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+set -ex
+cd "$(dirname "$0")/../../.."
+
+tmp_dir=/tmp/php-src-download-bundled/boost-context
+rm -rf "$tmp_dir"
+
+revision=refs/tags/boost-1.91.0
+
+git clone --depth 1 --revision="$revision" https://github.com/boostorg/context.git "$tmp_dir"
+
+rm -rf Zend/asm
+cp -R "$tmp_dir"/src/asm Zend/asm
+
+cd Zend/asm
+
+# remove unneeded files
+rm jump_arm_aapcs_pe_armasm.asm
+rm jump_arm64_aapcs_pe_armclang.S
+rm jump_i386_ms_pe_clang_gas.S
+rm jump_i386_ms_pe_gas.asm
+rm jump_i386_x86_64_sysv_macho_gas.S
+rm jump_ppc32_ppc64_sysv_macho_gas.S
+rm jump_x86_64_ms_pe_clang_gas.S
+rm make_arm_aapcs_pe_armasm.asm
+rm make_arm64_aapcs_pe_armclang.S
+rm make_i386_ms_pe_clang_gas.S
+rm make_i386_ms_pe_gas.asm
+rm make_i386_x86_64_sysv_macho_gas.S
+rm make_ppc32_ppc64_sysv_macho_gas.S
+rm make_x86_64_ms_pe_clang_gas.S
+rm ontop_*.S
+rm ontop_*.asm
+rm tail_ontop_ppc32_sysv.cpp
+
+# move renamed files
+# GH-13896 introduced these 2 files named as .S but since https://github.com/boostorg/context/pull/265 they are named as .asm
+mv jump_x86_64_ms_pe_gas.asm jump_x86_64_ms_pe_gas.S
+mv make_x86_64_ms_pe_gas.asm make_x86_64_ms_pe_gas.S
+
+# add extra files
+git restore LICENSE
+git restore save_xmm_x86_64_ms_masm.asm # added in GH-18352, not an upstream boost.context file
diff --git a/.github/scripts/download-bundled/pcre2.sh b/.github/scripts/download-bundled/pcre2.sh
new file mode 100755
index 000000000000..360cbb91db82
--- /dev/null
+++ b/.github/scripts/download-bundled/pcre2.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+set -ex
+cd "$(dirname "$0")/../../.."
+
+tmp_dir=/tmp/php-src-download-bundled/pcre2
+rm -rf "$tmp_dir"
+
+revision=refs/tags/pcre2-10.44
+
+git clone --depth 1 --recurse-submodules --revision="$revision" https://github.com/PCRE2Project/pcre2.git "$tmp_dir"
+
+rm -rf ext/pcre/pcre2lib
+cp -R "$tmp_dir"/src ext/pcre/pcre2lib
+
+cd ext/pcre/pcre2lib
+
+# remove unneeded files
+rm config.h.generic
+rm pcre2.h.in
+rm pcre2_dftables.c
+rm pcre2_fuzzsupport.c
+rm pcre2_jit_test.c
+rm pcre2demo.c
+rm pcre2grep.c
+rm pcre2posix.c
+rm pcre2posix.h
+rm pcre2posix_test.c
+rm pcre2test.c
+
+# move renamed files
+mv pcre2.h.generic pcre2.h
+mv pcre2_chartables.c.dist pcre2_chartables.c
+
+# add extra files
+git restore config.h # based on config.h.generic but with many changes
diff --git a/.github/scripts/download-bundled/uriparser.config.patch b/.github/scripts/download-bundled/uriparser.config.patch
new file mode 100644
index 000000000000..9742154e5d7c
--- /dev/null
+++ b/.github/scripts/download-bundled/uriparser.config.patch
@@ -0,0 +1,14 @@
+diff --git a/ext/uri/uriparser/src/UriConfig.h b/ext/uri/uriparser/src/UriConfig.h
+index b9a85a8..ab78b96 100644
+--- a/ext/uri/uriparser/src/UriConfig.h
++++ b/ext/uri/uriparser/src/UriConfig.h
+@@ -41,7 +41,9 @@
+
+ # define PACKAGE_VERSION "@PROJECT_VERSION@"
+
++/*
+ #cmakedefine HAVE_WPRINTF
+ #cmakedefine HAVE_REALLOCARRAY
++*/
+
+ #endif /* !defined(URI_CONFIG_H) */
diff --git a/.github/scripts/download-bundled/uriparser.sh b/.github/scripts/download-bundled/uriparser.sh
new file mode 100755
index 000000000000..2b8d61a2d56f
--- /dev/null
+++ b/.github/scripts/download-bundled/uriparser.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -ex
+cd "$(dirname "$0")/../../.."
+
+tmp_dir=/tmp/php-src-download-bundled/uriparser
+rm -rf "$tmp_dir"
+
+revision=refs/tags/uriparser-1.0.2
+
+git clone --depth 1 --revision="$revision" https://github.com/uriparser/uriparser.git "$tmp_dir"
+
+rm -rf ext/uri/uriparser
+mkdir ext/uri/uriparser
+cp -R "$tmp_dir"/src ext/uri/uriparser
+cp -R "$tmp_dir"/include ext/uri/uriparser
+cp "$tmp_dir"/COPYING.BSD-3-Clause ext/uri/uriparser
+
+cd ext/uri/uriparser
+
+# move renamed files
+mv src/UriConfig.h.in src/UriConfig.h
+
+# patch customized files
+git apply -v ../../../.github/scripts/download-bundled/uriparser.config.patch
diff --git a/.github/scripts/test-directory-unchanged.sh b/.github/scripts/test-directory-unchanged.sh
new file mode 100755
index 000000000000..20ca410e4ec9
--- /dev/null
+++ b/.github/scripts/test-directory-unchanged.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -ex
+
+# use the repo root directory as "--git-dir"
+cd "$(dirname "$0")/../.."
+
+dir="$1"
+
+# notify git about untracked (except ignored) files
+git add -N "$dir"
+
+# display overview of changed files
+git status "$dir"
+
+# display diff of working directory vs HEAD commit and set exit code
+git diff -a --exit-code HEAD "$dir"
diff --git a/.github/scripts/windows/build_task.bat b/.github/scripts/windows/build_task.bat
index b65479451849..1177cef3be4d 100644
--- a/.github/scripts/windows/build_task.bat
+++ b/.github/scripts/windows/build_task.bat
@@ -26,12 +26,18 @@ if %errorlevel% neq 0 exit /b 3
if "%THREAD_SAFE%" equ "0" set ADD_CONF=%ADD_CONF% --disable-zts
if "%INTRINSICS%" neq "" set ADD_CONF=%ADD_CONF% --enable-native-intrinsics=%INTRINSICS%
if "%ASAN%" equ "1" set ADD_CONF=%ADD_CONF% --enable-sanitizer --enable-debug-pack
+if "%CLANG_TOOLSET%" equ "1" set ADD_CONF=%ADD_CONF% --with-toolset=clang
rem C4018: comparison: signed/unsigned mismatch
rem C4146: unary minus operator applied to unsigned type
rem C4244: type conversion, possible loss of data
rem C4267: 'size_t' type conversion, possible loss of data
-set CFLAGS=/W3 /WX /wd4018 /wd4146 /wd4244 /wd4267
+if "%CLANG_TOOLSET%" equ "1" (
+ rem Clang is much stricter than MSVC, produces too many warnings that would fail the build with /WX
+ set CFLAGS=/W3 /wd4018 /wd4146 /wd4244 /wd4267
+) else (
+ set CFLAGS=/W3 /WX /wd4018 /wd4146 /wd4244 /wd4267
+)
cmd /c configure.bat ^
--enable-snapshot-build ^
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 0ab56f77ab3f..ffb45c9a20cd 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -10,7 +10,7 @@ on:
- docs/**
jobs:
pages:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
diff --git a/.github/workflows/real-time-benchmark.yml b/.github/workflows/real-time-benchmark.yml
index 9276539841e7..dc28e8438db2 100644
--- a/.github/workflows/real-time-benchmark.yml
+++ b/.github/workflows/real-time-benchmark.yml
@@ -16,32 +16,22 @@ on:
options:
- "0"
- "1"
- instruction_count:
- description: 'Whether Valgrind instruction count should be measured'
+ collect_extended_perf_stats:
+ description: 'Whether to collect extended perf stats as artifacts'
required: true
default: "0"
type: choice
options:
- "0"
- "1"
- opcache:
- description: 'Whether opcache is enabled for the benchmarked commit'
+ debug_environment:
+ description: 'Whether to collect environment debug logs as artifacts'
required: true
- default: "1"
- type: choice
- options:
- - "0"
- - "1"
- - "2"
- baseline_opcache:
- description: 'Whether opcache is enabled for the baseline commit'
- required: true
- default: "1"
+ default: "0"
type: choice
options:
- "0"
- "1"
- - "2"
run_micro_bench:
description: 'Whether to run the micro_bench.php test'
required: true
@@ -54,23 +44,22 @@ permissions:
contents: read
pull-requests: write
concurrency:
- group: ${{ github.workflow }}
+ group: ${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: false
jobs:
REAL_TIME_BENCHMARK:
name: REAL_TIME_BENCHMARK
if: github.repository == 'php/php-src' || github.event_name == 'workflow_dispatch'
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
env:
REPOSITORY: ${{ github.repository }}
BRANCH: "master"
COMMIT: ${{ github.sha }}
BASELINE_COMMIT: "d5f6e56610c729710073350af318c4ea1b292cfe"
ID: "master"
- OPCACHE: "1"
- BASELINE_OPCACHE: "2"
JIT: "1"
- INSTRUCTION_COUNT: "1"
+ COLLECT_EXTENDED_PERF_STATS: "0"
+ DEBUG_ENVIRONMENT: "0"
RUN_MICRO_BENCH: "0"
YEAR: ""
steps:
@@ -82,7 +71,7 @@ jobs:
echo "YEAR=$YEAR" >> $GITHUB_ENV
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- PR_INFO=$(gh pr view ${{ inputs.pull_request }} --json headRepositoryOwner,headRepository,headRefName,headRefOid,baseRefOid --repo ${{ github.repository }} | jq -r '.headRepositoryOwner.login, .headRepository.name, .headRefName, .headRefOid, .baseRefOid')
+ PR_INFO=$(gh pr view ${{ inputs.pull_request }} --json headRepositoryOwner,headRepository,headRefName,headRefOid,baseRefName --repo ${{ github.repository }} | jq -r '.headRepositoryOwner.login, .headRepository.name, .headRefName, .headRefOid, .baseRefName')
REPOSITORY="$(echo "$PR_INFO" | sed -n '1p')/$(echo "$PR_INFO" | sed -n '2p')"
echo "REPOSITORY=$REPOSITORY" >> $GITHUB_ENV
@@ -93,21 +82,22 @@ jobs:
COMMIT=$(echo "$PR_INFO" | sed -n '4p')
echo "COMMIT=$COMMIT" >> $GITHUB_ENV
- BASELINE_COMMIT=$(echo "$PR_INFO" | sed -n '5p')
+ BASELINE_BRANCH=$(echo "$PR_INFO" | sed -n '5p')
+
+ BASELINE_COMMIT=$(gh api /repos/${{ github.repository }}/compare/$BASELINE_BRANCH...$COMMIT --jq '.merge_base_commit.sha')
echo "BASELINE_COMMIT=$BASELINE_COMMIT" >> $GITHUB_ENV
echo "ID=benchmarked" >> $GITHUB_ENV
- echo "OPCACHE=${{ inputs.opcache }}" >> $GITHUB_ENV
- echo "BASELINE_OPCACHE=${{ inputs.baseline_opcache }}" >> $GITHUB_ENV
echo "JIT=${{ inputs.jit }}" >> $GITHUB_ENV
- echo "INSTRUCTION_COUNT=${{ inputs.instruction_count }}" >> $GITHUB_ENV
+ echo "COLLECT_EXTENDED_PERF_STATS=${{ inputs.collect_extended_perf_stats }}" >> $GITHUB_ENV
+ echo "DEBUG_ENVIRONMENT=${{ inputs.debug_environment }}" >> $GITHUB_ENV
echo "RUN_MICRO_BENCH=${{ inputs.run_micro_bench }}" >> $GITHUB_ENV
fi
- name: Install dependencies
run: |
- set -ex
+ set -e
sudo apt-get update
sudo apt-get install gpg
@@ -151,33 +141,20 @@ jobs:
repository: php/real-time-benchmark-data
ssh-key: ${{ secrets.PHP_VERSION_BENCHMARK_RESULTS_DEPLOY_KEY }}
path: 'php-version-benchmarks/docs/results'
- - name: Setup infra config
- run: |
- set -e
-
- cp ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini.dist ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
- ESCAPED_DOCKER_REGISTRY=$(printf '%s\n' "${{ secrets.PHP_VERSION_BENCHMARK_DOCKER_REGISTRY }}" | sed -e 's/[\/&]/\\&/g')
- sed -i "s/INFRA_DOCKER_REGISTRY=public.ecr.aws\/abcdefgh/INFRA_DOCKER_REGISTRY=$ESCAPED_DOCKER_REGISTRY/g" ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
- sed -i "s/INFRA_MEASURE_INSTRUCTION_COUNT=0/INFRA_MEASURE_INSTRUCTION_COUNT=${{ env.INSTRUCTION_COUNT }}/g" ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
- cp ./php-version-benchmarks/build/infrastructure/config/aws.tfvars.dist ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
- sed -i 's/access_key = ""/access_key = "${{ secrets.PHP_VERSION_BENCHMARK_AWS_ACCESS_KEY }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
- sed -i 's/secret_key = ""/secret_key = "${{ secrets.PHP_VERSION_BENCHMARK_AWS_SECRET_KEY }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
- sed -i 's/github_token = ""/github_token = "${{ secrets.GITHUB_TOKEN }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
- name: Setup PHP config - baseline PHP version
run: |
set -e
- BASELINE_SHORT_SHA="$(echo "${{ env.BASELINE_COMMIT }}" | cut -c1-4)"
+ BASELINE_SHORT_SHA="$(echo "${{ env.BASELINE_COMMIT }}" | cut -c 1-7)"
cat << EOF > ./php-version-benchmarks/config/php/baseline.ini
PHP_NAME="PHP - baseline@$BASELINE_SHORT_SHA"
PHP_ID=php_baseline
- PHP_REPO=https://github.com/${{ env.REPOSITORY }}.git
+ PHP_REPO=${{ github.server_url }}/${{ env.REPOSITORY }}.git
PHP_BRANCH=${{ env.BRANCH }}
PHP_COMMIT=${{ env.BASELINE_COMMIT }}
- PHP_OPCACHE=${{ env.BASELINE_OPCACHE }}
PHP_JIT=0
EOF
- name: Setup PHP config - baseline PHP version with JIT
@@ -185,17 +162,16 @@ jobs:
run: |
set -e
- BASELINE_SHORT_SHA="$(echo "${{ env.BASELINE_COMMIT }}" | cut -c1-4)"
+ BASELINE_SHORT_SHA="$(echo "${{ env.BASELINE_COMMIT }}" | cut -c 1-7)"
cat << EOF > ./php-version-benchmarks/config/php/baseline_jit.ini
PHP_NAME="PHP - baseline@$BASELINE_SHORT_SHA (JIT)"
PHP_ID=php_baseline_jit
- PHP_REPO=https://github.com/${{ env.REPOSITORY }}.git
+ PHP_REPO=${{ github.server_url }}/${{ env.REPOSITORY }}.git
PHP_BRANCH=${{ env.BRANCH }}
PHP_COMMIT=${{ env.BASELINE_COMMIT }}
- PHP_OPCACHE=${{ env.BASELINE_OPCACHE }}
PHP_JIT=${{ env.JIT }}
EOF
@@ -213,15 +189,16 @@ jobs:
LAST_RESULT_SHA="$(cd ./php-version-benchmarks/tmp/php_${{ env.ID }}/ && git --no-pager log --until="$YESTERDAY" -n 1 --pretty='%H')"
fi
+ echo "LAST_RESULT_SHA=$LAST_RESULT_SHA" >> $GITHUB_ENV
+
cat << EOF > ./php-version-benchmarks/config/php/previous.ini
PHP_NAME="PHP - previous ${{ env.BRANCH }}"
PHP_ID=php_previous
- PHP_REPO=https://github.com/${{ env.REPOSITORY }}.git
+ PHP_REPO=${{ github.server_url }}/${{ env.REPOSITORY }}.git
PHP_BRANCH=${{ env.BRANCH }}
PHP_COMMIT=$LAST_RESULT_SHA
- PHP_OPCACHE=1
PHP_JIT=0
EOF
- name: Setup PHP config - benchmarked PHP version
@@ -232,11 +209,10 @@ jobs:
PHP_NAME="PHP - ${{ env.BRANCH }}"
PHP_ID=php_${{ env.ID }}
- PHP_REPO=https://github.com/${{ env.REPOSITORY }}.git
+ PHP_REPO=${{ github.server_url }}/${{ env.REPOSITORY }}.git
PHP_BRANCH=${{ env.BRANCH }}
PHP_COMMIT=${{ env.COMMIT }}
- PHP_OPCACHE=${{ env.OPCACHE }}
PHP_JIT=0
EOF
- name: Setup PHP config - benchmarked PHP version with JIT
@@ -248,11 +224,10 @@ jobs:
PHP_NAME="PHP - ${{ env.BRANCH }} (JIT)"
PHP_ID=php_${{ env.ID }}_jit
- PHP_REPO=https://github.com/${{ env.REPOSITORY }}.git
+ PHP_REPO=${{ github.server_url }}/${{ env.REPOSITORY }}.git
PHP_BRANCH=${{ env.BRANCH }}
PHP_COMMIT=${{ env.COMMIT }}
- PHP_OPCACHE=${{ env.OPCACHE }}
PHP_JIT=${{ env.JIT }}
EOF
@@ -269,13 +244,60 @@ jobs:
if [ "${{ env.RUN_MICRO_BENCH }}" -eq "1" ]; then
cp ./php-version-benchmarks/config/test/6_micro_bench.php.ini.dist ./php-version-benchmarks/config/test/6_micro_bench.php.ini
fi
+ - name: Setup infra config
+ run: |
+ set -e
+
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+ WORKSPACE="manual"
+ BASE_COMMIT="$(echo "${{ env.BASELINE_COMMIT }}" | cut -c 1-10)"
+ else
+ WORKSPACE="nightly"
+ BASE_COMMIT="$(echo "${{ env.LAST_RESULT_SHA }}" | cut -c 1-10)"
+ fi
+ COMPARE_COMMIT="$(echo "${{ env.COMMIT }}" | cut -c 1-10)"
+
+ cp ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini.dist ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
+ sed -i "s|INFRA_DOCKER_REGISTRY=public.ecr.aws/abcdefgh|INFRA_DOCKER_REGISTRY=${{ secrets.PHP_VERSION_BENCHMARK_DOCKER_REGISTRY }}|g" ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
+ sed -i "s|INFRA_WORKSPACE=|INFRA_WORKSPACE=$WORKSPACE|g" ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
+ sed -i "s/INFRA_COLLECT_EXTENDED_PERF_STATS=0/INFRA_COLLECT_EXTENDED_PERF_STATS=${{ env.COLLECT_EXTENDED_PERF_STATS }}/g" ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
+ sed -i "s/INFRA_DEBUG_ENVIRONMENT=0/INFRA_DEBUG_ENVIRONMENT=${{ env.DEBUG_ENVIRONMENT }}/g" ./php-version-benchmarks/config/infra/aws/x86_64-metal.ini
+
+ cp ./php-version-benchmarks/build/infrastructure/config/aws.tfvars.dist ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+ sed -i 's/access_key = ""/access_key = "${{ secrets.PHP_VERSION_BENCHMARK_AWS_ACCESS_KEY }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+ sed -i 's/secret_key = ""/secret_key = "${{ secrets.PHP_VERSION_BENCHMARK_AWS_SECRET_KEY }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+ sed -i 's/state_bucket = ""/state_bucket = "${{ secrets.PHP_VERSION_BENCHMARK_STATE_BUCKET }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+ sed -i 's/github_token = ""/github_token = "${{ secrets.GITHUB_TOKEN }}"/g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+
+ WORKFLOW_RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
+ sed -i "s|log_url = \"\"|log_url = \"$WORKFLOW_RUN_URL\"|g" ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+ sed -i 's|artifact_url = ""|artifact_url = "#ARTIFACT_URL#"|g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+
+ CHANGESET_URL="${{ github.server_url }}/${{ github.repository }}/compare/$BASE_COMMIT..$COMPARE_COMMIT"
+ sed -i 's|extra_title = ""|extra_title = "Changeset"|g' ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
+ sed -i "s|extra_text = \"\"|extra_text = \"$CHANGESET_URL\"|g" ./php-version-benchmarks/build/infrastructure/config/aws.tfvars
- name: Run benchmark
- run: ./php-version-benchmarks/benchmark.sh run aws
- - name: Store results
+ run: |
+ set -e
+
+ ./php-version-benchmarks/benchmark.sh run aws
+
+ echo "NEWEST_RESULT_DIRECTORY=$(ls -td ${{ github.workspace }}/php-version-benchmarks/docs/results/${{ env.YEAR }}/*/ | head -1)" >> $GITHUB_ENV
+ - name: Upload artifacts
+ id: upload
+ uses: actions/upload-artifact@v6
+ with:
+ name: results
+ path: |
+ ./php-version-benchmarks/tmp/results/${{ env.YEAR }}/**/*
+ retention-days: 30
+ - name: Commit results
if: github.repository == 'php/php-src' && github.event_name != 'workflow_dispatch'
run: |
set -ex
+ sed -i "s|#ARTIFACT_URL#|${{ steps.upload.outputs.artifact-url }}|g" "${NEWEST_RESULT_DIRECTORY}result.md"
+
cd ./php-version-benchmarks/docs/results
git pull --autostash
if [ -e ".git/MERGE_HEAD" ]; then
@@ -288,31 +310,10 @@ jobs:
fi
git commit -m "Add result for ${{ github.repository }}@${{ github.sha }}"
git push
- - name: Upload artifact
- if: github.event_name == 'workflow_dispatch'
- uses: actions/upload-artifact@v6
- with:
- name: results
- path: ./php-version-benchmarks/docs/results/${{ env.YEAR }}
- retention-days: 30
- name: Comment results
if: github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd ./php-version-benchmarks/tmp/php_${{ env.ID }}
- NEWEST_RESULT_DIRECTORY=$(ls -td ${{ github.workspace }}/php-version-benchmarks/docs/results/${{ env.YEAR }}/*/ | head -1)
gh pr comment ${{ inputs.pull_request }} --body-file "${NEWEST_RESULT_DIRECTORY}result.md" --repo ${{ github.repository }}
- - name: Cleanup
- if: always()
- run: |
- set -ex
-
- rm -rf ./php-version-benchmarks/tmp/
- rm -f ./php-version-benchmarks/build/infrastructure/config/*.tfvars
- rm -rf ./php-version-benchmarks/build/infrastructure/aws/.terraform/
- rm -f ./php-version-benchmarks/build/infrastructure/aws/.terraform.lock.hcl
- rm -f ./php-version-benchmarks/build/infrastructure/aws/aws.tfplan
- rm -f ./php-version-benchmarks/build/infrastructure/aws/terraform.tfstate
- rm -f ./php-version-benchmarks/build/infrastructure/aws/terraform.tfstate.backup
- rm -f ./php-version-benchmarks/config/infra/aws/*.ini
diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml
index b7609608f8be..efe5a650d2c6 100644
--- a/.github/workflows/test-suite.yml
+++ b/.github/workflows/test-suite.yml
@@ -345,18 +345,18 @@ jobs:
if: ${{ inputs.all_variations }}
uses: ./.github/actions/test-macos
- name: Test Tracing JIT
- if: ${{ matrix.arch == 'X64' || !matrix.zts }}
+ if: ${{ matrix.jit }}
uses: ./.github/actions/test-macos
with:
enableOpcache: true
jitType: tracing
- name: Test OpCache
- if: ${{ inputs.all_variations || (matrix.arch == 'ARM64' && matrix.zts) }}
+ if: ${{ inputs.all_variations }}
uses: ./.github/actions/test-macos
with:
enableOpcache: true
- name: Test Function JIT
- if: ${{ inputs.all_variations && (matrix.arch == 'X64' || !matrix.zts) }}
+ if: ${{ inputs.all_variations && matrix.jit }}
uses: ./.github/actions/test-macos
with:
enableOpcache: true
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
new file mode 100644
index 000000000000..fa79580f42ec
--- /dev/null
+++ b/.github/workflows/unit-tests.yml
@@ -0,0 +1,79 @@
+name: Unit Tests
+on:
+ push:
+ paths:
+ - 'main/network.c'
+ - 'main/io/**'
+ - 'main/php_io.h'
+ - 'tests/unit/**'
+ - '.github/workflows/unit-tests.yml'
+ branches:
+ - master
+ pull_request:
+ paths:
+ - 'main/network.c'
+ - 'main/io/**'
+ - 'main/php_io.h'
+ - 'tests/unit/**'
+ - '.github/workflows/unit-tests.yml'
+ branches:
+ - '**'
+ workflow_dispatch: ~
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.url || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ CC: ccache gcc
+ CXX: ccache g++
+
+jobs:
+ UNIT_TESTS:
+ if: github.repository == 'php/php-src' || github.event_name == 'pull_request'
+ name: UNIT_TESTS_LINUX_X64
+ runs-on: ubuntu-24.04
+ timeout-minutes: 20
+ steps:
+ - name: git checkout
+ uses: actions/checkout@v6
+
+ - name: Install dependencies
+ run: |
+ set -x
+ sudo apt-get update
+ sudo apt-get install -y \
+ libcmocka-dev \
+ autoconf \
+ gcc \
+ make \
+ unzip \
+ bison \
+ re2c \
+ locales \
+ ccache
+
+ - name: ccache
+ uses: hendrikmuhs/ccache-action@v1.2
+ with:
+ key: "unit-tests-${{hashFiles('main/php_version.h')}}"
+ append-timestamp: false
+ save: ${{ github.event_name != 'pull_request' }}
+
+ - name: ./configure (minimal build)
+ uses: ./.github/actions/configure-unit-tests
+
+ - name: make libphp.a
+ run: |
+ set -x
+ make -j$(/usr/bin/nproc) >/dev/null
+
+ - name: Run unit tests
+ run: |
+ set -x
+ cd tests/unit
+ make test
+
diff --git a/.github/workflows/verify-bundled-files.yml b/.github/workflows/verify-bundled-files.yml
new file mode 100644
index 000000000000..473d6e2d8f4d
--- /dev/null
+++ b/.github/workflows/verify-bundled-files.yml
@@ -0,0 +1,70 @@
+name: Verify Bundled Files
+
+on:
+ push:
+ paths: &paths
+ - '.github/scripts/download-bundled/**'
+ - 'Zend/asm/**'
+ - 'ext/pcre/pcre2lib/**'
+ - 'ext/uri/uriparser/**'
+ pull_request:
+ paths: *paths
+ schedule:
+ - cron: "0 1 * * *"
+ workflow_dispatch: ~
+
+permissions:
+ contents: read
+
+jobs:
+ VERIFY_BUNDLED_FILES:
+ if: github.repository == 'php/php-src' || github.event_name == 'workflow_dispatch'
+ name: Verify Bundled Files
+ runs-on: ubuntu-24.04
+ steps:
+ - name: git checkout
+ uses: actions/checkout@v6
+
+ - name: Detect changed files
+ if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
+ uses: dorny/paths-filter@v3
+ id: changes
+ with:
+ filters: |
+ 'boost-context':
+ - '.github/scripts/download-bundled/boost-context.*'
+ - 'Zend/asm/**'
+ pcre2:
+ - '.github/scripts/download-bundled/pcre2.*'
+ - 'ext/pcre/pcre2lib/**'
+ uriparser:
+ - '.github/scripts/download-bundled/uriparser.*'
+ - 'ext/uri/uriparser/**'
+
+ - name: 'boost.context'
+ if: ${{ !cancelled() && (steps.changes.outputs.boost-context == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
+ run: |
+ echo "::group::Download"
+ .github/scripts/download-bundled/boost-context.sh
+ echo "::endgroup::"
+ echo "::group::Verify files"
+ .github/scripts/test-directory-unchanged.sh Zend/asm
+ echo "::endgroup::"
+ - name: PCRE2
+ if: ${{ !cancelled() && (steps.changes.outputs.pcre2 == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
+ run: |
+ echo "::group::Download"
+ .github/scripts/download-bundled/pcre2.sh
+ echo "::endgroup::"
+ echo "::group::Verify files"
+ .github/scripts/test-directory-unchanged.sh ext/pcre/pcre2lib
+ echo "::endgroup::"
+ - name: uriparser
+ if: ${{ !cancelled() && (steps.changes.outputs.uriparser == 'true' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
+ run: |
+ echo "::group::Download"
+ .github/scripts/download-bundled/uriparser.sh
+ echo "::endgroup::"
+ echo "::group::Verify files"
+ .github/scripts/test-directory-unchanged.sh ext/uri/uriparser
+ echo "::endgroup::"
diff --git a/CODING_STANDARDS.md b/CODING_STANDARDS.md
index c599194ed50e..3a53d0e258ca 100644
--- a/CODING_STANDARDS.md
+++ b/CODING_STANDARDS.md
@@ -79,6 +79,16 @@ rewritten to comply with these rules.
return value for functions that perform some operation that may
succeed or fail.
+1. When throwing a `ValueError` or emitting a warning, use consistent
+ phrasing for error messages. Common patterns are:
+
+ * Type errors: `must be of type int` (use the type name, not e.g. `must be an integer`)
+ * Range/boundary: `must be between X and Y` / `must be greater than [or equal to] X` / `must be less than X` / `must be finite`
+ * String constraints: `must not contain any null bytes` / `must not be empty` / `must be a single character`
+ * Valid value: `must be a valid X` (e.g. `must be a valid encoding`, `must be a valid calendar ID`)
+ * Enum-like: `must be one of X, Y, or Z`
+ * Structural: `must have X` / `must have key X` / `must have N elements`
+
## User functions/methods naming conventions
1. Function names for user-level functions should be enclosed with in the
@@ -276,7 +286,7 @@ rewritten to comply with these rules.
1. The length of constant string literals should be calculated via ``strlen()``
instead of using ``sizeof()-1`` as it is clearer and any modern compiler
- will optimize it away. Legacy usages of the latter style exists within the
+ will optimize it away. Legacy usages of the latter style exist within the
codebase but should not be refactored, unless larger refactoring around that
code is taking place.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 31d893e8d42a..aabd945ad385 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,6 +27,7 @@ had several contributions accepted, commit privileges are often quickly granted.
* [Git commit rules](#git-commit-rules)
* [Copyright and license headers](#copyright-and-license-headers)
* [NEWS file](#news)
+* [LLM usage in GitHub comments](#llm-usage-in-github-comments)
## Pull requests
@@ -36,8 +37,8 @@ implement RFCs. Please be sure to include tests as appropriate!
By submitting a pull request, you certify that you have the necessary rights
to submit the work, that the work does not violate any third-party rights
(including those of your employer, if applicable), and that you license your
-contribution under the PHP License or under another license if explicitly
-accepted by the PHP project maintainers.
+contribution under the [Modified BSD License](LICENSE) or under another license
+if explicitly accepted by the PHP project maintainers.
If you are fixing a bug, then please submit your PR against the lowest actively
supported branch of PHP that the bug affects (only green branches on
@@ -90,7 +91,7 @@ repository. Mailing list subscription is explained on the
[mailing lists page](https://www.php.net/mailing-lists.php).
You may also want to read
-[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/post/the-mysterious-php-rfc-process-and-how-you-can-change-the-web)
+[The Mysterious PHP RFC Process](https://web.archive.org/web/20210621140006/https://blogs.oracle.com/opal/the-mysterious-php-rfc-process-and-how-you-can-change-the-web)
for additional notes on the best way to approach submitting an RFC.
## Technical resources
@@ -100,6 +101,7 @@ scattered across different websites, and often outdated. Nonetheless, they can
provide a good starting point for learning about the fundamentals of the code
base.
+* https://php.github.io/php-src/
* https://www.phpinternalsbook.com/
* https://www.npopov.com/
* [Internal value representation](https://www.npopov.com/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html), [part 2](https://www.npopov.com/2015/06/19/Internal-value-representation-in-PHP-7-part-2.html)
@@ -150,9 +152,8 @@ about what you're working on, you can contact us via the
issues.
Although not a formal channel, you can also find a number of core developers on
-the #php.pecl channel on [EFnet](http://www.efnet.org/). Similarly, many
-documentation writers can be found on #php.doc. Windows development IRC channel
-is available at #winphp-dev on FreeNode.
+the [php community discord server](https://phpc.chat/) (`#php-internals` channel)
+where many people are eager to help.
## PHP source code directory structure
@@ -175,6 +176,7 @@ locations.
├─ config.guess # https://git.savannah.gnu.org/cgit/config.git
├─ config.sub # https://git.savannah.gnu.org/cgit/config.git
├─ libtool.m4 # https://git.savannah.gnu.org/cgit/libtool.git
+ ├─ lt*.m4 # https://git.savannah.gnu.org/cgit/libtool.git
├─ ltmain.sh # https://git.savannah.gnu.org/cgit/libtool.git
├─ pkg.m4 # https://gitlab.freedesktop.org/pkg-config/pkg-config
├─ shtool # https://www.gnu.org/software/shtool/
@@ -353,29 +355,21 @@ Having said that, here are the organizational rules:
`--enable-zts` switch to ensure your code handles TSRM correctly and doesn't
break for those who need that.
-Currently, we have the following branches in use:
-
-| Branch | |
-| --------- | --------- |
-| master | Active development branch for PHP 8.5, which is open for backwards incompatible changes and major internal API changes. |
-| PHP-8.4 | Is used to release the PHP 8.4.x series. This is a current stable version and is open for bugfixes only. |
-| PHP-8.3 | Is used to release the PHP 8.3.x series. This is a current stable version and is open for bugfixes only. |
-| PHP-8.2 | Is used to release the PHP 8.2.x series. This is an old stable version and is open for security fixes only. |
-| PHP-8.1 | Is used to release the PHP 8.1.x series. This is an old stable version and is open for security fixes only. |
-| PHP-8.0 | This branch is closed. |
-| PHP-7.4 | This branch is closed. |
-| PHP-7.3 | This branch is closed. |
-| PHP-7.2 | This branch is closed. |
-| PHP-7.1 | This branch is closed. |
-| PHP-7.0 | This branch is closed. |
-| PHP-5.6 | This branch is closed. |
-| PHP-5.5 | This branch is closed. |
-| PHP-5.4 | This branch is closed. |
-| PHP-5.3 | This branch is closed. |
-| PHP-5.2 | This branch is closed. |
-| PHP-5.1 | This branch is closed. |
-| PHP-4.4 | This branch is closed. |
-| PHP-X.Y.Z | These branches are used for the release managers for tagging the releases, hence they are closed to the general public. |
+The master branch is an active development branch for the newest version of PHP,
+which is open for backwards incompatible changes and major internal API changes.
+
+For PHP-X.Y branches, they are used to release the PHP X.Y.z series. Please see
+the [supported versions page](https://www.php.net/supported-versions.php) to get
+the status of each version.
+
+If a version is described as "Active support", the corresponding branch is a
+current stable version and is open for bugfixes only. If a version is described
+as "Security fixes only", the corresponding branch is an old stable version
+and is open for security fixes only. If a version is described as "End of life",
+the corresponding branch is closed.
+
+Note that PHP-X.Y.Z branches are used for the release managers for tagging the
+releases, hence they are closed to the general public.
The next few rules are more of a technical nature:
@@ -435,15 +429,13 @@ New source code files should include the following header block:
```c
/*
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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 |
+----------------------------------------------------------------------+
| Author: |
+----------------------------------------------------------------------+
@@ -534,6 +526,12 @@ If for some reason a feature is introduced in a branch lower than master,
although this is strictly prohibited by other policies, then the entry must
also be in master.
+## LLM usage in GitHub comments
+
+When using LLMs to generate comments to maintainers for any purpose other than
+direct translation, we would highly appreciate it if you disclosed the relevant
+paragraphs as such via markdown quote.
+
## Thanks
Thank you for contributing to PHP!
diff --git a/EXTENSIONS b/EXTENSIONS
index 148a6ff687fd..01807655e3a5 100644
--- a/EXTENSIONS
+++ b/EXTENSIONS
@@ -373,8 +373,8 @@ STATUS: Working
EXTENSION: mbstring
PRIMARY MAINTAINER: Rui Hirokawa (2001 - 2013)
Nikita Popov (2017 - 2020)
- Alex Dowad (2021 - 2024)
- Yuya Hamada (2024 - 2024)
+ Alex Dowad (2021 - 2026)
+ Yuya Hamada (2024 - 2026)
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 0815d7eb7911..ee42a57fffbb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,68 +1,27 @@
---------------------------------------------------------------------
- The PHP License, version 3.01
-Copyright (c) 1999 - 2024 The PHP Group. All rights reserved.
---------------------------------------------------------------------
+Copyright © 1999–2026, The PHP Group and Contributors.
+Copyright © 1999–2026, Zend Technologies Ltd., a subsidiary company of Perforce Software, Inc.
Redistribution and use in source and binary forms, with or without
-modification, is permitted provided that the following conditions
-are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- 3. The name "PHP" must not be used to endorse or promote products
- derived from this software without prior written permission. For
- written permission, please contact group@php.net.
-
- 4. Products derived from this software may not be called "PHP", nor
- may "PHP" appear in their name, without prior written permission
- from group@php.net. You may indicate that your software works in
- conjunction with PHP by saying "Foo for PHP" instead of calling
- it "PHP Foo" or "phpfoo"
-
- 5. The PHP Group may publish revised and/or new versions of the
- license from time to time. Each version will be given a
- distinguishing version number.
- Once covered code has been published under a particular version
- of the license, you may always continue to use it under the terms
- of that version. You may also choose to use such covered code
- under the terms of any subsequent version of the license
- published by the PHP Group. No one other than the PHP Group has
- the right to modify the terms applicable to covered code created
- under this License.
-
- 6. Redistributions of any form whatsoever must retain the following
- acknowledgment:
- "This product includes PHP software, freely available from
- ".
-
-THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
-ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
-DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-OF THE POSSIBILITY OF SUCH DAMAGE.
-
---------------------------------------------------------------------
-
-This software consists of voluntary contributions made by many
-individuals on behalf of the PHP Group.
-
-The PHP Group can be contacted via Email at group@php.net.
-
-For more information on the PHP Group and the PHP project,
-please see .
-
-PHP includes the Zend Engine, freely available at
-.
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/NEWS b/NEWS
index 7e138fcc2bb1..3c5101c096cb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,25 +1,92 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
-?? ??? ????, PHP 8.5.9
+?? ??? ????, PHP 8.6.0alpha3
- Core:
- . Fixed bug GH-22290 (AST pretty printing does not correctly handle strings
- containing NUL). (iliaal)
- . Fixed bug GH-22206 (missing return in global register detection).
- (P3p111n0)
+ . Implemented partial function application RFC. (Arnaud)
+ . Fixed bug GH-22263 (reset typed property default on every unserialize
+ failure path). (David Carlier)
+
+- DOM:
+ . Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
+ declares a default value for the attribute). (iliaal)
+
+- GMP:
+ . Fixed GMP power and shift operators to reject GMP right operands outside
+ the unsigned long range instead of silently truncating them. (Weilin Du)
+ . Fixed GMP integer string parsing to reject strings containing NUL bytes
+ instead of silently truncating them. (Weilin Du)
+ . Fixed GMP error messages that referenced outdated parameter names.
+ (Weilin Du)
+
+- Intl:
+ . Fixed grammatical issues in Normalizer invalid form and IntlCalendar time
+ zone offset error messages. (Weilin Du)
+
+- ODBC:
+ . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
+ driver-reported display size). (iliaal)
+
+- Opcache:
+ . Re-enable JIT for ZTS builds on Apple Silicon. (realFlowControl)
+
+- PDO_ODBC:
+ . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
+ driver-reported display size). (iliaal)
+ . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
+ longer than the declared maxlen). (iliaal)
+ . Fixed bug GH-22665 (Out-of-bounds write when the ODBC driver reports a
+ diagnostic message length beyond the error buffer). (iliaal)
+
+- Reflection:
+ . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes).
+ (DanielEScherzer)
+
+- SOAP:
+ . Fixed header injection through the Content-Type context option, the
+ soapaction and the cookie names and values. (David Carlier)
+
+- Sockets:
+ . Fixed socket_set_option() validation error messages for UDP_SEGMENT and
+ TCP_USER_TIMEOUT, and SO_LINGER options. (Weilin Du)
+ . Fixed various memory related issues in ext/sockets. (David Carlier)
+
+- Streams:
+ . Added a new IO copy API used by php_stream_copy_to_stream_ex() that
+ leverages platform primitives (sendfile, splice, copy_file_range,
+ TransmitFile) for faster stream copying. (Jakub Zelenka, David Carlier)
+
+16 Jul 2026, PHP 8.6.0alpha2
+
+- Core:
+ . Sync Boost.Context assembly with 1.91.0. (kn1g78)
+ . Fixed bug GH-22387 (AST pretty-printing drops meaningful parentheses around
+ RHS of instanceof). (timwolla)
+ . Fixed bug GH-15672 and GH-15911 (Stack overflow when an internal function
+ recurses through zend_call_function, such as a self-attached SPL
+ iterator). (iliaal)
. Lock unmodified readonly properties for modification after clone-with.
(NickSdot)
+ . abort() instead of exit() on hard OOM. (realFlowControl)
+ . perf: ZTS: move AG and SCNG into native __thread storage. (henderkes)
- Calendar:
. Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with
INT_MAX year). (arshidkv12)
+- Curl:
+ . Added CURLOPT_SEEKFUNCTION and the CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL
+ and CURL_SEEKFUNC_CANTSEEK constants, letting libcurl rewind a streamed
+ request body to resend it on a redirect, multi-pass authentication or a
+ retried reused connection. (GrahamCampbell)
+
- Date:
. Update timelib to 2022.17. (Derick)
. Fixed bug GH-19803 (Parsing a string with a single white space does create
an error). (Derick)
. Fixed Unix timestamps in February of the year 0 are misparsed with
@-notation. (LukasGelbmann)
+ . Fixed bug GH-11368 (idate() doesn't work for the year -1). (Derick)
. Fixed bug GH-11310 (__debugInfo does nothing on userland classes extending
Date classes). (Derick)
@@ -29,75 +96,59 @@ PHP NEWS
- DOM:
. Fixed bug GH-22570 (Stack overflow when serializing a deeply nested
Dom\XMLDocument). (iliaal)
- . Fixed getElementsByClassName() item() returning the wrong element on
- random access. (David Carlier)
+ . Fixed Dom\DtdNamedNodeMap integer dimension access so negative indexes
+ return NULL and indexes outside the int range throw ValueError instead of
+ returning the first entity or notation. (Weilin Du)
+ . Fixed bug GH-22623 (use after free with namespace nodes from
+ XSLTProcessor::registerFunctions())/ (David Carlier)
+ . Fixed bug GH-22554 (use-after-free with XPath callback returning a node
+ from a foreign document). (David Carlier)
- Exif:
. Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman)
-- Hash:
- . Fixed bug GH-18173 (ext/hash relies on implementation-defined malloc
- alignment). (iliaal)
+- GMP:
+ . Fixed bug GH-22549 (Assertion failure / UB on a compound GMP power or shift
+ assignment with a negative exponent). (iliaal)
-- ODBC:
- . Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
- driver-reported display size). (iliaal)
+- Intl:
+ . Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to
+ reject offset values outside the 32-bit range instead of silently
+ truncating them. (Weilin Du)
+ . IntlDateFormatter::parse()/datefmt_parse() and
+ IntlDateFormatter::localtime()/datefmt_localtime() now raise TypeError
+ when the offset argument is not of type int. (Weilin Du)
+
+- JSON:
+ . Report unterminated JSON strings as syntax errors. (timwolla)
+ . Improve performance error position tracking during JSON decoding.
+ (henderkes)
+ . Fixed bug GH-22514 (Incorrect error column in PHP 8.6 JSON parser).
+ (henderkes, timwolla)
- Opcache:
- . Fixed bug GH-22158 (Tracing JIT dispatches the observer begin handler
- through the wrong run_time_cache slot on megamorphic calls). (ptondereau,
- iliaal)
- . Fixed bug GH-22443 (Tracing JIT SIGSEGV on megamorphic dynamic calls from
- an undereferenced run_time_cache map_ptr offset). (iliaal)
. Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache
preloaded trait). (iliaal)
- OpenSSL:
+ . Added $salt_length parameter to openssl_sign() and openssl_verify() with
+ new OPENSSL_RSA_PSS_SALTLEN_* constants. (Jakub Zelenka)
. Fixed timeout for supplemental read at end of a blocking stream in SSL
stream wrapper. (ilutov)
-
-- Intl:
- . Fixed Locale::lookup() and locale_lookup() to return NULL instead of the
- fallback locale when a language tag cannot be canonicalized. (Weilin Du)
- . Fixed memory leaks when calling Collator::__construct() or
- Spoofchecker::__construct() twice. (Weilin Du)
- . Fixed memory leak when calling IntlListFormatter::__construct() twice.
- (Weilin Du)
- . Fixed IntlChar methods leaving stale global error state after successful
- calls. (Xuyang Zhang)
+ . Fixed stream_socket_get_crypto_status() after supplemental read. (ilutov)
- PDO_ODBC:
. Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN
carries no credentials). (iliaal)
- . Fixed bug GH-22667 (Heap buffer over-read when a column value exceeds the
- driver-reported display size). (iliaal)
- . Fixed bug GH-22666 (Heap buffer overflow when an output parameter value is
- longer than the declared maxlen). (iliaal)
- . Fixed bug GH-22665 (Out-of-bounds write when the ODBC driver reports a
- diagnostic message length beyond the error buffer). (iliaal)
-
-- Phar:
- . Fixed inconsistent handling of the magic ".phar" directory. Paths such as
- "/.phar" remain protected, while non-magic paths that merely start with
- ".phar" are handled consistently across file and directory creation,
- copying, ArrayAccess, stream lookup, directory iteration and extraction.
- (Weilin Du)
- PHPDBG:
- . Fixed bug GH-17387 (Trivial crash in phpdbg lexer). (iliaal)
. Fixed fleaked lowercased lookup keys in phpdbg_resolve_opline_break.
(jorgsowa)
. Fixed off-by-one in phpdbg_safe_class_lookup() causing class lookups to
always fail during phpdbg's signal-safe interruption path. (jorgsowa)
- Reflection:
- . Fixed bug GH-22324 (Ignore leading namespace separator in
- ReflectionParameter::__construct()). (jorgsowa)
- . Fixed bug GH-22441 (ReflectionClass::hasProperty() and getProperty() ignore
- dynamic properties shadowing a private parent property). (iliaal)
- . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value
- with null bytes truncates output). (DanielEScherzer)
. Fixed bug GH-22683 (Reflection(Class)Constant::__toString() should not warn
on NAN conversions). (Khaled Alam)
. Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes).
@@ -107,23 +158,27 @@ PHP NEWS
. Fixed bug GH-21314 (Different session garbage collector behavior between
PHP 8.3 and PHP 8.5). (jorgsowa)
-- SPL:
- . Fix class_parents for classes with leading slash in non-autoload mode.
- (jorgsowa)
- . Ignore leading back-slash in class_parents(), class_implements(), and
- class_uses(). (jorgsowa)
- . Fixed bug GH-16217 (SplFileObject::fputcsv() on an uninitialized object
- segfaults). (iliaal)
+- SOAP:
+ . Fixed bug GH-22585 (OOM on bailout with uninitialized
+ do_request() parameters). (David Carlier)
+ . Fixed xsd:hexBinary decoding to reject odd-length values instead of
+ silently truncating the last nibble. (Weilin Du)
+ . Made SOAP encoding errors report the affected type or failing operation
+ instead of the generic "Violation of encoding rules" message. (Weilin Du)
- Standard:
- . Fixed bug GH-22395 (base_convert() outputs at most 64 characters).
- (Weilin Du)
+ . Fixed sleep() and usleep() to reject values that overflow the underlying
+ unsigned int timeout. (Weilin Du)
+ . Fixed bug GH-22671 (assert.bail aborts the process when the assert callback
+ throws an exception whose reporting re-throws). (iliaal)
. Fixed bug GH-22678 (Use-after-free in array_multisort() when the comparator
mutates the array being sorted). (azchin, iliaal)
+- Streams:
+ . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL
+ and a proxy set). (CVE-2026-12184) (ndossche)
+
- URI:
- . Fixed behavior of Uri\WhatWg\Url wither methods with regards to empty
- opaque hosts. (kocsismate)
. Fixed bug GH-22628 (Percent-encoding of caret in WHATWG URL paths is not
performed). (kocsismate)
. Fixed bug GH-22629 (WHATWG Validation error incorrect with empty host and
@@ -135,1261 +190,359 @@ PHP NEWS
unchanged comment). (Weilin Du)
. Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores
ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du)
-
-02 Jul 2026, PHP 8.5.8
-
-- Core:
- . Fixed bug GH-22280 (Incorrect compile error for goto to label preceding
- try/finally block). (Pratik Bhujel)
- . Fixed bug GH-22112 (Assertion when error handler throws during NaN to
- bool/string coercion). (iliaal)
-
-- BCMath:
- . Fixed issues with oversized allocations and signed overflow in bcround()
- and BcMath\Number::round(). (edorian)
-
-- Date:
- . Fix incorrect recurrence check of DatePeriod::createFromISO8601String().
- (ndossche)
-
-- Exif:
- . Read correct value for single and double tags. (ndossche)
-
-- GD:
- . Fixed bug GH-22121 (Double free in gdImageSetStyle() after
- overflow-triggered early return). (iliaal)
-
-- Intl:
- . Fix incorrect argument positions for invalid start/end arguments in
- transliterator_transliterate().
- (Weilin Du)
- . Fixed IntlTimeZone::getDisplayName() to synchronize object error state
- for invalid display types. (Weilin Du)
-
-- Lexbor:
- . Merge patch c3a6847. (ilutov, timwolla)
-
-- Opcache:
- . Fixed bug GH-22265 (Another tailcall vm_interrupt bug). (Levi Morrison)
- . Fixed bug GH-20469 (Unsafe inheritance cache replay with reentrant
- autoloading). (Levi Morrison)
- . Fixed bug GH-21972 (Corrupted variable type when a typed by-value return
- contains a reference wrapper). (Weilin Du)
-
-- OpenSSL:
- . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
- openssl_encrypt with AES-WRAP-PAD). (CVE-2026-14355) (David Carlier)
-
-- Phar:
- . Fixed a bypass of the magic ".phar" directory protection in
- Phar::addEmptyDir() for paths starting with "/.phar", while allowing
- non-magic directory names that merely share the ".phar" prefix. (Weilin Du)
- . Fixed an integer underflow when parsing ZIP extra fields. (Weilin Du)
-
-- Reflection:
- . Preserve class-name case in ReflectionClass::getProperty() error messages
- and autoloading. (jorgsowa)
-
-- SOAP:
- . Fixed bug GH-22218 (SoapServer::handle() crash on $_SERVER not being
- an array). (David Carlier / Rex-Reynolds)
- . Fixed bug GH-22285 (Soap server requires the raw input to be passed
- to $server->handle). (David Carlier / ndossche)
-
-- Sqlite:
- . Fix error checks for column retrieval. (ndossche)
-
-- URI:
- . Add LEXBOR_STATIC to CFLAGS_URI on Windows so ext/uri does not see
- LXB_API as __declspec(dllimport) when linked statically into PHP.
- (Luther Monson)
- . Clean error logs before each Uri\WhatWg\Url wither call so that errors from
- previous wither calls are not returned the next time a UrlValidationError
- is thrown. (kocsismate)
-
-- Zip:
- . Fixed error-related memory leaks. (ndossche)
-
-- Zlib:
- . Fixed memory leak if deflate initialization fails and there is a dict.
- (ndossche)
- . Fixed memory leak in inflate_add(). (ndossche)
-
-02 Jun 2026, PHP 8.5.7
-
-- CLI:
- . Fixed bug GH-21901 (Stale getopt() optional value). (onthebed)
-
-- Core:
- . Fixed bug GH-22071 (JIT assertion on abstract static method call).
+ . Fixed bug GH-22176 (memory leak with ZipArchive::registerCancelBack()
+ is used with reference returning function during shutdown).
(David Carlier)
+ . ZipArchive::addGlob() and ZipArchive::addPattern() now raise a TypeError
+ for invalid "remove_all_path", "comp_method", "comp_flags", and
+ "enc_method" options instead of emitting a warning. (David Carlier)
-- Date:
- . Fixed bug GH-18422 (int overflow in php_date_llabs). (iliaal)
-
-- DOM:
- . Fixed bug GH-22077 (UAF in custom XPath function).
- (afflerbach/David Carlier)
-
-- Opcache:
- . Fixed tracing JIT crash when a VM interrupt is handled during an observed
- user function call. (Levi Morrison)
- . Fixed bug GH-21746 (Segfault with tracing JIT). (Arnaud)
- . Fixed bug GH-22004 (Assertion failure at ext/opcache/jit/zend_jit_trace.c).
- (Arnaud)
- . Fixed tailcall VM crash when a VM interrupt is handled from a VM helper.
- (Levi Morrison, Arnaud)
-
-- OpenSSL:
- . Fix compatibility issues with OpenSSL 4.0. (jordikroon, Remi)
-
-- Standard:
- . Fixed bug GH-21689 (version_compare() incorrectly handles versions ending
- with a dot). (timwolla)
-
-- URI:
- . Fixed CVE-2026-44927 (In uriparser before 1.0.2, there is pointer
- difference truncation to int in various places). (CVE-2026-44927)
- (Sebastian Pipping)
- . Fixed CVE-2026-44928 (In uriparser before 1.0.2, the function family
- EqualsUri can misclassify two unequal URIs as equal). (CVE-2026-44928)
- (Sebastian Pipping)
-
-07 May 2026, PHP 8.5.6
+02 Jul 2026, PHP 8.6.0alpha1
- Core:
- . Fixed bug GH-19983 (GC assertion failure with fibers, generators and
- destructors). (iliaal)
- . Fixed ZEND_API mismatch on zend_ce_closure forward decl for Windows+Clang.
+ . Added first-class callable cache to share instances for the duration of the
+ request. (ilutov)
+ . It is now possible to use reference assign on WeakMap without the key
+ needing to be present beforehand. (ndossche)
+ . Added `clamp()`. (kylekatarnls, thinkverse)
+ . Fix OSS-Fuzz #429429090 (Failed assertion on unset() with uninitialized
+ container). (ilutov)
+ . Fixed GH-20564 (Don't call autoloaders with pending exception). (ilutov)
+ . Fix deprecation now showing when accessing null key of an array with JIT.
+ (alexandre-daubois)
+ . Fixed bug GH-20174 (Assertion failure in
+ ReflectionProperty::skipLazyInitialization after failed LazyProxy
+ initialization). (Arnaud)
+ . Enabled the TAILCALL VM on Windows when compiling with Clang >= 19 x86_64.
(henderkes)
- . Fixed bug GH-21504 (Incorrect RC-handling for ZEND_EXT_STMT op1). (ilutov)
- . Fixed bug GH-21478 (Forward property operations to real instance for
- initialized lazy proxies). (iliaal)
- . Fixed bug GH-21605 (Missing addref for Countable::count()). (ilutov)
- . Fixed bug GH-21699 (Assertion failure in shutdown_executor when resolving
- self::/parent::/static:: callables if the error handler throws). (macoaure)
- . Fixed bug GH-21603 (Missing addref for __unset). (ilutov)
- . Fixed bug GH-21760 (Trait with class constant name conflict against
- enum case causes SEGV). (Pratik Bhujel)
-
-- CLI:
- . Fixed bug GH-21754 (`--rf` command line option with a method triggers
- ext/reflection deprecation warnings). (DanielEScherzer)
-
-- Curl:
- . Add support for brotli and zstd on Windows. (Shivam Mathur)
-
-- DOM:
- . Fixed GHSA-4jhr-8w89-j733 and GH-21566 (Dom\XMLDocument::C14N() emits
- duplicate xmlns declarations after setAttributeNS()). (CVE-2026-7263)
+ . 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)
-
-- FPM:
- . Fixed GHSA-7qg2-v9fj-4mwv (XSS within status endpoint). (CVE-2026-6735)
- (Jakub Zelenka)
-
-- Iconv:
- . Fixed bug GH-17399 (iconv memory leak on bailout). (iliaal)
-
-- Lexbor:
- . Upgrade to lexbor v2.7.0. (CVE-2026-29078, CVE-2026-29079)
- (ndossche, ilutov)
-
-- MBString:
- . Fixed GHSA-wm6j-2649-pv75 (Null pointer dereference in
- php_mb_check_encoding() via mb_ereg_search_init()). (CVE-2026-7259)
- (vi3tL0u1s)
- . Fixed GHSA-74r9-qxhc-fx53 (Out-of-bounds access in mbfl_name2encoding_ex()).
- (CVE-2026-6104) (ilutov)
-
-- Opcache:
- . Fixed bug GH-21158 (JIT: Assertion jit->ra[var].flags & (1<<0) failed in
- zend_jit_use_reg). (Arnaud)
- . Fixed bug GH-21593 (Borked function JIT JMPNZ smart branch). (ilutov)
- . Fixed bug GH-21460 (COND optimization regression). (Dmitry, Arnaud)
- . Fixed faulty returns out of zend_try block in zend_jit_trace(). (ilutov)
-
-- OpenSSL:
- . Fix memory leak regression in openssl_pbkdf2(). (ndossche)
- . Fix a bunch of memory leaks and crashes on edge cases. (ndossche)
-
-- PDO_Firebird:
- . Fixed GHSA-w476-322c-wpvm (SQL injection via NUL bytes in quoted strings).
- (CVE-2025-14179) (SakiTakamachi)
-
-- PDO_PGSQL:
- . Fixed bug GH-21683 (pdo_pgsql throws with ATTR_PREFETCH=0
- on empty result set). (thomasschiet)
-
-- Phar:
- . Restore is_link handler in phar_intercept_functions_shutdown. (iliaal)
- . Fixed bug GH-21797 (phar: NULL dereference in Phar::webPhar() when
- SCRIPT_NAME is absent from SAPI environment). (iliaal)
- . Fix memory leak in Phar::offsetGet(). (iliaal)
- . Fix memory leak in phar_add_file(). (iliaal)
- . Fixed bug GH-21799 (phar: propagate phar_stream_flush return value from
- phar_stream_close). (iliaal)
- . Fix memory leak in phar_verify_signature() when md_ctx is invalid.
- (JarneClauw)
-
-- Random:
- . Fixed bug GH-21731 (Random\Engine\Xoshiro256StarStar::__unserialize()
- accepts all-zero state). (iliaal)
-
-- Session:
- . Fixed memory leak when session GC callback return a refcounted value.
- (jorgsowa)
-
-- SOAP:
- . Fixed GHSA-85c2-q967-79q5 (Stale SOAP_GLOBAL(ref_map) pointer with Apache
- Map). (CVE-2026-6722) (ilutov)
- . Fixed GHSA-m33r-qmcv-p97q (Use-after-free after header parsing failure with
- SOAP_PERSISTENCE_SESSION). (CVE-2026-7261) (ilutov)
- . Fixed GHSA-hmxp-6pc4-f3vv (Broken Apache map value NULL check).
- (CVE-2026-7262) (ilutov)
-
-- SPL:
- . Fixed bug GH-21499 (RecursiveArrayIterator getChildren UAF after parent
- free). (Girgias)
- . Fix concurrent iteration and deletion issues in SplObjectStorage.
- (ndossche)
-
-- Sqlite3:
- . Fixed wrong free list comparator pointer type. (David Carlier)
-
-- Standard:
- . Fixed GHSA-96wq-48vp-hh57 (Signed integer overflow of char array offset).
- (CVE-2026-7568) (TimWolla)
- . Fixed GHSA-m8rr-4c36-8gq4 (Consistently pass unsigned char to ctype.h
- functions). (CVE-2026-7258) (ilutov)
-
-- Streams:
- . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL
- and a proxy set). (CVE-2026-12184) (ndossche)
-
-- URI:
- . Fixed CVE-2026-42371 (uriparser before 1.0.1 has numeric truncation in
- text range comparison). (CVE-2026-42371) (Joshua W. Windle)
-
-26 Mar 2026, PHP 8.5.5
-
-- Core:
- . Fixed bug GH-20672 (Incorrect property_info sizing for locally shadowed
- trait properties). (ilutov)
- . Fixed bugs GH-20875, GH-20873, GH-20854 (Propagate IN_GET guard in
- get_property_ptr_ptr for lazy proxies). (iliaal)
-
-- Bz2:
- . Fix truncation of total output size causing erroneous errors. (ndossche)
-
-- DOM:
- . Fixed bug GH-21486 (Dom\HTMLDocument parser mangles xml:space and
- xml:lang attributes). (ndossche)
-
-- FFI:
- . Fixed resource leak in FFI::cdef() onsymbol resolution failure.
- (David Carlier)
-
-- GD:
- . Fixed bug GH-21431 (phpinfo() to display libJPEG 10.0 support).
- (David Carlier)
-
-- Opcache:
- . Fixed bug GH-21052 (Preloaded constant erroneously propagated to file-cached
- script). (ilutov)
- . Fixed bug GH-20838 (JIT compiler produces wrong arithmetic results).
- (Dmitry, iliaal)
- . Fixed bug GH-21267 (JIT tracing: infinite loop on FETCH_OBJ_R with
- IS_UNDEF property in polymorphic context). (Dmitry, iliaal)
- . Fixed bug GH-21395 (uaf in jit). (ndossche)
-
-- OpenSSL:
- . Fixed bug GH-21083 (Skip private_key_bits validation for EC/curve-based
- keys). (iliaal)
- . Fix missing error propagation for BIO_printf() calls. (ndossche)
-
-- PCNTL:
- . Fixed signal handler installation on AIX by bumping the storage size of the
- num_signals global. (Calvin Buckley)
-
-- PCRE:
- . Fixed re-entrancy issue on php_pcre_match_impl, php_pcre_replace_impl,
- php_pcre_split_impl, and php_pcre_grep_impl. (David Carlier)
-
-- Phar:
- . Fixed bug GH-21333 (use after free when unlinking entries during iteration
- of a compressed phar). (David Carlier)
-
-- SNMP:
- . Fixed bug GH-21336 (SNMP::setSecurity() undefined behavior with
- NULL arguments). (David Carlier)
-
-- SOAP:
- . Fixed Set-Cookie parsing bug wrong offset while scanning attributes.
+ . Fixed bug GH-22046 (The unserialize function can lead to segfault when
+ non-Serializable internal classes are serialized back with the C format).
+ (kocsismate)
+ . Fixed bug GH-22292 (AST pretty printing does not correctly handle invalid
+ variable names). (timwolla)
+ . Fixed bug GH-22291 (AST pretty printing does not correctly handle braces in
+ string interpolation). (timwolla)
+ . Fixed bug GH-22373 (AST pretty-printing drops meaningful parentheses
+ surrounding property access). (timwolla)
+ . Fixed GH-22422 (zend_arena layout mismatch leaked memory in separately
+ built extensions under AddressSanitizer). (iliaal)
+ . TSRM: use local-exec TLS in PIE executables. (henderkes)
+ . perf: make all static extensions use TSRMG_STATIC. (henderkes)
+ . Fixed bug GH-22257 (type confusion in Exception::getTraceAsString()).
(David Carlier)
+ . TSRM: make CG, EG, SCNG and AG compile-time offsets. (henderkes)
+ . Deprecate returning values from __construct() and __destruct(). (timwolla)
-- SPL:
- . Fixed bug GH-21454 (missing write lock validation in SplHeap).
- (ndossche)
-
-- Standard:
- . Fixed bug GH-20906 (Assertion failure when messing up output buffers).
- (ndossche)
- . Fixed bug GH-20627 (Cannot identify some avif images with getimagesize).
- (y-guyon)
- . Fixed bug GH-22171 (Invalid auth header generation in
- http(s) stream wrapper). (David Carlier)
-
-- Sysvshm:
- . Fix memory leak in shm_get_var() when variable is corrupted. (ndossche)
-
-- XSL:
- . Fix GH-21357 (XSLTProcessor works with DOMDocument, but fails with
- Dom\XMLDocument). (ndossche)
- . Fixed bug GH-21496 (UAF in dom_objects_free_storage).
- (David Carlier/ndossche)
+- BCMath:
+ . Added NUL-byte validation to BCMath functions. (jorgsowa)
-12 Mar 2026, PHP 8.5.4
-
-- Core:
- . Fixed bug GH-21029 (zend_mm_heap corrupted on Aarch64, LTO builds). (Arnaud)
- . Fixed bug GH-21059 (Segfault when preloading constant AST closure). (ilutov)
- . Fixed bug GH-21072 (Crash on (unset) cast in constant expression).
- (arshidkv12)
- . Fix deprecation now showing when accessing null key of an array with JIT.
- (alexandre-daubois)
- . Fixed bug GH-20657 (Assertion failure in zend_lazy_object_get_info triggered
- by setRawValueWithoutLazyInitialization() and newLazyGhost()). (Arnaud)
- . Fixed bug GH-20504 (Assertion failure in zend_get_property_guard when
- accessing properties on Reflection LazyProxy via isset()). (Arnaud)
- . Fixed OSS-Fuzz #478009707 (Borked assign-op/inc/dec on untyped hooked
- property backing value). (ilutov)
- . Fixed bug GH-21215 (Build fails with -std=). (Arnaud)
- . Fixed bug GH-13674 (Build system installs libtool wrappers when using
- slibtool). (Michael Orlitzky)
+- BZ2:
+ . Reject oversized input in bzdecompress(). (arshidkv12)
- Curl:
- . Don't truncate length. (ndossche)
-
-- Date:
- . Fixed bug GH-20936 (DatePeriod::__set_state() cannot handle null start).
- (ndossche)
- . Fix timezone offset with seconds losing precision. (ndossche)
-
-- DOM:
- . Fixed bug GH-21077 (Accessing Dom\Node::baseURI can throw TypeError).
- (ndossche)
- . Fixed bug GH-21097 (Accessing Dom\Node properties can can throw TypeError).
- (ndossche)
-
-- LDAP:
- . Fixed bug GH-21262 (ldap_modify() too strict controls argument validation
- makes it impossible to unset attribute). (David Carlier)
-
-- MBString:
- . Fixed bug GH-21223; mb_guess_encoding no longer crashes when passed huge
- list of candidate encodings (with 200,000+ entries). (Jordi Kroon)
-
-- Opcache:
- . Fixed bug GH-20718 ("Insufficient shared memory" when using JIT on Solaris).
- (Petr Sumbera)
- . Fixed bug GH-21227 (Borked SCCP of array containing partial object).
- (ilutov)
-
-- OpenSSL:
- . Fix a bunch of leaks and error propagation. (ndossche)
-
-- Windows:
- . Fixed compilation with clang (missing intrin.h include). (Kévin Dunglas)
-
-29 Jan 2026, PHP 8.5.3
-
-- Core:
- . Fixed bug GH-20806 (preserve_none feature compatiblity with LTO).
- (henderkes)
- . Fixed bug GH-20767 (build failure with musttail/preserve_none feature
- on macOs). (David Carlier)
- . Fixed bug GH-20837 (NULL dereference when calling ob_start() in shutdown
- function triggered by bailout in php_output_lock_error()). (timwolla)
- . Fix OSS-Fuzz #471533782 (Infinite loop in GC destructor fiber). (ilutov)
- . Fix OSS-Fuzz #472563272 (Borked block_pass JMP[N]Z optimization). (ilutov)
- . Fixed bug GH-20914 (Internal enums can be cloned and compared). (Arnaud)
- . Fix OSS-Fuzz #474613951 (Leaked parent property default value). (ilutov)
- . Fixed bug GH-20895 (ReflectionProperty does not return the PHPDoc of a
- property if it contains an attribute with a Closure). (timwolla)
- . Fixed bug GH-20766 (Use-after-free in FE_FREE with GC interaction). (Bob)
- . Fix OSS-Fuzz #471486164 (Broken by-ref assignment to uninitialized hooked
- backing value). (ilutov)
- . Fix OSS-Fuzz #438780145 (Nested finally with repeated return type check may
- uaf). (ilutov)
- . Fixed bug GH-20905 (Lazy proxy bailing __clone assertion). (ilutov)
- . Fixed bug GH-20479 (Hooked object properties overflow). (ndossche)
+ . Add support for CURLINFO_SIZE_DELIVERED (libcurl >= 8.20.0). (Ayesh)
- Date:
. Update timelib to 2022.16. (Derick)
- DOM:
- . Fixed GH-21041 (Dom\HTMLDocument corrupts closing tags within scripts).
- (lexborisov)
-
-- MbString:
- . Fixed bug GH-20833 (mb_str_pad() divide by zero if padding string is
- invalid in the encoding). (ndossche)
- . Fixed bug GH-20836 (Stack overflow in mb_convert_variables with
- recursive array references). (alexandre-daubois)
-
-- Opcache:
- . Fixed bug GH-20818 (Segfault in Tracing JIT with object reference).
- (khasinski)
-
-- OpenSSL:
- . Fix memory leaks when sk_X509_new_null() fails. (ndossche)
- . Fix crash when in openssl_x509_parse() when i2s_ASN1_INTEGER() fails.
- (ndossche)
- . Fix crash in openssl_x509_parse() when X509_NAME_oneline() fails.
- (ndossche)
-
-- Phar:
- . Fixed bug GH-20882 (buildFromIterator breaks with missing base directory).
- (ndossche)
-
-- PGSQL:
- . Fixed INSERT/UPDATE queries building with PQescapeIdentifier() and possible
- UB. (David Carlier)
-
-- Readline:
- . Fixed bug GH-18139 (Memory leak when overriding some settings
- via readline_info()). (ndossche)
-
-- SPL:
- . Fixed bug GH-20856 (heap-use-after-free in SplDoublyLinkedList iterator
- when modifying during iteration). (ndossche)
-
-- Standard:
- . Fixed bug #74357 (lchown fails to change ownership of symlink with ZTS)
- (Jakub Zelenka)
- . Fixed bug GH-20843 (var_dump() crash with nested objects)
+ . Removed LIBXML_XINCLUDE from valid options for XMLDocument, as it was a
+ no-op. (ndossche)
+ . Readonly DOM properties are now declared with asymmetric visibility
+ (public private(set)). ReflectionProperty::isWritable() reports them
+ correctly, and external writes raise "Cannot modify private(set)
+ property" instead of the previous readonly modification error.
(David Carlier)
-
-15 Jan 2026, PHP 8.5.2
-
-- Core:
- . Fix OSS-Fuzz #465488618 (Wrong assumptions when dumping function signature
- with dynamic class const lookup default argument). (ilutov)
- . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing
- malformed INI input via parse_ini_string()). (ndossche)
- . Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov)
- . Fixed bug GH-20352 (UAF in php_output_handler_free via re-entrant
- ob_start() during error deactivation). (ndossche)
- . Fixed bug GH-20745 ("Casting out of range floats to int" applies to
- strings). (Bob)
-
-- DOM:
- . Fixed bug GH-20722 (Null pointer dereference in DOM namespace node cloning
- via clone on malformed objects). (ndossche)
- . Fixed bug GH-20444 (Dom\XMLDocument::C14N() seems broken compared
- to DOMDocument::C14N()). (ndossche)
+ . Fixed Dom\Notation nodes missing tree connection, so that ownerDocument,
+ parentNode, isConnected and baseURI now return correct values, and
+ textContent returns NULL per the DOM specification. (jordikroon)
- EXIF:
- . Fixed bug GH-20631 (Integer underflow in exif HEIF parsing
- when pos.size < 2). (Oblivionsage)
-
-- Intl:
- . Fixed IntlListFormatter::getErrorCode() and getErrorMessage() not
- reflecting format() failures. (Weilin Du)
- . Fix leak in umsg_format_helper(). (ndossche)
-
-- LDAP:
- . Fix memory leak in ldap_set_options(). (ndossche)
+ . Added support for reading EXIF metadata from WebP images (GH-19904).
+ (iliaal)
-- Lexbor:
- . Fixed bug GH-20668 (\Uri\WhatWg\Url::withHost() crashes (SEGV) for URLs
- using the file: scheme). (lexborisov)
-
-- Mbstring:
- . Fixed bug GH-20674 (mb_decode_mimeheader does not handle separator).
- (Yuya Hamada)
-
-- OpenSSL:
- . Fixed bug GH-20802 (undefined behavior with invalid SNI_server_certs
- options). (David Carlier)
-
-- PCNTL:
- . Fixed bug with pcntl_getcpuaffinity() on solaris regarding invalid
- process ids handling. (David Carlier)
-
-- Phar:
- . Fixed bug GH-20732 (Phar::LoadPhar undefined behavior when reading fails).
+- Fileinfo:
+ . Fixed bug GH-20679 (finfo_file() doesn't work on remote resources).
(ndossche)
- . Fix SplFileInfo::openFile() in write mode. (ndossche)
- . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi)
- . Fixed bug #74154 (Phar extractTo creates empty files). (ndossche)
-
-- Session:
- . Fix support for MM module. (Michael Orlitzky)
-
-- Sqlite3:
- . Fixed bug GH-20699 (SQLite3Result fetchArray return array|false,
- null returned). (ndossche, plusminmax)
+ . Fixed bug #66095 (Hide libmagic dynamic symbols). (orlitzky)
-- Standard:
- . Fix error check for proc_open() command. (ndossche)
- . Fix memory leak in mail() when header key is numeric. (Girgias)
- . Fixed bug GH-20582 (Heap Buffer Overflow in iptcembed). (ndossche)
-
-- URI:
- . Fixed bug GH-20771 (Assertion failure when getUnicodeHost() returns
- empty string). (ndossche)
-
-- Zlib:
- . Fix OOB gzseek() causing assertion failure. (ndossche)
-
-18 Dec 2025, PHP 8.5.1
-
-- Core:
- . Sync all boost.context files with release 1.86.0. (mvorisek)
- . Fixed bug GH-20435 (SensitiveParameter doesn't work for named argument
- passing to variadic parameter). (ndossche)
- . Fixed bug GH-20546 (preserve_none attribute configure check on macOs
- issue). (David Carlier/cho-m)
- . Fixed bug GH-20286 (use-after-destroy during userland stream_close()).
- (ndossche, David Carlier)
-
-- Bz2:
- . Fix assertion failures resulting in crashes with stream filter
- object parameters. (ndossche)
+- GD:
+ . imagesetstyle()/imagefilter()/imagecrop() check array argument entries
+ types. (David Carlier)
-- DOM:
- . Fix memory leak when edge case is hit when registering xpath callback.
- (ndossche)
- . Fixed bug GH-20395 (querySelector and querySelectorAll requires elements
- in $selectors to be lowercase). (ndossche)
- . Fix missing NUL byte check on C14NFile(). (ndossche)
+- GMP:
+ . gmp_fact() reject values larger than unsigned long. (David Carlier)
+ . gmp_pow/binomial/root/rootrem and shift/pow operators reject values larger
+ than unsigned long. (David Carlier)
+ . GMP exponentiation and shift operators now emit a deprecation warning
+ when converting a float right operand to int loses precision. (Weilin Du)
-- Fibers:
- . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI
- small value). (David Carlier)
+- Hash:
+ . Upgrade xxHash to 0.8.2. (timwolla)
- Intl:
+ . Fixed malformed ResourceBundle::get() error message when fallback is
+ disabled. (Weilin Du)
+ . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(),
+ with the alias of locale_get_display_keyword() and
+ locale_get_display_keyword_value() respectively. (Weilin Du)
+ . Fix incorrect argument positions for invalid start/end arguments in
+ transliterator_transliterate(). (Weilin Du)
+ . Fixed IntlTimeZone::getDisplayName() to synchronize object error state
+ for invalid display types. (Weilin Du)
+ . Fixed Locale::lookup() and locale_lookup() to return NULL instead of the
+ fallback locale when a language tag cannot be canonicalized. (Weilin Du)
+ . Added IntlNumberRangeFormatter class to format an interval of two numbers
+ with a given skeleton, locale, collapse type and identity fallback.
+ (BogdanUngureanu)
. Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message
suggests missing constants). (DanielEScherzer)
+ . Added grapheme_strrev (Yuya Hamada)
+ . Passing a non-stringable object as a time zone to Intl time zone
+ 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)
+ . Fixed UConverter::transcode() silently truncating from_subst and to_subst
+ option lengths greater than 127 bytes. (Weilin Du)
+ . Fixed IntlIterator::current() to return NULL instead of an undefined value
+ when the iterator is not positioned on a valid element. (Weilin Du)
+
+- IO:
+ . Added new polling API. (Jakub Zelenka)
+
+- JSON:
+ . Enriched JSON last error / exception message with error location.
+ (Juan Morales)
-- Lexbor:
- . Fixed bug GH-20501 (\Uri\WhatWg\Url lose host after calling
- withPath() or withQuery()). (lexborisov)
- . Fixed bug GH-20502 (\Uri\WhatWg\Url crashes (SEGV) when parsing
- malformed URL due to Lexbor memory corruption). (lexborisov)
-
-- LibXML:
- . Fix some deprecations on newer libxml versions regarding input
- buffer/parser handling. (ndossche)
-
-- mysqli:
- . Make mysqli_begin_transaction() report errors properly. (Kamil Tekiela)
-
-- MySQLnd:
- . Fixed bug GH-20528 (Regression breaks mysql connexion using an IPv6 address
- enclosed in square brackets). (Remi)
-
-- Opcache:
- . Fixed bug GH-20329 (opcache.file_cache broken with full interned string
- buffer). (Arnaud)
-
-- PDO:
- . Fixed bug GH-20553 (PDO::FETCH_CLASSTYPE ignores $constructorArgs in
- PHP 8.5.0). (Girgias)
- . Fixed GHSA-8xr5-qppj-gvwj (PDO quoting result null deref). (CVE-2025-14180)
- (Jakub Zelenka)
-
-- Phar:
- . Fixed bug GH-20442 (Phar does not respect case-insensitiveness of
- __halt_compiler() when reading stub). (ndossche, TimWolla)
- . Fix broken return value of fflush() for phar file entries. (ndossche)
- . Fix assertion failure when fseeking a phar file out of bounds. (ndossche)
-
-- PHPDBG:
- . Fixed ZPP type violation in phpdbg_get_executable() and phpdbg_end_oplog().
- (Girgias)
-
-- SPL:
- . Fixed bug GH-20614 (SplFixedArray incorrectly handles references
- in deserialization). (ndossche)
-
-- Standard:
- . Fix memory leak in array_diff() with custom type checks. (ndossche)
- . Fixed bug GH-20583 (Stack overflow in http_build_query
- via deep structures). (ndossche)
- . Fixed GHSA-www2-q4fc-65wf (Null byte termination in dns_get_record()).
- (ndossche)
- . Fixed GHSA-h96m-rvf9-jgm2 (Heap buffer overflow in array_merge()).
- (CVE-2025-14178) (ndossche)
- . Fixed GHSA-3237-qqm7-mfv7 (Information Leak of Memory in getimagesize).
- (CVE-2025-14177) (ndossche)
-
-- Streams:
- . Fixed bug GH-20370 (User stream filters could violate typed property
- constraints). (alexandre-daubois)
-
-- URI:
- . Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering
- null byte). (kocsismate)
- . Fixed CVE-2025-67899 (uriparser through 0.9.9 allows unbounded recursion
- and stack consumption). (Sebastian Pipping)
-
-- XML:
- . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle
- special characters in attributes when passing data to callback). (ndossche)
-
-- Zip:
- . Fix crash in property existence test. (ndossche)
- . Don't truncate return value of zip_fread() with user sizes. (ndossche)
-
-- Zlib:
- . Fix assertion failures resulting in crashes with stream filter
- object parameters. (ndossche)
-
-20 Nov 2025, PHP 8.5.0
-
-- Core:
- . Added the #[\NoDiscard] attribute to indicate that a function's return
- value is important and should be consumed. (timwolla, edorian)
- . Added the (void) cast to indicate that not using a value is intentional.
- (timwolla, edorian)
- . Added get_error_handler(), get_exception_handler() functions. (Arnaud)
- . Added support for casts in constant expressions. (nielsdos)
- . Added the pipe (|>) operator. (crell)
- . Added support for `final` with constructor property promotion.
- (DanielEScherzer)
- . Added support for configuring the URI parser for the FTP/FTPS as well as
- the SSL/TLS stream wrappers as described in
- https://wiki.php.net/rfc/url_parsing_api#plugability. (kocsismate)
- . Added PHP_BUILD_PROVIDER constant. (timwolla)
- . Added PHP_BUILD_DATE constant. (cmb)
- . Added support for Closures and first class callables in constant
- expressions. (timwolla, edorian)
- . Add support for backtraces for fatal errors. (enorris)
- . Add clone-with support to the clone() function. (timwolla, edorian)
- . Add RFC 3986 and WHATWG URL compliant APIs for URL parsing
- and manipulation (kocsismate, timwolla)
- . Fixed AST printing for immediately invoked Closure. (Dmitrii Derepko)
- . Properly handle __debugInfo() returning an array reference. (nielsdos)
- . Properly handle reference return value from __toString(). (nielsdos)
- . Improved error message of UnhandledMatchError for
- zend.exception_string_param_max_len=0. (timwolla)
- . Fixed bug GH-15753 and GH-16198 (Bind traits before parent class). (ilutov)
- . Fixed bug GH-17951 (memory_limit is not always limited by max_memory_limit).
- (manuelm)
- . Fixed bug GH-20183 (Stale EG(opline_before_exception) pointer through eval).
- (ilutov)
- . Fixed bug GH-20113 (Missing new Foo(...) error in constant expressions).
- (ilutov)
- . Fixed bug GH-19844 (Don't bail when closing resources on shutdown). (ilutov)
- . Fixed bug GH-20177 (Accessing overridden private property in
- get_object_vars() triggers assertion error). (ilutov)
- . Fix OSS-Fuzz #447521098 (Fatal error during sccp shift eval). (ilutov)
- . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg)
- . Fixed bug GH-19352 (Cross-compilation with musl C library).
- (henderkes, Peter Kokot)
- . Fixed bug GH-19765 (object_properties_load() bypasses readonly property
- checks). (timwolla)
- . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla)
- . Fixed bug GH-19839 (Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland
- array). (ilutov)
- . Fixed bug GH-19823 (register_argc_argv deprecation emitted twice when
- using OPcache). (timwolla)
- . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is
- configured). (nielsdos)
- . Fixed bug GH-19719 (Allow empty statements before declare(strict_types)).
- (nielsdos)
- . Fixed bug GH-19934 (CGI with auto_globals_jit=0 causes uouv). (ilutov)
- . Fixed bug GH-19613 (Stale array iterator pointer). (ilutov)
- . Fixed bug GH-19679 (zend_ssa_range_widening may fail to converge). (Arnaud)
- . Fixed bug GH-19681 (PHP_EXPAND_PATH broken with bash 5.3.0). (Remi)
- . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler()
- triggers "Constant already defined" warning). (ilutov)
- . Fixed bug GH-19476 (pipe operator fails to correctly handle returning
- by reference). (alexandre-daubois)
- . Fixed bug GH-19081 (Wrong lineno in property error with constructor property
- promotion). (ilutov)
- . Fixed bug GH-17959 (Relax missing trait fatal error to error exception).
- (ilutov)
- . Fixed bug GH-18033 (NULL-ptr dereference when using register_tick_function
- in destructor). (nielsdos)
- . Fixed bug GH-18026 (Improve "expecting token" error for ampersand). (ilutov)
- . The report_memleaks INI directive has been deprecated. (alexandre-daubois)
- . Fixed OSS-Fuzz #439125710 (Pipe cannot be used in write context).
- (nielsdos)
- . Fixed bug GH-19548 (Shared memory violation on property inheritance).
- (alexandre-daubois)
- . Fixed bug GH-19544 (GC treats ZEND_WEAKREF_TAG_MAP references as WeakMap
- references). (Arnaud, timwolla)
- . Fixed bug GH-18373 (Don't substitute self/parent with anonymous class).
- (ilutov)
- . Fix support for non-userland stream notifiers. (timwolla)
- . Fixed bug GH-19305 (Operands may be being released during comparison).
- (Arnaud)
- . Fixed bug GH-19306 (Generator can be resumed while fetching next value from
- delegated Generator). (Arnaud)
- . Fixed bug GH-19326 (Calling Generator::throw() on a running generator with
- a non-Generator delegate crashes). (Arnaud)
- . Fix OSS-Fuzz #427814452 (pipe compilation fails with assert).
- (nielsdos, ilutov)
- . Fixed bug GH-16665 (\array and \callable should not be usable in
- class_alias). (nielsdos)
- . Use `clock_gettime_nsec_np()` for high resolution timer on macOS
- if available. (timwolla)
- . Make `clone()` a function. (timwolla, edorian)
- . Introduced the TAILCALL VM, enabled by default when compiling with Clang>=19
- on x86_64 or aarch64. (Arnaud)
- . Enacted the follow-up phase of the "Path to Saner Increment/Decrement
- operators" RFC, meaning that incrementing non-numeric strings is now
- deprecated. (Girgias).
- . Various closure binding issues are now deprecated. (alexandre-daubois)
- . Constant redeclaration has been deprecated. (alexandre-daubois)
- . Marks the stack as non-executable on Haiku. (David Carlier)
- . Deriving $_SERVER['argc'] and $_SERVER['argv'] from the query string is
- now deprecated. (timwolla, nicolasgrekas)
- . Using null as an array offset or when calling array_key_exists() is now
- deprecated. (alexandre-daubois)
- . The disable_classes INI directive has been removed. (Girgias)
- . The locally predefined variable $http_response_header is deprecated.
- (Girgias)
- . Non-canonical cast names (boolean), (integer), (double), and (binary) have
- been deprecated. (Girgias)
- . The $exclude_disabled parameter of the get_defined_functions() function has
- been deprecated, as it no longer has any effect since PHP 8.0. (Girgias)
- . Terminating case statements with a semicolon instead of a colon has
- been deprecated. (theodorejb)
- . The backtick operator as an alias for shell_exec() has been deprecated.
- (timwolla)
- . Returning null from __debugInfo() has been deprecated. (DanielEScherzer)
- . Support #[\Override] on properties. (Jiří Pudil)
- . Destructing non-array values (other than NULL) using [] or list() now
- emits a warning. (Girgias)
- . Casting floats that are not representable as ints now emits a warning.
- (Girgias)
- . Casting NAN to other types now emits a warning. (Girgias)
- . Implement GH-15680 (Enhance zend_dump_op_array to properly represent
- non-printable characters in string literals). (nielsdos, WangYihang)
- . Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos)
- . Do not use RTLD_DEEPBIND if dlmopen is available. (Daniil Gentili)
- . Added #[\DelayedTargetValidation] attribute. (DanielEScherzer)
- . Support #[\Deprecated] on traits. (DanielEScherzer)
-
-- BCMath:
- . Simplify `bc_divide()` code. (SakiTakamachi)
- . If the result is 0, n_scale is set to 0. (SakiTakamachi)
- . If size of BC_VECTOR array is within 64 bytes, stack area is now used.
- (SakiTakamachi)
- . Fixed bug GH-20006 (Power of 0 of BcMath number causes UB). (nielsdos)
-
-- Bz2:
- . Fixed bug GH-19810 (Broken bzopen() stream mode validation). (ilutov)
-
-- CLI:
- . Add --ini=diff to print INI settings changed from the builtin default.
- (timwolla)
- . Drop support for -z CLI/CGI flag. (nielsdos)
- . Fixed GH-17956 - development server 404 page does not adapt to mobiles.
- (pascalchevrel)
- . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server
- with PHP_CLI_SERVER_WORKERS. (leotaku)
- . Fixed bug GH-19461 (Improve error message on listening error with IPv6
- address). (alexandre-daubois)
-
-- COM:
- . Fixed property access of PHP objects wrapped in variant. (cmb)
- . Fixed method calls for PHP objects wrapped in variant. (cmb)
-
-- Curl:
- . Added CURLFOLLOW_ALL, CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY
- values for CURLOPT_FOLLOWLOCATION curl_easy_setopt option. (David Carlier)
- . Added curl_multi_get_handles(). (timwolla)
- . Added curl_share_init_persistent(). (enorris)
- . Added CURLINFO_USED_PROXY, CURLINFO_HTTPAUTH_USED, and CURLINFO_PROXYAUTH_USED
- support to curl_getinfo. (Ayesh Karunaratne)
- . Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan)
- . Add support for CURLINFO_QUEUE_TIME_T in curl_getinfo() (thecaliskan)
- . Add support for CURLOPT_SSL_SIGNATURE_ALGORITHMS. (Ayesh Karunaratne)
- . The curl_close() function has been deprecated. (DanielEScherzer)
- . The curl_share_close() function has been deprecated. (DanielEScherzer)
- . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead
- of the curl_copy_handle() function to clone a CurlHandle. (timwolla)
-
-- Date:
- . Fix undefined behaviour problems regarding integer overflow in extreme edge
- cases. (nielsdos, cmb, ilutov)
- . The DATE_RFC7231 and DateTimeInterface::RFC7231 constants have been
- deprecated. (jorgsowa)
- . Fixed date_sunrise() and date_sunset() with partial-hour UTC offset.
- (ilutov)
- . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals.
- (nielsdos)
- . The __wakeup() magic method of DateTimeInterface, DateTime,
- DateTimeImmutable, DateTimeZone, DateInterval, and DatePeriod has been
- deprecated in favour of the __unserialize() magic method. (Girgias)
-
-- DOM:
- . Added Dom\Element::$outerHTML. (nielsdos)
- . Added Dom\Element::insertAdjacentHTML(). (nielsdos)
- . Added $children property to ParentNode implementations. (nielsdos)
- . Make cloning DOM node lists, maps, and collections fail. (nielsdos)
- . Added Dom\Element::getElementsByClassName(). (nielsdos)
- . Fixed bug GH-18877 (\Dom\HTMLDocument querySelectorAll selecting only the
- first when using ~ and :has). (nielsdos, lexborisov)
- . Fix getNamedItemNS() incorrect namespace check. (nielsdos)
-
-- Enchant:
- . Added enchant_dict_remove_from_session(). (nielsdos)
- . Added enchant_dict_remove(). (nielsdos)
- . Fix missing empty string checks. (nielsdos)
-
-- EXIF:
- . Add OffsetTime* Exif tags. (acc987)
- . Added support to retrieve Exif from HEIF file. (Benstone Zhang)
- . Fix OSS-Fuzz #442954659 (zero-size box in HEIF file causes infinite loop).
- (nielsdos)
- . Fix OSS-Fuzz #442954659 (Crash in exif_scan_HEIF_header). (nielsdos)
- . Various hardening fixes to HEIF parsing. (nielsdos)
-
-
-- FileInfo:
- . The finfo_close() function has been deprecated. (timwolla)
- . The $context parameter of the finfo_buffer() function has been deprecated
- as it is ignored. (Girgias)
- . Upgrade to file 5.46. (nielsdos)
- . Change return type of finfo_close() to true. (timwolla)
-
-- Filter:
- . Added support for configuring the URI parser for FILTER_VALIDATE_URL
- as described in https://wiki.php.net/rfc/url_parsing_api#plugability.
- (kocsismate)
- . Fixed bug GH-16993 (filter_var_array with FILTER_VALIDATE_INT|FILTER_NULL_ON_FAILURE
- should emit warning for invalid filter usage). (alexandre-daubois)
- . Added FILTER_THROW_ON_FAILURE flag. (DanielEScherzer)
-
-- FPM:
- . Fixed bug GH-19817 (Decode SCRIPT_FILENAME issue in php 8.5).
- (Jakub Zelenka)
- . Fixed bug GH-19989 (PHP 8.5 FPM access log lines also go to STDERR).
- (Jakub Zelenka)
- . Fixed GH-17645 (FPM with httpd ProxyPass does not decode script path).
- (Jakub Zelenka)
- . Make FPM access log limit configurable using log_limit. (Jakub Zelenka)
- . Fixed failed debug assertion when php_admin_value setting fails. (ilutov)
- . Fixed GH-8157 (post_max_size evaluates .user.ini too late in php-fpm).
- (Jakub Zelenka)
-
-- GD:
- . Fixed bug #68629 (Transparent artifacts when using imagerotate). (pierre,
- cmb)
- . Fixed bug #64823 (ZTS GD fails to find system TrueType font). (cmb)
- . Fix incorrect comparison with result of php_stream_can_cast(). (Girgias)
- . The imagedestroy() function has been deprecated. (DanielEScherzer)
+- Fibers:
+ . Fixed bug GH-20483 (ASAN stack overflow with fiber.stack_size INI small
+ value). (David Carlier)
-- Iconv:
- . Extends the ICONV_CONST preprocessor for illumos/solaris. (jMichaelA)
+- Mail:
+ . Fixed bug GH-20862 (null pointer dereference in
+ php_mail_detect_multiple_crlf via error_log (jordikroon)
-- Intl:
- . Bumped ICU requirement to ICU >= 57.1. (cmb)
- . IntlDateFormatter::setTimeZone()/datefmt_set_timezone() throws an exception
- with uninitialised classes or clone failure. (David Carlier)
- . Added DECIMAL_COMPACT_SHORT/DECIMAL_COMPACT_LONG for NumberFormatter class.
- (BogdanUngureanu)
- . Added Locale::isRightToLeft to check if a locale is written right to left.
- (David Carlier)
- . Added null bytes presence in locale inputs for Locale class. (David Carlier)
- . Added grapheme_levenshtein() function. (Yuya Hamada)
- . Added Locale::addLikelySubtags/Locale::minimizeSubtags to handle
- adding/removing likely subtags to a locale. (David Carlier)
- . Added IntlListFormatter class to format a list of items with a locale,
- operands types and units. (BogdanUngureanu)
- . Added grapheme_strpos(), grapheme_stripos(), grapheme_strrpos(),
- grapheme_strripos(), grapheme_substr(), grapheme_strstr(), grapheme_stristr() and
- grapheme_levenshtein() functions add $locale parameter (Yuya Hamada).
- . Fixed bug GH-11952 (Fix locale strings canonicalization for IntlDateFormatter
- and NumberFormatter). (alexandre-daubois)
- . Fixed bug GH-18566 ([intl] Weird numeric sort in Collator). (nielsdos)
- . Fix return value on failure for resourcebundle count handler. (Girgias)
- . Fixed bug GH-19307 (PGO builds of shared ext-intl are broken). (cmb)
- . Intl's internal error mechanism has been modernized so that it
- indicates more accurately which call site caused what error.
- Moreover, some ext/date exceptions have been wrapped inside a
- IntlException now. (Girgias)
- . The intl.error_level INI setting has been deprecated. (Girgias)
-
-- LDAP:
- . Allow ldap_get_option to retrieve global option by allowing NULL for
- connection instance ($ldap). (Remi)
-
-- MBstring:
- . Updated Unicode data tables to Unicode 17.0. (Yuya Hamada)
-
-- MySQLi:
- . Fixed bugs GH-17900 and GH-8084 (calling mysqli::__construct twice).
- (nielsdos)
- . The mysqli_execute() alias function has been deprecated. (timwolla)
-
-- MySQLnd:
- . Added mysqlnd.collect_memory_statistics to ini quick reference.
- (hauk92)
+- Mbstring:
+ . ini_set() with mbstring.detect_order changes the order of mb_detect_order
+ as intended, since mbstring.detect_order is an INI_ALL setting. (tobee94)
+ . Added GB18030-2022 to default encoding list for zh-CN. (HeRaNO)
+ . Fixed bug GH-20836 (Stack overflow in mb_convert_variables with
+ recursive array references). (alexandre-daubois)
+ . Fixed bug GH-21223; mb_guess_encoding no longer crashes when passed huge
+ list of candidate encodings (with 200,000+ entries). (Jordi Kroon)
+ . mbregex has been deprecated. (youkidearitai)
-- ODBC:
- . Removed driver-specific build flags and support. (Calvin Buckley)
- . Remove ODBCVER and assume ODBC 3.5. (Calvin Buckley)
+- Mysqli:
+ . Added mysqli_quote_string() and mysqli::quote_string(). (Kamil Tekiela)
- Opcache:
- . Make OPcache non-optional (Arnaud, timwolla)
- . Added opcache.file_cache_read_only. (Samuel Melrose)
- . Updated default value of opcache.jit_hot_loop. (Arnaud)
- . Log a warning when opcache lock file permissions could not be changed.
- (Taavi Eomäe)
- . Fixed bug GH-20012 (heap buffer overflow in jit). (Arnaud)
- . Partially fixed bug GH-17733 (Avoid calling wrong function when reusing file
- caches across differing environments). (ilutov)
- . Disallow changing opcache.memory_consumption when SHM is already set up.
- (timwolla)
- . Fixed bug GH-15074 (Compiling opcache statically into ZTS PHP fails).
- (Arnaud)
- . Fixed bug GH-17422 (OPcache bypasses the user-defined error handler for
- deprecations). (Arnaud, timwolla)
- . Fixed bug GH-19301 (opcache build failure). (Remi)
- . Fixed bug GH-20081 (access to uninitialized vars in preload_load()).
- (Arnaud)
- . Fixed bug GH-20121 (JIT broken in ZTS builds on MacOS 15).
- (Arnaud, Shivam Mathur)
- . Fixed bug GH-19875 (JIT 1205 segfault on large file compiled in subprocess).
- (Arnaud)
- . Fixed segfault in function JIT due to NAN to bool warning. (Girgias)
- . Fixed bug GH-19984 (Double-free of EG(errors)/persistent_script->warnings on
- persist of already persisted file). (ilutov, Arnaud)
- . Fixed bug GH-19889 (race condition in zend_runtime_jit(),
- zend_jit_hot_func()). (Arnaud)
- . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex).
- (Arnaud)
- . Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud)
- . Fixed bug GH-19486 (Incorrect opline after deoptimization). (Arnaud)
- . Fixed bug GH-19601 (Wrong JIT stack setup on aarch64/clang). (Arnaud)
- . Fixed bug GH-19388 (Broken opcache.huge_code_pages). (Arnaud)
- . Fixed bug GH-19657 (Build fails on non-glibc/musl/freebsd/macos/win
- platforms). (Arnaud)
- . Fixed ZTS OPcache build on Cygwin. (cmb)
- . Fixed bug GH-19493 (JIT variable not stored before YIELD). (Arnaud)
+ . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during
+ preloading). (Arnaud, welcomycozyhom)
- OpenSSL:
- . Added openssl.libctx INI that allows to select the OpenSSL library context
- type and convert various parts of the extension to use the custom libctx.
- (Jakub Zelenka)
- . Add $digest_algo parameter to openssl_public_encrypt() and
- openssl_private_decrypt() functions. (Jakub Zelenka)
- . Implement #81724 (openssl_cms_encrypt only allows specific ciphers).
+ . Added AES-SIV support. (jordikroon)
+ . Implemented GH-20310 (No critical extension indication in
+ 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)
+ . Added stream crypto status for exposing OpenSSL WANT_READ / WANT_WRITE.
(Jakub Zelenka)
- . Implement #80495 (Enable to set padding in openssl_(sign|verify).
- (Jakub Zelenka)
- . Implement #47728 (openssl_pkcs7_sign ignores new openssl flags).
- (Jakub Zelenka)
- . Fixed bug GH-19994 (openssl_get_cipher_methods inconsistent with fetching).
- (Jakub Zelenka)
- . Fixed build when --with-openssl-legacy-provider set. (Jakub Zelenka)
- . Fixed bug GH-19369 (8.5 | Regression in openssl_sign() - support for alias
- algorithms appears to be broken). (Jakub Zelenka)
- . The $key_length parameter for openssl_pkey_derive() has been deprecated.
- (Girgias)
-
-- Output:
- . Fixed calculation of aligned buffer size. (cmb)
- PCNTL:
- . Extend pcntl_waitid with rusage parameter. (vrza)
-
-- PCRE:
- . Remove PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK from pcre compile options.
- (mvorisek)
+ . pcntl_exec() now throws a ValueError if the $args array is not a list
+ array. (Weilin Du)
-- PDO:
- . Fixed bug GH-20095 (Incorrect class name in deprecation message for PDO
- mixins). (timwolla)
- . Driver specific methods and constants in the PDO class
- are now deprecated. (Arnaud)
- . The "uri:" DSN scheme has been deprecated due to security concerns with
- DSNs coming from remote URIs. (timwolla)
-
-- PDO_ODBC:
- . Fetch larger block sizes and better handle SQL_NO_TOTAL when calling
- SQLGetData. (Calvin Buckley, Saki Takamachi)
+- PDO_DBLIB:
+ . Added dblib_handle_check_liveness handler. (freddy77)
- PDO_PGSQL:
- . Added Iterable support for PDO::pgsqlCopyFromArray. (KentarouTakeda)
- . Implement GH-15387 Pdo\Pgsql::setAttribute(PDO::ATTR_PREFETCH, 0) or
- Pdo\Pgsql::prepare(…, [ PDO::ATTR_PREFETCH => 0 ]) make fetch() lazy
- instead of storing the whole result set in memory (Guillaume Outters)
-
-- PDO_SQLITE:
- . Add PDO\Sqlite::ATTR_TRANSACTION_MODE connection attribute.
- (Samuel Štancl)
- . Implement GH-17321: Add setAuthorizer to Pdo\Sqlite. (nielsdos)
- . PDO::sqliteCreateCollation now throws a TypeError if the callback
- has a wrong return type. (David Carlier)
- . Added Pdo_Sqlite::ATTR_BUSY_STATEMENT constant to check
- if a statement is currently executing. (David Carlier)
- . Added Pdo_Sqlite::ATTR_EXPLAIN_STATEMENT constant to set a statement
- in either EXPLAIN_MODE_PREPARED, EXPLAIN_MODE_EXPLAIN,
- EXPLAIN_MODE_EXPLAIN_QUERY_PLAN modes. (David Carlier)
- . Fix bug GH-13952 (sqlite PDO::quote silently corrupts strings
- with null bytes) by throwing on null bytes. (divinity76)
+ . Clear session-local state disconnect-equivalent processing.
+ (KentarouTakeda)
- PGSQL:
- . Added pg_close_stmt to close a prepared statement while allowing
- its name to be reused. (David Carlier)
- . Added Iterable support for pgsql_copy_from. (David Carlier)
- . pg_connect checks if connection_string contains any null byte,
- pg_close_stmt check if the statement contains any null byte.
- (David Carlier)
- . Added pg_service to get the connection current service identifier.
+ . Enabled 64 bits support for pg_lo_truncate()/pg_lo_tell() if the server
+ supports it. (KentarouTakeda)
+ . pg_fetch_object() now surfaces non-instantiable class errors before
+ fetching, resolves the constructor via the get_constructor handler, and
+ reports the empty-constructor ValueError on the $constructor_args argument.
(David Carlier)
- . Fix segfaults when attempting to fetch row into a non-instantiable class
- name. (Girgias, nielsdos)
- Phar:
- . Fix potential buffer length truncation due to usage of type int instead
- of type size_t. (Girgias)
- . Fixed memory leaks when verifying OpenSSL signature. (Girgias)
-
-- POSIX:
- . Added POSIX_SC_OPEN_MAX constant to get the number of file descriptors
- a process can handle. (David Carlier)
- . posix_ttyname() sets last_error to EBADF on invalid file descriptors,
- posix_isatty() raises E_WARNING on invalid file descriptors,
- posix_fpathconf checks invalid file descriptors. (David Carlier)
- . posix_kill and posix_setpgid throws a ValueError on invalid process_id.
- (David Carlier)
- . posix_setpgid throws a ValueError on invalid process_group_id,
- posix_setrlimit throws a ValueError on invalid soft_limit and hard_limit
- arguments. (David Carlier)
+ . Support reference values in Phar::mungServer(). (ndossche)
+ . Invalid values now throw in Phar::mungServer() instead of being silently
+ ignored. (ndossche)
+ . Fixed a bypass of the magic ".phar" directory protection in
+ Phar::addEmptyDir() for paths starting with "/.phar". (Weilin Du)
+ . Fixed an integer underflow when parsing ZIP extra fields. (Weilin Du)
+ . Phar::addEmptyDir() now allows non-magic directory names that merely
+ share the ".phar" prefix. (Weilin Du)
+ . Support overridden methods in SplFileInfo for getMTime() and getPathname()
+ when building a phar. (ndossche)
+ . Mark Phar::buildFromIterator() base directory argument as a path.
+ (ndossche)
+
+- phpdbg:
+ . Fixed GH-22480 (Use-after-free when re-watching an already-watched
+ variable). (iliaal)
-- Random:
- . Moves from /dev/urandom usage to arc4random_buf on Haiku. (David Carlier)
+- Posix:
+ . Added validity check to the flags argument for posix_access(). (arshidkv12)
- Reflection:
- . Added ReflectionConstant::getExtension() and ::getExtensionName().
- (DanielEScherzer)
- . Added ReflectionProperty::getMangledName() method. (alexandre-daubois)
- . ReflectionConstant is no longer final. (sasezaki)
- . The setAccessible() methods of various Reflection objects have been
- deprecated, as those no longer have an effect. (timwolla)
- . ReflectionClass::getConstant() for constants that do not exist has been
- deprecated. (DanielEScherzer)
- . ReflectionProperty::getDefaultValue() for properties without default values
- has been deprecated. (DanielEScherzer)
- . Fixed bug GH-12856 (ReflectionClass::getStaticPropertyValue() returns UNDEF
- zval for uninitialized typed properties). (nielsdos)
- . Fixed bug GH-15766 (ReflectionClass::__toString() should have better output
- for enums). (DanielEScherzer)
- . Fix GH-19691 (getModifierNames() not reporting asymmetric visibility).
- (DanielEScherzer)
- . Fixed bug GH-17927 (Reflection: have some indication of property hooks in
- `_property_string()`). (DanielEScherzer)
- . Fixed bug GH-19187 (ReflectionNamedType::getName() prints nullable type when
- retrieved from ReflectionProperty::getSettableType()). (ilutov)
. Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true
for classes with property hooks). (alexandre-daubois)
-
-- SAPI:
- . Fixed bug GH-18582 and #81451: http_response_code() does not override the
- status code generated by header(). (ilutov, Jakub Zelenka)
+ . Added ReflectionConstant::inNamespace(). (Khaled Alam)
+ . Added ReflectionProperty::isReadable() and ::isWritable(). (ilutov)
+ . Fixed bug GH-21362 (ReflectionMethod::invoke/invokeArgs() did not verify
+ Closure instance identity for Closure::__invoke()). (Ilia Alshanetsky)
+ . Added ReflectionParameter::getDocComment(). (chschneider)
- Session:
- . session_start() throws a ValueError on option argument if not a hashmap
- or a TypeError if read_and_close value is not compatible with int.
- (David Carlier)
- . Added support for partitioned cookies. (nielsdos)
- . Fix RC violation of session SID constant deprecation attribute. (ilutov)
- . Fixed GH-19197: build broken with ZEND_STRL usage with memcpy
- when implemented as macro. (David Carlier)
-
-- SimpleXML:
- . Fixed bug GH-12231 (SimpleXML xpath should warn when returning other return
- types than node lists). (nielsdos)
-
-- SNMP:
- . snmpget, snmpset, snmp_get2, snmp_set2, snmp_get3, snmp_set3 and
- SNMP::__construct() throw an exception on invalid hostname, community
- timeout and retries arguments. (David Carlier)
-
-- SOAP:
- . Added support for configuring the URI parser for SoapClient::__doRequest()
- as described in https://wiki.php.net/rfc/url_parsing_api#plugability.
- (kocsismate)
- . Implement request #55503 (Extend __getTypes to support enumerations).
- (nielsdos, datibbaw)
- . Implement request #61105 (Support Soap 1.2 SoapFault Reason Text lang
- attribute). (nielsdos)
- . Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action"
- header is correct). (nielsdos)
- . Fix namespace handling of WSDL and XML schema in SOAP,
- fixing at least GH-16320 and bug #68576. (nielsdos)
- . Fixed bug #70951 (Segmentation fault on invalid WSDL cache). (nielsdos)
- . Fixed bug GH-19773 (SIGSEGV due to uninitialized soap_globals->lang_en).
- (nielsdos, KaseyJenkins)
- . Fixed bug GH-19226 (Segfault when spawning new thread in soap extension).
- (Florian Engelhardt)
+ . Fixed bug 71162 (updateTimestamp never called when session data is empty).
+ (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)
+
+- Shmop:
+ . Fixed bug GH-9945 (shmop_open() silently truncates keys outside the key_t
+ range). (Weilin Du)
+
+- Soap:
+ . Soap::__setCookie() when cookie name is a digit is now not stored and
+ represented as a string anymore but a int. (David Carlier)
+ . Fixed bug GH-21421 (SoapClient typemap property breaks engine assumptions).
+ (ndossche)
+ . WSDL/XML Schema parsing now rejects out-of-range integer values for
+ occurrence constraints and integer restriction facets. Negative minOccurs
+ and maxOccurs values are rejected as well. (Weilin Du)
- Sockets:
- . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage.
+ . Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in
+ milliseconds transmitted data can remain unacknowledged. (James Lucas)
+ . Added AF_UNSPEC support for sock_addrinfo_lookup() as a sole umbrella for
+ AF_INET* family only. (David Carlier)
+ . Fixed GH-20532 (socket_addrinfo_lookup gives the error code with a new
+ optional parameter). (David Carlier)
+ . Added AF_PACKET support completion for socket_sendto()/socket_recvfrom().
(David Carlier)
- . Added TCP_FUNCTION_BLK to change the TCP stack algorithm on FreeBSD.
- (David Carlier)
- . Added IP_BINDANY for a socket to bind to any address. (David Carlier)
- . Added SO_BUSY_POOL to reduce packets poll latency. (David Carlier)
- . Added UDP_SEGMENT support to optimise multiple large datagrams over UDP
- if the kernel and hardware supports it. (David Carlier)
- . Added SHUT_RD, SHUT_WR and SHUT_RDWR constants for socket_shutdown().
- (David Carlier)
- . Added TCP_FUNCTION_ALIAS, TCP_REUSPORT_LB_NUMA, TCP_REUSPORT_LB_NUMA_NODOM,
- TCP_REUSPORT_LB_CURDOM, TCP_BBR_ALGORITHM constants.
- . socket_set_option() catches possible overflow with SO_RCVTIMEO/SO_SNDTIMEO
- with timeout setting on windows. (David Carlier)
- . socket_create_listen() throws an exception on invalid port value.
- (David Carlier)
- . socket_bind() throws an exception on invalid port value.
- (David Carlier)
- . socket_sendto() throws an exception on invalid port value.
- (David Carlier)
- . socket_addrinfo_lookup throws an exception on invalid hints value types.
- (David Carlier)
- . socket_addrinfo_lookup throws an exception if any of the hints value
- overflows. (David Carlier)
- . socket_addrinfo_lookup throws an exception if one or more hints entries
- has an index as numeric. (David Carlier)
- . socket_set_option with the options MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP
- will throw an exception if its value is not a valid array/object.
- (David Carlier)
- . socket_getsockname/socket_create/socket_bind handled AF_PACKET family socket.
- (David Carlier)
- . socket_set_option for multicast context throws a ValueError
- when the socket family is not of AF_INET/AF_INET6 family. (David Carlier)
- Sodium:
- . Fix overall theoretical overflows on zend_string buffer allocations.
- (David Carlier/nielsdos)
+ . Added support for libsodium 1.0.21 IPcrypt and XOF APIs. (jedisct1)
+ . pwhash argument-validation errors now throw ValueError instead of
+ SodiumException. (iliaal)
- SPL:
- . Fixed bug GH-20101 (SplHeap/SplPriorityQueue serialization
- exposes INDIRECTs). (nielsdos)
- . Improve __unserialize() hardening for SplHeap/SplPriorityQueue. (nielsdos)
- . Deprecate ArrayObject and ArrayIterator with objects. (Girgias)
- . Unregistering all autoloaders by passing the spl_autoload_call() function
- as a callback argument to spl_autoload_unregister() has been deprecated.
- Instead if this is needed, one should iterate over the return value of
- spl_autoload_functions() and call spl_autoload_unregister() on each
- value. (Girgias)
- . The SplObjectStorage::contains(), SplObjectStorage::attach(), and
- SplObjectStorage::detach() methods have been deprecated in favour of
- SplObjectStorage::offsetExists(), SplObjectStorage::offsetSet(), and
- SplObjectStorage::offsetUnset() respectively. (Girgias)
-
-- Sqlite:
- . Added Sqlite3Stmt::busy to check if a statement is still being executed.
- (David Carlier)
- . Added Sqlite3Stmt::explain to produce an explain query plan from
- the statement. (David Carlier)
- . Added Sqlite3Result::fetchAll to return all results at once from a query.
- (David Carlier)
+ . DirectoryIterator key can now work better with filesystem supporting larger
+ directory indexing. (David Carlier)
+ . Fixed bug GH-21831 (SplObjectStorage::removeAllExcept() use-after-free with
+ re-entrant getHash()). (Pratik Bhujel)
+ . Fix bugs GH-8561, GH-8562, GH-8563, and GH-8564 (Fixing various
+ SplFileObject iterator desync bugs). (iliaal)
+ . Fix bug GH-22062 (SplDoublyLinkedList iterator UAF via destructor releasing
+ next node). (David Carlier)
+
+- Sysvshm:
+ . Fixed shm_attach() to throw ValueError for keys outside the key_t range.
+ (Weilin Du)
+
+- Sqlite3:
+ . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche)
- Standard:
- . Add HEIF/HEIC support to getimagesize. (Benstone Zhang)
- . Added support for partitioned cookies. (nielsdos)
- . Implement #71517 (Implement SVG support for getimagesize() and friends).
- (nielsdos)
- . Implement GH-19188: Add support for new INI mail.cr_lf_mode.
- (alexandre-daubois)
- . Optimized PHP html_entity_decode function. (Artem Ukrainskiy)
- . Minor optimization to array_chunk(). (nielsdos)
- . Optimized pack(). (nielsdos, divinity76)
- . Fixed crypt() tests on musl when using --with-external-libcrypt
- (Michael Orlitzky).
- . Fixed bug GH-18062 (is_callable(func(...), callable_name: $name) for first
- class callables returns wrong name). (timwolla)
- . Added array_first() and array_last(). (nielsdos)
- . Fixed bug GH-18823 (setlocale's 2nd and 3rd argument ignores strict_types).
- (nielsdos)
- . Fixed exit code handling of sendmail cmd and added warnings.
- (Jesse Hathaway)
- . Fixed bug GH-18897 (printf: empty precision is interpreted as precision 6,
- not as precision 0). (nielsdos)
- . Fixed bug GH-20257 (mail() heap overflow with an empty message in lf mode).
- (David Carlier)
- . Fixed bug GH-20201 (AVIF images misdetected as HEIF after introducing HEIF
- support in getimagesize()). (nielsdos)
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
while COW violation flag is still set). (alexandre-daubois)
- . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()).
- (alexandre-daubois)
- . Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their
- data on serialize()). (alexandre-daubois)
- . Fixed GH-19610 (Deprecation warnings in functions taking as argument).
- (Girgias)
- . Fixed bug GH-19577 (Avoid integer overflow when using a small offset
- and PHP_INT_MAX with LimitIterator). (alexandre-daubois)
- . Fixed bug GH-19153 (#[\Attribute] validation should error on
- trait/interface/enum/abstract class). (DanielEScherzer)
- . Fixed bug GH-19070 (setlocale($type, NULL) should not be deprecated).
- (nielsdos)
- . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois)
- . Passing strings which are not one byte long to ord() is now deprecated.
- (Girgias)
- . Passing integers outside the interval [0, 255] to chr() is now deprecated.
- (Girgias)
- . The socket_set_timeout() alias function has been deprecated. (timwolla)
- . Passing null to readdir(), rewinddir(), and closedir() to use the last
- opened directory has been deprecated. (Girgias)
+ . Added form feed (\f) in the default trimmed characters of trim(), rtrim()
+ and ltrim(). (Weilin Du)
+ . Invalid mode values now throw in array_filter() instead of being silently
+ defaulted to 0. (Jorg Sowa)
+ . Fixed bug GH-21058 (error_log() crashes with message_type 3 and
+ null destination). (David Carlier)
+ . Fixed bug GH-13204 (glob() fails if square bracket is in current directory).
+ (ndossche)
+ . Add array size maximum to array_diff(). (ndossche)
+ . Add enum SortDirection. (timwolla)
+ . pathinfo() raises a ValueError with an invalid $flags argument.
+ (David Carlier)
+ . Passing an invalid flag value to the second argument of scandir() will now
+ throw a ValueError. (alexandre-daubois)
+ . array_change_key_case() now raises a ValueError when an invalid $case
+ argument value is passed. (Girgias)
+ . linkinfo() now raises a ValueError when the argument is an empty string.
+ (Weilin Du)
+ . getenv() and putenv() now raises a ValueError when the first argument
+ contains NUL bytes. (Weilin Du)
+ . dl() now raises a ValueError when the $extension_filename argument contains
+ NUL bytes. (Weilin Du)
+ . openlog() now raises a ValueError when the $prefix argument contains NUL
+ bytes. (Weilin Du)
+ . parse_str() now raises a ValueError when the $string argument contains NUL
+ bytes. (Weilin Du)
+ . proc_open() now raises a ValueError when the $cwd argument contains NUL
+ 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)
+ . Fixed bug GH-17384 (number_format() may exhaust memory with decimals
+ outside the range from -2147483648 to 2147483647). (Weilin Du)
- Streams:
- . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows).
- (cmb)
- . Fixed bug GH-19798: XP_SOCKET XP_SSL (Socket stream modules): Incorrect
- condition for Win32/Win64. (Jakub Zelenka)
- . Fixed bug GH-14506 (Closing a userspace stream inside a userspace handler
- causes heap corruption). (nielsdos)
- . Avoid double conversion to string in php_userstreamop_readdir(). (nielsdos)
-
-- Tests:
- . Allow to shuffle tests even in non-parallel mode. (dhuang00)
-
-- Tidy:
- . tidy::__construct/parseFile/parseString methods throw an exception if
- the configuration argument is invalid. (David Carlier)
- . Fixed GH-19021 (improved tidyOptGetCategory detection).
- (arjendekorte, David Carlier, Peter Kokot)
-
-- Tokenizer:
- . Fixed bug GH-19507 (Corrupted result after recursive tokenization during
- token_get_all()). (kubawerlos, nielsdos, Arnaud)
-
-- Windows:
- . Fixed bug GH-10992 (Improper long path support for relative paths). (cmb,
- nielsdos)
- . Fixed bug GH-16843 (Windows phpize builds ignore source subfolders). (cmb)
- . Fix GH-19722 (_get_osfhandle asserts in debug mode when given a socket).
- (dktapps)
-
-- XML:
- . The xml_parser_free() function has been deprecated. (DanielEScherzer)
-
-- XMLWriter:
- . Improved performance and reduce memory consumption. (nielsdos)
-
-- XSL:
- . Implement request #30622 (make $namespace parameter functional). (nielsdos)
+ . 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. (Jakub Zelenka)
+ . Added so_reuseaddr streams context socket option that allows disabling
+ 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)
+ . Fixed bug GH-21221 (Prevent closing of innerstream of php://temp stream).
+ (ilutov)
+ . Improved stream_socket_server() bind failure error reporting. (ilutov)
+ . Fixed bug #49874 (ftell() and fseek() inconsistency when using stream
+ filters). (Jakub Zelenka)
-- Zlib:
- . gzfile, gzopen and readgzfile, their "use_include_path" argument
- is now a boolean. (David Carlier)
- . Fixed bug GH-16883 (gzopen() does not use the default stream context when
- opening HTTP URLs). (nielsdos)
- . Implemented GH-17668 (zlib streams should support locking). (nielsdos)
+- URI:
+ . Added Uri\Rfc3986\Uri::getUriType() and Uri\WhatWg\Url::isSpecialScheme().
+ (kocsismate)
+ . Added Uri\Rfc3986\Uri::getHostType() and Uri\WhatWg\Url::getHostType().
+ (kocsismate)
+ . Added Uri\Rfc3986\UriBuilder. (kocsismate)
- Zip:
- . Fixed missing zend_release_fcall_info_cache on the following methods
- ZipArchive::registerProgressCallback() and ZipArchive::registerCancelCallback()
- on failure. (David Carlier)
+ . Fixed bug GH-21682 (ZipArchive instances should not be serializable).
+ serialize()/unserialize() now throw unless a subclass overrides
+ __serialize()/__unserialize(). (iliaal)
+ . Fixed ZipArchive callback being called after executor has shut down.
+ (ilutov)
+ . Support minimum version for libzip dependency updated to 1.0.0.
+ (David Carlier)
+ . Added ZipArchive::openString() method.
+ (Tim Starling, Soner Sayakci, Ghaith Olabi)
+
+- Zlib:
+ . deflate_init() now raises a TypeError when the value for option
+ "level", "memory", "window", or "strategy" is not of type int.
+ (Weilin Du)
+ . inflate_init() now raises a TypeError when the value for option
+ "window" is not of type int. (Weilin Du)
+
+<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
diff --git a/README.md b/README.md
index 5b8ec6f0d7dc..d83203d74c0b 100644
--- a/README.md
+++ b/README.md
@@ -11,10 +11,12 @@
PHP is a popular general-purpose scripting language that is especially suited to
web development. Fast, flexible and pragmatic, PHP powers everything from your
-blog to the most popular websites in the world. PHP is distributed under the
-[PHP License v3.01](LICENSE).
+blog to the most popular websites in the world.
-[](https://github.com/php/php-src/actions/workflows/push.yml)
+PHP is distributed under the [Modified BSD License](LICENSE)
+(SPDX-License-Identifier: `BSD-3-Clause`).
+
+[](https://github.com/php/php-src/actions/workflows/test.yml)
[](https://issues.oss-fuzz.com/issues?q=project:php)
## Documentation
@@ -49,7 +51,7 @@ sudo apt install -y pkg-config build-essential autoconf bison re2c libxml2-dev l
On Fedora, you can install these using:
```shell
-sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel
+sudo dnf install re2c bison autoconf make ccache libxml2-devel sqlite-devel
```
On MacOS, you can install these using `brew`:
@@ -128,8 +130,10 @@ permissions.
## PHP extensions
Extensions provide additional functionality on top of PHP. PHP consists of many
-essential bundled extensions. Additional extensions can be found in the PHP
-Extension Community Library - [PECL](https://pecl.php.net).
+essential bundled extensions. Additional extensions can be found on the
+[PIE Extensions](https://packagist.org/extensions) list, and installed with
+[🥧 PIE, the PHP Installer for Extensions](https://github.com/php/pie) or the
+deprecated PHP Extension Community Library - [PECL](https://pecl.php.net).
## Contributing
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/TSRM/TSRM.c b/TSRM/TSRM.c
index e99993204b6f..a5032e456aae 100644
--- a/TSRM/TSRM.c
+++ b/TSRM/TSRM.c
@@ -36,12 +36,13 @@ struct _tsrm_tls_entry {
tsrm_tls_entry *next;
};
-
typedef struct {
size_t size;
ts_allocate_ctor ctor;
ts_allocate_dtor dtor;
- size_t fast_offset;
+ ptrdiff_t fast_offset;
+ /* When set, storage comes from __thread memory instead of being allocated by TSRM. */
+ void *(*tls_addr)(void);
int done;
} tsrm_resource_type;
@@ -58,6 +59,7 @@ static int resource_types_table_size;
/* Reserved space for fast globals access */
static size_t tsrm_reserved_pos = 0;
static size_t tsrm_reserved_size = 0;
+static size_t tsrm_reserved_front = 0;
static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */
static MUTEX_T tsrm_env_mutex; /* tsrm environ mutex */
@@ -155,6 +157,7 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb
tsrm_reserved_pos = 0;
tsrm_reserved_size = 0;
+ tsrm_reserved_front = 0;
tsrm_env_mutex = tsrm_mutex_alloc();
@@ -163,14 +166,20 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb
static void ts_free_resources(tsrm_tls_entry *thread_resources)
{
+ bool own_thread = thread_resources->thread_id == tsrm_thread_id();
+
/* Need to destroy in reverse order to respect dependencies. */
for (int i = thread_resources->count - 1; i >= 0; i--) {
if (!resource_types_table[i].done) {
+ /* A __thread block of a foreign thread is inaccessible. */
+ if (resource_types_table[i].tls_addr && !own_thread) {
+ continue;
+ }
if (resource_types_table[i].dtor) {
resource_types_table[i].dtor(thread_resources->storage[i]);
}
- if (!resource_types_table[i].fast_offset) {
+ if (!resource_types_table[i].fast_offset && !resource_types_table[i].tls_addr) {
free(thread_resources->storage[i]);
}
}
@@ -179,7 +188,9 @@ static void ts_free_resources(tsrm_tls_entry *thread_resources)
free(thread_resources->storage);
}
-/* Shutdown TSRM (call once for the entire process) */
+/* Shutdown TSRM (call once for the entire process). Tears down every thread left
+ * in the table. For resources allocated with ts_allocate_tls_id(), only the dtor
+ * of the calling thread is invoked. */
TSRM_API void tsrm_shutdown(void)
{/*{{{*/
if (is_thread_shutdown) {
@@ -205,7 +216,7 @@ TSRM_API void tsrm_shutdown(void)
} else {
free(p->storage);
}
- free(p);
+ free((char *) p - tsrm_reserved_front);
p = next_p;
}
}
@@ -232,6 +243,7 @@ TSRM_API void tsrm_shutdown(void)
tsrm_reserved_pos = 0;
tsrm_reserved_size = 0;
+ tsrm_reserved_front = 0;
}/*}}}*/
/* {{{ */
@@ -256,7 +268,10 @@ static void tsrm_update_active_threads(void)
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
for (j=p->count; jthread_id == tsrm_thread_id());
+ p->storage[j] = resource_types_table[j].tls_addr();
+ } else if (resource_types_table[j].fast_offset) {
p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset);
} else {
p->storage[j] = (void *) malloc(resource_types_table[j].size);
@@ -301,6 +316,7 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = NULL;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
tsrm_update_active_threads();
@@ -319,17 +335,20 @@ TSRM_API void tsrm_reserve(size_t size)
}/*}}}*/
+/* Carve a fixed-offset front region out of the reserved space. It is placed
+ * before the TLS entry, so the hot globals get compile-time-constant negative
+ * offsets from the cache pointer. */
+TSRM_API void tsrm_reserve_fast_front(size_t size)
+{
+ tsrm_reserved_front = TSRM_ALIGNED_SIZE(size);
+ tsrm_reserved_size -= tsrm_reserved_front;
+}
+
+
/* allocates a new fast thread-safe-resource id */
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
{/*{{{*/
- TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size));
-
tsrm_mutex_lock(tsmm_mutex);
-
- /* obtain a resource id */
- *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
- TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
-
size = TSRM_ALIGNED_SIZE(size);
if (tsrm_reserved_size - tsrm_reserved_pos < size) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource"));
@@ -338,9 +357,26 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
tsrm_mutex_unlock(tsmm_mutex);
return 0;
}
-
- *offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos;
+ ptrdiff_t fixed_offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos;
tsrm_reserved_pos += size;
+ tsrm_mutex_unlock(tsmm_mutex);
+
+ return ts_allocate_fast_id_at(rsrc_id, offset, fixed_offset, size, ctor, dtor);
+}/*}}}*/
+
+
+TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
+{
+ TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size));
+
+ tsrm_mutex_lock(tsmm_mutex);
+
+ /* obtain a resource id */
+ *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
+ TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
+
+ size = TSRM_ALIGNED_SIZE(size);
+ *offset = (size_t) fixed_offset;
/* store the new resource type in the resource sizes table */
if (resource_types_table_size < id_count) {
@@ -359,6 +395,7 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = NULL;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
tsrm_update_active_threads();
@@ -366,7 +403,42 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
return *rsrc_id;
-}/*}}}*/
+}
+
+/* allocates a resource id whose per-thread storage is a native __thread block */
+TSRM_API ts_rsrc_id ts_allocate_tls_id(ts_rsrc_id *rsrc_id, void *(*tls_addr)(void), size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
+{
+ TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new TLS resource id, %d bytes", size));
+
+ tsrm_mutex_lock(tsmm_mutex);
+
+ *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
+
+ if (resource_types_table_size < id_count) {
+ tsrm_resource_type *_tmp;
+ _tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
+ if (!_tmp) {
+ TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
+ *rsrc_id = 0;
+ tsrm_mutex_unlock(tsmm_mutex);
+ return 0;
+ }
+ resource_types_table = _tmp;
+ resource_types_table_size = id_count;
+ }
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].tls_addr = tls_addr;
+ resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
+
+ tsrm_update_active_threads();
+ tsrm_mutex_unlock(tsmm_mutex);
+
+ TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new TLS resource id %d", *rsrc_id));
+ return *rsrc_id;
+}
static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource)
{
@@ -378,7 +450,10 @@ static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{/*{{{*/
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
- (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size);
+ /* The entry follows the fixed-offset front region.
+ * hot globals live at negative offsets from the TLS cache pointer. */
+ char *block = (char *) malloc(tsrm_reserved_front + TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size);
+ (*thread_resources_ptr) = (tsrm_tls_entry *) (block + tsrm_reserved_front);
(*thread_resources_ptr)->storage = NULL;
if (id_count > 0) {
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
@@ -397,7 +472,9 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_
if (resource_types_table[i].done) {
(*thread_resources_ptr)->storage[i] = NULL;
} else {
- if (resource_types_table[i].fast_offset) {
+ if (resource_types_table[i].tls_addr) {
+ (*thread_resources_ptr)->storage[i] = resource_types_table[i].tls_addr();
+ } else if (resource_types_table[i].fast_offset) {
(*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset);
} else {
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
@@ -485,9 +562,11 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
/* In case that extensions don't use the pointer passed from the dtor, but incorrectly
* use the global pointer, we need to setup the global pointer temporarily here. */
set_thread_local_storage_resource_to(thread_resources);
- /* Free up the old resource from the old thread instance */
+ /* Dead thread with a recycled id: its __thread blocks are gone, and this
+ * thread's blocks were never constructed, so keep tls dtors from running. */
+ thread_resources->thread_id = 0;
ts_free_resources(thread_resources);
- free(thread_resources);
+ free((char *) thread_resources - tsrm_reserved_front);
/* Allocate a new resource at the same point in the linked list, and relink the next pointer */
allocate_new_resource(last_thread_resources, thread_id);
thread_resources = *last_thread_resources;
@@ -529,7 +608,7 @@ void ts_free_thread(void)
tsrm_tls_table[hash_value] = thread_resources->next;
}
tsrm_tls_set(0);
- free(thread_resources);
+ free((char *) thread_resources - tsrm_reserved_front);
break;
}
if (thread_resources->next) {
@@ -559,7 +638,7 @@ void ts_free_id(ts_rsrc_id id)
if (resource_types_table[rsrc_id].dtor) {
resource_types_table[rsrc_id].dtor(p->storage[rsrc_id]);
}
- if (!resource_types_table[rsrc_id].fast_offset) {
+ if (!resource_types_table[rsrc_id].fast_offset && !resource_types_table[rsrc_id].tls_addr) {
free(p->storage[rsrc_id]);
}
}
diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h
index 80d6cbad0443..752ea1803f9c 100644
--- a/TSRM/TSRM.h
+++ b/TSRM/TSRM.h
@@ -20,6 +20,7 @@
# include
#endif
+#include
#include
#include
@@ -94,6 +95,15 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate
TSRM_API void tsrm_reserve(size_t size);
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
+/* Fast resources at caller-chosen, compile-time-constant offsets. The fixed
+ * front region must be reserved after tsrm_reserve() and before any fast id. */
+TSRM_API void tsrm_reserve_fast_front(size_t size);
+TSRM_API ts_rsrc_id ts_allocate_fast_id_at(ts_rsrc_id *rsrc_id, size_t *offset, ptrdiff_t fixed_offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
+
+/* Resource whose per-thread storage is a native __thread block.
+ * Must be called at startup before any other thread exists. */
+TSRM_API ts_rsrc_id ts_allocate_tls_id(ts_rsrc_id *rsrc_id, void *(*tls_addr)(void), size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
+
/* fetches the requested resource for the current thread */
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id);
#define ts_resource(id) ts_resource_ex(id, NULL)
@@ -155,7 +165,7 @@ TSRM_API bool tsrm_is_managed_thread(void);
#if !__has_attribute(tls_model) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MUSL__) || defined(__HAIKU__)
# define TSRM_TLS_MODEL_ATTR
# define TSRM_TLS_MODEL_DEFAULT
-#elif __PIC__
+#elif defined(__PIC__) && !defined(__PIE__)
# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec")))
# define TSRM_TLS_MODEL_INITIAL_EXEC
#else
@@ -175,9 +185,14 @@ TSRM_API bool tsrm_is_managed_thread(void);
#define TSRMG_BULK_STATIC(id, type) ((type) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(id)])
#define TSRMG_FAST_STATIC(offset, type, element) (TSRMG_FAST_BULK_STATIC(offset, type)->element)
#define TSRMG_FAST_BULK_STATIC(offset, type) ((type) (((char*) TSRMLS_CACHE)+(offset)))
+#ifdef __cplusplus
+#define TSRMLS_MAIN_CACHE_EXTERN() extern "C" { extern TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR; }
+#define TSRMLS_CACHE_EXTERN() extern "C" { extern TSRM_TLS void *TSRMLS_CACHE; }
+#else
#define TSRMLS_MAIN_CACHE_EXTERN() extern TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR;
-#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;
#define TSRMLS_CACHE_EXTERN() extern TSRM_TLS void *TSRMLS_CACHE;
+#endif
+#define TSRMLS_MAIN_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE = NULL;
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache()
#define TSRMLS_CACHE _tsrm_ls_cache
diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c
index 4c8fc9d19aa9..90317ab64b19 100644
--- a/TSRM/tsrm_win32.c
+++ b/TSRM/tsrm_win32.c
@@ -1,14 +1,12 @@
/*
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Daniel Beulshausen |
+----------------------------------------------------------------------+
diff --git a/TSRM/tsrm_win32.h b/TSRM/tsrm_win32.h
index c5bdc492be1b..9c16bd7c654b 100644
--- a/TSRM/tsrm_win32.h
+++ b/TSRM/tsrm_win32.h
@@ -1,14 +1,12 @@
/*
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Daniel Beulshausen |
+----------------------------------------------------------------------+
diff --git a/UPGRADING b/UPGRADING
index 39400b5ea6d6..97670e09246d 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -1,4 +1,4 @@
-PHP 8.5 UPGRADE NOTES
+PHP 8.6 UPGRADE NOTES
1. Backward Incompatible Changes
2. New Features
@@ -20,812 +20,474 @@ PHP 8.5 UPGRADE NOTES
========================================
- Core:
- . It is no longer possible to use "array" and "callable" as class alias names
- in class_alias().
- . Loosely comparing uncomparable objects (e.g. enums, \CurlHandle and other
- internal classes) to booleans was previously inconsistent. If compared to a
- boolean literal $object == true, it would behave the same way as (bool)
- $object. If compared to a statically unknown value $object == $true, it
- would always return false. This behavior was consolidated to always follow
- the behavior of (bool) $object.
- . The return value of gc_collect_cycles() no longer includes strings and
- resources that were indirectly collected through cycles.
- . It is now allowed to substitute static with self or the concrete class name
- in final subclasses.
- . The tick handlers are now deactivated after all shutdown functions, destructors
- have run and the output handlers have been cleaned up.
- This is a consequence of fixing GH-18033.
- . Traits are now bound before the parent class. This is a subtle behavioral
- change, but should more closely match user expectations, demonstrated by
- GH-15753 and GH-16198.
- . Errors emitted during compilation and class linking are now always delayed
- and handled after compilation or class linking. Fatal errors emitted during
- compilation or class linking cause any delayed errors to be handled
- immediately, without calling user-defined error handlers.
- . Exceptions thrown by user-defined error handlers when handling class linking
- errors are not promoted to fatal errors anymore and do not prevent linking.
- . Applying #[\Attribute] to an abstract class, enum, interface, or trait triggers
- an error during compilation. Previously, the attribute could be added, but when
- ReflectionAttribute::newInstance() was called an error would be thrown.
- The error can be delayed from compilation to runtime using the new
- #[\DelayedTargetValidation] attribute.
- . The disable_classes INI setting has been removed as it causes various
- engine assumptions to be broken.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#remove_disable_classes_ini_setting
- . Destructuring non-array values (other than NULL) using [] or list() now
- emits a warning.
- RFC: https://wiki.php.net/rfc/warnings-php-8-5#destructuring_non-array_values
- . A warning is now emitted when casting floats to int if they cannot be represented
- as one. This affects explicit int casts and implicit int casts.
- RFC: https://wiki.php.net/rfc/warnings-php-8-5#casting_out_of_range_floats_to_int
- . A warning is now emitted when casting NAN to other types.
- RFC: https://wiki.php.net/rfc/warnings-php-8-5#coercing_nan_to_other_types
-
-- BZ2:
- . bzcompress() now throws a ValueError when $block_size is not between
- 1 and 9.
- . bzcompress() now throws a ValueError when $work_factor is not between
- 0 and 250.
+ . ??/empty() on a magic property no longer call __get() when __isset()
+ has materialised the property by writing into the property table.
+ The freshly-written value is returned directly. isset() is unaffected.
-- DOM:
- . Cloning a DOMNamedNodeMap, DOMNodeList, Dom\NamedNodeMap, Dom\NodeList,
- Dom\HTMLCollection, and Dom\DtdNamedNodeMap now fails.
- This never actually resulted in a working object,
- so the impact should actually be zero.
-
-- FileInfo:
- . finfo_file() and finfo::file() now throws a ValueError instead of a
- TypeError when $filename contains nul bytes.
- This aligns the type of Error thrown to be consistent with the rest of
- the language.
-
-- Intl:
- . The extension now requires at least ICU 57.1.
- . The behaviour of Collator::SORT_REGULAR with respect to handling numeric
- strings is now aligned with the behaviour of SORT_REGULAR in ext/standard.
- This is a consequence of fixing bug GH-18566.
+- COM
+ . It is no longer possible to clone variant objects, this is because
+ the cloning behaviour was ill defined.
-- LDAP:
- . ldap_get_option() and ldap_set_option() now throw a ValueError when
- passing an invalid option.
-
-- MBstring:
- . Unicode data tables have been updated to Unicode 17.0
+- DOM:
+ . Properties previously documented as @readonly (e.g. DOMNode::$nodeType,
+ DOMDocument::$xmlEncoding, DOMEntity::$actualEncoding, ::$encoding,
+ ::$version) are now declared with asymmetric visibility
+ (public private(set)). Attempts to write to them from outside the
+ class now raise "Cannot modify private(set) property ::$
+ from global scope" instead of the prior readonly modification error.
+ ReflectionProperty::isWritable() also reports these properties
+ accurately.
+ . Array access on Dom\DtdNamedNodeMap objects now returns null for negative
+ integer indexes instead of returning the first node.
+ . Array access on Dom\DtdNamedNodeMap objects now raises a ValueError when
+ the integer index is greater than INT_MAX instead of overflowing to a
+ smaller index.
-- MySQLi:
- . Calling the mysqli constructor on an already-constructed object
- is now no longer possible and throws an Error.
+- GD:
+ . imagesetstyle(), imagefilter() and imagecrop() filter their array arguments
+ types / values and raise a TypeError / ValueError accordingly.
-- ODBC:
- . ODBC now assumes that at least ODBC 3.5 functionality is available. The
- ODBCVER definition and build system flags to control it have been removed.
- . ODBC no longer has build flags to build against specific drivers (except
- for DB2) and removes special cases for those drivers. It is strongly
- recommended to use a driver manager like iODBC or unixODBC on non-Windows.
+- GMP:
+ . GMP power and shift operators now throw ValueError when GMP right operands
+ are outside the unsigned long range, instead of silently truncating them.
+ . GMP integer string parsing now throw ValueError strings containing NUL
+ bytes, instead of silently truncating them.
-- Opcache:
- . The Opcache extension is now always built into the PHP binary and is always
- loaded. The INI directives opcache.enable and opcache.enable_cli are still
- honored.
- The --enable-opcache/--disable-opcache configure flags have been removed,
- and the build does not produce opcache.so or php_opcache.dll objects
- anymore.
- Using zend_extension=opcache.so or zend_extension=php_opcache.dll INI
- directives will emit a warning.
+- Intl:
+ . Passing a non-stringable object as a time zone to Intl APIs that accept
+ time zone objects or strings now raises a TypeError instead of an Error.
+ . IntlIterator::current() now returns null when called before the iterator is
+ positioned, or after the iterator becomes invalid, instead of exposing an
+ undefined value.
+ . 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.
+ . UConverter::transcode() now rejects from_subst and to_subst option values
+ longer than 127 bytes instead of silently truncating the length before
+ passing it to ICU.
+ . ResourceBundle::get() and resourcebundle_get() now report fallback-disabled
+ resource lookups with "without fallback to " instead of the
+ malformed "without fallback from to ".
+ . IntlDateFormatter::parse()/datefmt_parse() and
+ IntlDateFormatter::localtime()/datefmt_localtime() now raise a TypeError
+ when the offset argument is not of type int instead of silently converting
+ the value.
- PCNTL:
- . pcntl_exec() now throws ValueErrors when entries of the $args parameter
- contain null bytes.
- . pcntl_exec() now throws ValueErrors when entries or keys of the
- $env_vars parameter contain null bytes.
+ . pcntl_alarm() now raises a ValueError if the seconds argument is
+ lower than zero or greater than platform's UINT_MAX.
+ . pcntl_exec() now raises a ValueError if the $args argument is not a list
+ array.
- PCRE:
- . The extension is compiled without semi-deprecated
- PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK compile option.
- https://github.com/PCRE2Project/pcre2/issues/736#issuecomment-2754024651
-
-- PDO:
- . The constructor arguments set in conjunction with PDO::FETCH_CLASS now
- follow the usual CUFA (call_user_func_array) semantics.
- This means string keys will act like a named argument.
- Moreover, automatic wrapping for by-value arguments passed to a by-ref
- parameter has been removed, and the usual E_WARNING about this is now
- emitted.
- To pass a variable by-ref to a constructor argument use the general
- array value reference assignment: $ctor_args = [&$valByRef]
- . Attempting to call PDOStatement::setFetchMode during a call to PDO::fetch(),
- PDO::fetchObject(), PDO::fetchAll(),
- for example using tricks such as passing the statement object as a constructor
- argument when fetching into an object, will now throw an Error.
- . The value of the constants PDO::FETCH_GROUP, PDO::FETCH_UNIQUE,
- PDO::FETCH_CLASSTYPE, PDO::FETCH_PROPS_LATE, and PDO::FETCH_SERIALIZE
- have changed.
- . A ValueError is now thrown if PDO::FETCH_PROPS_LATE is used with a fetch
- mode different than PDO::FETCH_CLASS, consistent with other fetch flags.
- . A ValueError is now thrown if PDO::FETCH_INTO is used as a fetch mode in
- PDO::fetchAll(), similar to PDO::FETCH_LAZY.
-
-- PDO_FIREBIRD:
- . A ValueError is now thrown when trying to set a cursor name that is too
- long on a PDOStatement resulting from the Firebird driver.
+ . preg_grep() now returns false instead of a partial array when a PCRE
+ execution error occurs (e.g. malformed UTF-8 input with the /u modifier).
+ This is consistent with other preg_* functions.
+
+- Phar:
+ . Phar::mungServer() now raises a ValueError when an invalid argument value
+ is passed instead of being silently ignored.
+ . Phar::addEmptyDir() now rejects "/.phar" paths in addition to ".phar"
+ paths, and raises the same BadMethodCallException for attempts to create
+ the reserved magic ".phar" directory through that form.
+ . Phar::addEmptyDir() now treats non-magic names that merely share the
+ ".phar" prefix as ordinary directories.
+
+- PGSQL:
+ . pg_fetch_object() now reports the ValueError for a non-empty
+ $constructor_args on a class without a constructor on the
+ $constructor_args argument instead of $class. Errors raised when
+ the requested class is not instantiable (abstract, interface, enum)
+ now surface before the row is fetched.
+
+- Posix:
+ . posix_access() now raises a ValueError when an invalid $flags
+ argument value is passed.
+ . posix_mkfifo() now raises a ValueError when an invalid $permissions
+ argument value is passed.
- Session:
- . Attempting to write session data where $_SESSION has a key containing
- the pipe character will now emit a warning instead of silently failing.
+ . Setting session.cookie_path, session.cookie_domain, or session.cache_limiter
+ to a value containing NUL bytes now emits a warning and leaves the setting
+ unchanged. Previously, NUL bytes were silently accepted: for cookie_path
+ and cookie_domain this caused the SAPI to drop the Set-Cookie header; for
+ cache_limiter the value was silently truncated at the NUL byte.
+ . A ValueError is not thrown if $name is a string containing NUL bytes in
+ session_module_name().
+ . session_encode() now returns an empty string instead of false for empty
+ sessions. It only returns false now when the session data could not be
+ encoded. This mainly happens with the default serialization handler
+ if a key contains the pipe | character.
+ . When session.lazy_write is enabled and a session handler implements
+ SessionUpdateTimestampHandlerInterface, sessions that were read as empty
+ and remain empty at write time will now trigger updateTimestamp() instead
+ of write(). Previously, write() was always called for empty sessions
+ because session_encode() returned false, bypassing the lazy_write
+ comparison. Custom session handlers that rely on write() being called
+ with empty data (e.g. to destroy the session) should implement the same
+ logic in their updateTimestamp() method.
+ . The defaults of three session INI settings have changed to provide secure
+ behavior out of the box:
+ - session.use_strict_mode is now 1 (was 0). Strict mode rejects
+ uninitialized session IDs, mitigating session fixation. Custom session
+ handlers that previously relied on accepting externally supplied IDs
+ without a corresponding storage entry must either implement
+ validateId() / create_sid() or explicitly set this to 0.
+ - session.cookie_httponly is now 1 (was 0). Session cookies are no
+ longer accessible to JavaScript via document.cookie. Applications
+ that read the session cookie from JavaScript must explicitly set
+ this to 0.
+ - session.cookie_samesite is now "Lax" (was unset). Session cookies
+ are no longer sent on cross-site requests other than top-level
+ navigations using safe HTTP methods. Applications that depend on
+ session cookies being sent on cross-site POST submissions must
+ explicitly set this to "None" (and also set session.cookie_secure
+ to 1).
+ RFC: https://wiki.php.net/rfc/session_security_defaults
+
+- Shmop:
+ . shmop_open() now raises a ValueError when the $key argument is outside the
+ platform's key_t range instead of passing a truncated key to the operating
+ system.
-- SimpleXML:
- . Passing an XPath expression that returns something other than a node set
- to SimpleXMLElement::xpath() will now emit a warning and return false,
- instead of silently failing and returning an empty array.
+- SOAP:
+ . WSDL/XML Schema parsing now rejects out-of-range integer values for
+ occurrence constraints and integer restriction facets. Negative minOccurs
+ and maxOccurs values are rejected as well.
+ . SOAP encoding errors now report the affected type or failing operation in
+ the error message instead of the generic "Encoding: Violation of encoding
+ rules" message. Code that compares the exact message may need to be
+ updated.
+
+- Sodium:
+ . The password-hashing functions sodium_crypto_pwhash(),
+ sodium_crypto_pwhash_str(),
+ sodium_crypto_pwhash_scryptsalsa208sha256() and
+ sodium_crypto_pwhash_scryptsalsa208sha256_str() now throw ValueError
+ instead of SodiumException when an argument is out of range, such as an
+ opslimit or memlimit below the documented minimum. SodiumException is
+ still thrown for internal libsodium failures.
- SPL:
- . ArrayObject no longer accepts enums, as modifying the $name or $value
- properties can break engine assumptions.
- . SplFileObject::fwrite's parameter $length is now nullable. The default
- value changed from 0 to null.
+ . SplObjectStorage::getHash() implementations may no longer mutate any
+ SplObjectStorage instance. Attempting to do so now throws an Error.
+ . SplFileObject::next() now advances the stream when no prior current()
+ call has cached a line. A subsequent current() call returns the new line
+ rather than the previous one.
+ . SplFileObject::fgets() no longer caches the returned line for subsequent
+ current() calls. current() now re-reads from the current stream position
+ instead of returning the line fgets() just returned.
+ . SplFileObject::next() past EOF no longer increments key() without bound.
+ SplFileObject::seek() past EOF now produces the same key() value as
+ SplTempFileObject; the two previously returned different values.
- Standard:
- . Using a printf-family function with a formatter that did not specify the
- precision previously incorrectly reset the precision instead of treating
- it as a precision of 0. See GH-18897.
+ . Form feed (\f) is now added in the default trimmed characters of trim(),
+ rtrim() and ltrim().
+ RFC: https://wiki.php.net/rfc/trim_form_feed
+ . array_filter() now raises a ValueError when an invalid $mode argument value
+ is passed.
+ . array_change_key_case() now raises a ValueError when an invalid $case
+ argument value is passed.
+ . getenv() and putenv() now raises a ValueError when the first argument
+ contains NUL bytes.
+ . dl() now raises a ValueError when the $extension_filename argument contains
+ NUL bytes.
+ . openlog() now raises a ValueError when the $prefix argument contains NUL
+ bytes.
+ . parse_str() now raises a ValueError when the $string argument contains NUL
+ bytes.
+ . linkinfo() now raises a ValueError when the $path argument is empty.
+ . pathinfo() now raises a ValueError when an invalid $flag argument value is
+ passed.
+ . scandir() now raises a ValueError when an invalid $sorting_order argument
+ value is passed.
+ . number_format() now raises a ValueError when $decimals is outside the
+ integer range instead of silently clamping very large positive values.
+ . sleep() now raises a ValueError when $seconds is greater than the platform
+ limit (UINT_MAX seconds, or UINT_MAX / 1000 seconds on Windows) instead of
+ allowing the value to overflow.
+ . usleep() now raises a ValueError when $microseconds is greater than
+ UINT_MAX instead of allowing the value to overflow.
+ . proc_open() now raises a ValueError when the $cwd argument contains NUL
+ bytes.
-- SOAP:
- . SoapClient::__doRequest() expects a new, optional $uriParserClass parameter
- as described in https://wiki.php.net/rfc/url_parsing_api#plugability.
+- Sysvshm:
+ . shm_attach() now raises a ValueError when the $key argument is outside the
+ platform's key_t range instead of passing a truncated key to the operating
+ system.
+
+- Zip:
+ . ZipArchive::extractTo now raises a TypeError for the files argument if one
+ or more of the entries is not a string.
+ . ZipArchive::addGlob() and ZipArchive::addPattern() now raise a TypeError
+ when the "remove_all_path" option is not of type bool, or when the
+ "comp_method", "comp_flags", or "enc_method" options are not of type int
+ (numeric strings are coerced), instead of emitting a warning.
+
+- Zlib:
+ . deflate_init() now raises a TypeError when the value for option "level",
+ "memory", "window", or "strategy" is not of type int.
+ . inflate_init() now raises a TypeError when the value for option "window" is
+ not of type int.
========================================
2. New Features
========================================
- Core:
- . Closure is now a proper subtype of callable
- . Added support for Closures and first class callables in constant
- expressions.
- RFC: https://wiki.php.net/rfc/closures_in_const_expr
- RFC: https://wiki.php.net/rfc/fcc_in_const_expr
- . Fatal Errors (such as an exceeded maximum execution time) now include a
- backtrace.
- RFC: https://wiki.php.net/rfc/error_backtraces_v2
- . Added the #[\NoDiscard] attribute to indicate that a function's return
- value is important and should be consumed.
- RFC: https://wiki.php.net/rfc/marking_return_value_as_important
- . Added the (void) cast to indicate that not using a value is intentional.
- The (void) cast has no effect on the program's execution by itself, but
- it can be used to suppress warnings emitted by #[\NoDiscard] and possibly
- also diagnostics emitted by external IDEs or static analysis tools.
- RFC: https://wiki.php.net/rfc/marking_return_value_as_important
- . Added asymmetric visibility support for static properties.
- RFC: https://wiki.php.net/rfc/static-aviz
- . Added support for casts in constant expressions.
- . Added support for attributes on compile-time non-class constants.
- RFC: https://wiki.php.net/rfc/attributes-on-constants
- . The #[\Deprecated] attribute can now be used on constants.
- RFC: https://wiki.php.net/rfc/attributes-on-constants
- . Added the pipe (|>) operator.
- RFC: https://wiki.php.net/rfc/pipe-operator-v3
- . Constructor property promotion can now be used for final properties.
- RFC: https://wiki.php.net/rfc/final_promotion
- . #[\Override] can now be applied to properties.
- RFC: https://wiki.php.net/rfc/override_properties
- . The #[\DelayedTargetValidation] attribute can be used to suppress
- compile-time errors from core (or extension) attributes that are used on
- invalid targets. These errors are instead reported at runtime if and when
- ReflectionAttribute::newInstance() is called.
- RFC: https://wiki.php.net/rfc/delayedtargetvalidation_attribute
+ . It is now possible to use reference assign on WeakMap without the key
+ needing to be present beforehand.
+ . It is now possible to define the __debugInfo() magic method on enums.
+ RFC: https://wiki.php.net/rfc/debugable-enums
+ . #[\Override] can now be applied to class constants, including enum cases.
+ RFC: https://wiki.php.net/rfc/override_constants
+ . Implemented partial function application
+ RFC: https://wiki.php.net/rfc/partial_function_application_v2
- Curl:
- . Added support for share handles that are persisted across multiple PHP
- requests, safely allowing for more effective connection reuse.
- RFC: https://wiki.php.net/rfc/curl_share_persistence_improvement
- . Added support for CURLINFO_USED_PROXY (libcurl >= 8.7.0),
- CURLINFO_HTTPAUTH_USED, and CURLINFO_PROXYAUTH_USED
- (libcurl >= 8.12.0) to the curl_getinfo() function.
- When curl_getinfo() returns an array, the same information
- is available as "used_proxy", "httpauth_used", and "proxyauth_used"
- keys.
- CURLINFO_USED_PROXY gets zero set if no proxy was used in the
- previous transfer or a non-zero value if a proxy was used.
- CURLINFO_HTTPAUTH_USED and CURLINFO_PROXYAUTH_USED get bitmasks
- indicating the HTTP and proxy authentication methods that were
- used in the previous request. See CURLAUTH_* constants for
- possible values.
- . Added CURLOPT_INFILESIZE_LARGE Curl option, which is a safe
- replacement for CURLOPT_INFILESIZE. On certain systems,
- CURLOPT_INFILESIZE only accepts a 32-bit signed integer as the file
- size (2.0 GiB) even on 64-bit systems. CURLOPT_INFILESIZE_LARGE
- accepts the largest integer value the system can handle.
- . Added CURLFOLLOW_OBEYCODE, CURLFOLLOW_FIRSTONLY and CURLFOLLOW_ALL values for
- CURLOPT_FOLLOWLOCATION curl_easy_setopt option.
- CURLFOLLOW_OBEYCODE to follow more strictly in regard to redirect
- if they are allowed. CURLFOLLOW_FIRSTONLY to follow only the
- first redirect thus if there is any follow up redirect, it won't go
- any further. CURLFOLLOW_ALL is equivalent to setting CURLOPT_FOLLOWLOCATION
- to true.
- . Added support for CURLINFO_CONN_ID (libcurl >= 8.2.0) to the curl_getinfo()
- function. This constant allows retrieving the unique ID of the connection
- used by a cURL transfer. It is primarily useful when connection reuse or
- connection pooling logic is needed in PHP-level applications. When
- curl_getinfo() returns an array, this value is available as the "conn_id" key.
- . Added support for CURLINFO_QUEUE_TIME_T (libcurl >= 8.6.0) to the curl_getinfo()
- function. This constant allows retrieving the time (in microseconds) that the
- request spent in libcurl’s connection queue before it was sent.
- This value can also be retrieved by passing CURLINFO_QUEUE_TIME_T to the
- curl_getinfo() $option parameter.
- . Added support for CURLOPT_SSL_SIGNATURE_ALGORITHMS to specify the signature
- algorithms to use for TLS.
+ . curl_getinfo() return array now includes a new size_delivered key, which
+ indicates the total number of bytes passed to the download write callback.
+ This value can also be obtained by passing CURLINFO_SIZE_DELIVERED as the
+ $option parameter.
+ Requires libcurl 8.20.0 or later.
+ . Added CURLOPT_SEEKFUNCTION to register a callback that repositions a
+ streamed request body so libcurl can rewind and resend it on a redirect,
+ multi-pass authentication, or a retried reused connection instead of
+ failing with CURLE_SEND_FAIL_REWIND. The callback receives the CurlHandle,
+ offset and origin, and must return one of CURL_SEEKFUNC_OK,
+ CURL_SEEKFUNC_FAIL or CURL_SEEKFUNC_CANTSEEK.
-- DOM:
- . Added Dom\Element::$outerHTML.
- . Added $children property to Dom\ParentNode implementations.
-
-- EXIF:
- . Add OffsetTime* Exif tags.
- . Added support for HEIF/HEIC.
-
-- Filter:
- . Add new FILTER_THROW_ON_FAILURE flag which can be passed to the filter
- functions and will force an exception to be triggered when validation fails.
- The new flag cannot be combined with FILTER_NULL_ON_FAILURE; trying
- to do so will result in a ValueError being thrown.
- RFC: https://wiki.php.net/rfc/filter_throw_on_failure
+- Fileinfo:
+ . finfo_file() now works with remote streams.
- Intl:
- . Added class constants NumberFormatter::CURRENCY_ISO,
- NumberFormatter::CURRENCY_PLURAL, NumberFormatter::CASH_CURRENCY,
- and NumberFormatter::CURRENCY_STANDARD for various currency-related
- number formats.
- . Added Locale::addLikelySubtags and Locale::minimizeSubtags to
- handle likely tags on a given locale.
- . Added IntlListFormatter class to format, order, and punctuate
- a list of items with a given locale, IntlListFormatter::TYPE_AND,
- IntlListFormatter::TYPE_OR, IntlListFormatter::TYPE_UNITS operands and
- IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT and
- IntlListFormatter::WIDTH_NARROW widths.
- It is supported from icu 67.
-
-- PDO_Sqlite:
- . Added class constant Pdo_Sqlite::ATTR_BUSY_STATEMENT.
- . Added class constants Pdo_Sqlite::ATTR_EXPLAIN_STATEMENT,
- Pdo_Sqlite::EXPLAIN_MODE_PREPARED, Pdo_Sqlite::EXPLAIN_MODE_EXPLAIN,
- Pdo_Sqlite::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN.
- . Added PDO\Sqlite::ATTR_TRANSACTION_MODE connection attribute with
- possible values PDO\Sqlite::TRANSACTION_MODE_DEFERRED,
- PDO\Sqlite::TRANSACTION_MODE_IMMEDIATE,
- and PDO\Sqlite::TRANSACTION_MODE_EXCLUSIVE, allowing to configure
- the transaction mode to use when calling beginTransaction().
+ . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(),
+ with the alias of locale_get_display_keyword() and
+ locale_get_display_keyword_value() respectively.
+ RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue
+ . Added IntlNumberRangeFormatter class to format an interval of two numbers
+ with a given skeleton, locale, IntlNumberRangeFormatter::COLLAPSE_AUTO,
+ IntlNumberRangeFormatter::COLLAPSE_NONE,
+ IntlNumberRangeFormatter::COLLAPSE_UNIT,
+ IntlNumberRangeFormatter::COLLAPSE_ALL collapse and
+ IntlNumberRangeFormatter::IDENTITY_FALLBACK_SINGLE_VALUE,
+ IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE,
+ IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY and
+ IntlNumberRangeFormatter::IDENTITY_FALLBACK_RANGE identity fallbacks.
+ It is supported from icu 63.
+
+- IO:
+ . Added new polling API.
+ RFC: https://wiki.php.net/rfc/poll_api
+
+- JSON:
+ . Added extra info about error location to the JSON error messages returned
+ from json_last_error_msg() and JsonException message.
-- Session:
- . session_set_cookie_params(), session_get_cookie_params(), and session_start()
- now support partitioned cookies via the "partitioned" key.
- RFC: https://wiki.php.net/rfc/CHIPS
-
-- SOAP:
- . Enumeration cases are now dumped in __getTypes().
- . Implemented request #61105:
- support for Soap 1.2 Reason Text xml:lang attribute.
- The signature of SoapFault::__construct() and SoapServer::fault() therefore
- now have an optional $lang parameter.
- This support solves compatibility with .NET SOAP clients.
-
-- Sqlite:
- . Added class constants Sqlite3Stmt::EXPLAIN_MODE_PREPARED,
- Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN and
- Sqlite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN.
-
-- Standard:
- . mail() now returns the actual sendmail error and detects if the sendmail
- process was terminated unexpectedly. In such cases, a warning is emitted
- and the function returns false. Previously, these errors were silently
- ignored. This change affects only the sendmail transport.
- . getimagesize() now supports HEIF/HEIC images.
- . getimagesize() now supports SVG images when ext-libxml is also loaded.
- Similarly, image_type_to_extension() and image_type_to_mime_type()
- now also handle IMAGETYPE_SVG.
- . The array returned by getimagesize() now has two additional entries:
- "width_unit" and "height_unit" to indicate in which units the dimensions
- are expressed. These units are px by default. They are not necessarily
- the same (just to give one example: one may be cm and the other may be px).
- . setcookie() and setrawcookie() now support the "partitioned" key.
- RFC: https://wiki.php.net/rfc/CHIPS
-
-- XSL:
- . The $namespace argument of XSLTProcessor::getParameter(),
- XSLTProcessor::setParameter() and XSLTProcessor::removeParameter()
- now actually works instead of being treated as empty.
- This only works if the $name argument does not use Clark notation
- and is not a QName because in those cases the namespace is taken
- from the namespace href or prefix respectively.
+- OpenSSL:
+ . Added TLS session resumption support for streams with new stream context
+ options: session_data, session_new_cb, session_cache, session_cache_size,
+ session_timeout, session_id_context, session_get_cb, session_remove_cb,
+ and num_tickets. This allows saving and restoring client sessions across
+ 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
+ influences the result of the phar buildFrom family of functions.
+ 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.
+ . Added stream socket context options so_keepalive, tcp_keepidle,
+ tcp_keepintvl and tcp_keepcnt that allow setting socket keepalive
+ options.
+ . Allowed casting casting filtered streams as file descriptor for select.
+ . Added the "write_seek_mode stream" filter parameter for the bz2, iconv,
+ zlib, and string stream filters. This parameter must be set via an
+ associative array where the key is "write_seek_mode stream" and the
+ value is one of the following strings "preserve", "reset", or "strict".
-- Zlib:
- . flock() is now supported on zlib streams. Previously, this always
- failed to perform any locking action.
+- 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
+ . Added Uri\Rfc3986\UriBuilder.
+ RFC: https://wiki.php.net/rfc/uri_followup#uri_building
========================================
3. Changes in SAPI modules
========================================
- CLI:
- . Trying to set a process title that is too long with cli_set_process_title()
- will now fail instead of silently truncating the given title.
- . Added a new --ini=diff option to print INI settings changed from the builtin
- default.
-
-- FPM:
- . FPM with httpd ProxyPass optionally decodes the full script path. Added
- fastcgi.script_path_encoded INI setting to prevent this new behavior.
- . FPM access log limit now respects log_limit value.
+ . The built-in development server now accepts requests using the QUERY HTTP
+ method instead of returning 501 Not Implemented.
========================================
4. Deprecated Functionality
========================================
- Core:
- . Trying to produce output (e.g. with `echo`) within a user output handler
- is deprecated. The deprecation warning will bypass the handler producing the
- output to ensure it is visible; if there are nested output handlers the next
- one will still be used.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4
- . Non-canonical cast names (boolean), (integer), (double), and (binary) have
- been deprecated, use (bool), (int), (float), and (string) respectively.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_non-standard_cast_names
- . The $exclude_disabled parameter of the get_defined_functions() function has
- been deprecated, as it no longer has any effect since PHP 8.0.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_exclude_disabled_parameter_of_get_defined_functions
- . Terminating case statements with a semicolon instead of a colon has
- been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_semicolon_after_case_in_switch_statement
- . The backtick operator as an alias for shell_exec() has been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_backticks_as_an_alias_for_shell_exec
- . Returning null from __debugInfo() has been deprecated.
- Return an empty array instead.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_debuginfo_returning_null
- . The report_memleaks INI directive has been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_report_memleaks_ini_directive
- . Constant redeclaration has been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_constant_redeclaration
- . Enacted the follow-up phase of the "Path to Saner Increment/Decrement
- operators" RFC, meaning that incrementing non-numeric strings is now
- deprecated. Instead the str_increment() function should be used.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#enact_follow-up_phase_of_the_path_to_saner_incrementdecrement_operators_rfc
- . The following closure binding issues, which already emit an E_WARNING, are
- now deprecated:
- - Binding an instance to a static closure
- - Binding methods to objects that are not instances of the class
- (or subclass) that the method is defined
- - Unbinding $this from a method
- - Unbinding $this from a closure that uses `$this`
- - Binding a closure to the scope of an internal class
- - Rebinding the scope of a closure created from a function or method
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_closure_binding_issues
- . Using null as an array offset or when calling array_key_exists() is now
- deprecated. Instead an empty string should be used.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_using_values_null_as_an_array_offset_and_when_calling_array_key_exists
- . Deriving $_SERVER['argc'] and $_SERVER['argv'] from the query string for non-CLI
- SAPIs has been deprecated. Configure register_argc_argv=0 and switch to either
- $_GET or $_SERVER['QUERY_STRING'] to access the information, after verifying
- that the usage is safe.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_register_argc_argv_ini_directive
- . The __sleep() and __wakeup() magic methods have been soft-deprecated. The
- __serialize() and __unserialize() magic methods should be used instead,
- or at the same time if compatibility with PHP 7 is required.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_sleep_and_wakeup_magic_methods
- RFC: https://wiki.php.net/rfc/soft-deprecate-sleep-wakeup
-
-- Curl:
- . The curl_close() function has been deprecated, as CurlHandle objects are
- freed automatically.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_curl_close
- . The curl_share_close() function has been deprecated, as CurlShareHandle
- objects are freed automatically.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_curl_share_close
-
-- Date:
- . The DATE_RFC7231 and DateTimeInterface::RFC7231 constants have been
- deprecated. This is because the associated timezone is ignored and always
- uses GMT.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_date_rfc7231_and_datetimeinterfacerfc7231
- . The __wakeup() magic method of DateTimeInterface, DateTime,
- DateTimeImmutable, DateTimeZone, DateInterval, and DatePeriod has been
- deprecated in favour of the __unserialize() magic method.
- Related to RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_sleep_and_wakeup_magic_methods
-
-- FileInfo:
- . The finfo_close() function has been deprecated.
- As finfo objects are freed automatically.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_finfo_close
- . The $context parameter of the finfo_buffer() function has been deprecated
- as it is ignored.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_context_parameter_for_finfo_buffer
-
-- GD:
- . The imagedestroy() function has been deprecated, as GdImage objects are
- freed automatically.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_imagedestroy
-
-- Hash:
- . The MHASH_* constants have been deprecated. These have been overlooked
- when the mhash*() function family has been deprecated per
- https://wiki.php.net/rfc/deprecations_php_8_1#mhash_function_family
-
-- Intl:
- . The intl.error_level INI setting has been deprecated.
- Errors should either be checked manually or exceptions should be enabled
- by using the intl.use_exceptions INI setting.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_intlerror_level_ini_setting
-
-- LDAP:
- - Specific Oracle Instant Client calls and constants have been deprecated.
- List of affected calls:
- ldap_connect() with wallet support
- ldap_connect_wallet()
- List of affected constants:
- GSLC_SSL_NO_UATH
- GSLC_SSL_ONEWAY_UATH
- GSLC_SSL_TWOWAY_UATH
- https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_building_ext_ldap_against_oracle_ldap
-
-- MySQLi:
- . The mysqli_execute() alias function has been deprecated.
- Use mysqli_stmt_execute() instead.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#formally_deprecate_mysqli_execute
-
-- OpenSSL:
- . The $key_length parameter for openssl_pkey_derive() has been deprecated.
- This is because it is either ignored, or truncates the key, which can be
- a vulnerability.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_key_length_parameter_of_openssl_pkey_derive
-
-- PDO:
- . The "uri:" DSN scheme has been deprecated due to security concerns with
- DSNs coming from remote URIs.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_pdo_s_urischeme
- . Driver specific constants in the PDO class have been deprecated.
- List of affected constants and their replacement:
- PDO::DBLIB_ATTR_CONNECTION_TIMEOUT => Pdo\Dblib::ATTR_CONNECTION_TIMEOUT
- PDO::DBLIB_ATTR_QUERY_TIMEOUT => Pdo\Dblib::ATTR_QUERY_TIMEOUT
- PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER => Pdo\Dblib::ATTR_STRINGIFY_UNIQUEIDENTIFIER
- PDO::DBLIB_ATTR_VERSION => Pdo\Dblib::ATTR_VERSION
- PDO::DBLIB_ATTR_TDS_VERSION => Pdo\Dblib::ATTR_TDS_VERSION
- PDO::DBLIB_ATTR_SKIP_EMPTY_ROWSETS => Pdo\Dblib::ATTR_SKIP_EMPTY_ROWSETS
- PDO::DBLIB_ATTR_DATETIME_CONVERT => Pdo\Dblib::ATTR_DATETIME_CONVERT
- PDO::FB_ATTR_DATE_FORMAT => Pdo\Firebird::ATTR_DATE_FORMAT
- PDO::FB_ATTR_TIME_FORMAT => Pdo\Firebird::ATTR_TIME_FORMAT
- PDO::FB_ATTR_TIMESTAMP_FORMAT => Pdo\Firebird::ATTR_TIMESTAMP_FORMAT
- PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => Pdo\Mysql::ATTR_USE_BUFFERED_QUERY
- PDO::MYSQL_ATTR_LOCAL_INFILE => Pdo\Mysql::ATTR_LOCAL_INFILE
- PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY => Pdo\Mysql::ATTR_LOCAL_INFILE_DIRECTORY
- PDO::MYSQL_ATTR_INIT_COMMAND => Pdo\Mysql::ATTR_INIT_COMMAND
- PDO::MYSQL_ATTR_MAX_BUFFER_SIZE => Pdo\Mysql::ATTR_MAX_BUFFER_SIZE
- PDO::MYSQL_ATTR_READ_DEFAULT_FILE => Pdo\Mysql::ATTR_READ_DEFAULT_FILE
- PDO::MYSQL_ATTR_READ_DEFAULT_GROUP => Pdo\Mysql::ATTR_READ_DEFAULT_GROUP
- PDO::MYSQL_ATTR_COMPRESS => Pdo\Mysql::ATTR_COMPRESS
- PDO::MYSQL_ATTR_DIRECT_QUERY => Pdo\Mysql::ATTR_DIRECT_QUERY
- PDO::MYSQL_ATTR_FOUND_ROWS => Pdo\Mysql::ATTR_FOUND_ROWS
- PDO::MYSQL_ATTR_IGNORE_SPACE => Pdo\Mysql::ATTR_IGNORE_SPACE
- PDO::MYSQL_ATTR_SSL_KEY => Pdo\Mysql::ATTR_SSL_KEY
- PDO::MYSQL_ATTR_SSL_CERT => Pdo\Mysql::ATTR_SSL_CERT
- PDO::MYSQL_ATTR_SSL_CA => Pdo\Mysql::ATTR_SSL_CA
- PDO::MYSQL_ATTR_SSL_CAPATH => Pdo\Mysql::ATTR_SSL_CAPATH
- PDO::MYSQL_ATTR_SSL_CIPHER => Pdo\Mysql::ATTR_SSL_CIPHER
- PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => Pdo\Mysql::ATTR_SSL_VERIFY_SERVER_CERT
- PDO::MYSQL_ATTR_SERVER_PUBLIC_KEY => Pdo\Mysql::ATTR_SERVER_PUBLIC_KEY
- PDO::MYSQL_ATTR_MULTI_STATEMENTS => Pdo\Mysql::ATTR_MULTI_STATEMENTS
- PDO::ODBC_ATTR_USE_CURSOR_LIBRARY => Pdo\Odbc::ATTR_USE_CURSOR_LIBRARY
- PDO::ODBC_ATTR_ASSUME_UTF8 => Pdo\Odbc::ATTR_ASSUME_UTF8
- PDO::ODBC_SQL_USE_IF_NEEDED => Pdo\Odbc::SQL_USE_IF_NEEDED
- PDO::ODBC_SQL_USE_DRIVER => Pdo\Odbc::SQL_USE_DRIVER
- PDO::ODBC_SQL_USE_ODBC => Pdo\Odbc::SQL_USE_ODBC
- PDO::PGSQL_ATTR_DISABLE_PREPARES => Pdo\Pgsql::ATTR_DISABLE_PREPARES
- PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => Pdo\Sqlite::ATTR_EXTENDED_RESULT_CODES
- PDO::SQLITE_ATTR_OPEN_FLAGS => Pdo\Sqlite::OPEN_FLAGS
- PDO::SQLITE_ATTR_READONLY_STATEMENT => Pdo\Sqlite::ATTR_READONLY_STATEMENT
- PDO::SQLITE_DETERMINISTIC => Pdo\Sqlite::DETERMINISTIC
- PDO::SQLITE_OPEN_READONLY => Pdo\Sqlite::OPEN_READONLY
- PDO::SQLITE_OPEN_READWRITE => Pdo\Sqlite::OPEN_READWRITE
- PDO::SQLITE_OPEN_CREATE => Pdo\Sqlite::OPEN_CREATE
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods
- . Driver specific methods in the PDO class have been deprecated.
- List of affected methods and their replacement:
- PDO::pgsqlCopyFromArray() => Pdo\Pgsql::copyFromArray()
- PDO::pgsqlCopyFromFile() => Pdo\Pgsql::copyFromFile()
- PDO::pgsqlCopyToArray() => Pdo\Pgsql::copyToArray()
- PDO::pgsqlCopyToFile() => Pdo\Pgsql::copyToFile()
- PDO::pgsqlGetNotify() => Pdo\Pgsql::getNotify()
- PDO::pgsqlGetPid() => Pdo\Pgsql::getPid()
- PDO::pgsqlLOBCreate() => Pdo\Pgsql::lobCreate()
- PDO::pgsqlLOBOpen() => Pdo\Pgsql::lobOpen()
- PDO::pgsqlLOBUnlink() => Pdo\Pgsql::lobUnlink()
- PDO::sqliteCreateAggregate() => Pdo\Sqlite::createAggregate()
- PDO::sqliteCreateCollation() => Pdo\Sqlite::createCollation()
- PDO::sqliteCreateFunction() => Pdo\Sqlite::createFunction()
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods
-
-- PDO_PGSQL: Constants related to transaction states have been deprecated:
- PDO::PGSQL_TRANSACTION_IDLE, PDO::PGSQL_TRANSACTION_ACTIVE, PDO::PGSQL_TRANSACTION_INTRANS,
- PDO::PGSQL_TRANSACTION_INERROR, PDO::PGSQL_TRANSACTION_UNKNOWN.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#extpdo_deprecations
-
-- Reflection:
- . The setAccessible() methods of various Reflection objects have been
- deprecated, as those no longer have an effect.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionsetaccessible
- . Calling ReflectionClass::getConstant() for constants that do not exist has
- been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionclassgetconstant_for_missing_constants
- . Calling ReflectionProperty::getDefaultValue() for properties without default
- values has been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionpropertygetdefaultvalue_for_properties_without_default_values
-
-- SPL:
- . Unregistering all autoloaders by passing the spl_autoload_call() function
- as a callback argument to spl_autoload_unregister() has been deprecated.
- Instead if this is needed, one should iterate over the return value of
- spl_autoload_functions() and call spl_autoload_unregister() on each value.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_passing_spl_autoload_call_to_spl_autoload_unregister
- . The SplObjectStorage::contains(), SplObjectStorage::attach(), and
- SplObjectStorage::detach() methods have been deprecated in favour of
- SplObjectStorage::offsetExists(), SplObjectStorage::offsetSet(), and
- SplObjectStorage::offsetUnset() respectively.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_splobjectstoragecontains_splobjectstorageattach_and_splobjectstoragedetach
- . Using ArrayObject and ArrayIterator with objects has been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_arrayobject_and_arrayiterator_with_objects
-
-- Standard:
- . The socket_set_timeout() alias function has been deprecated.
- Use stream_set_timeout() instead.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#formally_deprecate_socket_set_timeout
- . Passing null to to readdir(), rewinddir(), and closedir() to use the last
- opened directory has been deprecated. Provide the last opened directory
- explicitly instead.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_passing_null_to_readdir_rewinddir_and_closedir
- . Passing integers outside the interval [0, 255] to chr() is now deprecated.
- This is because a byte can only hold a value within this interval.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_passing_integers_outside_the_interval_0_255_to_chr
- . Passing a string which is not a single byte to ord() is now deprecated,
- this is indicative of a bug.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_passing_string_which_are_not_one_byte_long_to_ord
- . The locally predefined variable $http_response_header is deprecated.
- Instead one should call the http_get_last_response_headers() function.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_http_response_header_predefined_variable
-
-- XML:
- . The xml_parser_free() function has been deprecated, as XMLParser objects
- are freed automatically.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_xml_parser_free
+ . Specifying a return type of array|null / ?array for __debugInfo() is now
+ deprecated. Specify array instead.
+ . Returning values from __construct() and __destruct() is now deprecated.
+ RFC: https://wiki.php.net/rfc/deprecate-return-value-from-construct
+ . Making __construct() and __destruct() a Generator is now deprecated.
+ RFC: https://wiki.php.net/rfc/deprecate-return-value-from-construct
+
+- GMP
+ . The shift (<<, >>) and exponentiation (**) operators on GMP objects now
+ emit a deprecation warning when converting a float right operand to int
+ loses precision.
+
+- Mbstring:
+ . Mbregex has been deprecated, because the underlying Oniguruma library
+ is no longer maintained.
+ RFC: https://wiki.php.net/rfc/eol-oniguruma
========================================
5. Changed Functions
========================================
-- BCMath:
- . bcround() and BcMath\Number::round() now throw a ValueError when the precision
- argument is out of range.
-
-- Intl:
- . IntlDateFormatter::setTimeZone()/datefmt_set_timezone()
- throws an IntlException on uninitialised classes/clone failures.
- . grapheme_extract() properly assigns $next value when skipping over
- invalid starting bytes. Previously there were cases where it would
- point to the start of the grapheme boundary instead of the end.
- . Locale:: methods throw a ValueError when locale inputs contain null
- bytes.
- . transliterator_get_error_code(), transliterator_get_error_message()
- TransLiterator::getErrorCode(), and TransLiterator::getErrorMessage()
- have dropped the false from the return type union. Returning false
- was actually never possible.
- . grapheme_strpos(), grapheme_stripos(), grapheme_strrpos(),
- grapheme_strripos(), grapheme_substr(), grapheme_strstr()
- and grapheme_stristr() functions add $locale parameter.
- RFC: https://wiki.php.net/rfc/grapheme_add_locale_for_case_insensitive
-
-- LDAP:
- . ldap_get_option() now accepts a NULL connection, like ldap_set_option(),
- to allow retrieval of global options.
-
-- libxml:
- . libxml_set_external_entity_loader() now has a formal return type of true.
+- GMP:
+ . gmp_fact() now throws a ValueError() if $num does not fit into a unsigned
+ long.
+ . gmp_pow(), gmp_binomial(), gmp_root() and gmp_rootrem() now throw a
+ ValueError if their second argument does not fit into an unsigned long.
+ . The shift (<<, >>) and exponentiation (**) operators on GMP objects now
+ throw a ValueError if the right operand does not fit into an unsigned long.
+ . gmp_powm() modulo-by-zero now raises a DivisionByZeroError whose message
+ includes the function name and argument index ($modulus).
+
+- mysqli:
+ . The return structure of mysqli_get_charset() no longer contains the
+ undocumented "comment" element. The value of "charsetnr" is now set to a
+ constant 0 as this number was an implementation detail that should not have
+ been exposed to the public.
- OpenSSL:
- . openssl_public_encrypt() and openssl_private_decrypt() have a new parameter
- $digest_algo that allows specifying the hash digest algorithm for OAEP padding.
- . openssl_sign() and openssl_verify() have a new parameter $padding to allow
- using more secure RSA PSS padding.
- . openssl_cms_encrypt() $cipher_algo parameter can be a string with the
- cipher name. That allows to use more algorithms including AES GCM cipher
- algorithms for auth enveloped data.
+ . Output of openssl_x509_parse() contains criticalExtensions listing all
+ critical certificate extensions.
+ . openssl_sign() and openssl_verify() now have an additional optional
+ argument $salt_length that allows controlling the RSA-PSS salt length
+ when OPENSSL_PKCS1_PSS_PADDING is used. It accepts an explicit length or
+ one of the new OPENSSL_RSA_PSS_SALTLEN_* constants.
-- PCNTL:
- . pcntl_exec() now has a formal return type of false.
- . pcntl_waitid() takes an additional resource_usage argument to
- gather various platform specific metrics about the child process.
-
-- PDO_PGSQL:
- . PDO::pgsqlCopyFromArray now supports Iterable inputs.
- . Pdo\Pgsql::setAttribute and Pdo\Pgsql::prepare support setting
- PDO::ATTR_PREFETCH to 0 which enters lazy fetch mode.
- In this mode, statements cannot be run in parallel.
-
-- PDO_SQLITE:
- . SQLite PDO::quote() will now throw an exception or emit a warning,
- depending on the error mode, if the string contains a null byte.
- . PDO::sqliteCreateCollation will now throw an exception
- if the callback has the wrong return type, making it more
- in line with Pdo_Sqlite::createCollation behavior.
+- PDO_DBLIB:
+ . When using persistent connections, there is now a liveness check in the
+ constructor.
-- PGSQL:
- . pg_copy_from now supports Iterable inputs.
- . pg_connect checks if the connection_string argument contains
- any null byte.
- . pg_close_stmt checks if the statement_name argument contains
- any null byte.
-
-- POSIX:
- . posix_ttyname sets last_error to EBADF when encountering
- an invalid file descriptor.
- . posix_isatty raises an E_WARNING message when encountering
- an invalid file descriptor.
- . posix_fpathconf checks invalid file descriptors and sets
- last_error to EBADF and raises an E_WARNING message.
- . posix_kill throws a ValueError when the process_id argument is lower
- or greater than what the platform supports (signed integer or long
- range), posix_setpgid throws a ValueError when the process_id or
- the process_group_id is lower than zero or greater than
- what the platform supports.
- . posix_setrlimit throws a ValueError when the hard_limit or soft_limit
- arguments are lower than -1 or if soft_limit is greater than hard_limit.
-
-- Reflection:
- . The output of ReflectionClass::__toString() for enums has changed to
- better indicate that the class is an enum, and that the enum cases
- are enum cases rather than normal class constants.
- . The output of ReflectionProperty::__toString() for properties with
- hooks has changed to indicate what hooks the property has, whether those
- hooks are final, and whether the property is virtual. This also affects
- the output of ReflectionClass::__toString() when a class contains hooked
- properties.
- . ReflectionAttribute::newInstance() can now throw errors for internal
- attributes if the attribute was applied on an invalid target and the
- error was delayed from compile time to runtime via the
- #[\DelayedTargetValidation] attribute.
- RFC: https://wiki.php.net/rfc/delayedtargetvalidation_attribute
-
-- Session:
- . session_start is stricter in regard to the options argument.
- It throws a ValueError if the array is not a hashmap, or
- a TypeError if the read_and_close value is not a valid type
- compatible with int.
-
-- SNMP:
- . snmpget, snmpset, snmp_get2, snmp_set2, snmp_get3, snmp_set3
- and SNMP::__construct() throw a ValueError when the hostname
- is too large, contains any null byte or if the port is given
- when negative or greater than 65535, timeout and retries values
- are lower than -1 or too large.
+- Phar:
+ . Phar::mungServer() now supports reference values.
- Sockets:
- . socket_create_listen, socket_bind and socket_sendto throw a
- ValueError if the port is lower than 0 or greater than 65535,
- and also if any of the hints array entries are indexed numerically.
- . socket_addrinfo_lookup throws a TypeError if any of the hints
- values cannot be cast to int and can throw a ValueError if
- any of these values overflow.
- . socket_set_option with MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP
- options will throw an exception if the value isn't a valid object
- or array.
- . socket_create/socket_bind can create AF_PACKET family sockets.
- . socket_getsockname gets the interface index and its string
- representation with AF_PACKET socket.
- . socket_set_option with multicast context throws a ValueError
- when the created socket is not of AF_INET/AF_INET6 family.
-
-- Tidy:
- . tidy::__construct/parseFile/parseString now throws a ValueError if the
- configuration contains an invalid value or attempts to set a read-only
- internal entry, and a TypeError if a configuration key is not a string.
+ . socket_addrinfo_lookup() now has an additional optional argument $error
+ when not null, and on failure, gives the error code (one of the EAI_*
+ constants).
-- Zlib:
- . The "use_include_path" argument for the
- gzfile, gzopen and readgzfile functions has been changed
- from int to boolean.
- . gzfile, gzopen and readgzfile functions now respect the default
- stream context.
+- 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
========================================
-- Core:
- . get_error_handler() allows retrieving the current user-defined error handler
- function.
- RFC: https://wiki.php.net/rfc/get-error-exception-handler
- . get_exception_handler() allows retrieving the current user-defined exception
- handler function.
- RFC: https://wiki.php.net/rfc/get-error-exception-handler
- . The clone language construct is now a function and supports reassigning
- (readonly) properties during cloning via the new $withProperties parameter.
- RFC: https://wiki.php.net/rfc/clone_with_v2
- . Added Closure::getCurrent() to receive currently executing closure.
-
-- Curl:
- . curl_multi_get_handles() allows retrieving all CurlHandles currently
- attached to a CurlMultiHandle. This includes both handles added using
- curl_multi_add_handle() and handles accepted by CURLMOPT_PUSHFUNCTION.
- . curl_share_init_persistent() allows creating a share handle that is
- persisted across multiple PHP requests.
- RFC: https://wiki.php.net/rfc/curl_share_persistence_improvement
-
-- DOM:
- . Added Dom\Element::getElementsByClassName().
- . Added Dom\Element::insertAdjacentHTML().
-
-- Enchant:
- . Added enchant_dict_remove_from_session() to remove a word added to the
- spellcheck session via enchant_dict_add_to_session().
- . Added enchant_dict_remove() to put a word on the exclusion list and
- remove it from the session dictionary.
-
- Intl:
- . Added locale_is_right_to_left/Locale::isRightToLeft, returns true if
- the locale is written right to left (after its enrichment with likely subtags).
- . Added grapheme_levenshtein() function.
- RFC: https://wiki.php.net/rfc/grapheme_levenshtein
-
-- Opcache:
- . Added opcache_is_script_cached_in_file_cache().
+ . grapheme_strrev()
+ RFC: https://wiki.php.net/rfc/grapheme_strrev
+ . Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue()
+ RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue
-- Pdo\Sqlite:
- . Added support for Pdo\Sqlite::setAuthorizer(), which is the equivalent of
- SQLite3::setAuthorizer(). The only interface difference is that the
- pdo version returns void.
-
-- PGSQL:
- . pg_close_stmt offers an alternative way to close a prepared
- statement from the DEALLOCATE sql command in that we can reuse
- its name afterwards.
- . pg_service returns the ongoing service name of the connection.
+- mysqli:
+ . Added mysqli::quote_string() and mysqli_quote_string().
+ RFC: https://wiki.php.net/rfc/mysqli_quote_string
- Reflection:
- . ReflectionConstant::getFileName() was introduced.
- . ReflectionConstant::getExtension() and
- ReflectionConstant::getExtensionName() were introduced.
- . ReflectionConstant::getAttributes() was introduced.
- RFC: https://wiki.php.net/rfc/attributes-on-constants
- . ReflectionProperty::getMangledName() was introduced.
-
-- Sqlite:
- . Sqlite3Stmt::busy to check if a statement had been fetched
- but not completely.
+ . ReflectionConstant::inNamespace()
+ . ReflectionProperty::isReadable() and ReflectionProperty::isWritable()
+ RFC: https://wiki.php.net/rfc/isreadable-iswriteable
+ . ReflectionParameter::getDocComment()
+ RFC: https://wiki.php.net/rfc/parameter-doccomments
- Standard:
- . Added array_first() and array_last().
- RFC: https://wiki.php.net/rfc/array_first_last
+ . 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
+ . stream_socket_get_crypto_status()
+
+- URI:
+ . Uri\Rfc3986\Uri::getUriType() and Uri\WhatWg\Url::isSpecialScheme()
+ RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection
+ . Uri\Rfc3986\Uri::getHostType() and Uri\WhatWg\Url::getHostType()
+ RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection
+
+- Zip:
+ . ZipArchive::openString()
+ . ZipArchive::closeString()
========================================
7. New Classes and Interfaces
========================================
-- Core:
- . NoDiscard attribute was added.
- RFC: https://wiki.php.net/rfc/marking_return_value_as_important
- . DelayedTargetValidation attribute was added.
- RFC: https://wiki.php.net/rfc/delayedtargetvalidation_attribute
+- Intl:
+ . IntlNumberRangeFormatter
-- Curl:
- . CurlSharePersistentHandle representing a share handle that is persisted
- across multiple PHP requests.
- RFC: https://wiki.php.net/rfc/curl_share_persistence_improvement
+- OpenSSL:
+ . Openssl\OpensslException
+ . Openssl\Session
+ RFC: https://wiki.php.net/rfc/tls_session_resumption
+ . Openssl\Psk
-- Filter:
- . Filter\FilterException and Filter\FilterFailedException for use
- when FILTER_THROW_ON_FAILURE has been enabled.
- RFC: https://wiki.php.net/rfc/filter_throw_on_failure
+- 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
+ . Io\Poll\Context
+ . Io\Poll\Watcher
+ . enum Io\Poll\Backend
+ . enum Io\Poll\Event
+ . interface Io\Poll\Handle
+ . Io\IoException
+ . Io\Poll\PollException
+ . Io\Poll\FailedPollOperationException
+ . Io\Poll\FailedContextInitializationException
+ . Io\Poll\FailedHandleAddException
+ . Io\Poll\FailedWatcherModificationException
+ . Io\Poll\FailedPollWaitException
+ . Io\Poll\BackendUnavailableException
+ . Io\Poll\InactiveWatcherException
+ . Io\Poll\HandleAlreadyWatchedException
+ . Io\Poll\InvalidHandleException
+ . StreamPollHandle
- URI:
- . Uri\UriException, Uri\InvalidUriException, Uri\UriComparisonMode,
- Uri\Rfc3986\Uri, Uri\WhatWg\InvalidUrlException,
- Uri\WhatWg\UrlValidationErrorType, Uri\WhatWg\UrlValidationError,
- and Uri\WhatWg\Url are added.
- RFC: https://wiki.php.net/rfc/url_parsing_api
+ . Uri\Rfc3986\UriBuilder
+ RFC: https://wiki.php.net/rfc/uri_followup#uri_building
========================================
8. Removed Extensions and SAPIs
@@ -835,252 +497,149 @@ PHP 8.5 UPGRADE NOTES
9. Other Changes to Extensions
========================================
-- Curl:
- . curl_easy_setopt with CURLOPT_FOLLOWLOCATION option's value no longer
- is treated as boolean but integer to handle CURLFOLLOW_OBEYCODE and
- CURLFOLLOW_FIRSTONLY.
-
-- Fileinfo:
- . Upgraded file from 5.45 to 5.46.
- . The return type of finfo_close() has been changed to true, rather
- than bool.
-
-- Intl:
- . Intl's internal error mechanism has been modernized so that it
- indicates more accurately which call site caused what error.
- Moreover, some ext/date exceptions have been wrapped inside a
- IntlException now.
- . IntlListFormatter::getErrorCode() and getErrorMessage() now reflect
- IntlListFormatter::format() failures.
-
-- Lexbor:
- . An always enabled lexbor extension is added. It contains the lexbor
- library that was separated from ext/dom for being reused among other
- extensions. The new extension is not directly exposed to userland.
-
-- Opcache:
- . The Opcache extension is now always built into the PHP binary and is always
- loaded. The INI directives opcache.enable and opcache.enable_cli are still
- honored.
-
-- URI:
- . An always enabled uri extension is added that can be used for handling
- URIs and URLs according to RFC 3986 and WHATWG URL.
- RFC: https://wiki.php.net/rfc/url_parsing_api
+- Hash:
+ . The bundled version of xxHash was upgraded to 0.8.2.
-- PDO_Sqlite:
- . Increased minimum release version support from 3.7.7 to 3.7.17.
+- mysqli
+ . Added new constant MYSQLI_OPT_COMPRESS.
-- Readline:
- . The return types of readline_add_history(), readline_clear_history(), and
- readline_callback_handler_install() have been changed to true, rather
- than bool.
-
-- Reflection:
- . ReflectionConstant is no longer final.
+- Opcache
+ . JIT is now supported for ZTS builds on Apple Silicon.
========================================
10. New Global Constants
========================================
-- Core:
- . PHP_BUILD_DATE.
- . PHP_BUILD_PROVIDER.
-
- Curl:
- . CURLINFO_USED_PROXY.
- . CURLINFO_HTTPAUTH_USED.
- . CURLINFO_PROXYAUTH_USED.
- . CURLINFO_CONN_ID.
- . CURLINFO_QUEUE_TIME_T.
- . CURLOPT_INFILESIZE_LARGE.
- . CURLFOLLOW_ALL.
- . CURLFOLLOW_OBEYCODE.
- . CURLFOLLOW_FIRSTONLY.
-
-- Filter:
- . FILTER_THROW_ON_FAILURE.
-
-- Intl:
- . DECIMAL_COMPACT_SHORT.
- . DECIMAL_COMPACT_LONG.
+ . CURLINFO_SIZE_DELIVERED (libcurl >= 8.20.0).
+ . CURLOPT_SEEKFUNCTION.
+ . CURL_SEEKFUNC_OK.
+ . CURL_SEEKFUNC_FAIL.
+ . CURL_SEEKFUNC_CANTSEEK.
- OpenSSL:
- . OPENSSL_PKCS1_PSS_PADDING.
- . PKCS7_NOSMIMECAP.
- . PKCS7_CRLFEOL.
- . PKCS7_NOCRL.
- . PKCS7_NO_DUAL_CONTENT.
-
-- POSIX:
- . POSIX_SC_OPEN_MAX.
+ . OPENSSL_RSA_PSS_SALTLEN_DIGEST.
+ . OPENSSL_RSA_PSS_SALTLEN_AUTO.
+ . OPENSSL_RSA_PSS_SALTLEN_MAX.
- Sockets:
- . IPPROTO_ICMP/IPPROTO_ICMPV6.
- . TCP_FUNCTION_BLK (FreeBSD only).
- . TCP_FUNCTION_ALIAS (FreeBSD only).
- . TCP_REUSPORT_LB_NUMA (FreeBSD only).
- . TCP_REUSPORT_LB_NUMA_NODOM (FreeBSD only).
- . TCP_REUSPORT_LB_NUMA_CURDOM (FreeBSD only).
- . TCP_BBR_ALGORITHM (FreeBSD only).
- . AF_PACKET (Linux only).
- . ETH_P_IP (Linux only).
- . ETH_P_IPV6 (Linux only).
- . ETH_P_LOOP (Linux only).
- . ETH_P_ALL (Linux only).
- . IP_BINDANY (FreeBSD/NetBSD/OpenBSD only).
- . SO_BUSY_POLL (Linux only).
- . UDP_SEGMENT (Linux only).
- . SHUT_RD.
- . SHUT_WR.
- . SHUT_RDWR.
-
-- Tokenizer:
- . T_VOID_CAST.
- . T_PIPE.
-
-- Standard:
- . IMAGETYPE_HEIF.
- . IMAGETYPE_SVG when libxml is loaded.
+ . TCP_USER_TIMEOUT (Linux only).
+ . AF_UNSPEC.
+ . EAI_BADFLAGS.
+ . EAI_NONAME.
+ . EAI_AGAIN.
+ . EAI_FAIL.
+ . EAI_NODATA.
+ . EAI_FAMILY.
+ . EAI_SOCKTYPE.
+ . EAI_SERVICE.
+ . EAI_ADDRFAMILY.
+ . EAI_SYSTEM.
+ . EAI_OVERFLOW
+ . EAI_INPROGRESS.
+ . EAI_CANCELED.
+ . EAI_NOTCANCELED.
+ . EAI_ALLDONE.
+ . EAI_INTR.
+ . EAI_IDN_ENCODE.
+
+- Standard
+ . ARRAY_FILTER_USE_VALUE.
+ . STREAM_CRYPTO_STATUS_NONE
+ . STREAM_CRYPTO_STATUS_WANT_READ
+ . STREAM_CRYPTO_STATUS_WANT_WRITE
========================================
11. Changes to INI File Handling
========================================
-- Core:
- . Added fatal_error_backtraces to control whether fatal errors should include
- a backtrace.
- RFC: https://wiki.php.net/rfc/error_backtraces_v2
- . Added startup-only max_memory_limit INI setting to control the maximum
- memory_limit that may be configured at startup or runtime. Exceeding this
- value emits a warning, unless set to -1, and sets memory_limit to the
- current max_memory_limit instead.
- ML discussion: https://externals.io/message/127108
+- Mbstring:
+ . The mbstring.detect_order INI directive now updates the internal detection
+ order when changed at runtime via ini_set(). Previously, runtime changes
+ using ini_set() did not take effect for mb_detect_order(). Setting the
+ directive to NULL or an empty string at runtime now leaves the previously
+ configured detection order unchanged.
-- Opcache:
- . Added opcache.file_cache_read_only to support a read-only
- opcache.file_cache directory, for use with read-only file systems
- (e.g. read-only Docker containers).
- Best used with opcache.validate_timestamps=0,
- opcache.enable_file_override=1,
- and opcache.file_cache_consistency_checks=0.
- Note: A cache generated with a different build of PHP, a different file
- path, or different settings (including which extensions are loaded), may be
- ignored.
- . The default value of opcache.jit_hot_loop is now 61 (a prime) to prevent it
- from being a multiple of loop iteration counts.
- It is recommended that this parameter is set to a prime number.
- . Changing opcache.memory_consumption when OPcache SHM is already set up
- will now correctly report a failure instead of silently doing nothing and
- showing misleading values in PHPInfo.
+- Mysqli:
+ . mysqli.default_port now checks the validity of the value which should be
+ between 0 and 65535 included.
-- OpenSSL:
- . Added openssl.libctx to select the OpenSSL library context type. Either
- custom libctx for each thread can be used or a single global (default)
- libctx is used.
+- Opcache:
+ . opcache.jit_debug accepts a new flag: ZEND_JIT_DEBUG_TRACE_EXIT_INFO_SRC.
+ When used along with ZEND_JIT_DEBUG_TRACE_EXIT_INFO, the source of exit
+ points is printed in exit info output, in debug builds.
========================================
12. Windows Support
========================================
-* The configuration variables PHP_VERSION, PHP_MINOR_VERSION, and
- PHP_RELEASE_VERSION are now always numbers. Previously, they have been
- strings for buildconf builds.
-
-* phpize builds now reflect the source tree in the build dir (as it already
- worked for in-tree builds); some extension builds (especially when using
- Makefile.frag.w32) may need adjustments.
-
-* --enable-sanitizer is now supported for MSVC builds. This enables ASan and
- debug assertions, and is supported as of MSVC 16.10 and Windows 10.
-
-* The --with-uncritical-warn-choke configuration option for clang builds is
- no longer supported. Select warnings to suppress via CFLAGS instead.
-
-* COM:
- . The extension is now build shared by default; previously it defaulted to a
- static extension, although the official Windows binaries built a shared
- extension.
-
-* FFI:
- . It is no longer necessary to specify the library when using FFI::cdef()
- and FFI::load(). However, this convenience feature should not be used in
- production.
-
-* Streams:
- . If only pipe streams are contained in the $read array, and the $write and
- $except arrays are empty, stream_select() now behaves similar to POSIX
- systems, i.e. the function only returns if at least one pipe is ready to be
- read, or after the timeout expires. Previously, stream_select() returned
- immediately, reporting all streams as ready to read.
-
========================================
13. Other Changes
========================================
- Core:
- . The high resolution timer (`hrtime()`) on macOS now uses the recommended
- `clock_gettime_nsec_np(CLOCK_UPTIME_RAW)` API instead of
- `mach_absolute_time()`.
-
-- CLI/CGI:
- . The `-z` or `--zend-extension` option has been removed as it was
- non-functional. Use `-d zend_extension=` instead.
-
-- PDO_ODBC
- . The fetch behaviour for larger columns has been changed. Rather than
- fetching 256 byte blocks, PDO_ODBC will try to fetch a larger block size;
- currently, this is the page size minus string overhead. Drivers that
- return SQL_NO_TOTAL in SQLGetData are also better handled as well.
- This should improve compatibility and performance. See GH-10809, GH-10733.
+ . In case of a hard OOM PHP now calls abort() instead of exit(1), changing
+ the exit code to 134 and possibly creating a core dump.
+ . The PHP_OS_FAMILY constant has an AIX value for when running on AIX or
+ IBM i via PASE.
========================================
14. Performance Improvements
========================================
- Core:
- . Remove OPcodes for identity comparisons against booleans, particularly
- for the match(true) pattern.
- . Add OPcode specialization for `=== []` and `!== []` comparisons.
- . Creating exception objects is now much faster.
- . The parts of the code that used SSE2 have been adapted to use SIMD
- with ARM NEON as well.
- . Introduced the TAILCALL VM, enabled by default when compiling with Clang>=19
- on x86_64 or aarch64. The TAILCALL VM is as fast as the HYBRID VM used when
- compiling with GCC. This makes PHP binaries built with Clang>=19 as fast as
- binaries built with GCC. The performance of the CALL VM, used with other
- compilers, has also improved considerably.
-
-- Intl:
- . Now avoids creating extra string copies when converting strings
- for use in the collator.
+ . printf() using only "%s" and "%d" will be compiled into the equivalent
+ string interpolation, avoiding the overhead of a function call and
+ repeatedly parsing the format string.
+ . Arguments are now passed more efficiently to known constructors (e.g. when
+ using new self()).
+ . array_map() using a first-class callable or partial function application
+ callback will be compiled into the equivalent foreach-loop, avoiding the
+ creation of intermediate Closures, the overhead of calling userland
+ callbacks from internal functions and providing for better insight for the
+ JIT.
+ . The performance of the TAILCALL VM has been improved.
+ . The TAILCALL VM is now enabled on Windows when compiling with Clang >= 19
+ x86_64.
+ . The performance of ZTS builds has been improved.
-- MBString:
- . The parts of the code that used SSE2 have been adapted to use SIMD
- with ARM NEON as well.
+- DOM:
+ . Made splitText() faster and consume less memory.
-- Opcache:
- . Improved performance of fetching TLS variables in JIT'ed code in non-Glibc
- builds.
+- JSON:
+ . Improve performance of encoding arrays and objects.
+ . Improved performance of indentation generation in json_encode()
+ when using PHP_JSON_PRETTY_PRINT.
-- ReflectionProperty:
- . Improved performance of the following methods: getValue(), getRawValue(),
- isInitialized(), setValue(), setRawValue().
+- Intl:
+ . Improved performance of IntlCalendar::getAvailableLocales() and
+ IntlDateFormatter::localtime() / datefmt_localtime() by pre-allocating
+ their returned arrays.
+ . Improved performance of transliterator_list_ids() and
+ resourcebundle_locales() by pre-allocating their returned arrays.
-- SPL:
- . Improved performance of dimension accessors and methods of SplFixedArray.
+- Phar:
+ . Reduced temporary allocations when iterating Phar directories.
- Standard:
- . Improved performance of array functions with callbacks
- (array_find, array_filter, array_map, usort, ...).
- . Improved performance of urlencode() and rawurlencode().
- . Improved unpack() performance with nameless repetitions by avoiding
- creating temporary strings and reparsing them.
- . Improved pack() performance.
- . Minor improvements in array_chunk() performance.
-
-- XMLReader:
- . Improved property access performance.
-
-- XMLWriter:
- . Improved performance and reduced memory consumption.
+ . Improved performance of array_fill_keys().
+ . Improved performance of array_map() with multiple arrays passed.
+ . Improved performance of array_sum() and array_product() for
+ integer-only arrays.
+ . Improved performance of array_unshift().
+ . Improved performance of array_walk().
+ . Improved performance of intval('+0b...', 2) and intval('0b...', 2).
+ . Improved performance of str_split().
+
+- URI:
+ . Improved performance of Uri\WhatWg\Url::parse() when collecting
+ validation errors by pre-allocating the error array.
+ . 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:
+ . Improved performance of ZipArchive::addGlob() and
+ ZipArchive::addPattern() by pre-allocating their returned arrays.
+ . Avoid string copies in ZipArchive::addFromString().
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 40b20b73d6b1..6e8444f26899 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -1,4 +1,4 @@
-PHP 8.5 INTERNALS UPGRADE NOTES
+PHP 8.6 INTERNALS UPGRADE NOTES
1. Internal API changes
@@ -14,167 +14,258 @@ PHP 8.5 INTERNALS UPGRADE NOTES
1. Internal API changes
========================
- . PG(arg_separator).input and PG(arg_separator).output are now `zend_string*`
- instead of `char*`.
- . DL_LOAD now doesn't use RTLD_DEEPBIND deepbind anymore on platforms
- where dlmopen with LM_ID_NEWLM is available:
- this means shared library symbol isolation (if needed) must be enabled on
- the user side when requiring libphp.so, by using dlmopen with LM_ID_NEWLM
- instead of dlopen.
- RTLD_DEEPBIND is still enabled when the Apache SAPI is in use.
- . The ptr field of the php_stream_notifier struct is now a void* instead
- of a zval. If the zval was used to store IS_PTR values only, the
- extra layer of indirection can be removed. In other cases a zval can
- be heap-allocated and stored in the pointer as a minimal change to keep
- compatibility.
- . The validator callbacks for internal attribute now return `zend_string *`
- rather than `void`; instead of emitting an error when an attribute is
- applied incorrectly, the error message should be returned as a zend_string
- pointer. If the error will be delayed until runtime, it is stored in the
- new `validation_error` field of the `zend_attribute` struct.
- RFC: https://wiki.php.net/rfc/delayedtargetvalidation_attribute
- . Added zend_safe_assign_to_variable_noref() function to safely assign
- a value to a non-reference zval.
- . Added zval_ptr_safe_dtor() to safely destroy a zval when a destructor
- could interfere.
- . zend_get_callable_name() now returns the name of the underlying function
- for fake closures.
- . Added smart_string_append_printf() matching smart_str_append_printf() for
- char* instead of zend_string*-based smart strings.
- . Added php_build_provider() to retrieve the value of PHP_BUILD_PROVIDER at
- runtime.
- . Removed the cache_slot argument of zend_check_user_type_slow() because
- now it only relies on the CE cache.
- . Added ZEND_NONSTRING attribute macro for character arrays that do not
- represent strings. This allows to silence the GCC 15.x
- `-Wunterminated-string-initialization` warning.
- . Added the zend_update_exception_properties() function for instantiating
- Exception child classes. It updates the $message, $code, and $previous
- properties.
- . zend_exception_get_default() was removed, use zend_ce_exception directly.
- . zend_get_error_exception() was removed, use zend_ce_error_exception
+- Removed:
+ . The misnamed ZVAL_IS_NULL() has been removed. Use Z_ISNULL() instead.
+ . The zval_is_true() alias of zend_is_true() has been removed. Call
+ zend_is_true() directly instead.
+ . The _zval_get_*() compatibility macros for PHP 7.2 have been removed
+ call the variant without the leading underscore instead.
+ Affected: _zval_get_long, _zval_get_double, _zval_get_string,
+ _zval_get_long_func, _zval_get_double_func, _zval_get_string_func
+ . CHECK_ZVAL_NULL_PATH() and CHECK_NULL_PATH() have been removed, use
+ zend_str_has_nul_byte(Z_STR_P(...)) and zend_char_has_nul_byte()
+ respectively.
+ . ZEND_LTOA() (and ZEND_LTOA_BUF_LEN) has been removed, as it was
+ unsafe. Directly use ZEND_LONG_FMT with a function from the
+ printf family.
+ . The zval_dtor() alias of zval_ptr_dtor_nogc() has been removed.
+ Call zval_ptr_dtor_nogc() directly instead.
+ . The internal zend_copy_parameters_array() function is no longer exposed.
+ . The internal zend_hash_minmax() function is no longer exposed. Scan the
+ HashTable directly and use zend_compare() for value comparisons instead.
+ . The zend_make_callable() function has been removed, if a callable zval
+ needs to be obtained use the zend_get_callable_zval_from_fcc() function
+ instead. If this was used to store a callable, then an FCC should be
+ stored instead.
+ . The zend_exception_save() and zend_exception_restore() functions were
+ removed.
+ . The zend_set_hash_symbol() API has been removed.
+ . The WRONG_PARAM_COUNT and ZEND_WRONG_PARAM_COUNT() macros have been
+ removed. Call zend_wrong_param_count(); followed by RETURN_THROWS();
+ instead.
+ . PHP_HAVE_STREAMS macro removed from .
+ . The INI_STR(), INI_INT(), INI_FLT(), and INI_BOOL() macros have been
+ removed. Instead new zend_ini_{bool|long|double|str|string}_literal()
+ macros have been added. This fixes an internal naming inconsistency as
+ "str" usually means zend_string*, and "string" means char*.
+ However INI_STR() returned a char*
+ . The INI_ORIG_{INT|STR|FLT|BOOL}() macros have been removed as they are
+ unused. If this behaviour is required fall back to the zend_ini_*
+ functions.
+ . The unused ZEND_AST_PARENT_PROPERTY_HOOK_CALL has been removed.
+ . The EMPTY_SWITCH_DEFAULT_CASE() macro has been removed. Use
+ default: ZEND_UNREACHABLE(); instead.
+ . The ZEND_RESULT_CODE type has been removed. Use zend_result directly.
+ . The zend_parse_parameters_none_throw(), zend_parse_parameters_throw(),
+ and ZEND_PARSE_PARAMS_THROW have been removed due to being misleading,
+ since ZPP always throws, unless ZEND_PARSE_PARAMS_QUIET is given. Use
+ the non-throw versions.
+ . The XtOffsetOf() alias of C’s offsetof() macro has been removed. Use
+ offsetof() directly.
+ . The deprecated Z_COPYABLE(), Z_COPYABLE_P(), Z_OPT_COPYABLE(), and
+ Z_OPT_COPYABLE_P() macros have been removed. Check for IS_ARRAY directly.
+ . The deprecated Z_IMMUTABLE(), Z_IMMUTABLE_P(), Z_OPT_IMMUTABLE(), and
+ Z_OPT_IMMUTABLE_P() macros have been removed. Check for
+ IS_ARRAY && !REFCOUNTED directly.
+ . The unused Z_GC_*() macros have been removed. Use the corresponding
+ GC_*() macro on the result of Z_COUNTED().
+ . The zend_binary_zval_strcmp() and zend_binary_zval_strncmp() functions
+ have been removed, because they are unsafe by relying on the zvals
+ having a specific type. Use zend_binary_strcmp() / zend_binary_strncmp(),
+ string_compare_function() or similar instead.
+ . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead
+ use php_check_open_basedir() directly.
+ . The Z_CONSTANT(), Z_CONSTANT_P(), Z_OPT_CONSTANT(), and
+ Z_OPT_CONSTANT_P() macros have been removed. Check for IS_CONSTANT_AST
directly.
- . ZEND_IS_XDIGIT() macro was removed because it was unused and its name
- did not match its actual behavior.
- . The following zend_string-related legacy aliases were removed:
- * IS_INTERNED() - use ZSTR_IS_INTERNED()
- * STR_EMPTY_ALLOC() - use ZSTR_EMPTY_ALLOC()
- * _STR_HEADER_SIZE - use _ZSTR_HEADER_SIZE
- * STR_ALLOCA_ALLOC() - use ZSTR_ALLOCA_ALLOC()
- * STR_ALLOCA_INIT() - use ZSTR_ALLOCA_INIT()
- * STR_ALLOCA_FREE() - use ZSTR_ALLOCA_FREE()
- . zend_register_constant() now returns a pointer to the added constant
- on success and NULL on failure instead of SUCCESS/FAILURE.
- The specialized registration methods that previously had void returns
- also return pointers to the added constants:
- * zend_register_bool_constant()
- * zend_register_null_constant()
- * zend_register_long_constant()
- * zend_register_double_constant()
- * zend_register_string_constant()
- * zend_register_stringl_constant()
- . EG(fake_scope) now is a _const_ zend_class_entry*.
- . zend_begin_record_errors() or EG(record_errors)=true cause errors to be
- delayed. Before, errors would be recorded but not delayed.
- . zend_mm_refresh_key_child() must be called on any zend_mm_heap inherited
- from the parent process after a fork().
- . HASH_KEY_IS_* constants have been moved in the zend_hash_key_type enum.
+ . The {_}php_stream_fopen_with_path() functions have been removed as they are
+ unused.
+ . The php_error_docref1() and php_error_docref2() functions have been
+ removed, instead rely on the error_include_args INI option to show the
+ arguments to functions in a consistent manner.
+
+- Changed:
+ . Internal functions that return by reference are now expected to
+ automatically unwrap references when the result of the call is stored in an
+ IS_TMP_VAR variable. This may be achieved by calling the
+ zend_return_unwrap_ref() function.
+ . ZEND_AST_METHOD_REFERENCE has been renamed to
+ ZEND_AST_TRAIT_METHOD_REFERENCE.
+ . Functions using zend_forbid_dynamic_call() *must* be flagged with
+ ZEND_ACC2_FORBID_DYN_CALLS (@forbid-dynamic-calls in stubs). In debug
+ builds, failing to include that flag will lead to assertion failures.
+ . The zend_get_call_trampoline_func() API now takes the __call or
+ __callStatic zend_function* instead of a CE and a boolean argument.
+ . ZSTR_INIT_LITERAL(), zend_string_starts_with_literal(), and
+ zend_string_starts_with_literal_ci() now support strings containing NUL
+ bytes. Passing non-literal char* is no longer supported.
+ . The zend_active_function{_ex}() functions now return a const zend_function
+ pointer.
+ . zend_function.arg_info is now always a zend_arg_info*. Before, it was a
+ zend_internal_arg_info on internal functions, unless the
+ ZEND_ACC_USER_ARG_INFO flag was set.
+ . ZEND_INI_GET_ADDR() is now a void* pointer instead of a char* pointer. This
+ more correctly represents the generic nature of the returned pointer and
+ allows to remove explicit casts, but possibly breaks pointer arithmetic
+ performed on the result.
+ . The zend_dval_to_lval_cap() function no longer takes a second
+ zend_string* parameter.
+ . EG(in_autoload) was renamed to EG(autoload_current_classnames) and no
+ longer is a pointer, but a directly embedded HashTable struct.
+ . Extended php_stream_filter_ops with seek method.
+ . zend_argument_error_variadic() now takes a new 'function' parameter.
+
+- Added:
+ . New zend_class_entry.ce_flags2 and zend_function.fn_flags2 fields were
+ added, given the primary flags were running out of bits.
+ . Added zend_hash_str_lookup().
+ . Added zend_ast_call_get_args() to fetch the argument node from any call
+ node.
+ . Added Z_PARAM_ENUM().
+ . Added zend_enum_fetch_case_id().
+ . Added zend_enum_get_case_by_id().
+ . Added zend_bin2hex() and zend_bin2hex_str() as helper functions to remove
+ dependencies on /ext/hash in various extensions.
+ . Added a C23_ENUM() helper macro to define forward-compatible fixed-size
+ enums.
+ . Added zend_fcall_info.consumed_args together with
+ zend_fci_consumed_arg(), which allows moving a selected callback argument
+ instead of copying it in zend_call_function(). Currently only a single
+ consumed argument is supported.
+ . Added ZEND_CONTAINER_OF().
+ . Added zend_reflection_property_set_raw_value_without_lazy_initialization(),
+ zend_reflection_property_set_raw_value() to expose the functionality of
+ ReflectionProperty::setRawValueWithoutLazyInitialization() and
+ ReflectionProperty::setRawValue() to C extensions.
+ . Added zend_argument_error_ex(), zend_argument_type_error_ex(),
+ zend_argument_value_error_ex().
+ . Added zend_ast_dup().
+ . Added zend_compile_ast().
+ . Added zend_check_type_ex().
+ . Added zend_create_partial_closure().
+ . Added a new IO copy API in . php_io_copy() copies bytes between
+ file descriptors using the most efficient platform primitive available
+ (sendfile, splice, copy_file_range, TransmitFile), and is now used by
+ php_stream_copy_to_stream_ex(). The mmap-based copy fallback was removed.
+ . Added zend_string_equals_cstr_ci().
+ . Added zend_cstr_append_char(), zend_cstr_concat(), and
+ zend_cstr_concat3() as helper functions to allocate NUL-terminated raw C
+ strings from one or more buffers.
+ . Added zend_string_ends_with() and related variants.
+ . Added trait support for internal classes.
========================
2. Build system changes
========================
-- Abstract
- . Preprocessor macro SIZEOF_PTRDIFF_T has been removed.
- . Preprocessor macro SIZEOF_INTMAX_T has been removed.
-
-- Windows build system changes
- . SAPI() and ADD_SOURCES() now support the optional `duplicate_sources`
- parameter. If truthy, no rules to build the object files are generated.
- This allows to build additional variants of SAPIs (e.g. a DLL and EXE)
- without duplicate build rules. It is up to the SAPI maintainers to ensure
- that appropriate build rules are created.
-
-- Unix build system changes
- . libdir is properly set when --libdir (ex: /usr/lib64) and --with-libdir (ex: lib64)
- configure options are used to ${libdir}/php (ex: /usr/lib64/php)
- . PHP_ODBC_CFLAGS, PHP_ODBC_LFLAGS, PHP_ODBC_LIBS, PHP_ODBC_TYPE preprocessor
- macros defined by ext/odbc are now defined in php_config.h instead of the
- build-defs.h header.
- . Autoconf macro AX_CHECK_COMPILE_FLAG updated to serial 11.
- . Autoconf macro PHP_AP_EXTRACT_VERSION has been removed.
- . Autoconf macro PHP_BUILD_THREAD_SAFE has been removed (set enable_zts
- manually).
- . Autoconf macro PHP_CHECK_SIZEOF is obsolete (use AC_CHECK_SIZEOF).
- . Autoconf macro PHP_DEF_HAVE has been removed (use AC_DEFINE).
- . Autoconf macro PHP_OUTPUT has been removed (use AC_CONFIG_FILES).
- . Autoconf macro PHP_TEST_BUILD has been removed (use AC_* macros).
- . Preprocessor macro HAVE_PTRDIFF_T has been removed.
- . Preprocessor macro HAVE_INTMAX_T has been removed.
- . Preprocessor macro HAVE_SSIZE_T has been removed.
- . Preprocessor macro SIZEOF_SSIZE_T has been removed.
+- Abstract:
+ . Minimum required PHP version found on the host system for running scripts
+ like build/gen_stub.php during development has been updated from 7.4 to 8.1.
+ . build/gen_stub.php may now generate a _decl.h file in addition to
+ the _arginfo.h file, if the stub declares enums and is annotated with
+ @generate-c-enums. For each enum the file will contain a C enum. Enum
+ values can be compared to the result of
+ zend_enum_fetch_case_id(zend_object*).
+
+- Unix build system changes:
+ . scripts/dev/update-autoconf.sh has been added to update config.*/libtool.
+ . libtool has been upgraded to 2.5.4 (serial 63), which fixes many bugs.
+ . As part of the upgrade to the new libtool:
+ . libtool is now spread across multiple files. phpize has been updated to
+ handle this.
+ . On macOS, libtool now uses -undefined dynamic_lookup for shared objects,
+ instead of -undefined suppress -flat_namespace.
+ . --with-pic is now --enable-pic. The old flag will result in an error.
+ . Symbol HAVE_ST_BLOCKS has been removed from php_config.h (use
+ HAVE_STRUCT_STAT_ST_BLOCKS).
+ . Added a new configure option --disable-apache2-conf to prevent apxs from
+ editing httpd.conf during installation.
+
+- Windows build system changes:
+ . Function SETUP_OPENSSL() doesn't accept 6th argument anymore and doesn't
+ define the HAVE_OPENSSL_SSL_H preprocessor macro anymore.
+ . Function SETUP_SQLITE3() doesn't define HAVE_SQLITE3_H and HAVE_SQLITE3EXT_H
+ preprocessor macros anymore.
+ . Added a new function CHECK_HEADER() which is intended to be used instead of
+ the CHECK_HEADER_ADD_INCLUDE().
========================
3. Module changes
========================
-- Gd:
- . The gdImageScale*() and gdImageRotate*() helpers are now internal in the
- bundled libgd, like they have been in external libgd as of gd-2.1.1.
-
-- Hash:
- . Hash functions now use proper hash_spec_result enum for return values
- instead of using SUCCESS and FAILURE.
-
-- JSON:
- . php_json_encode_serializable_object() now assumes `EG(active)`,
- if not a bailout is caused. Therefore a minor BC break exists if the
- `PHP_JSON_PARTIAL_OUTPUT_ON_ERROR` option is in use.
- However, this situation is highly unlikely.
-
-- Libxml:
- . The refcount APIs now return an `unsigned int` instead of an `int`.
- . Removed php_libxml_xmlCheckUTF8(). Use xmlCheckUTF8() from libxml instead.
-
-- PDO:
- . Added `php_pdo_stmt_valid_db_obj_handle()` to check if the database object
- is still valid. This is useful when a GC cycle is collected and the
- database object can be destroyed prior to destroying the statement.
-
-- Random:
- . The handlers parameter of php_random_engine_common_init() has been
- removed. Use the default_object_handlers field on the CE instead.
-
-- Standard:
- . Added php_url_decode_ex() and php_raw_url_decode_ex() that unlike their
- non-ex counterparts do not work in-place.
- . The php_std_date() function has been removed. Use php_format_date() with
- the "D, d M Y H:i:s \\G\\M\\T" format instead.
- . Added php_url_encode_to_smart_str() to encode a URL to a smart_str buffer.
- . The functionality of getimagesize(), image_type_to_mime_type(),
- and image_type_to_extension() is now extensible using the internal APIs
- php_image_register_handler() and php_image_unregister_handler() in
- php_image.h.
- . ext/standard/php_smart_string.h and ext/standard/php_smart_string_public.h
- were removed. Use the corresponding headers in Zend/ instead.
-
-- URI:
- . New extension with an internal API for URI handling is available via the
- php_uri_* symbols exposed in the ext/uri/ headers.
+- ext/date:
+ . php_idate() now returns the result state, and moves the return value into an
+ out parameter.
+
+- ext/intl:
+ . Added intl_icu_compat.h with helpers and feature macros for ICU
+ version-specific API differences. Code in ext/intl should use the
+ intl_icu_compat_* helpers and INTL_ICU_HAS_* macros instead of adding
+ direct U_ICU_VERSION_* guards for supported ICU API variants.
+ . The internal grapheme_get_break_iterator() helper no longer accepts a
+ stack buffer argument; pass only the UErrorCode* status argument.
+
+- ext/mbstring:
+ . Added GB18030-2022 to default encoding list for zh-CN.
+
+- ext/mysqlnd:
+ . Dropped session_options parameter from all methods in mysqlnd_auth.
+ The same information is present in conn->options and should be used
+ instead.
+ . Removed charsets plugin.
+
+- ext/session:
+ . php_session_flush() now returns a bool rather than a zend_result.
+ . The mod_user_names global has been removed.
+ . The mod_user_uses_object_methods_as_handlers global has been added,
+ it indicates whether the session handlers are methods of an object or not.
+ . Removed session_adapt_url().
+ . PS_OPEN_ARGS is now defined as
+ `void **mod_data, zend_string *save_path, zend_string *session_name`
+ rather than
+ `void **mod_data, const char *save_path, const char *session_name`
+ . PS_FUNCS() now includes the PS_VALIDATE_SID_FUNC()
+ . PS_MOD() now requires that the PS_CREATE_SID_FUNC() and
+ PS_VALIDATE_SID_FUNC() functions are defined.
+ . PS_FUNCS_SID() and PS_MOD_SID() have been removed.
+ Either use PS_FUNCS()/PS_MOD() or PS_FUNCS_UPDATE_TIMESTAMP()/
+ PS_MOD_UPDATE_TIMESTAMP() if timestamp support exists.
+
+- ext/standard:
+ . _php_error_log() now has a formal return type of zend_result.
+ . _php_error_log() now accepts zend_string* values instead of char*.
+ . _php_error_log_ex() has been removed.
+ . php_mail()'s extra_cmd parameter is now a zend_string*.
+ . The php_math_round_mode_from_enum() function now takes a
+ zend_enum_RoundingMode parameter.
+
+- ext/uri:
+ . The value parameter of the php_uri_property_handler_write callback is now
+ const zval * instead of zval *, reflecting that write handlers must
+ not modify the input zval.
+
+- ext/xml:
+ . Removed the XML_ExpatVersion() libxml compatibility wrapper,
+ as it was unused.
+ . Removed the XML_GetCurrentByteCount() libxml compatibility wrapper,
+ as it was unused and could return the wrong result.
========================
4. OpCode changes
========================
-* New ZEND_DECLARE_ATTRIBUTED_CONST is used when a global constant is declared
- with `const` and has attributes; this opcode is used *instead* of the
- ZEND_DECLARE_CONST, and in addition to the name of the constant and the
- value to use, has a ZEND_OP_DATA with a pointer to the compiled attributes.
+- Added ZEND_TYPE_ASSERT to check a value's type against the parameter
+ type of a function, throwing a TypeError on failure as if the function
+ was called. Used in optimizations that elide function calls.
========================
5. SAPI changes
========================
-- SAPIs must now call php_child_init() after a fork. If php-src code was
- executed in other threads than the one initiating the fork,
- refresh_memory_manager() must be called in every such thread.
+- SAPIs should explicitly release a thread's resources by calling
+ ts_free_thread() before terminating it. tsrm_shutdown() can only release the
+ resources of the calling thread, for resources allocated with
+ ts_allocate_tls_id().
+
+- AG and SCNG are now allocated with ts_allocate_tls_id() and live in native
+ __thread storage on ZTS builds.
diff --git a/Zend/LICENSE b/Zend/LICENSE
deleted file mode 100644
index 51f5cccde950..000000000000
--- a/Zend/LICENSE
+++ /dev/null
@@ -1,56 +0,0 @@
---------------------------------------------------------------------
- The Zend Engine License, Version 2.00
-Copyright (c) 1999-2006 Zend Technologies Ltd. All rights reserved.
---------------------------------------------------------------------
-
-Redistribution and use in source and binary forms, with or without
-modification, is permitted provided that the following conditions
-are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- 3. The names "Zend" and "Zend Engine" must not be used to endorse
- or promote products derived from this software without prior
- permission from Zend Technologies Ltd. For written permission,
- please contact license@zend.com.
-
- 4. Zend Technologies Ltd. may publish revised and/or new versions
- of the license from time to time. Each version will be given a
- distinguishing version number.
- Once covered code has been published under a particular version
- of the license, you may always continue to use it under the
- terms of that version. You may also choose to use such covered
- code under the terms of any subsequent version of the license
- published by Zend Technologies Ltd. No one other than Zend
- Technologies Ltd. has the right to modify the terms applicable
- to covered code created under this License.
-
- 5. Redistributions of any form whatsoever must retain the following
- acknowledgment:
- "This product includes the Zend Engine, freely available at
- http://www.zend.com"
-
- 6. All advertising materials mentioning features or use of this
- software must display the following acknowledgment:
- "The Zend Engine is freely available at http://www.zend.com"
-
-THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES LTD. ``AS IS'' AND
-ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZEND
-TECHNOLOGIES LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
---------------------------------------------------------------------
diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c
index ec98d814ed45..3775f8165c3e 100644
--- a/Zend/Optimizer/block_pass.c
+++ b/Zend/Optimizer/block_pass.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -30,9 +28,9 @@
#include "zend_dump.h"
/* Checks if a constant (like "true") may be replaced by its value */
-bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
+bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, bool copy)
{
- zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
+ const zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
if (c) {
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
@@ -42,9 +40,9 @@ bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
if (copy) {
Z_TRY_ADDREF_P(result);
}
- return 1;
+ return true;
} else {
- return 0;
+ return false;
}
}
@@ -52,9 +50,9 @@ bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
if (c) {
ZVAL_COPY_VALUE(result, &c->value);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/* Data dependencies macros */
@@ -62,9 +60,9 @@ bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
-static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
+static void strip_leading_nops(const zend_op_array *op_array, zend_basic_block *b)
{
- zend_op *opcodes = op_array->opcodes;
+ const zend_op *opcodes = op_array->opcodes;
do {
b->start++;
@@ -72,7 +70,7 @@ static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
} while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP);
}
-static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
+static void strip_nops(const zend_op_array *op_array, zend_basic_block *b)
{
uint32_t i, j;
@@ -106,7 +104,7 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
}
}
-static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) {
+static uint32_t get_const_switch_target(const zend_cfg *cfg, const zend_op_array *op_array, const zend_basic_block *block, zend_op *opline, const zval *val) {
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG)
@@ -176,7 +174,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
&& zend_optimizer_update_op1_const(op_array, opline, &c)) {
VAR_SOURCE(op1) = NULL;
if (opline->opcode != ZEND_JMP_NULL
- && !zend_bitset_in(used_ext, VAR_NUM(src->result.var))) {
+ && !zend_bitset_in(used_ext, VAR_NUM(src->result.var))
+ /* FETCH_W with ZEND_FETCH_GLOBAL_LOCK does not free op1, which will be used again. */
+ && !(opline->opcode == ZEND_FETCH_W && (opline->extended_value & ZEND_FETCH_GLOBAL_LOCK))) {
literal_dtor(&ZEND_OP1_LITERAL(src));
MAKE_NOP(src);
}
@@ -289,20 +289,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
MAKE_NOP(opline);
++(*opt_count);
break;
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
+ case ZEND_QM_ASSIGN:
if (src < op_array->opcodes + block->start) {
break;
}
@@ -310,8 +297,26 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline);
++(*opt_count);
+ if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
+ src->opcode = ZEND_FREE;
+ } else if (src->op1_type == IS_CONST) {
+ MAKE_NOP(src);
+ } else if (src->op1_type == IS_CV) {
+ src->opcode = ZEND_CHECK_VAR;
+ SET_UNUSED(src->result);
+ }
break;
default:
+ if (!zend_op_may_elide_result(src->opcode)) {
+ break;
+ }
+ if (src < op_array->opcodes + block->start) {
+ break;
+ }
+ src->result_type = IS_UNUSED;
+ VAR_SOURCE(opline->op1) = NULL;
+ MAKE_NOP(opline);
+ ++(*opt_count);
break;
}
}
@@ -339,59 +344,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
break;
-#if 0
- /* pre-evaluate functions:
- constant(x)
- function_exists(x)
- extension_loaded(x)
- BAD: interacts badly with Accelerator
- */
- if((opline->op1_type & IS_VAR) &&
- VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
- VAR_SOURCE(opline->op1)->extended_value == 1) {
- zend_op *fcall = VAR_SOURCE(opline->op1);
- zend_op *sv = fcall-1;
- if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
- sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
- Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
- ) {
- zval *arg = &OPLINE_OP1_LITERAL(sv);
- char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
- size_t flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
- if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
- (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
- ) {
- zend_function *function;
- if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
- literal_dtor(arg);
- MAKE_NOP(sv);
- MAKE_NOP(fcall);
- LITERAL_BOOL(opline->op1, 1);
- opline->op1_type = IS_CONST;
- }
- } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
- zval c;
- if (zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC)) {
- literal_dtor(arg);
- MAKE_NOP(sv);
- MAKE_NOP(fcall);
- ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
- /* no copy ctor - get already copied it */
- opline->op1_type = IS_CONST;
- }
- } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
- if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
- literal_dtor(arg);
- MAKE_NOP(sv);
- MAKE_NOP(fcall);
- LITERAL_BOOL(opline->op1, 1);
- opline->op1_type = IS_CONST;
- }
- }
- }
- }
-#endif
-
case ZEND_FETCH_LIST_R:
case ZEND_FETCH_LIST_W:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
@@ -409,7 +361,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
break;
}
if (opline->op1_type == IS_CONST) {
- int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
+ uint32_t target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
literal_dtor(&ZEND_OP1_LITERAL(opline));
literal_dtor(&ZEND_OP2_LITERAL(opline));
opline->opcode = ZEND_JMP;
@@ -696,7 +648,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
} else if (opline->op1_type == IS_TMP_VAR &&
!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
src = VAR_SOURCE(opline->op1);
- if (src) {
+ if (src && (src->op1_type != IS_VAR)) {
if (src->opcode == ZEND_BOOL_NOT) {
VAR_SOURCE(opline->op1) = NULL;
COPY_NODE(opline->op1, src->op1);
@@ -740,7 +692,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
(!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
opline->result.var == opline->op1.var)) {
src = VAR_SOURCE(opline->op1);
- if (src) {
+ if (src && (src->op1_type != IS_VAR)) {
if (src->opcode == ZEND_BOOL ||
src->opcode == ZEND_QM_ASSIGN) {
VAR_SOURCE(opline->op1) = NULL;
@@ -822,7 +774,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
src->extended_value == IS_STRING &&
src->op1_type != IS_CONST) {
/* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
- zend_op *src = VAR_SOURCE(opline->op2);
VAR_SOURCE(opline->op2) = NULL;
COPY_NODE(opline->op2, src->op1);
MAKE_NOP(src);
@@ -949,14 +900,14 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
src = VAR_SOURCE(opline->op1);
if (src && src->opcode == ZEND_QM_ASSIGN) {
zend_op *op = src + 1;
- bool optimize = 1;
+ bool optimize = true;
while (op < opline) {
if ((op->op1_type == opline->op1_type
&& op->op1.var == opline->op1.var)
|| (op->op2_type == opline->op1_type
&& op->op2.var == opline->op1.var)) {
- optimize = 0;
+ optimize = false;
break;
}
op++;
@@ -986,6 +937,8 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
src = VAR_SOURCE(opline->op1);
if (src &&
src->opcode != ZEND_COPY_TMP &&
+ /* See gh20628_borked_live_range_calc.phpt. */
+ src->opcode != ZEND_NEW &&
src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
src->opcode != ZEND_ADD_ARRAY_UNPACK &&
(src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
@@ -1009,10 +962,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
/* Rebuild plain (optimized) op_array from CFG */
-static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx)
+static void assemble_code_blocks(const zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_basic_block *blocks = cfg->blocks;
- zend_basic_block *end = blocks + cfg->blocks_count;
+ const zend_basic_block *end = blocks + cfg->blocks_count;
zend_basic_block *b;
zend_op *new_opcodes;
zend_op *opline;
@@ -1025,7 +978,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
opline = op_array->opcodes + b->start + b->len - 1;
if (opline->opcode == ZEND_JMP) {
- zend_basic_block *next = b + 1;
+ const zend_basic_block *next = b + 1;
while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
next++;
@@ -1042,9 +995,9 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
len += b->len;
} else {
/* this block will not be used, delete all constants there */
- zend_op *op = op_array->opcodes + b->start;
- zend_op *end = op + b->len;
- for (; op < end; op++) {
+ const zend_op *op = op_array->opcodes + b->start;
+ const zend_op *last_op = op + b->len;
+ for (; op < last_op; op++) {
if (op->op1_type == IS_CONST) {
literal_dtor(&ZEND_OP1_LITERAL(op));
}
@@ -1109,7 +1062,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ const HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
uint32_t s = 0;
ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable));
@@ -1125,7 +1078,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
/* adjust exception jump targets & remove unused try_catch_array entries */
if (op_array->last_try_catch) {
- int i, j;
+ uint32_t i, j;
uint32_t *map;
ALLOCA_FLAG(use_heap);
@@ -1160,15 +1113,15 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
}
if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
- zend_op *opline = new_opcodes;
- zend_op *end = opline + len;
- while (opline < end) {
- if (opline->opcode == ZEND_FAST_RET &&
- opline->op2.num != (uint32_t)-1 &&
- opline->op2.num < (uint32_t)j) {
- opline->op2.num = map[opline->op2.num];
+ zend_op *finally_opline = new_opcodes;
+ const zend_op *last_finally_op = finally_opline + len;
+ while (finally_opline < last_finally_op) {
+ if (finally_opline->opcode == ZEND_FAST_RET &&
+ finally_opline->op2.num != (uint32_t)-1 &&
+ finally_opline->op2.num < j) {
+ finally_opline->op2.num = map[finally_opline->op2.num];
}
- opline++;
+ finally_opline++;
}
}
}
@@ -1177,14 +1130,14 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
/* rebuild map (just for printing) */
memset(cfg->map, -1, sizeof(int) * op_array->last);
- for (int n = 0; n < cfg->blocks_count; n++) {
+ for (uint32_t n = 0; n < cfg->blocks_count; n++) {
if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
cfg->map[cfg->blocks[n].start] = n;
}
}
}
-static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
+static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, const zend_basic_block *block, int n, uint32_t *opt_count)
{
int b;
zend_basic_block *target_block = cfg->blocks + block->successors[n];
@@ -1200,7 +1153,7 @@ static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg
return target_block;
}
-static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
+static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, const zend_basic_block *block, int n, uint32_t *opt_count)
{
int b;
zend_basic_block *target_block = cfg->blocks + block->successors[n];
@@ -1219,7 +1172,7 @@ static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg
static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block)
{
zend_basic_block *next_block = block + 1;
- zend_basic_block *end = cfg->blocks + cfg->blocks_count;
+ const zend_basic_block *end = cfg->blocks + cfg->blocks_count;
while (1) {
if (next_block == end) {
@@ -1237,7 +1190,7 @@ static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg,
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
-static zend_always_inline bool in_hitlist(int target, int *jmp_hitlist, int jmp_hitlist_count)
+static zend_always_inline bool in_hitlist(int target, const int *jmp_hitlist, int jmp_hitlist_count)
{
int i;
@@ -1483,9 +1436,9 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr
/* Find a set of variables which are used outside of the block where they are
* defined. We won't apply some optimization patterns for such variables. */
-static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
+static void zend_t_usage(const zend_cfg *cfg, const zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
{
- int n;
+ uint32_t n;
zend_basic_block *block, *next_block;
uint32_t var_num;
uint32_t bitset_len;
@@ -1566,14 +1519,14 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
}
if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
- bool printed = 0;
+ bool printed = false;
uint32_t i;
for (i = op_array->last_var; i< op_array->T; i++) {
if (zend_bitset_in(used_ext, i)) {
if (!printed) {
fprintf(stderr, "NON-LOCAL-VARS: %d", i);
- printed = 1;
+ printed = true;
} else {
fprintf(stderr, ", %d", i);
}
@@ -1687,13 +1640,12 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
zend_arena_release(&ctx->arena, checkpoint);
}
-static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *opt_count)
+static void zend_merge_blocks(const zend_op_array *op_array, const zend_cfg *cfg, uint32_t *opt_count)
{
- int i;
zend_basic_block *b, *bb;
zend_basic_block *prev = NULL;
- for (i = 0; i < cfg->blocks_count; i++) {
+ for (uint32_t i = 0; i < cfg->blocks_count; i++) {
b = cfg->blocks + i;
if (b->flags & ZEND_BB_REACHABLE) {
if ((b->flags & ZEND_BB_FOLLOW) &&
@@ -1707,7 +1659,7 @@ static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *
for (bb = prev + 1; bb != b; bb++) {
zend_op *op = op_array->opcodes + bb->start;
- zend_op *end = op + bb->len;
+ const zend_op *end = op + bb->len;
while (op < end) {
if (op->op1_type == IS_CONST) {
literal_dtor(&ZEND_OP1_LITERAL(op));
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c
index d0aaccec7ce2..a4ecb19c85ef 100644
--- a/Zend/Optimizer/compact_literals.c
+++ b/Zend/Optimizer/compact_literals.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
| Xinchen Hui |
@@ -110,7 +108,7 @@ static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_op *opline, *end;
- int i, j, n, *map;
+ int n, *map;
uint32_t cache_size;
zval zv, *pos;
literal_info *info;
@@ -124,6 +122,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot;
if (op_array->last_literal) {
+ uint32_t j;
info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
/* Mark literals of specific types */
@@ -258,9 +257,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
op_array->function_name ? op_array->function_name->val : "main");
fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
- for (int i = 0; i < op_array->last_literal; i++) {
+ for (uint32_t i = 0; i < op_array->last_literal; i++) {
zend_string *str = zval_get_string(op_array->literals + i);
- fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
+ fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
zend_string_release(str);
}
fflush(stderr);
@@ -272,7 +271,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
memset(map, 0, op_array->last_literal * sizeof(int));
- for (i = 0; i < op_array->last_literal; i++) {
+ for (uint32_t i = 0; i < op_array->last_literal; i++) {
if (!info[i].num_related) {
/* unset literal */
zval_ptr_dtor_nogc(&op_array->literals[i]);
@@ -734,12 +733,23 @@ 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;
cache_size += 2 * sizeof(void *);
}
break;
+ case ZEND_CALLABLE_CONVERT:
+ if (opline->extended_value != (uint32_t)-1) {
+ opline->extended_value = cache_size;
+ cache_size += sizeof(void *);
+ }
+ break;
+ case ZEND_CALLABLE_CONVERT_PARTIAL:
+ opline->op1.num = cache_size;
+ cache_size += 2 * sizeof(void *);
+ break;
}
opline++;
}
@@ -770,9 +780,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
{
fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
- for (int i = 0; i < op_array->last_literal; i++) {
+ for (uint32_t i = 0; i < op_array->last_literal; i++) {
zend_string *str = zval_get_string(op_array->literals + i);
- fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
+ fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
zend_string_release(str);
}
fflush(stderr);
diff --git a/Zend/Optimizer/compact_vars.c b/Zend/Optimizer/compact_vars.c
index 9898714a17c5..b4a861d3595c 100644
--- a/Zend/Optimizer/compact_vars.c
+++ b/Zend/Optimizer/compact_vars.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Removing unused variables |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Nikita Popov |
+----------------------------------------------------------------------+
diff --git a/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c
index a00fd8bc6ad3..0780ac190cdd 100644
--- a/Zend/Optimizer/dce.c
+++ b/Zend/Optimizer/dce.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, DCE - Dead Code Elimination |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Nikita Popov |
| Dmitry Stogov |
@@ -62,17 +60,17 @@ typedef struct {
static inline bool is_bad_mod(const zend_ssa *ssa, int use, int def) {
if (def < 0) {
/* This modification is not tracked by SSA, assume the worst */
- return 1;
+ return true;
}
if (ssa->var_info[use].type & MAY_BE_REF) {
/* Modification of reference may have side-effect */
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static inline bool may_have_side_effects(
- zend_op_array *op_array, zend_ssa *ssa,
+ const zend_op_array *op_array, const zend_ssa *ssa,
const zend_op *opline, const zend_ssa_op *ssa_op,
bool reorder_dtor_effects) {
switch (opline->opcode) {
@@ -124,19 +122,20 @@ static inline bool may_have_side_effects(
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_ARRAY_KEY_EXISTS:
+ case ZEND_COPY_TMP:
/* No side effects */
- return 0;
+ return false;
case ZEND_FREE:
return opline->extended_value == ZEND_FREE_VOID_CAST;
case ZEND_ADD_ARRAY_ELEMENT:
/* TODO: We can't free two vars. Keep instruction alive. "$b"]; */
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) {
- return 1;
+ return true;
}
- return 0;
+ return false;
case ZEND_ROPE_END:
/* TODO: Rope dce optimization, see #76446 */
- return 1;
+ return true;
case ZEND_JMP:
case ZEND_JMPZ:
case ZEND_JMPNZ:
@@ -149,7 +148,7 @@ static inline bool may_have_side_effects(
case ZEND_BIND_INIT_STATIC_OR_JMP:
case ZEND_JMP_FRAMELESS:
/* For our purposes a jumps and branches are side effects. */
- return 1;
+ return true;
case ZEND_BEGIN_SILENCE:
case ZEND_END_SILENCE:
case ZEND_ECHO:
@@ -161,10 +160,16 @@ static inline bool may_have_side_effects(
case ZEND_EXT_FCALL_END:
case ZEND_TICKS:
case ZEND_YIELD:
- case ZEND_YIELD_FROM:
case ZEND_VERIFY_NEVER_TYPE:
/* Intrinsic side effects */
- return 1;
+ return true;
+ case ZEND_YIELD_FROM: {
+ uint32_t t1 = OP1_INFO();
+ if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY && MAY_BE_EMPTY_ONLY(t1)) {
+ return false;
+ }
+ return true;
+ }
case ZEND_DO_FCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_DO_ICALL:
@@ -174,31 +179,31 @@ static inline bool may_have_side_effects(
case ZEND_FRAMELESS_ICALL_2:
case ZEND_FRAMELESS_ICALL_3:
/* For now assume all calls have side effects */
- return 1;
+ return true;
case ZEND_RECV:
case ZEND_RECV_INIT:
/* Even though RECV_INIT can be side-effect free, these cannot be simply dropped
* due to the prologue skipping code. */
- return 1;
+ return true;
case ZEND_ASSIGN_REF:
- return 1;
+ return true;
case ZEND_ASSIGN:
{
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) {
- return 1;
+ return true;
}
if (!reorder_dtor_effects) {
if (opline->op2_type != IS_CONST
&& (OP2_INFO() & MAY_HAVE_DTOR)
&& ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) {
/* DCE might shorten lifetime */
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
case ZEND_UNSET_VAR:
- return 1;
+ return true;
case ZEND_UNSET_CV:
{
uint32_t t1 = OP1_INFO();
@@ -207,9 +212,9 @@ static inline bool may_have_side_effects(
* an unset may be considered dead even if there is a later assignment to the
* variable. Removing the unset in this case would not be correct if the variable
* is a reference, because unset breaks references. */
- return 1;
+ return true;
}
- return 0;
+ return false;
}
case ZEND_PRE_INC:
case ZEND_POST_INC:
@@ -223,7 +228,7 @@ static inline bool may_have_side_effects(
case ZEND_ASSIGN_OBJ:
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
- return 1;
+ return true;
}
if (!reorder_dtor_effects) {
opline++;
@@ -231,33 +236,33 @@ static inline bool may_have_side_effects(
if (opline->op1_type != IS_CONST
&& (OP1_INFO() & MAY_HAVE_DTOR)) {
/* DCE might shorten lifetime */
- return 1;
+ return true;
}
}
- return 0;
+ return false;
case ZEND_PRE_INC_OBJ:
case ZEND_PRE_DEC_OBJ:
case ZEND_POST_INC_OBJ:
case ZEND_POST_DEC_OBJ:
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
- return 1;
+ return true;
}
- return 0;
+ return false;
case ZEND_BIND_STATIC:
if (op_array->static_variables) {
/* Implicit and Explicit bind static is effectively prologue of closure so
report it has side effects like RECV, RECV_INIT; This allows us to
reflect on the closure and discover used variable at runtime */
if ((opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) {
- return 1;
+ return true;
}
/* Modifies static variables which are observable through reflection */
if ((opline->extended_value & ZEND_BIND_REF) && opline->op2_type != IS_UNUSED) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
case ZEND_CHECK_VAR:
return (OP1_INFO() & MAY_BE_UNDEF) != 0;
case ZEND_FE_RESET_R:
@@ -267,12 +272,12 @@ static inline bool may_have_side_effects(
return (OP1_INFO() & MAY_BE_ANY) != MAY_BE_ARRAY;
default:
/* For everything we didn't handle, assume a side-effect */
- return 1;
+ return true;
}
}
-static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
- zend_ssa_var *var = &ctx->ssa->vars[var_num];
+static zend_always_inline void add_to_worklists(const context *ctx, int var_num, int check) {
+ const zend_ssa_var *var = &ctx->ssa->vars[var_num];
if (var->definition >= 0) {
if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
zend_bitset_incl(ctx->instr_worklist, var->definition);
@@ -284,14 +289,14 @@ static zend_always_inline void add_to_worklists(context *ctx, int var_num, int c
}
}
-static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
- zend_ssa_var *var = &ctx->ssa->vars[var_num];
+static inline void add_to_phi_worklist_no_val(const context *ctx, int var_num) {
+ const zend_ssa_var *var = &ctx->ssa->vars[var_num];
if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
}
}
-static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, zend_ssa *ssa, int check) {
+static zend_always_inline void add_operands_to_worklists(const context *ctx, const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, int check) {
if (ssa_op->result_use >= 0) {
add_to_worklists(ctx, ssa_op->result_use, check);
}
@@ -315,16 +320,16 @@ static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *
}
}
-static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) {
- zend_ssa *ssa = ctx->ssa;
+static zend_always_inline void add_phi_sources_to_worklists(const context *ctx, zend_ssa_phi *phi, int check) {
+ const zend_ssa *ssa = ctx->ssa;
int source;
FOREACH_PHI_SOURCE(phi, source) {
add_to_worklists(ctx, source, check);
} FOREACH_PHI_SOURCE_END();
}
-static inline bool is_var_dead(context *ctx, int var_num) {
- zend_ssa_var *var = &ctx->ssa->vars[var_num];
+static inline bool is_var_dead(const context *ctx, int var_num) {
+ const zend_ssa_var *var = &ctx->ssa->vars[var_num];
if (var->definition_phi) {
return zend_bitset_in(ctx->phi_dead, var_num);
} else if (var->definition >= 0) {
@@ -338,9 +343,9 @@ static inline bool is_var_dead(context *ctx, int var_num) {
}
// Sometimes we can mark the var as EXT_UNUSED
-static bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
+static bool try_remove_var_def(const context *ctx, int free_var, int use_chain, const zend_op *opline) {
if (use_chain >= 0) {
- return 0;
+ return false;
}
zend_ssa_var *var = &ctx->ssa->vars[free_var];
int def = var->definition;
@@ -381,54 +386,56 @@ static bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_o
def_opline->result.var = 0;
def_op->result_def = -1;
var->definition = -1;
- return 1;
+ return true;
default:
break;
}
}
}
- return 0;
+ return false;
}
static zend_always_inline bool may_be_refcounted(uint32_t type) {
return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
}
-static inline bool is_free_of_live_var(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
+static inline bool is_free_of_live_var(const context *ctx, const zend_op *opline, const zend_ssa_op *ssa_op) {
switch (opline->opcode) {
case ZEND_FREE:
/* It is always safe to remove FREEs of non-refcounted values, even if they are live. */
if ((ctx->ssa->var_info[ssa_op->op1_use].type & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) != 0
&& !may_be_refcounted(ctx->ssa->var_info[ssa_op->op1_use].type)) {
- return 0;
+ return false;
}
ZEND_FALLTHROUGH;
case ZEND_FE_FREE:
return !is_var_dead(ctx, ssa_op->op1_use);
default:
- return 0;
+ return false;
}
}
/* Returns whether the instruction has been DCEd */
-static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
- zend_ssa *ssa = ctx->ssa;
+static bool dce_instr(const context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
+ const zend_ssa *ssa = ctx->ssa;
int free_var = -1;
uint8_t free_var_type;
if (opline->opcode == ZEND_NOP) {
- return 0;
+ return false;
}
/* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
if (is_free_of_live_var(ctx, opline, ssa_op)) {
- return 0;
+ return false;
}
- if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op1_use)) {
if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
if (may_be_refcounted(ssa->var_info[ssa_op->op1_use].type)
- && opline->opcode != ZEND_CASE && opline->opcode != ZEND_CASE_STRICT) {
+ && opline->opcode != ZEND_CASE
+ && opline->opcode != ZEND_CASE_STRICT
+ && opline->opcode != ZEND_COPY_TMP) {
free_var = ssa_op->op1_use;
free_var_type = opline->op1_type;
}
@@ -440,7 +447,7 @@ static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
if (free_var >= 0) {
// TODO: We can't free two vars. Keep instruction alive.
zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
- return 0;
+ return false;
}
free_var = ssa_op->op2_use;
free_var_type = opline->op2_type;
@@ -459,12 +466,12 @@ static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
ssa_op->op1_use = free_var;
ssa_op->op1_use_chain = ssa->vars[free_var].use_chain;
ssa->vars[free_var].use_chain = ssa_op - ssa->ops;
- return 0;
+ return false;
}
- return 1;
+ return true;
}
-static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
+static inline int get_common_phi_source(const zend_ssa *ssa, zend_ssa_phi *phi) {
int common_source = -1;
int source;
FOREACH_PHI_SOURCE(phi, source) {
@@ -484,7 +491,7 @@ static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
return common_source;
}
-static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
+static void try_remove_trivial_phi(const context *ctx, zend_ssa_phi *phi) {
zend_ssa *ssa = ctx->ssa;
if (phi->pi < 0) {
/* Phi assignment with identical source operands */
@@ -507,17 +514,17 @@ static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
static inline bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) {
if (ssa_op->op1_def >= 0
&& ssa->vars[ssa_op->op1_def].var < op_array->num_args) {
- return 1;
+ return true;
}
if (ssa_op->op2_def >= 0
&& ssa->vars[ssa_op->op2_def].var < op_array->num_args) {
- return 1;
+ return true;
}
if (ssa_op->result_def >= 0
&& ssa->vars[ssa_op->result_def].var < op_array->num_args) {
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static inline bool may_throw_dce_exception(const zend_op *opline) {
@@ -562,12 +569,12 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *optimizer
} FOREACH_PHI_END();
/* Mark reachable instruction without side effects as dead */
- int b = ssa->cfg.blocks_count;
+ uint32_t b = ssa->cfg.blocks_count;
while (b > 0) {
int op_data = -1;
b--;
- zend_basic_block *block = &ssa->cfg.blocks[b];
+ const zend_basic_block *block = &ssa->cfg.blocks[b];
if (!(block->flags & ZEND_BB_REACHABLE)) {
continue;
}
diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c
index bf85764c93b4..77dc322fbdec 100644
--- a/Zend/Optimizer/dfa_pass.c
+++ b/Zend/Optimizer/dfa_pass.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -226,7 +224,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
}
/* update try/catch array */
- for (j = 0; j < op_array->last_try_catch; j++) {
+ for (uint32_t j = 0; j < op_array->last_try_catch; j++) {
op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
if (op_array->try_catch_array[j].finally_op) {
@@ -256,11 +254,11 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
static bool safe_instanceof(const zend_class_entry *ce1, const zend_class_entry *ce2) {
if (ce1 == ce2) {
- return 1;
+ return true;
}
if (!(ce1->ce_flags & ZEND_ACC_LINKED)) {
/* This case could be generalized, similarly to unlinked_instanceof */
- return 0;
+ return false;
}
return instanceof_function(ce1, ce2);
}
@@ -297,7 +295,7 @@ static inline bool can_elide_return_type_check(
zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
uint32_t use_type = use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF);
if (use_type & MAY_BE_REF) {
- return 0;
+ return false;
}
if (use_type & MAY_BE_UNDEF) {
@@ -322,20 +320,20 @@ static bool opline_supports_assign_contraction(
zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
if (opline->opcode == ZEND_NEW) {
/* see Zend/tests/generators/aborted_yield_during_new.phpt */
- return 0;
+ return false;
}
/* Frameless calls override the return value, but the return value may overlap with the arguments. */
switch (opline->opcode) {
case ZEND_FRAMELESS_ICALL_3:
- if ((opline + 1)->op1_type == IS_CV && (opline + 1)->op1.var == cv_var) return 0;
+ if ((opline + 1)->op1_type == IS_CV && (opline + 1)->op1.var == cv_var) return false;
ZEND_FALLTHROUGH;
case ZEND_FRAMELESS_ICALL_2:
- if (opline->op2_type == IS_CV && opline->op2.var == cv_var) return 0;
+ if (opline->op2_type == IS_CV && opline->op2.var == cv_var) return false;
ZEND_FALLTHROUGH;
case ZEND_FRAMELESS_ICALL_1:
- if (opline->op1_type == IS_CV && opline->op1.var == cv_var) return 0;
- return 1;
+ if (opline->op1_type == IS_CV && opline->op1.var == cv_var) return false;
+ return true;
}
if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
@@ -374,10 +372,10 @@ static bool opline_supports_assign_contraction(
&& opline->op1_type == IS_CV
&& opline->op1.var == cv_var
&& zend_may_throw(opline, &ssa->ops[ssa->vars[src_var].definition], op_array, ssa)) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static bool variable_defined_or_used_in_range(zend_ssa *ssa, int var, int start, int end)
@@ -391,20 +389,20 @@ static bool variable_defined_or_used_in_range(zend_ssa *ssa, int var, int start,
(ssa_op->op2_use >= 0 && ssa->vars[ssa_op->op2_use].var == var) ||
(ssa_op->result_use >= 0 && ssa->vars[ssa_op->result_use].var == var)
) {
- return 1;
+ return true;
}
start++;
}
- return 0;
+ return false;
}
-int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
+static uint32_t zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
{
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- int removed_ops = 0;
+ const zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ uint32_t removed_ops = 0;
if (func_info->callee_info) {
- zend_call_info *call_info = func_info->callee_info;
+ const zend_call_info *call_info = func_info->callee_info;
do {
zend_op *op = call_info->caller_init_opline;
@@ -413,22 +411,21 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
|| (op->opcode == ZEND_FRAMELESS_ICALL_3 && (op + 1)->op1_type == IS_CONST))
&& call_info->callee_func
&& zend_string_equals_literal_ci(call_info->callee_func->common.function_name, "in_array")) {
-
- bool strict = 0;
+ bool strict = false;
bool has_opdata = op->opcode == ZEND_FRAMELESS_ICALL_3;
ZEND_ASSERT(!call_info->is_prototype);
if (has_opdata) {
if (zend_is_true(CT_CONSTANT_EX(op_array, (op + 1)->op1.constant))) {
- strict = 1;
+ strict = true;
}
}
if (op->op2_type == IS_CONST
&& Z_TYPE_P(CT_CONSTANT_EX(op_array, op->op2.constant)) == IS_ARRAY) {
- bool ok = 1;
+ bool ok = true;
- HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, op->op2.constant));
+ const HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, op->op2.constant));
HashTable *dst;
zval *val, tmp;
zend_ulong idx;
@@ -443,7 +440,7 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
} else {
zend_array_destroy(dst);
- ok = 0;
+ ok = false;
break;
}
} ZEND_HASH_FOREACH_END();
@@ -451,7 +448,7 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
ZEND_HASH_FOREACH_VAL(src, val) {
if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
zend_array_destroy(dst);
- ok = 0;
+ ok = false;
break;
}
zend_hash_add(dst, Z_STR_P(val), &tmp);
@@ -479,7 +476,7 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
return removed_ops;
}
-static zend_always_inline void take_successor_0(zend_ssa *ssa, int block_num, zend_basic_block *block)
+static zend_always_inline void take_successor_0(zend_ssa *ssa, uint32_t block_num, zend_basic_block *block)
{
if (block->successors_count == 2) {
if (block->successors[1] != block->successors[0]) {
@@ -489,7 +486,7 @@ static zend_always_inline void take_successor_0(zend_ssa *ssa, int block_num, ze
}
}
-static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, zend_basic_block *block)
+static zend_always_inline void take_successor_1(zend_ssa *ssa, uint32_t block_num, zend_basic_block *block)
{
if (block->successors_count == 2) {
if (block->successors[1] != block->successors[0]) {
@@ -500,11 +497,9 @@ static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, ze
}
}
-static zend_always_inline void take_successor_ex(zend_ssa *ssa, int block_num, zend_basic_block *block, int target_block)
+static zend_always_inline void take_successor_ex(zend_ssa *ssa, uint32_t block_num, zend_basic_block *block, int target_block)
{
- int i;
-
- for (i = 0; i < block->successors_count; i++) {
+ for (uint32_t i = 0; i < block->successors_count; i++) {
if (block->successors[i] != target_block) {
zend_ssa_remove_predecessor(ssa, block_num, block->successors[i]);
}
@@ -531,10 +526,9 @@ static void replace_predecessor(zend_ssa *ssa, int block_id, int old_pred, int n
int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
zend_ssa_phi *phi;
- int i;
int old_pred_idx = -1;
int new_pred_idx = -1;
- for (i = 0; i < block->predecessors_count; i++) {
+ for (uint32_t i = 0; i < block->predecessors_count; i++) {
if (predecessors[i] == old_pred) {
old_pred_idx = i;
}
@@ -582,10 +576,9 @@ static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa
zend_basic_block *src = &ssa->cfg.blocks[from];
zend_basic_block *old = &ssa->cfg.blocks[to];
zend_basic_block *dst = &ssa->cfg.blocks[new_to];
- int i;
zend_op *opline;
- for (i = 0; i < src->successors_count; i++) {
+ for (uint32_t i = 0; i < src->successors_count; i++) {
if (src->successors[i] == to) {
src->successors[i] = new_to;
}
@@ -650,10 +643,10 @@ static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa
replace_predecessor(ssa, new_to, to, from);
}
-static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, int block_num)
+static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, uint32_t block_num)
{
if (block->predecessors_count == 1 && ssa->blocks[block_num].phis == NULL) {
- int *predecessors, i;
+ int *predecessors;
zend_basic_block *fe_fetch_block = NULL;
ZEND_ASSERT(block->successors_count == 1);
@@ -669,7 +662,7 @@ static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_b
}
}
}
- for (i = 0; i < block->predecessors_count; i++) {
+ for (uint32_t i = 0; i < block->predecessors_count; i++) {
zend_ssa_replace_control_link(op_array, ssa, predecessors[i], block_num, block->successors[0]);
}
zend_ssa_remove_block(op_array, ssa, block_num);
@@ -686,7 +679,7 @@ static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_b
static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
{
int removed_ops = 0;
- int block_num = 0;
+ uint32_t block_num = 0;
for (block_num = 1; block_num < ssa->cfg.blocks_count; block_num++) {
zend_basic_block *block = &ssa->cfg.blocks[block_num];
@@ -706,17 +699,17 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
block_num++;
}
while (block_num < ssa->cfg.blocks_count) {
- int next_block_num = block_num + 1;
+ uint32_t next_block_num = block_num + 1;
zend_basic_block *block = &ssa->cfg.blocks[block_num];
uint32_t op_num;
zend_op *opline;
zend_ssa_op *ssa_op;
- bool can_follow = 1;
+ bool can_follow = true;
while (next_block_num < ssa->cfg.blocks_count
&& !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) {
if (ssa->cfg.blocks[next_block_num].flags & ZEND_BB_UNREACHABLE_FREE) {
- can_follow = 0;
+ can_follow = false;
}
next_block_num++;
}
@@ -941,11 +934,13 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
if (block_num > 0) {
zend_ssa_unlink_block(op_array, ssa, block, block_num);
/* backtrack to previous basic block */
+ int backtracking_block_num = block_num;
do {
- block_num--;
- } while (block_num >= 0
- && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE));
- if (block_num >= 0) {
+ backtracking_block_num--;
+ } while (backtracking_block_num >= 0
+ && !(ssa->cfg.blocks[backtracking_block_num].flags & ZEND_BB_REACHABLE));
+ if (backtracking_block_num >= 0) {
+ block_num = backtracking_block_num;
continue;
}
}
@@ -989,7 +984,7 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
if ((opline->op1_type == IS_CV && opline->op1.var == cv)
|| (opline->op2_type == IS_CV && opline->op2.var == cv)
|| (opline->result_type == IS_CV && opline->result.var == cv)) {
- return 0;
+ return false;
}
opline--;
i--;
@@ -1026,12 +1021,12 @@ static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ss
op_array->opcodes[use].result.var = cv;
}
- return 1;
+ return true;
}
}
}
- return 0;
+ return false;
}
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c
index 840a18341a0f..8dbd6855d68d 100644
--- a/Zend/Optimizer/escape_analysis.c
+++ b/Zend/Optimizer/escape_analysis.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache, Escape Analysis |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -78,7 +76,7 @@ static zend_result zend_build_equi_escape_sets(int *parent, zend_op_array *op_ar
zend_ssa_var *ssa_vars = ssa->vars;
int ssa_vars_count = ssa->vars_count;
zend_ssa_phi *p;
- int i, j;
+ int i;
int *size;
ALLOCA_FLAG(use_heap)
@@ -94,7 +92,7 @@ static zend_result zend_build_equi_escape_sets(int *parent, zend_op_array *op_ar
if (p->pi >= 0) {
union_find_unite(parent, size, i, p->sources[0]);
} else {
- for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ for (uint32_t j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
union_find_unite(parent, size, i, p->sources[j]);
}
}
@@ -155,7 +153,7 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
if (ssa_op->result_def == var) {
switch (opline->opcode) {
case ZEND_INIT_ARRAY:
- return 1;
+ return true;
case ZEND_NEW: {
/* objects with destructors should escape */
zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
@@ -175,22 +173,22 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
&& !ce->__set
&& !(ce->ce_flags & forbidden_flags)
&& (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
- return 1;
+ return true;
}
break;
}
case ZEND_QM_ASSIGN:
if (opline->op1_type == IS_CONST
&& Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) {
- return 1;
+ return true;
}
if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
- return 1;
+ return true;
}
break;
case ZEND_ASSIGN:
if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
- return 1;
+ return true;
}
break;
}
@@ -199,22 +197,22 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i
case ZEND_ASSIGN:
if (opline->op2_type == IS_CONST
&& Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) {
- return 1;
+ return true;
}
if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
- return 1;
+ return true;
}
break;
case ZEND_ASSIGN_DIM:
if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
/* implicit object/array allocation */
- return 1;
+ return true;
}
break;
}
}
- return 0;
+ return false;
}
/* }}} */
@@ -229,7 +227,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_QM_ASSIGN:
case ZEND_ASSIGN:
- return 1;
+ return true;
case ZEND_NEW: {
/* objects with destructors should escape */
zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
@@ -243,7 +241,7 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
&& !ce->__get
&& !ce->__set
&& !ce->parent) {
- return 1;
+ return true;
}
break;
}
@@ -260,11 +258,11 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va
case ZEND_PRE_DEC_OBJ:
case ZEND_POST_INC_OBJ:
case ZEND_POST_DEC_OBJ:
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
/* }}} */
@@ -282,7 +280,7 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
if (opline->op1_type == IS_CV) {
if (OP1_INFO() & MAY_BE_OBJECT) {
/* object aliasing */
- return 1;
+ return true;
}
}
break;
@@ -294,7 +292,7 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
case ZEND_FETCH_OBJ_IS:
break;
case ZEND_ASSIGN_OP:
- return 1;
+ return true;
case ZEND_ASSIGN_DIM_OP:
case ZEND_ASSIGN_OBJ_OP:
case ZEND_ASSIGN_STATIC_PROP_OP:
@@ -310,22 +308,22 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
case ZEND_INIT_ARRAY:
case ZEND_ADD_ARRAY_ELEMENT:
if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
- return 1;
+ return true;
}
if (OP1_INFO() & MAY_BE_OBJECT) {
/* object aliasing */
- return 1;
+ return true;
}
/* reference dependencies processed separately */
break;
case ZEND_OP_DATA:
if ((opline-1)->opcode != ZEND_ASSIGN_DIM
&& (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
- return 1;
+ return true;
}
if (OP1_INFO() & MAY_BE_OBJECT) {
/* object aliasing */
- return 1;
+ return true;
}
opline--;
ssa_op--;
@@ -333,12 +331,12 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
|| (OP1_INFO() & MAY_BE_REF)
|| (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
/* assignment into escaping structure */
- return 1;
+ return true;
}
/* reference dependencies processed separately */
break;
default:
- return 1;
+ return true;
}
}
@@ -349,17 +347,17 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
|| (OP1_INFO() & MAY_BE_REF)
|| (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
/* assignment into escaping variable */
- return 1;
+ return true;
}
if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
if (OP2_INFO() & MAY_BE_OBJECT) {
/* object aliasing */
- return 1;
+ return true;
}
}
break;
default:
- return 1;
+ return true;
}
}
@@ -371,11 +369,11 @@ static bool is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int v
case ZEND_ADD_ARRAY_ELEMENT:
break;
default:
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
/* }}} */
@@ -393,12 +391,12 @@ zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *o
return SUCCESS;
}
- has_allocations = 0;
+ has_allocations = false;
for (i = op_array->last_var; i < ssa_vars_count; i++) {
if (ssa_vars[i].definition >= 0
&& (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
&& is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
- has_allocations = 1;
+ has_allocations = true;
break;
}
}
@@ -470,7 +468,7 @@ zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *o
bool changed;
do {
- changed = 0;
+ changed = false;
for (i = 0; i < ssa_vars_count; i++) {
if (ssa_vars[i].use_chain >= 0) {
root = ees[i];
@@ -506,13 +504,13 @@ zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *o
if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
num_non_escaped--;
if (num_non_escaped == 0) {
- changed = 0;
+ changed = false;
} else {
- changed = 1;
+ changed = true;
}
break;
} else {
- changed = 1;
+ changed = true;
}
}
} FOREACH_USE_END();
diff --git a/Zend/Optimizer/nop_removal.c b/Zend/Optimizer/nop_removal.c
index fd5a87cbfb28..0a6a04fc4a9b 100644
--- a/Zend/Optimizer/nop_removal.c
+++ b/Zend/Optimizer/nop_removal.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -32,21 +30,20 @@
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
- zend_op *end, *opline;
+ zend_op *opline;
uint32_t new_count, i, shift;
- int j;
uint32_t *shiftlist;
ALLOCA_FLAG(use_heap);
shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
i = new_count = shift = 0;
- end = op_array->opcodes + op_array->last;
+ const zend_op *end = op_array->opcodes + op_array->last;
for (opline = op_array->opcodes; opline < end; opline++) {
/* Kill JMP-over-NOP-s */
if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
/* check if there are only NOPs under the branch */
- zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
+ const zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
while (target->opcode == ZEND_NOP) {
target--;
@@ -81,7 +78,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx
}
/* update try/catch array */
- for (j = 0; j < op_array->last_try_catch; j++) {
+ for (uint32_t j = 0; j < op_array->last_try_catch; j++) {
op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
if (op_array->try_catch_array[j].finally_op) {
diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c
index 8b29f47c9497..05cdce4fc4cf 100644
--- a/Zend/Optimizer/optimize_func_calls.c
+++ b/Zend/Optimizer/optimize_func_calls.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
| Xinchen Hui |
@@ -37,7 +35,7 @@ typedef struct _optimizer_call_info {
uint32_t func_arg_num;
} optimizer_call_info;
-static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opline)
+static void zend_delete_call_instructions(const zend_op_array *op_array, zend_op *opline)
{
int call = 0;
@@ -76,7 +74,7 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli
}
}
-static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
+static void zend_try_inline_call(zend_op_array *op_array, const zend_op *fcall, zend_op *opline, const zend_function *func)
{
const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
@@ -153,7 +151,7 @@ static bool has_known_send_mode(const optimizer_call_info *info, uint32_t arg_nu
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_op *opline = op_array->opcodes;
- zend_op *end = opline + op_array->last;
+ const zend_op *end = opline + op_array->last;
int call = 0;
void *checkpoint;
optimizer_call_info *call_stack;
@@ -193,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;
@@ -225,19 +224,20 @@ 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);
}
}
call_stack[call].func = NULL;
call_stack[call].opline = NULL;
- call_stack[call].try_inline = 0;
+ call_stack[call].try_inline = false;
call_stack[call].func_arg_num = (uint32_t)-1;
break;
case ZEND_FETCH_FUNC_ARG:
@@ -265,7 +265,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
&& opline->op2_type == IS_UNUSED) {
/* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
* Performing the replacement would create an invalid opcode. */
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
break;
}
@@ -279,7 +279,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
case ZEND_SEND_VAL_EX:
if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
break;
}
@@ -291,7 +291,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
case ZEND_CHECK_FUNC_ARG:
if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
call_stack[call - 1].func_arg_num = (uint32_t)-1;
break;
}
@@ -305,7 +305,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
/* Don't transform SEND_FUNC_ARG if any FETCH opcodes weren't transformed. */
if (call_stack[call - 1].last_check_func_arg_opline == NULL) {
if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
}
break;
}
@@ -314,7 +314,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
ZEND_FALLTHROUGH;
case ZEND_SEND_VAR_EX:
if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
break;
}
@@ -329,7 +329,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
case ZEND_SEND_VAR_NO_REF_EX:
if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
break;
}
@@ -347,14 +347,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_SEND_VAR:
case ZEND_SEND_REF:
if (opline->op2_type == IS_CONST) {
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
break;
}
break;
case ZEND_SEND_UNPACK:
case ZEND_SEND_USER:
case ZEND_SEND_ARRAY:
- call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].try_inline = false;
break;
default:
break;
diff --git a/Zend/Optimizer/optimize_temp_vars_5.c b/Zend/Optimizer/optimize_temp_vars_5.c
index de0b189d5dca..6a5c99f4755b 100644
--- a/Zend/Optimizer/optimize_temp_vars_5.c
+++ b/Zend/Optimizer/optimize_temp_vars_5.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
diff --git a/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c
index fe92db583fcd..962bdb6e4be3 100644
--- a/Zend/Optimizer/pass1.c
+++ b/Zend/Optimizer/pass1.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -149,7 +147,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (opline->op2_type == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
/* substitute persistent constants */
- if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &result, 1)) {
+ if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &result, true)) {
if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &result)) {
break;
}
@@ -171,7 +169,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
zend_ast *ast = Z_ASTVAL_P(c);
if (ast->kind != ZEND_AST_CONSTANT
- || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &result, 1)
+ || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &result, true)
|| Z_TYPE(result) == IS_CONSTANT_AST) {
break;
}
@@ -193,7 +191,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (send1_opline->opcode != ZEND_SEND_VAL ||
send1_opline->op1_type != IS_CONST) {
/* don't collect constants after unknown function call */
- collect_constants = 0;
+ collect_constants = false;
break;
}
if (send1_opline->op2.num == 2) {
@@ -205,7 +203,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (send1_opline->opcode != ZEND_SEND_VAL ||
send1_opline->op1_type != IS_CONST) {
/* don't collect constants after unknown function call */
- collect_constants = 0;
+ collect_constants = false;
break;
}
}
@@ -217,7 +215,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
init_opline->op2_type != IS_CONST ||
Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
/* don't collect constants after unknown function call */
- collect_constants = 0;
+ collect_constants = false;
break;
}
@@ -261,9 +259,19 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
/* don't collect constants after any other function call */
- collect_constants = 0;
+ collect_constants = false;
break;
}
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ case ZEND_FRAMELESS_ICALL_0:
+ case ZEND_FRAMELESS_ICALL_1:
+ case ZEND_FRAMELESS_ICALL_2:
+ case ZEND_FRAMELESS_ICALL_3:
+ /* don't collect constants after any UCALL/FCALL/FRAMELESS ICALL */
+ collect_constants = 0;
+ break;
case ZEND_STRLEN:
if (opline->op1_type == IS_CONST &&
zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
@@ -271,7 +279,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
break;
case ZEND_DEFINED:
- if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &result, 0)) {
+ if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &result, false)) {
break;
}
ZVAL_TRUE(&result);
@@ -309,7 +317,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
}
}
- collect_constants = 0;
+ collect_constants = false;
break;
case ZEND_JMPZ:
@@ -331,7 +339,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
}
}
- collect_constants = 0;
+ collect_constants = false;
break;
case ZEND_RETURN:
@@ -354,7 +362,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_VERIFY_NEVER_TYPE:
case ZEND_BIND_INIT_STATIC_OR_JMP:
case ZEND_JMP_FRAMELESS:
- collect_constants = 0;
+ collect_constants = false;
break;
}
opline++;
diff --git a/Zend/Optimizer/pass3.c b/Zend/Optimizer/pass3.c
index 2cbd0e340652..2d2a44685226 100644
--- a/Zend/Optimizer/pass3.c
+++ b/Zend/Optimizer/pass3.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -37,10 +35,10 @@ static zend_always_inline bool in_hitlist(zend_op *target, zend_op **jmp_hitlist
for (i = 0; i < jmp_hitlist_count; i++) {
if (jmp_hitlist[i] == target) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
#define CHECK_LOOP(target) \
diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c
index 1982cf66fa01..ba94e9b7b91f 100644
--- a/Zend/Optimizer/sccp.c
+++ b/Zend/Optimizer/sccp.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, SCCP - Sparse Conditional Constant Propagation |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Nikita Popov |
| Dmitry Stogov |
@@ -244,7 +242,7 @@ static bool can_replace_op1(
case ZEND_SEND_ARRAY:
case ZEND_SEND_USER:
case ZEND_FE_RESET_RW:
- return 0;
+ return false;
/* Do not accept CONST */
case ZEND_ROPE_ADD:
case ZEND_ROPE_END:
@@ -254,7 +252,7 @@ static bool can_replace_op1(
case ZEND_MAKE_REF:
case ZEND_UNSET_CV:
case ZEND_ISSET_ISEMPTY_CV:
- return 0;
+ return false;
case ZEND_INIT_ARRAY:
case ZEND_ADD_ARRAY_ELEMENT:
return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
@@ -262,18 +260,18 @@ static bool can_replace_op1(
return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
case ZEND_VERIFY_RETURN_TYPE:
// TODO: This would require a non-local change ???
- return 0;
+ return false;
case ZEND_OP_DATA:
return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF &&
(opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF;
default:
if (ssa_op->op1_def != -1) {
ZEND_UNREACHABLE();
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
static bool can_replace_op2(
@@ -284,9 +282,9 @@ static bool can_replace_op2(
case ZEND_BIND_LEXICAL:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static bool try_replace_op1(
@@ -295,11 +293,11 @@ static bool try_replace_op1(
zval zv;
ZVAL_COPY(&zv, value);
if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
- return 1;
+ return true;
}
zval_ptr_dtor_nogc(&zv);
}
- return 0;
+ return false;
}
static bool try_replace_op2(
@@ -308,11 +306,11 @@ static bool try_replace_op2(
zval zv;
ZVAL_COPY(&zv, value);
if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) {
- return 1;
+ return true;
}
zval_ptr_dtor_nogc(&zv);
}
- return 0;
+ return false;
}
static inline zend_result ct_eval_binary_op(zval *result, uint8_t binop, zval *op1, zval *op2) {
@@ -402,7 +400,7 @@ static inline zend_result ct_eval_fetch_dim(zval *result, zval *op1, zval *op2,
return FAILURE;
}
if (index >= 0 && index < Z_STRLEN_P(op1)) {
- ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
+ ZVAL_CHAR(result, Z_STRVAL_P(op1)[index]);
return SUCCESS;
}
}
@@ -717,7 +715,7 @@ static inline zend_result ct_eval_in_array(zval *result, uint32_t extended_value
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
} else {
- res = 0;
+ res = false;
}
} else if (Z_TYPE_P(op1) <= IS_FALSE) {
res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
@@ -725,11 +723,11 @@ static inline zend_result ct_eval_in_array(zval *result, uint32_t extended_value
zend_string *key;
zval key_tmp;
- res = 0;
+ res = false;
ZEND_HASH_MAP_FOREACH_STR_KEY(ht, key) {
ZVAL_STR(&key_tmp, key);
if (zend_compare(op1, &key_tmp) == 0) {
- res = 1;
+ res = true;
break;
}
} ZEND_HASH_FOREACH_END();
@@ -1596,7 +1594,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
case ZEND_SHORT_CIRCUITING_CHAIN_EMPTY:
ZVAL_TRUE(&zv);
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
SET_RESULT(result, &zv);
break;
@@ -1654,7 +1652,6 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
{
zend_call_info *call;
zval *name, *args[3] = {NULL};
- int i;
if (!ctx->call_map) {
SET_RESULT_BOT(result);
@@ -1676,7 +1673,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
break;
}
- for (i = 0; i < call->num_args; i++) {
+ for (uint32_t i = 0; i < call->num_args; i++) {
zend_op *opline = call->arg_info[i].opline;
if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
SET_RESULT_BOT(result);
@@ -1833,7 +1830,7 @@ static void sccp_mark_feasible_successors(
zend_op *opline, zend_ssa_op *ssa_op) {
sccp_ctx *ctx = (sccp_ctx *) scdf;
zval *op1, zv;
- int s;
+ uint32_t s;
/* We can't determine the branch target at compile-time for these */
switch (opline->opcode) {
@@ -2042,7 +2039,7 @@ static void join_phi_values(zval *a, zval *b, bool escape) {
}
}
-static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
+static void sccp_visit_phi(scdf_ctx *scdf, const zend_ssa_phi *phi) {
sccp_ctx *ctx = (sccp_ctx *) scdf;
zend_ssa *ssa = scdf->ssa;
ZEND_ASSERT(phi->ssa_var >= 0);
@@ -2050,7 +2047,6 @@ static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
zend_basic_block *block = &ssa->cfg.blocks[phi->block];
int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
- int i;
zval result;
MAKE_TOP(&result);
#if SCP_DEBUG
@@ -2062,7 +2058,7 @@ static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
}
} else {
- for (i = 0; i < block->predecessors_count; i++) {
+ for (uint32_t i = 0; i < block->predecessors_count; i++) {
ZEND_ASSERT(phi->sources[i] >= 0);
if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
#if SCP_DEBUG
@@ -2092,7 +2088,6 @@ static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
zend_ssa *ssa = ctx->scdf.ssa;
zend_op_array *op_array = ctx->scdf.op_array;
zend_call_info *call;
- int i;
ZEND_ASSERT(ctx->call_map);
call = ctx->call_map[opline - op_array->opcodes];
@@ -2102,7 +2097,7 @@ static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
zend_ssa_remove_instr(ssa, call->caller_init_opline,
&ssa->ops[call->caller_init_opline - op_array->opcodes]);
- for (i = 0; i < call->num_args; i++) {
+ for (uint32_t i = 0; i < call->num_args; i++) {
zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
&ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
}
@@ -2126,11 +2121,11 @@ static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
* we need to collect.
* d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects.
*/
-static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
+static uint32_t try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
{
zend_ssa *ssa = ctx->scdf.ssa;
zend_op_array *op_array = ctx->scdf.op_array;
- int removed_ops = 0;
+ uint32_t removed_ops = 0;
if (var->definition >= 0) {
zend_op *opline = &op_array->opcodes[var->definition];
@@ -2374,12 +2369,12 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var,
/* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
* can be replaced, because some instructions don't accept constant operands or only accept them
* if they have a certain type. */
-static int replace_constant_operands(sccp_ctx *ctx) {
+static uint32_t replace_constant_operands(sccp_ctx *ctx) {
zend_ssa *ssa = ctx->scdf.ssa;
zend_op_array *op_array = ctx->scdf.op_array;
int i;
zval tmp;
- int removed_ops = 0;
+ uint32_t removed_ops = 0;
/* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
* and INIT_ARRAY. */
@@ -2472,10 +2467,10 @@ static void sccp_context_free(sccp_ctx *sccp) {
}
}
-int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
+uint32_t sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
{
sccp_ctx sccp;
- int removed_ops = 0;
+ uint32_t removed_ops = 0;
void *checkpoint = zend_arena_checkpoint(ctx->arena);
sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
diff --git a/Zend/Optimizer/scdf.c b/Zend/Optimizer/scdf.c
index 54925e8287b6..31f40a7ed9f9 100644
--- a/Zend/Optimizer/scdf.c
+++ b/Zend/Optimizer/scdf.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Sparse Conditional Data Flow Propagation Framework |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Nikita Popov |
+----------------------------------------------------------------------+
@@ -70,9 +68,8 @@ void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
} else {
/* Block is already executable, only a new edge became feasible.
* Reevaluate phi nodes to account for changed source operands. */
- zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
- zend_ssa_phi *phi;
- for (phi = ssa_block->phis; phi; phi = phi->next) {
+ const zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
+ for (const zend_ssa_phi *phi = ssa_block->phis; phi; phi = phi->next) {
zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
scdf->handlers.visit_phi(scdf, phi);
}
@@ -101,7 +98,7 @@ void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array,
}
void scdf_solve(scdf_ctx *scdf, const char *name) {
- zend_ssa *ssa = scdf->ssa;
+ const zend_ssa *ssa = scdf->ssa;
DEBUG_PRINT("Start SCDF solve (%s)\n", name);
while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
|| !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
@@ -109,7 +106,7 @@ void scdf_solve(scdf_ctx *scdf, const char *name) {
) {
int i;
while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
- zend_ssa_phi *phi = ssa->vars[i].definition_phi;
+ const zend_ssa_phi *phi = ssa->vars[i].definition_phi;
ZEND_ASSERT(phi);
if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
scdf->handlers.visit_phi(scdf, phi);
@@ -140,17 +137,14 @@ void scdf_solve(scdf_ctx *scdf, const char *name) {
while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
/* This block is now live. Interpret phis and instructions in it. */
zend_basic_block *block = &ssa->cfg.blocks[i];
- zend_ssa_block *ssa_block = &ssa->blocks[i];
+ const zend_ssa_block *ssa_block = &ssa->blocks[i];
DEBUG_PRINT("Pop block %d from worklist\n", i);
zend_bitset_incl(scdf->executable_blocks, i);
- {
- zend_ssa_phi *phi;
- for (phi = ssa_block->phis; phi; phi = phi->next) {
- zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
- scdf->handlers.visit_phi(scdf, phi);
- }
+ for (const zend_ssa_phi *phi = ssa_block->phis; phi; phi = phi->next) {
+ zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
+ scdf->handlers.visit_phi(scdf, phi);
}
if (block->len == 0) {
@@ -158,7 +152,7 @@ void scdf_solve(scdf_ctx *scdf, const char *name) {
scdf_mark_edge_feasible(scdf, i, block->successors[0]);
} else {
zend_op *opline = NULL;
- int j, end = block->start + block->len;
+ uint32_t j, end = block->start + block->len;
for (j = block->start; j < end; j++) {
opline = &scdf->op_array->opcodes[j];
zend_bitset_excl(scdf->instr_worklist, j);
@@ -185,7 +179,7 @@ void scdf_solve(scdf_ctx *scdf, const char *name) {
* not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
* is necessary for the correctness of temporary compaction. */
static bool is_live_loop_var_free(
- scdf_ctx *scdf, const zend_op *opline, const zend_ssa_op *ssa_op) {
+ const scdf_ctx *scdf, const zend_op *opline, const zend_ssa_op *ssa_op) {
if (!zend_optimizer_is_loop_var_free(opline)) {
return false;
}
@@ -195,7 +189,7 @@ static bool is_live_loop_var_free(
return false;
}
- zend_ssa_var *ssa_var = &scdf->ssa->vars[var];
+ const zend_ssa_var *ssa_var = &scdf->ssa->vars[var];
uint32_t def_block;
if (ssa_var->definition >= 0) {
def_block = scdf->ssa->cfg.map[ssa_var->definition];
@@ -205,7 +199,7 @@ static bool is_live_loop_var_free(
return zend_bitset_in(scdf->executable_blocks, def_block);
}
-static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, const zend_basic_block *block) {
+static bool kept_alive_by_loop_var_free(const scdf_ctx *scdf, const zend_basic_block *block) {
const zend_op_array *op_array = scdf->op_array;
const zend_cfg *cfg = &scdf->ssa->cfg;
if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) {
@@ -220,7 +214,7 @@ static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, const zend_basic_block *
return false;
}
-static uint32_t cleanup_loop_var_free_block(scdf_ctx *scdf, zend_basic_block *block) {
+static uint32_t cleanup_loop_var_free_block(const scdf_ctx *scdf, const zend_basic_block *block) {
zend_ssa *ssa = scdf->ssa;
const zend_op_array *op_array = scdf->op_array;
const zend_cfg *cfg = &ssa->cfg;
@@ -256,12 +250,11 @@ static uint32_t cleanup_loop_var_free_block(scdf_ctx *scdf, zend_basic_block *bl
/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
* blocks, as well as remove them from the successor / predecessor lists and mark them
* unreachable. Blocks already marked unreachable are not removed. */
-uint32_t scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
+uint32_t scdf_remove_unreachable_blocks(const scdf_ctx *scdf) {
zend_ssa *ssa = scdf->ssa;
- int i;
uint32_t removed_ops = 0;
- for (i = 0; i < ssa->cfg.blocks_count; i++) {
- zend_basic_block *block = &ssa->cfg.blocks[i];
+ for (uint32_t i = 0; i < ssa->cfg.blocks_count; i++) {
+ const zend_basic_block *block = &ssa->cfg.blocks[i];
if (!zend_bitset_in(scdf->executable_blocks, i) && (block->flags & ZEND_BB_REACHABLE)) {
if (!kept_alive_by_loop_var_free(scdf, block)) {
removed_ops += block->len;
diff --git a/Zend/Optimizer/scdf.h b/Zend/Optimizer/scdf.h
index 0d90147e84cb..d7bee4b73816 100644
--- a/Zend/Optimizer/scdf.h
+++ b/Zend/Optimizer/scdf.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Call Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Nikita Popov |
+----------------------------------------------------------------------+
@@ -39,7 +37,7 @@ typedef struct _scdf_ctx {
void (*visit_instr)(
struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op);
void (*visit_phi)(
- struct _scdf_ctx *scdf, zend_ssa_phi *phi);
+ struct _scdf_ctx *scdf, const zend_ssa_phi *phi);
void (*mark_feasible_successors)(
struct _scdf_ctx *scdf, int block_num, zend_basic_block *block,
zend_op *opline, zend_ssa_op *ssa_op);
@@ -49,10 +47,10 @@ typedef struct _scdf_ctx {
void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa);
void scdf_solve(scdf_ctx *scdf, const char *name);
-uint32_t scdf_remove_unreachable_blocks(scdf_ctx *scdf);
+uint32_t scdf_remove_unreachable_blocks(const scdf_ctx *scdf);
/* Add uses to worklist */
-static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) {
+static inline void scdf_add_to_worklist(const scdf_ctx *scdf, int var_num) {
const zend_ssa *ssa = scdf->ssa;
const zend_ssa_var *var = &ssa->vars[var_num];
int use;
@@ -66,7 +64,7 @@ static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) {
}
/* This should usually not be necessary, however it's used for type narrowing. */
-static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) {
+static inline void scdf_add_def_to_worklist(const scdf_ctx *scdf, int var_num) {
const zend_ssa_var *var = &scdf->ssa->vars[var_num];
if (var->definition >= 0) {
zend_bitset_incl(scdf->instr_worklist, var->definition);
@@ -75,11 +73,10 @@ static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) {
}
}
-static inline uint32_t scdf_edge(const zend_cfg *cfg, int from, int to) {
+static inline uint32_t scdf_edge(const zend_cfg *cfg, int from, uint32_t to) {
const zend_basic_block *to_block = cfg->blocks + to;
- int i;
- for (i = 0; i < to_block->predecessors_count; i++) {
+ for (uint32_t i = 0; i < to_block->predecessors_count; i++) {
uint32_t edge = to_block->predecessor_offset + i;
if (cfg->predecessors[edge] == from) {
@@ -89,7 +86,7 @@ static inline uint32_t scdf_edge(const zend_cfg *cfg, int from, int to) {
ZEND_UNREACHABLE();
}
-static inline bool scdf_is_edge_feasible(const scdf_ctx *scdf, int from, int to) {
+static inline bool scdf_is_edge_feasible(const scdf_ctx *scdf, int from, uint32_t to) {
uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
return zend_bitset_in(scdf->feasible_edges, edge);
}
diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c
index b525f8d5ee22..34a2b3a9ef64 100644
--- a/Zend/Optimizer/ssa_integrity.c
+++ b/Zend/Optimizer/ssa_integrity.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, SSA validation |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Nikita Popov |
+----------------------------------------------------------------------+
@@ -25,20 +23,20 @@ static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) {
int use;
FOREACH_USE(&ssa->vars[var], use) {
if (use == check) {
- return 1;
+ return true;
}
} FOREACH_USE_END();
- return 0;
+ return false;
}
static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) {
zend_ssa_phi *phi;
FOREACH_PHI_USE(&ssa->vars[var], phi) {
if (phi == check) {
- return 1;
+ return true;
}
} FOREACH_PHI_USE_END();
- return 0;
+ return false;
}
static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) {
@@ -59,30 +57,29 @@ static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check
int source;
FOREACH_PHI_SOURCE(phi, source) {
if (source == check) {
- return 1;
+ return true;
}
} FOREACH_PHI_SOURCE_END();
- return 0;
+ return false;
}
static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) {
- int i, *predecessors = &cfg->predecessors[block->predecessor_offset];
- for (i = 0; i < block->predecessors_count; i++) {
+ int *predecessors = &cfg->predecessors[block->predecessor_offset];
+ for (uint32_t i = 0; i < block->predecessors_count; i++) {
if (predecessors[i] == check) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
static inline bool is_in_successors(zend_basic_block *block, int check) {
- int s;
- for (s = 0; s < block->successors_count; s++) {
+ for (uint32_t s = 0; s < block->successors_count; s++) {
if (block->successors[s] == check) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
static inline bool is_var_type(uint8_t type) {
@@ -329,7 +326,7 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
/* Phis */
FOREACH_PHI(phi) {
- unsigned num_sources = NUM_PHI_SOURCES(phi);
+ uint32_t num_sources = NUM_PHI_SOURCES(phi);
for (i = 0; i < num_sources; i++) {
int source = phi->sources[i];
if (source < 0) {
@@ -360,7 +357,7 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
for (i = 0; i < cfg->blocks_count; i++) {
zend_basic_block *block = &cfg->blocks[i];
int *predecessors = &cfg->predecessors[block->predecessor_offset];
- int s, j;
+ uint32_t j;
if (i != 0 && block->start < (block-1)->start + (block-1)->len) {
FAIL("Block %d start %d smaller previous end %d\n",
@@ -384,7 +381,7 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
continue;
}
- for (s = 0; s < block->successors_count; s++) {
+ for (uint32_t s = 0; s < block->successors_count; s++) {
zend_basic_block *next_block;
if (block->successors[s] < 0) {
FAIL("Successor number %d of %d negative", s, i);
@@ -400,7 +397,6 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
for (j = 0; j < block->predecessors_count; j++) {
if (predecessors[j] >= 0) {
- int k;
zend_basic_block *prev_block = &cfg->blocks[predecessors[j]];
if (!(prev_block->flags & ZEND_BB_REACHABLE)) {
FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i);
@@ -408,7 +404,7 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
if (!is_in_successors(prev_block, i)) {
FAIL("Block %d successors missing %d\n", predecessors[j], i);
}
- for (k = 0; k < block->predecessors_count; k++) {
+ for (uint32_t k = 0; k < block->predecessors_count; k++) {
if (k != j && predecessors[k] == predecessors[j]) {
FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]);
}
diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c
index 8a2f8ea2a7e1..b67f57cf041d 100644
--- a/Zend/Optimizer/zend_call_graph.c
+++ b/Zend/Optimizer/zend_call_graph.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Call Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -23,8 +21,6 @@
#include "zend_inference.h"
#include "zend_call_graph.h"
#include "zend_func_info.h"
-#include "zend_inference.h"
-#include "zend_call_graph.h"
static void zend_op_array_calc(zend_op_array *op_array, void *context)
{
@@ -128,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;
@@ -144,9 +141,10 @@ 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 = 1;
+ call_info->named_args = true;
break;
}
@@ -160,7 +158,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
case ZEND_SEND_ARRAY:
case ZEND_SEND_UNPACK:
if (call_info) {
- call_info->send_unpack = 1;
+ call_info->send_unpack = true;
}
break;
}
@@ -169,26 +167,26 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
free_alloca(call_stack, use_heap);
}
-static bool zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
+static bool zend_is_indirectly_recursive(const zend_op_array *root, const zend_op_array *op_array, zend_bitset visited)
{
- zend_func_info *func_info;
+ const zend_func_info *func_info;
zend_call_info *call_info;
- bool ret = 0;
+ bool ret = false;
if (op_array == root) {
- return 1;
+ return true;
}
func_info = ZEND_FUNC_INFO(op_array);
if (zend_bitset_in(visited, func_info->num)) {
- return 0;
+ return false;
}
zend_bitset_incl(visited, func_info->num);
call_info = func_info->caller_info;
while (call_info) {
if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
- call_info->recursive = 1;
- ret = 1;
+ call_info->recursive = true;
+ ret = true;
}
call_info = call_info->next_caller;
}
@@ -197,16 +195,15 @@ static bool zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_
static void zend_analyze_recursion(zend_call_graph *call_graph)
{
- zend_op_array *op_array;
+ const zend_op_array *op_array;
zend_func_info *func_info;
zend_call_info *call_info;
- int i;
- int set_len = zend_bitset_len(call_graph->op_arrays_count);
+ uint32_t set_len = zend_bitset_len(call_graph->op_arrays_count);
zend_bitset visited;
ALLOCA_FLAG(use_heap);
visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
- for (i = 0; i < call_graph->op_arrays_count; i++) {
+ for (uint32_t i = 0; i < call_graph->op_arrays_count; i++) {
op_array = call_graph->op_arrays[i];
func_info = call_graph->func_infos + i;
call_info = func_info->caller_info;
@@ -216,12 +213,12 @@ static void zend_analyze_recursion(zend_call_graph *call_graph)
continue;
}
if (call_info->caller_op_array == op_array) {
- call_info->recursive = 1;
+ call_info->recursive = true;
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
} else {
memset(visited, 0, sizeof(zend_ulong) * set_len);
if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
- call_info->recursive = 1;
+ call_info->recursive = true;
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
}
}
@@ -252,9 +249,7 @@ ZEND_API void zend_build_call_graph(zend_arena **arena, zend_script *script, zen
ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
{
- int i;
-
- for (i = 0; i < call_graph->op_arrays_count; i++) {
+ for (uint32_t i = 0; i < call_graph->op_arrays_count; i++) {
zend_analyze_calls(arena, script, 0, call_graph->op_arrays[i], call_graph->func_infos + i);
}
zend_analyze_recursion(call_graph);
@@ -262,7 +257,7 @@ ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, z
}
/* }}} */
-ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array) /* {{{ */
+ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, const zend_func_info *info, const zend_op_array *op_array) /* {{{ */
{
zend_call_info **map, *call;
if (!info->callee_info) {
@@ -272,13 +267,12 @@ ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info
map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last);
for (call = info->callee_info; call; call = call->next_callee) {
- int i;
map[call->caller_init_opline - op_array->opcodes] = call;
if (call->caller_call_opline) {
map[call->caller_call_opline - op_array->opcodes] = call;
}
if (!call->is_frameless) {
- for (i = 0; i < call->num_args; i++) {
+ for (uint32_t i = 0; i < call->num_args; i++) {
if (call->arg_info[i].opline) {
map[call->arg_info[i].opline - op_array->opcodes] = call;
}
diff --git a/Zend/Optimizer/zend_call_graph.h b/Zend/Optimizer/zend_call_graph.h
index b2cbb6822bcf..57c6bdffe998 100644
--- a/Zend/Optimizer/zend_call_graph.h
+++ b/Zend/Optimizer/zend_call_graph.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Call Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -39,12 +37,12 @@ struct _zend_call_info {
bool named_args; /* Function has named arguments */
bool is_prototype; /* An overridden child method may be called */
bool is_frameless; /* A frameless function sends arguments through operands */
- int num_args; /* Number of arguments, excluding named and variadic arguments */
+ uint32_t num_args; /* Number of arguments, excluding named and variadic arguments */
zend_send_arg_info arg_info[1];
};
struct _zend_func_info {
- int num;
+ uint32_t num;
uint32_t flags;
zend_ssa ssa; /* Static Single Assignment Form */
zend_call_info *caller_info; /* where this function is called from */
@@ -54,7 +52,7 @@ struct _zend_func_info {
};
typedef struct _zend_call_graph {
- int op_arrays_count;
+ uint32_t op_arrays_count;
zend_op_array **op_arrays;
zend_func_info *func_infos;
} zend_call_graph;
@@ -63,7 +61,7 @@ BEGIN_EXTERN_C()
ZEND_API void zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
-ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array);
+ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, const zend_func_info *info, const zend_op_array *op_array);
ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info);
END_EXTERN_C()
diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c
index a0d08b67aa70..837515169d70 100644
--- a/Zend/Optimizer/zend_cfg.c
+++ b/Zend/Optimizer/zend_cfg.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -35,7 +33,6 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc
zend_worklist_push(&work, b - cfg->blocks);
while (zend_worklist_len(&work)) {
- int i;
b = cfg->blocks + zend_worklist_pop(&work);
b->flags |= ZEND_BB_REACHABLE;
@@ -44,7 +41,7 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc
continue;
}
- for (i = 0; i < b->successors_count; i++) {
+ for (uint32_t i = 0; i < b->successors_count; i++) {
zend_basic_block *succ = blocks + b->successors[i];
if (b->len != 0) {
@@ -104,7 +101,7 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc
}
/* }}} */
-static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
+static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, uint32_t start) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
@@ -113,14 +110,14 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
if (op_array->last_try_catch) {
zend_basic_block *b;
- int j, changed;
+ int changed;
uint32_t *block_map = cfg->map;
do {
changed = 0;
/* Add exception paths */
- for (j = 0; j < op_array->last_try_catch; j++) {
+ for (uint32_t j = 0; j < op_array->last_try_catch; j++) {
/* check for jumps into the middle of try block */
b = blocks + block_map[op_array->try_catch_array[j].try_op];
@@ -202,7 +199,6 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
if (cfg->flags & ZEND_FUNC_FREE_LOOP_VAR) {
zend_basic_block *b;
- int j;
uint32_t *block_map = cfg->map;
/* Mark blocks that are unreachable, but free a loop var created in a reachable block. */
@@ -211,7 +207,7 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
continue;
}
- for (j = b->start; j < b->start + b->len; j++) {
+ for (uint32_t j = b->start; j < b->start + b->len; j++) {
zend_op *opline = &op_array->opcodes[j];
if (zend_optimizer_is_loop_var_free(opline)) {
zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline);
@@ -232,8 +228,8 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
- int i;
- int start = 0;
+ uint32_t i;
+ uint32_t start = 0;
for (i = 0; i < cfg->blocks_count; i++) {
if (blocks[i].flags & ZEND_BB_REACHABLE) {
@@ -274,13 +270,12 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
{
uint32_t flags = 0;
uint32_t i;
- int j;
uint32_t *block_map;
zend_function *fn;
int blocks_count = 0;
zend_basic_block *blocks;
zval *zv;
- bool extra_entry_block = 0;
+ bool extra_entry_block = false;
cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
@@ -445,11 +440,11 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
/* If the entry block has predecessors, we may need to split it */
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
&& op_array->last > 0 && block_map[0] > 1) {
- extra_entry_block = 1;
+ extra_entry_block = true;
}
if (op_array->last_try_catch) {
- for (j = 0; j < op_array->last_try_catch; j++) {
+ for (uint32_t j = 0; j < op_array->last_try_catch; j++) {
BB_START(op_array->try_catch_array[j].try_op);
if (op_array->try_catch_array[j].catch_op) {
BB_START(op_array->try_catch_array[j].catch_op);
@@ -494,7 +489,7 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
blocks_count++;
/* Build CFG, Step 3: Calculate successors */
- for (j = 0; j < blocks_count; j++) {
+ for (int j = 0; j < blocks_count; j++) {
zend_basic_block *block = &blocks[j];
zend_op *opline;
if (block->len == 0) {
@@ -594,13 +589,12 @@ ZEND_API void zend_build_cfg(zend_arena **arena, const zend_op_array *op_array,
ZEND_API void zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
{
- int j, s, edges;
zend_basic_block *b;
zend_basic_block *blocks = cfg->blocks;
zend_basic_block *end = blocks + cfg->blocks_count;
+ uint32_t edges = 0;
int *predecessors;
- edges = 0;
for (b = blocks; b < end; b++) {
b->predecessors_count = 0;
}
@@ -609,7 +603,7 @@ ZEND_API void zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /*
b->successors_count = 0;
b->predecessors_count = 0;
} else {
- for (s = 0; s < b->successors_count; s++) {
+ for (uint32_t s = 0; s < b->successors_count; s++) {
edges++;
blocks[b->successors[s]].predecessors_count++;
}
@@ -628,14 +622,13 @@ ZEND_API void zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /*
}
}
- for (j = 0; j < cfg->blocks_count; j++) {
+ for (uint32_t j = 0; j < cfg->blocks_count; j++) {
if (blocks[j].flags & ZEND_BB_REACHABLE) {
/* SWITCH_STRING/LONG may have few identical successors */
- for (s = 0; s < blocks[j].successors_count; s++) {
+ for (uint32_t s = 0; s < blocks[j].successors_count; s++) {
int duplicate = 0;
- int p;
- for (p = 0; p < s; p++) {
+ for (uint32_t p = 0; p < s; p++) {
if (blocks[j].successors[p] == blocks[j].successors[s]) {
duplicate = 1;
break;
@@ -655,16 +648,15 @@ ZEND_API void zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /*
/* Computes a postorder numbering of the CFG */
static void compute_postnum_recursive(
- int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
+ int *postnum, uint32_t *cur, const zend_cfg *cfg, int block_num) /* {{{ */
{
- int s;
zend_basic_block *block = &cfg->blocks[block_num];
if (postnum[block_num] != -1) {
return;
}
postnum[block_num] = -2; /* Marker for "currently visiting" */
- for (s = 0; s < block->successors_count; s++) {
+ for (uint32_t s = 0; s < block->successors_count; s++) {
compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
}
postnum[block_num] = (*cur)++;
@@ -676,8 +668,9 @@ static void compute_postnum_recursive(
ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
- int blocks_count = cfg->blocks_count;
- int j, k, changed;
+ uint32_t blocks_count = cfg->blocks_count;
+ uint32_t j;
+ int changed;
if (cfg->blocks_count == 1) {
blocks[0].level = 0;
@@ -701,7 +694,7 @@ ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, ze
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
continue;
}
- for (k = 0; k < blocks[j].predecessors_count; k++) {
+ for (uint32_t k = 0; k < blocks[j].predecessors_count; k++) {
int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
if (blocks[pred].idom >= 0) {
@@ -777,7 +770,7 @@ static bool dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
{
- int i, j, k, n;
+ int i, j, n;
int time;
zend_basic_block *blocks = cfg->blocks;
int *entry_times, *exit_times;
@@ -891,7 +884,7 @@ ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *c
continue;
}
blocks[j].loop_header = i;
- for (k = 0; k < blocks[j].predecessors_count; k++) {
+ for (uint32_t k = 0; k < blocks[j].predecessors_count; k++) {
zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
}
}
diff --git a/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h
index 93d455060686..3ed635ecdedc 100644
--- a/Zend/Optimizer/zend_cfg.h
+++ b/Zend/Optimizer/zend_cfg.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -44,14 +42,14 @@ typedef struct _zend_basic_block {
uint32_t flags;
uint32_t start; /* first opcode number */
uint32_t len; /* number of opcodes */
- int successors_count; /* number of successors */
- int predecessors_count; /* number of predecessors */
- int predecessor_offset; /* offset of 1-st predecessor */
- int idom; /* immediate dominator block */
+ uint32_t successors_count; /* number of successors */
+ uint32_t predecessors_count; /* number of predecessors */
+ int predecessor_offset; /* offset of 1-st predecessor, or -1 */
+ int idom; /* immediate dominator block, or -1 */
int loop_header; /* closest loop header, or -1 */
- int level; /* steps away from the entry in the dom. tree */
- int children; /* list of dominated blocks */
- int next_child; /* next dominated block */
+ int level; /* steps away from the entry in the dom. tree, or -1 */
+ int children; /* list of dominated blocks, or -1 */
+ int next_child; /* next dominated block, or -1 */
int successors_storage[2]; /* up to 2 successor blocks */
} zend_basic_block;
@@ -82,8 +80,8 @@ typedef struct _zend_basic_block {
*/
typedef struct _zend_cfg {
- int blocks_count; /* number of basic blocks */
- int edges_count; /* number of edges */
+ uint32_t blocks_count; /* number of basic blocks */
+ uint32_t edges_count; /* number of edges */
zend_basic_block *blocks; /* array of basic blocks */
int *predecessors;
uint32_t *map;
diff --git a/Zend/Optimizer/zend_dfg.c b/Zend/Optimizer/zend_dfg.c
index 101523141be9..72e15693a844 100644
--- a/Zend/Optimizer/zend_dfg.c
+++ b/Zend/Optimizer/zend_dfg.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, DFG - Data Flow Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -19,7 +17,7 @@
#include "zend_compile.h"
#include "zend_dfg.h"
-static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
+static zend_always_inline void zend_dfg_add_use_def_op_impl(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
{
uint32_t var_num;
const zend_op *next;
@@ -245,20 +243,18 @@ static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
{
- _zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
+ zend_dfg_add_use_def_op_impl(op_array, opline, build_flags, use, def);
}
/* }}} */
-void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
+void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg, uint32_t build_flags) /* {{{ */
{
- int set_size;
+ uint32_t set_size = dfg->size;
zend_basic_block *blocks = cfg->blocks;
- int blocks_count = cfg->blocks_count;
+ uint32_t blocks_count = cfg->blocks_count;
zend_bitset tmp, def, use, in, out;
- int k;
- int j;
+ uint32_t j;
- set_size = dfg->size;
tmp = dfg->tmp;
def = dfg->def;
use = dfg->use;
@@ -267,7 +263,7 @@ void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
/* Collect "def" and "use" sets */
for (j = 0; j < blocks_count; j++) {
- zend_op *opline, *end;
+ const zend_op *opline, *end;
zend_bitset b_use, b_def;
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
@@ -280,7 +276,7 @@ void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
b_def = DFG_BITSET(def, set_size, j);
for (; opline < end; opline++) {
if (opline->opcode != ZEND_OP_DATA) {
- _zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
+ zend_dfg_add_use_def_op_impl(op_array, opline, build_flags, b_use, b_def);
}
}
}
@@ -306,7 +302,7 @@ void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
}
if (blocks[j].successors_count != 0) {
zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
- for (k = 1; k < blocks[j].successors_count; k++) {
+ for (uint32_t k = 1; k < blocks[j].successors_count; k++) {
zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
}
} else {
@@ -318,8 +314,8 @@ void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg
/* Add predecessors of changed block to worklist */
{
- int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
- for (k = 0; k < blocks[j].predecessors_count; k++) {
+ const int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
+ for (uint32_t k = 0; k < blocks[j].predecessors_count; k++) {
zend_bitset_incl(worklist, predecessors[k]);
}
}
diff --git a/Zend/Optimizer/zend_dfg.h b/Zend/Optimizer/zend_dfg.h
index b59dc6279090..464ea6bb2ebe 100644
--- a/Zend/Optimizer/zend_dfg.h
+++ b/Zend/Optimizer/zend_dfg.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, DFG - Data Flow Graph |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -43,7 +41,7 @@ typedef struct _zend_dfg {
BEGIN_EXTERN_C()
-void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
+void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg, uint32_t build_flags);
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def);
END_EXTERN_C()
diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c
index 35830c6e8e7d..c6cc5193b2dc 100644
--- a/Zend/Optimizer/zend_dump.c
+++ b/Zend/Optimizer/zend_dump.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Bytecode Visualisation |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -25,16 +23,16 @@
#include "zend_dump.h"
#include "zend_smart_str.h"
-void zend_dump_ht(HashTable *ht)
+void zend_dump_ht(const HashTable *ht)
{
zend_ulong index;
zend_string *key;
zval *val;
- bool first = 1;
+ bool first = true;
ZEND_HASH_FOREACH_KEY_VAL(ht, index, key, val) {
if (first) {
- first = 0;
+ first = false;
} else {
fprintf(stderr, ", ");
}
@@ -190,38 +188,38 @@ static void zend_dump_range(const zend_ssa_range *r)
}
}
-static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags)
+static void zend_dump_type_info(uint32_t info, const zend_class_entry *ce, bool is_instanceof, uint32_t dump_flags)
{
- bool first = 1;
+ bool first = true;
fprintf(stderr, " [");
if (info & MAY_BE_GUARD) {
fprintf(stderr, "!");
}
if (info & MAY_BE_UNDEF) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "undef");
}
if (info & MAY_BE_INDIRECT) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "ind");
}
if (info & MAY_BE_REF) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "ref");
}
if (dump_flags & ZEND_DUMP_RC_INFERENCE) {
if (info & MAY_BE_RC1) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "rc1");
}
if (info & MAY_BE_RCN) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "rcn");
}
}
if (info & MAY_BE_CLASS) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "class");
if (ce) {
if (is_instanceof) {
@@ -231,37 +229,37 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
}
}
} else if ((info & MAY_BE_ANY) == MAY_BE_ANY) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "any");
} else {
if (info & MAY_BE_NULL) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "null");
}
if ((info & MAY_BE_FALSE) && (info & MAY_BE_TRUE)) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "bool");
} else if (info & MAY_BE_FALSE) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "false");
} else if (info & MAY_BE_TRUE) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "true");
}
if (info & MAY_BE_LONG) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "long");
}
if (info & MAY_BE_DOUBLE) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "double");
}
if (info & MAY_BE_STRING) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "string");
}
if (info & MAY_BE_ARRAY) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
if (info & MAY_BE_PACKED_GUARD) {
fprintf(stderr, "!");
}
@@ -272,18 +270,18 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
} else if (MAY_BE_HASH_ONLY(info)) {
fprintf(stderr, "hash ");
} else if ((info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY && (info & MAY_BE_ARRAY_KEY_ANY) != 0) {
- bool afirst = 1;
+ bool afirst = true;
fprintf(stderr, "[");
if (info & MAY_BE_ARRAY_EMPTY) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "empty");
}
if (MAY_BE_PACKED(info)) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "packed");
}
if (MAY_BE_HASH(info)) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "hash");
}
fprintf(stderr, "] ");
@@ -292,71 +290,71 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
if ((info & (MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING)) != 0 &&
((info & MAY_BE_ARRAY_KEY_LONG) == 0 ||
(info & MAY_BE_ARRAY_KEY_STRING) == 0)) {
- bool afirst = 1;
+ bool afirst = true;
fprintf(stderr, " [");
if (info & MAY_BE_ARRAY_KEY_LONG) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "long");
}
if (info & MAY_BE_ARRAY_KEY_STRING) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "string");
}
fprintf(stderr, "]");
}
if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) {
- bool afirst = 1;
+ bool afirst = true;
fprintf(stderr, " of [");
if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "any");
} else {
if (info & MAY_BE_ARRAY_OF_NULL) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "null");
}
if (info & MAY_BE_ARRAY_OF_FALSE) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "false");
}
if (info & MAY_BE_ARRAY_OF_TRUE) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "true");
}
if (info & MAY_BE_ARRAY_OF_LONG) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "long");
}
if (info & MAY_BE_ARRAY_OF_DOUBLE) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "double");
}
if (info & MAY_BE_ARRAY_OF_STRING) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "string");
}
if (info & MAY_BE_ARRAY_OF_ARRAY) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "array");
}
if (info & MAY_BE_ARRAY_OF_OBJECT) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "object");
}
if (info & MAY_BE_ARRAY_OF_RESOURCE) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "resource");
}
}
if (info & MAY_BE_ARRAY_OF_REF) {
- if (afirst) afirst = 0; else fprintf(stderr, ", ");
+ if (afirst) afirst = false; else fprintf(stderr, ", ");
fprintf(stderr, "ref");
}
fprintf(stderr, "]");
}
}
if (info & MAY_BE_OBJECT) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "object");
if (ce) {
if (is_instanceof) {
@@ -367,7 +365,7 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
}
}
if (info & MAY_BE_RESOURCE) {
- if (first) first = 0; else fprintf(stderr, ", ");
+ if (first) first = false; else fprintf(stderr, ", ");
fprintf(stderr, "resource");
}
}
@@ -482,7 +480,7 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
}
if (ZEND_OP_IS_FRAMELESS_ICALL(opline->opcode)) {
- zend_function *func = ZEND_FLF_FUNC(opline);
+ const zend_function *func = ZEND_FLF_FUNC(opline);
fprintf(stderr, "(%s)", ZSTR_VAL(func->common.function_name));
}
@@ -674,13 +672,13 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
}
if (opline->op2_type == IS_CONST) {
- zval *op = CRT_CONSTANT(opline->op2);
+ const zval *op = CRT_CONSTANT(opline->op2);
if (
opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_SWITCH_STRING
|| opline->opcode == ZEND_MATCH
) {
- HashTable *jumptable = Z_ARRVAL_P(op);
+ const HashTable *jumptable = Z_ARRVAL_P(op);
zend_string *key;
zend_ulong num_key;
zval *zv;
@@ -778,7 +776,7 @@ ZEND_API void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_
{
int len = 0;
const zend_ssa *ssa = NULL;
- zend_ssa_op *ssa_op = NULL;
+ const zend_ssa_op *ssa_op = NULL;
if (dump_flags & ZEND_DUMP_LINE_NUMBERS) {
fprintf(stderr, "L%04u ", opline->lineno);
@@ -800,7 +798,7 @@ ZEND_API void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_
static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
{
- zend_basic_block *b = cfg->blocks + n;
+ const zend_basic_block *b = cfg->blocks + n;
if (n > 0) {
fprintf(stderr, "\n");
@@ -856,8 +854,8 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags
fprintf(stderr, "\n");
if (b->predecessors_count) {
- int *p = cfg->predecessors + b->predecessor_offset;
- int *end = p + b->predecessors_count;
+ const int *p = cfg->predecessors + b->predecessor_offset;
+ const int *end = p + b->predecessors_count;
fprintf(stderr, " ; from=(BB%d", *p);
for (p++; p < end; p++) {
@@ -867,9 +865,8 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags
}
if (b->successors_count > 0) {
- int s;
fprintf(stderr, " ; to=(BB%d", b->successors[0]);
- for (s = 1; s < b->successors_count; s++) {
+ for (uint32_t s = 1; s < b->successors_count; s++) {
fprintf(stderr, ", BB%d", b->successors[s]);
}
fprintf(stderr, ")\n");
@@ -900,16 +897,14 @@ static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_
{
zend_dump_block_info(cfg, n, dump_flags);
if (ssa && ssa->blocks && ssa->blocks[n].phis) {
- zend_ssa_phi *p = ssa->blocks[n].phis;
+ const zend_ssa_phi *p = ssa->blocks[n].phis;
do {
- int j;
-
fprintf(stderr, " ");
zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var, dump_flags);
if (p->pi < 0) {
fprintf(stderr, " = Phi(");
- for (j = 0; j < cfg->blocks[n].predecessors_count; j++) {
+ for (uint32_t j = 0; j < cfg->blocks[n].predecessors_count; j++) {
if (j > 0) {
fprintf(stderr, ", ");
}
@@ -949,7 +944,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
{
const zend_cfg *cfg = NULL;
const zend_ssa *ssa = NULL;
- zend_func_info *func_info = NULL;
+ const zend_func_info *func_info = NULL;
uint32_t func_flags = 0;
if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) {
@@ -1040,11 +1035,8 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
}
if (cfg) {
- int n;
- zend_basic_block *b;
-
- for (n = 0; n < cfg->blocks_count; n++) {
- b = cfg->blocks + n;
+ for (uint32_t n = 0; n < cfg->blocks_count; n++) {
+ const zend_basic_block *b = cfg->blocks + n;
if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) {
const zend_op *opline;
const zend_op *end;
@@ -1060,7 +1052,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
}
if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) {
fprintf(stderr, "LIVE RANGES:\n");
- for (int i = 0; i < op_array->last_live_range; i++) {
+ for (uint32_t i = 0; i < op_array->last_live_range; i++) {
fprintf(stderr,
" %u: %04u - %04u ",
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
@@ -1087,7 +1079,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
}
if (op_array->last_try_catch) {
fprintf(stderr, "EXCEPTION TABLE:\n");
- for (int i = 0; i < op_array->last_try_catch; i++) {
+ for (uint32_t i = 0; i < op_array->last_try_catch; i++) {
fprintf(stderr, " BB%u",
cfg->map[op_array->try_catch_array[i].try_op]);
if (op_array->try_catch_array[i].catch_op) {
@@ -1120,7 +1112,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
}
if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) {
fprintf(stderr, "LIVE RANGES:\n");
- for (int i = 0; i < op_array->last_live_range; i++) {
+ for (uint32_t i = 0; i < op_array->last_live_range; i++) {
fprintf(stderr,
" %u: %04u - %04u ",
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
@@ -1147,7 +1139,7 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
}
if (op_array->last_try_catch) {
fprintf(stderr, "EXCEPTION TABLE:\n");
- for (int i = 0; i < op_array->last_try_catch; i++) {
+ for (uint32_t i = 0; i < op_array->last_try_catch; i++) {
fprintf(stderr,
" %04u",
op_array->try_catch_array[i].try_op);
@@ -1180,13 +1172,11 @@ ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_fl
void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
{
- int j;
-
fprintf(stderr, "\nDOMINATORS-TREE for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
- for (j = 0; j < cfg->blocks_count; j++) {
- zend_basic_block *b = cfg->blocks + j;
+ for (uint32_t j = 0; j < cfg->blocks_count; j++) {
+ const zend_basic_block *b = cfg->blocks + j;
if (b->flags & ZEND_BB_REACHABLE) {
zend_dump_block_info(cfg, j, 0);
}
@@ -1195,14 +1185,12 @@ void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg)
void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags)
{
- int j;
-
if (ssa->vars) {
fprintf(stderr, "\nSSA Variable for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
- for (j = 0; j < ssa->vars_count; j++) {
+ for (int j = 0; j < ssa->vars_count; j++) {
fprintf(stderr, " ");
zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var, dump_flags);
if (ssa->vars[j].scc >= 0) {
@@ -1220,14 +1208,13 @@ void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa,
static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set)
{
- bool first = 1;
- uint32_t i;
+ bool first = true;
fprintf(stderr, " ; %s = {", name);
- for (i = 0; i < op_array->last_var + op_array->T; i++) {
+ for (uint32_t i = 0; i < op_array->last_var + op_array->T; i++) {
if (zend_bitset_in(set, i)) {
if (first) {
- first = 0;
+ first = false;
} else {
fprintf(stderr, ", ");
}
@@ -1239,12 +1226,11 @@ static void zend_dump_var_set(const zend_op_array *op_array, const char *name, z
void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg)
{
- int j;
fprintf(stderr, "\nVariable Liveness for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
- for (j = 0; j < cfg->blocks_count; j++) {
+ for (uint32_t j = 0; j < cfg->blocks_count; j++) {
fprintf(stderr, " BB%d:\n", j);
zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j));
zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j));
@@ -1255,17 +1241,16 @@ void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zen
void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
{
- int j;
- zend_ssa_block *ssa_blocks = ssa->blocks;
- int blocks_count = ssa->cfg.blocks_count;
+ const zend_ssa_block *ssa_blocks = ssa->blocks;
+ uint32_t blocks_count = ssa->cfg.blocks_count;
fprintf(stderr, "\nSSA Phi() Placement for \"");
zend_dump_op_array_name(op_array);
fprintf(stderr, "\"\n");
- for (j = 0; j < blocks_count; j++) {
+ for (uint32_t j = 0; j < blocks_count; j++) {
if (ssa_blocks && ssa_blocks[j].phis) {
- zend_ssa_phi *p = ssa_blocks[j].phis;
- int first = 1;
+ const zend_ssa_phi *p = ssa_blocks[j].phis;
+ bool first = true;
fprintf(stderr, " BB%d:\n", j);
if (p->pi >= 0) {
@@ -1275,7 +1260,7 @@ void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa)
}
do {
if (first) {
- first = 0;
+ first = false;
} else {
fprintf(stderr, ", ");
}
diff --git a/Zend/Optimizer/zend_dump.h b/Zend/Optimizer/zend_dump.h
index db8f762bfc49..45740d9609cb 100644
--- a/Zend/Optimizer/zend_dump.h
+++ b/Zend/Optimizer/zend_dump.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Bytecode Visualisation |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -44,7 +42,7 @@ ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *s
ZEND_API void zend_dump_var(const zend_op_array *op_array, uint8_t var_type, uint32_t var_num);
void zend_dump_op_array_name(const zend_op_array *op_array);
void zend_dump_const(const zval *zv);
-void zend_dump_ht(HashTable *ht);
+void zend_dump_ht(const HashTable *ht);
END_EXTERN_C()
diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c
index 121d2d2cfe57..185fcc40be67 100644
--- a/Zend/Optimizer/zend_func_info.c
+++ b/Zend/Optimizer/zend_func_info.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Func Info |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
| Xinchen Hui |
@@ -24,7 +22,6 @@
#include "zend_inference.h"
#include "zend_call_graph.h"
#include "zend_func_info.h"
-#include "zend_inference.h"
#ifdef _WIN32
#include "win32/ioutil.h"
#endif
@@ -57,7 +54,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa
&& (call_info->num_args == 2 || call_info->num_args == 3)
&& ssa
&& !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
- zend_op_array *op_array = call_info->caller_op_array;
+ const zend_op_array *op_array = call_info->caller_op_array;
uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
ssa->ops ? &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes] : NULL);
uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
@@ -116,7 +113,7 @@ uint32_t zend_get_internal_func_info(
return 0;
}
- func_info_t *info = Z_PTR_P(zv);
+ const func_info_t *info = Z_PTR_P(zv);
if (info->info_func) {
return call_info ? info->info_func(call_info, ssa) : 0;
} else {
@@ -136,7 +133,7 @@ ZEND_API uint32_t zend_get_func_info(
uint32_t ret = 0;
const zend_function *callee_func = call_info->callee_func;
*ce = NULL;
- *ce_is_instanceof = 0;
+ *ce_is_instanceof = false;
if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
uint32_t internal_ret = zend_get_internal_func_info(callee_func, call_info, ssa);
@@ -178,7 +175,7 @@ ZEND_API uint32_t zend_get_func_info(
} else {
if (!call_info->is_prototype) {
// FIXME: the order of functions matters!!!
- zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
+ const zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
if (info) {
ret = info->return_info.type;
*ce = info->return_info.ce;
@@ -198,13 +195,13 @@ ZEND_API uint32_t zend_get_func_info(
return ret;
}
-static void zend_func_info_add(const func_info_t *func_infos, size_t n)
+static void zend_func_info_add(const func_info_t *new_func_infos, size_t n)
{
for (size_t i = 0; i < n; i++) {
- zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1);
+ zend_string *key = zend_string_init_interned(new_func_infos[i].name, new_func_infos[i].name_len, 1);
- if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) {
- fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
+ if (zend_hash_add_ptr(&func_info, key, (void**)&new_func_infos[i]) == NULL) {
+ fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", new_func_infos[i].name);
}
zend_string_release_ex(key, 1);
diff --git a/Zend/Optimizer/zend_func_info.h b/Zend/Optimizer/zend_func_info.h
index db00d843ee10..6b5b51cac58b 100644
--- a/Zend/Optimizer/zend_func_info.h
+++ b/Zend/Optimizer/zend_func_info.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, Func Info |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
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 b2479a57b6e9..2e6cc70ec254 100644
--- a/Zend/Optimizer/zend_inference.c
+++ b/Zend/Optimizer/zend_inference.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, e-SSA based Type & Range Inference |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -266,7 +264,7 @@ typedef struct _zend_scc_iterator {
};
} zend_scc_iterator;
-static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */
+static int zend_scc_next(const zend_op_array *op_array, const zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */
{
zend_ssa_phi *phi;
int use, var2;
@@ -487,14 +485,14 @@ ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /
#endif
-ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
+ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, const zend_ssa *ssa) /* {{{ */
{
zend_ssa_var *ssa_vars = ssa->vars;
- zend_ssa_op *ssa_ops = ssa->ops;
+ const zend_ssa_op *ssa_ops = ssa->ops;
int ssa_vars_count = ssa->vars_count;
zend_bitset worklist;
- int i, j, use;
- zend_ssa_phi *p;
+ int i, use;
+ const zend_ssa_phi *p;
ALLOCA_FLAG(use_heap);
if (!op_array->function_name || !ssa->vars || !ssa->ops) {
@@ -527,7 +525,7 @@ ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, ze
zend_bitset_incl(worklist, p->sources[0]);
}
} else {
- for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ for (uint32_t j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
ZEND_ASSERT(p->sources[j] >= 0);
if (ssa->vars[p->sources[j]].no_val) {
ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
@@ -1063,12 +1061,12 @@ static bool zend_inference_calc_binary_op_range(
case ZEND_BW_XOR:
// TODO
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
return 0;
}
-static bool zend_inference_calc_range(const zend_op_array *op_array, const zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp)
+static bool zend_inference_calc_range(const zend_op_array *op_array, const zend_ssa *ssa, int var, int widening, bool narrowing, zend_ssa_range *tmp)
{
uint32_t line;
const zend_op *opline;
@@ -1076,7 +1074,6 @@ static bool zend_inference_calc_range(const zend_op_array *op_array, const zend_
if (ssa->vars[var].definition_phi) {
const zend_ssa_phi *p = ssa->vars[var].definition_phi;
- int i;
tmp->underflow = 0;
tmp->min = ZEND_LONG_MAX;
@@ -1222,7 +1219,7 @@ static bool zend_inference_calc_range(const zend_op_array *op_array, const zend_
}
}
} else {
- for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
+ for (uint32_t i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
ZEND_ASSERT(p->sources[i] >= 0);
if (ssa->var_info[p->sources[i]].has_range) {
/* union */
@@ -1597,7 +1594,7 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, cons
return 0;
}
-static void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow)
+static void zend_inference_init_range(const zend_op_array *op_array, const zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow)
{
if (underflow) {
min = ZEND_LONG_MIN;
@@ -1645,7 +1642,7 @@ static bool zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_r
return 1;
}
-static bool zend_ssa_range_widening(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+static bool zend_ssa_range_widening(const zend_op_array *op_array, const zend_ssa *ssa, int var, int scc)
{
zend_ssa_range tmp;
@@ -1690,7 +1687,7 @@ static bool zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_
return 1;
}
-static bool zend_ssa_range_narrowing(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc)
+static bool zend_ssa_range_narrowing(const zend_op_array *op_array, const zend_ssa *ssa, int var, int scc)
{
zend_ssa_range tmp;
@@ -1735,7 +1732,7 @@ static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ss
zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len * 2, use_heap);
zend_bitset visited = worklist + worklist_len;
#ifdef NEG_RANGE
- int has_inner_cycles = 0;
+ bool has_inner_cycles = false;
memset(worklist, 0, sizeof(zend_ulong) * worklist_len);
memset(visited, 0, sizeof(zend_ulong) * worklist_len);
@@ -1743,7 +1740,7 @@ static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ss
while (j >= 0) {
if (!zend_bitset_in(visited, j) &&
zend_check_inner_cycles(op_array, ssa, worklist, visited, j)) {
- has_inner_cycles = 1;
+ has_inner_cycles = true;
break;
}
j = next_scc_var[j];
@@ -1862,7 +1859,7 @@ static void zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {
} else if (zend_inference_calc_range(op_array, ssa, j, 0, 1, &tmp)) {
zend_inference_init_range(op_array, ssa, j, tmp.underflow, tmp.min, tmp.max, tmp.overflow);
} else {
- zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ zend_inference_init_range(op_array, ssa, j, true, ZEND_LONG_MIN, ZEND_LONG_MAX, true);
}
} else {
/* Find SCC entry points */
@@ -1897,7 +1894,8 @@ static void zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {
for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) {
if (!ssa->var_info[j].has_range
&& !(ssa->var_info[j].type & MAY_BE_REF)) {
- zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1);
+ zend_inference_init_range(op_array, ssa, j, true, ZEND_LONG_MIN, ZEND_LONG_MAX,
+ true);
FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR);
}
}
@@ -2028,10 +2026,10 @@ static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) {
} \
} while (0)
-static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var)
+static void add_usages(const zend_op_array *op_array, const zend_ssa *ssa, zend_bitset worklist, int var)
{
if (ssa->vars[var].phi_use_chain) {
- zend_ssa_phi *p = ssa->vars[var].phi_use_chain;
+ const zend_ssa_phi *p = ssa->vars[var].phi_use_chain;
do {
zend_bitset_incl(worklist, p->ssa_var);
p = zend_ssa_next_use_phi(ssa, var, p);
@@ -2039,7 +2037,7 @@ static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset
}
if (ssa->vars[var].use_chain >= 0) {
int use = ssa->vars[var].use_chain;
- zend_ssa_op *op;
+ const zend_ssa_op *op;
do {
op = ssa->ops + use;
@@ -2081,7 +2079,7 @@ static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset
}
}
-static void emit_type_narrowing_warning(const zend_op_array *op_array, zend_ssa *ssa, int var)
+static void emit_type_narrowing_warning(const zend_op_array *op_array, const zend_ssa *ssa, int var)
{
int def_op_num = ssa->vars[var].definition;
const zend_op *def_opline = def_op_num >= 0 ? &op_array->opcodes[def_op_num] : NULL;
@@ -2129,7 +2127,7 @@ ZEND_API uint32_t ZEND_FASTCALL zend_array_type_info(const zval *zv)
}
-ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, int write, int insert)
+ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, bool write, bool insert)
{
uint32_t tmp = 0;
@@ -2257,7 +2255,7 @@ static uint32_t assign_dim_result_type(
/* For binary ops that have compound assignment operators */
static uint32_t binary_op_result_type(
- zend_ssa *ssa, uint8_t opcode, uint32_t t1, uint32_t t2, int result_var,
+ const zend_ssa *ssa, uint8_t opcode, uint32_t t1, uint32_t t2, int result_var,
zend_long optimization_level) {
uint32_t tmp = 0;
uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0);
@@ -2350,7 +2348,7 @@ static uint32_t binary_op_result_type(
/* TODO: +MAY_BE_OBJECT ??? */
tmp = MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN;
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
return tmp;
}
@@ -2434,7 +2432,7 @@ static const zend_property_info *lookup_prop_info(const zend_class_entry *ce, ze
return NULL;
}
-static const zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op)
+static const zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op)
{
const zend_property_info *prop_info = NULL;
if (opline->op2_type == IS_CONST) {
@@ -2461,9 +2459,9 @@ static const zend_property_info *zend_fetch_static_prop_info(const zend_script *
{
const zend_property_info *prop_info = NULL;
if (opline->op1_type == IS_CONST) {
- zend_class_entry *ce = NULL;
+ const zend_class_entry *ce = NULL;
if (opline->op2_type == IS_UNUSED) {
- int fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK;
+ uint32_t fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK;
switch (fetch_type) {
case ZEND_FETCH_CLASS_SELF:
case ZEND_FETCH_CLASS_STATIC:
@@ -2478,12 +2476,12 @@ static const zend_property_info *zend_fetch_static_prop_info(const zend_script *
break;
}
} else if (opline->op2_type == IS_CONST) {
- zval *zv = CRT_CONSTANT(opline->op2);
+ const zval *zv = CRT_CONSTANT(opline->op2);
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(zv + 1));
}
if (ce) {
- zval *zv = CRT_CONSTANT(opline->op1);
+ const zval *zv = CRT_CONSTANT(opline->op1);
prop_info = lookup_prop_info(ce, Z_STR_P(zv), op_array->scope);
if (prop_info && !(prop_info->flags & ZEND_ACC_STATIC)) {
prop_info = NULL;
@@ -2505,13 +2503,13 @@ static uint32_t zend_fetch_prop_type(const zend_script *script, const zend_prope
return zend_convert_type(script, prop_info->type, pce);
}
-static bool result_may_be_separated(zend_ssa *ssa, zend_ssa_op *ssa_op)
+static bool result_may_be_separated(const zend_ssa *ssa, const zend_ssa_op *ssa_op)
{
int tmp_var = ssa_op->result_def;
if (ssa->vars[tmp_var].use_chain >= 0
&& !ssa->vars[tmp_var].phi_use_chain) {
- zend_ssa_op *use_op = &ssa->ops[ssa->vars[tmp_var].use_chain];
+ const zend_ssa_op *use_op = &ssa->ops[ssa->vars[tmp_var].use_chain];
/* TODO: analyze instructions between ssa_op and use_op */
if (use_op == ssa_op + 1) {
@@ -3027,7 +3025,7 @@ static zend_always_inline zend_result _zend_update_type_info(
break;
case ZEND_ASSIGN_OBJ:
if (opline->op1_type == IS_CV) {
- zend_class_entry *ce = ssa_var_info[ssa_op->op1_use].ce;
+ const zend_class_entry *ce = ssa_var_info[ssa_op->op1_use].ce;
bool add_rc = (t1 & (MAY_BE_OBJECT|MAY_BE_REF)) && (!ce
|| ce->__set
/* Non-default write_property may be set within create_object. */
@@ -3708,7 +3706,7 @@ static zend_always_inline zend_result _zend_update_type_info(
case ZEND_FREE:
/* This may happen if the using opcode is DCEd. */
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
j = zend_ssa_next_use(ssa->ops, ssa_op->result_def, j);
if (j >= 0) {
@@ -3839,7 +3837,7 @@ static zend_always_inline zend_result _zend_update_type_info(
tmp &= ~MAY_BE_RC1;
}
if (opline->opcode == ZEND_FETCH_STATIC_PROP_IS) {
- tmp |= MAY_BE_UNDEF;
+ tmp |= MAY_BE_NULL;
}
}
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
@@ -3905,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;
@@ -4119,15 +4118,16 @@ ZEND_API zend_result zend_update_type_info(
const zend_op_array *op_array,
zend_ssa *ssa,
const zend_script *script,
- zend_op *opline,
+ const zend_op *opline,
zend_ssa_op *ssa_op,
const zend_op **ssa_opcodes,
zend_long optimization_level)
{
- return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0);
+ return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level,
+ false);
}
-static uint32_t get_class_entry_rank(zend_class_entry *ce) {
+static uint32_t get_class_entry_rank(const zend_class_entry *ce) {
uint32_t rank = 0;
if (ce->ce_flags & ZEND_ACC_LINKED) {
while (ce->parent) {
@@ -4140,7 +4140,7 @@ static uint32_t get_class_entry_rank(zend_class_entry *ce) {
/* Compute least common ancestor on class inheritance tree only */
static zend_class_entry *join_class_entries(
- zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) {
+ zend_class_entry *ce1, zend_class_entry *ce2, bool *is_instanceof) {
uint32_t rank1, rank2;
if (ce1 == ce2) {
return ce1;
@@ -4168,12 +4168,12 @@ static zend_class_entry *join_class_entries(
}
if (ce1) {
- *is_instanceof = 1;
+ *is_instanceof = true;
}
return ce1;
}
-static bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
+static bool safe_instanceof(const zend_class_entry *ce1, const zend_class_entry *ce2) {
if (ce1 == ce2) {
return 1;
}
@@ -4186,11 +4186,11 @@ static bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level)
{
- zend_basic_block *blocks = ssa->cfg.blocks;
+ const zend_basic_block *blocks = ssa->cfg.blocks;
zend_ssa_var *ssa_vars = ssa->vars;
zend_ssa_var_info *ssa_var_info = ssa->var_info;
int ssa_vars_count = ssa->vars_count;
- int i, j;
+ int j;
uint32_t tmp, worklist_len = zend_bitset_len(ssa_vars_count);
bool update_worklist = 1;
const zend_op **ssa_opcodes = NULL;
@@ -4202,11 +4202,11 @@ static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend
zend_ssa_phi *p = ssa_vars[j].definition_phi;
if (p->pi >= 0) {
zend_class_entry *ce = ssa_var_info[p->sources[0]].ce;
- int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
+ bool is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
tmp = get_ssa_var_info(ssa, p->sources[0]);
if (!p->has_range_constraint) {
- zend_ssa_type_constraint *constraint = &p->constraint.type;
+ const zend_ssa_type_constraint *constraint = &p->constraint.type;
tmp &= constraint->type_mask;
if (!(tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
tmp &= ~(MAY_BE_RC1|MAY_BE_RCN);
@@ -4214,7 +4214,7 @@ static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend
if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) {
if (!ce) {
ce = constraint->ce;
- is_instanceof = 1;
+ is_instanceof = true;
} else if (is_instanceof && safe_instanceof(constraint->ce, ce)) {
ce = constraint->ce;
} else {
@@ -4231,9 +4231,10 @@ static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend
UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j);
}
} else {
- int first = 1;
- int is_instanceof = 0;
+ bool first = true;
+ bool is_instanceof = false;
zend_class_entry *ce = NULL;
+ uint32_t i;
tmp = 0;
for (i = 0; i < blocks[p->block].predecessors_count; i++) {
@@ -4249,7 +4250,7 @@ static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend
if (first) {
ce = info->ce;
is_instanceof = info->is_instanceof;
- first = 0;
+ first = false;
} else {
is_instanceof |= info->is_instanceof;
ce = join_class_entries(ce, info->ce, &is_instanceof);
@@ -4259,8 +4260,8 @@ static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend
UPDATE_SSA_OBJ_TYPE(ce, ce ? is_instanceof : 0, j);
}
} else if (ssa_vars[j].definition >= 0) {
- i = ssa_vars[j].definition;
- if (_zend_update_type_info(op_array, ssa, script, worklist, op_array->opcodes + i, ssa->ops + i, NULL, optimization_level, 1) == FAILURE) {
+ int i = ssa_vars[j].definition;
+ if (_zend_update_type_info(op_array, ssa, script, worklist, op_array->opcodes + i, ssa->ops + i, NULL, optimization_level, true) == FAILURE) {
return FAILURE;
}
}
@@ -4268,18 +4269,18 @@ static zend_result zend_infer_types_ex(const zend_op_array *op_array, const zend
return SUCCESS;
}
-static bool is_narrowable_instr(zend_op *opline) {
+static bool is_narrowable_instr(const zend_op *opline) {
return opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB
|| opline->opcode == ZEND_MUL || opline->opcode == ZEND_DIV;
}
-static bool is_effective_op1_double_cast(zend_op *opline, zval *op2) {
+static bool is_effective_op1_double_cast(const zend_op *opline, const zval *op2) {
return (opline->opcode == ZEND_ADD && Z_LVAL_P(op2) == 0)
|| (opline->opcode == ZEND_SUB && Z_LVAL_P(op2) == 0)
|| (opline->opcode == ZEND_MUL && Z_LVAL_P(op2) == 1)
|| (opline->opcode == ZEND_DIV && Z_LVAL_P(op2) == 1);
}
-static bool is_effective_op2_double_cast(zend_op *opline, zval *op1) {
+static bool is_effective_op2_double_cast(const zend_op *opline, const zval *op1) {
/* In PHP it holds that (double)(0-$int) is bitwise identical to 0.0-(double)$int,
* so allowing SUB here is fine. */
return (opline->opcode == ZEND_ADD && Z_LVAL_P(op1) == 0)
@@ -4491,19 +4492,18 @@ static zend_result zend_type_narrowing(const zend_op_array *op_array, const zend
return SUCCESS;
}
-static bool is_recursive_tail_call(const zend_op_array *op_array,
- zend_op *opline)
+static bool is_recursive_tail_call(const zend_op_array *op_array, const zend_op *opline)
{
- zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ const zend_func_info *info = ZEND_FUNC_INFO(op_array);
if (info->ssa.ops && info->ssa.vars && info->call_map &&
info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 &&
info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) {
- zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition;
+ const zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition;
if (op->opcode == ZEND_DO_UCALL) {
- zend_call_info *call_info = info->call_map[op - op_array->opcodes];
+ const zend_call_info *call_info = info->call_map[op - op_array->opcodes];
if (call_info && op_array == &call_info->callee_func->op_array) {
return 1;
}
@@ -4519,7 +4519,7 @@ uint32_t zend_get_return_info_from_signature_only(
if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE &&
(use_tentative_return_info || !ZEND_ARG_TYPE_IS_TENTATIVE(func->common.arg_info - 1))
) {
- zend_arg_info *ret_info = func->common.arg_info - 1;
+ const zend_arg_info *ret_info = func->common.arg_info - 1;
type = zend_fetch_arg_info_type(script, ret_info, ce);
*ce_is_instanceof = ce != NULL;
} else {
@@ -4534,7 +4534,7 @@ uint32_t zend_get_return_info_from_signature_only(
&& !(func->common.fn_flags & ZEND_ACC_GENERATOR)) {
type |= MAY_BE_REF;
*ce = NULL;
- *ce_is_instanceof = 0;
+ *ce_is_instanceof = false;
}
return type;
}
@@ -4547,7 +4547,7 @@ ZEND_API void zend_init_func_return_info(
zend_ssa_range tmp_range = {0, 0, 0, 0};
bool is_instanceof = false;
ret->type = zend_get_return_info_from_signature_only(
- (zend_function *) op_array, script, &ret->ce, &is_instanceof, /* use_tentative_return_info */ 1);
+ (zend_function *) op_array, script, &ret->ce, &is_instanceof, /* use_tentative_return_info */ true);
ret->is_instanceof = is_instanceof;
ret->range = tmp_range;
ret->has_range = 0;
@@ -4555,21 +4555,20 @@ ZEND_API void zend_init_func_return_info(
static void zend_func_return_info(const zend_op_array *op_array,
const zend_script *script,
- int recursive,
- int widening,
+ bool recursive,
+ bool widening,
zend_ssa_var_info *ret)
{
- zend_func_info *info = ZEND_FUNC_INFO(op_array);
- zend_ssa *ssa = &info->ssa;
- int blocks_count = info->ssa.cfg.blocks_count;
- zend_basic_block *blocks = info->ssa.cfg.blocks;
- int j;
+ const zend_func_info *info = ZEND_FUNC_INFO(op_array);
+ const zend_ssa *ssa = &info->ssa;
+ uint32_t blocks_count = info->ssa.cfg.blocks_count;
+ const zend_basic_block *blocks = info->ssa.cfg.blocks;
uint32_t t1;
uint32_t tmp = 0;
zend_class_entry *tmp_ce = NULL;
int tmp_is_instanceof = -1;
zend_class_entry *arg_ce;
- int arg_is_instanceof;
+ bool arg_is_instanceof;
zend_ssa_range tmp_range = {0, 0, 0, 0};
int tmp_has_range = -1;
@@ -4588,12 +4587,12 @@ static void zend_func_return_info(const zend_op_array *op_array,
| MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF;
}
- for (j = 0; j < blocks_count; j++) {
+ for (uint32_t j = 0; j < blocks_count; j++) {
if ((blocks[j].flags & ZEND_BB_REACHABLE) && blocks[j].len != 0) {
zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) {
- zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL;
+ const zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL;
if (!recursive && ssa_op && info->ssa.var_info &&
ssa_op->op1_use >= 0 &&
info->ssa.var_info[ssa_op->op1_use].recursive) {
@@ -4624,7 +4623,7 @@ static void zend_func_return_info(const zend_op_array *op_array,
arg_is_instanceof = info->ssa.var_info[ssa_op->op1_use].is_instanceof;
} else {
arg_ce = NULL;
- arg_is_instanceof = 0;
+ arg_is_instanceof = false;
}
if (tmp_is_instanceof < 0) {
@@ -4640,7 +4639,7 @@ static void zend_func_return_info(const zend_op_array *op_array,
}
if (opline->op1_type == IS_CONST) {
- zval *zv = CRT_CONSTANT(opline->op1);
+ const zval *zv = CRT_CONSTANT(opline->op1);
if (Z_TYPE_P(zv) == IS_LONG) {
if (tmp_has_range < 0) {
@@ -4743,7 +4742,7 @@ static zend_result zend_infer_types(const zend_op_array *op_array, const zend_sc
return SUCCESS;
}
-static void zend_mark_cv_references(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa)
+static void zend_mark_cv_references(const zend_op_array *op_array, const zend_script *script, const zend_ssa *ssa)
{
int var, def;
const zend_op *opline;
@@ -5294,10 +5293,10 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
return (t1 & MAY_BE_OBJECT);
case IS_OBJECT:
return 0;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
/* GCC is getting confused here for the Wimplicit-fallthrough warning with
- * EMPTY_SWITCH_DEFAULT_CASE() macro */
+ * default: ZEND_UNREACHABLE(); macro */
return 0;
case ZEND_ARRAY_KEY_EXISTS:
if ((t2 & MAY_BE_ANY) != MAY_BE_ARRAY) {
@@ -5335,6 +5334,13 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
return 1;
}
return 0;
+ case ZEND_YIELD_FROM: {
+ uint32_t t1 = OP1_INFO();
+ if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY && MAY_BE_EMPTY_ONLY(t1)) {
+ return false;
+ }
+ return true;
+ }
default:
return 1;
}
diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h
index 666abc586592..e6b4207c7494 100644
--- a/Zend/Optimizer/zend_inference.h
+++ b/Zend/Optimizer/zend_inference.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, e-SSA based Type & Range Inference |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -217,11 +215,11 @@ static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b)
BEGIN_EXTERN_C()
-ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
+ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, const zend_ssa *ssa);
ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
ZEND_API zend_result zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level);
-ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, int write, int insert);
+ZEND_API uint32_t zend_array_element_type(uint32_t t1, uint8_t op_type, bool write, bool insert);
ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp);
@@ -238,7 +236,7 @@ ZEND_API bool zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, c
ZEND_API zend_result zend_update_type_info(
const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script,
- zend_op *opline, zend_ssa_op *ssa_op, const zend_op **ssa_opcodes,
+ const zend_op *opline, zend_ssa_op *ssa_op, const zend_op **ssa_opcodes,
zend_long optimization_level);
END_EXTERN_C()
diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c
index b5de66331460..0173d2ef47a2 100644
--- a/Zend/Optimizer/zend_optimizer.c
+++ b/Zend/Optimizer/zend_optimizer.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -41,7 +39,7 @@ struct {
int last;
} zend_optimizer_registered_passes = {{NULL}, 0};
-void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
+void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, const zval *name, zval* value)
{
if (!ctx->constants) {
ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
@@ -103,12 +101,10 @@ zend_result zend_optimizer_eval_strlen(zval *result, const zval *op1) /* {{{ */
/* }}} */
zend_result zend_optimizer_eval_special_func_call(
- zval *result, zend_string *name, zend_string *arg) {
+ zval *result, const zend_string *name, zend_string *arg) {
if (zend_string_equals_literal(name, "function_exists") ||
zend_string_equals_literal(name, "is_callable")) {
- zend_string *lc_name = zend_string_tolower(arg);
- zend_internal_function *func = zend_hash_find_ptr(EG(function_table), lc_name);
- zend_string_release_ex(lc_name, 0);
+ const zend_internal_function *func = zend_hash_find_ptr_lc(EG(function_table), arg);
if (func && func->type == ZEND_INTERNAL_FUNCTION
&& func->module->type == MODULE_PERSISTENT
@@ -122,9 +118,7 @@ zend_result zend_optimizer_eval_special_func_call(
return FAILURE;
}
if (zend_string_equals_literal(name, "extension_loaded")) {
- zend_string *lc_name = zend_string_tolower(arg);
- zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name);
- zend_string_release_ex(lc_name, 0);
+ zend_module_entry *m = zend_hash_find_ptr_lc(&module_registry, arg);
if (!m) {
if (PG(enable_dl)) {
@@ -145,7 +139,7 @@ zend_result zend_optimizer_eval_special_func_call(
return FAILURE;
}
if (zend_string_equals_literal(name, "constant")) {
- return zend_optimizer_get_persistent_constant(arg, result, 1) ? SUCCESS : FAILURE;
+ return zend_optimizer_get_persistent_constant(arg, result, true) ? SUCCESS : FAILURE;
}
if (zend_string_equals_literal(name, "dirname")) {
if (!IS_ABSOLUTE_PATH(ZSTR_VAL(arg), ZSTR_LEN(arg))) {
@@ -180,18 +174,18 @@ zend_result zend_optimizer_eval_special_func_call(
return FAILURE;
}
-bool zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
+bool zend_optimizer_get_collected_constant(const HashTable *constants, const zval *name, zval* value)
{
zval *val;
if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
ZVAL_COPY(value, val);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
-void zend_optimizer_convert_to_free_op1(zend_op_array *op_array, zend_op *opline)
+void zend_optimizer_convert_to_free_op1(const zend_op_array *op_array, zend_op *opline)
{
if (opline->op1_type == IS_CV) {
opline->opcode = ZEND_CHECK_VAR;
@@ -210,9 +204,9 @@ void zend_optimizer_convert_to_free_op1(zend_op_array *op_array, zend_op *opline
}
}
-int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv)
+uint32_t zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv)
{
- int i = op_array->last_literal;
+ uint32_t i = op_array->last_literal;
op_array->last_literal++;
op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
ZVAL_COPY_VALUE(&op_array->literals[i], zv);
@@ -220,7 +214,7 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv)
return i;
}
-static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
+static inline uint32_t zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
zval zv;
ZVAL_STR(&zv, str);
zend_string_hash_val(str);
@@ -263,7 +257,7 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
switch ((opline-1)->opcode) {
case ZEND_ASSIGN_OBJ_REF:
case ZEND_ASSIGN_STATIC_PROP_REF:
- return 0;
+ return false;
}
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
break;
@@ -271,7 +265,7 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_CHECK_VAR:
MAKE_NOP(opline);
zval_ptr_dtor_nogc(val);
- return 1;
+ return true;
case ZEND_SEND_VAR_EX:
case ZEND_SEND_FUNC_ARG:
case ZEND_FETCH_DIM_W:
@@ -286,7 +280,7 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_SEPARATE:
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_VAR_NO_REF_EX:
- return 0;
+ return false;
case ZEND_CATCH:
REQUIRES_STRING(val);
drop_leading_backslash(val);
@@ -368,10 +362,10 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
case ZEND_VERIFY_RETURN_TYPE:
/* This would require a non-local change.
* zend_optimizer_replace_by_const() supports this. */
- return 0;
+ return false;
case ZEND_COPY_TMP:
case ZEND_FETCH_CLASS_NAME:
- return 0;
+ return false;
case ZEND_ECHO:
{
zval zv;
@@ -382,7 +376,7 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
opline->op1.constant = zend_optimizer_add_literal(op_array, val);
if (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0) {
MAKE_NOP(opline);
- return 1;
+ return true;
}
/* TODO: In a subsequent pass, *after* this step and compacting nops, combine consecutive ZEND_ECHOs using the block information from ssa->cfg */
/* (e.g. for ext/opcache/tests/opt/sccp_010.phpt) */
@@ -412,7 +406,7 @@ bool zend_optimizer_update_op1_const(zend_op_array *op_array,
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
}
- return 1;
+ return true;
}
bool zend_optimizer_update_op2_const(zend_op_array *op_array,
@@ -424,7 +418,7 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array,
switch (opline->opcode) {
case ZEND_ASSIGN_REF:
case ZEND_FAST_CALL:
- return 0;
+ return false;
case ZEND_FETCH_CLASS:
case ZEND_INSTANCEOF:
REQUIRES_STRING(val);
@@ -478,13 +472,13 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array,
case ZEND_INIT_DYNAMIC_CALL:
if (Z_TYPE_P(val) == IS_STRING) {
if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
- return 0;
+ return false;
}
if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
/* Dynamic call to various special functions must stay dynamic,
* otherwise would drop a warning */
- return 0;
+ return false;
}
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
@@ -594,7 +588,7 @@ bool zend_optimizer_update_op2_const(zend_op_array *op_array,
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
}
- return 1;
+ return true;
}
bool zend_optimizer_replace_by_const(zend_op_array *op_array,
@@ -603,7 +597,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
uint32_t var,
zval *val)
{
- zend_op *end = op_array->opcodes + op_array->last;
+ const zend_op *end = op_array->opcodes + op_array->last;
while (opline < end) {
if (opline->op1_type == type &&
@@ -622,7 +616,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
case ZEND_MATCH:
case ZEND_MATCH_ERROR:
case ZEND_JMP_NULL: {
- zend_op *end = op_array->opcodes + op_array->last;
+ const zend_op *end = op_array->opcodes + op_array->last;
while (opline < end) {
if (opline->op1_type == type && opline->op1.var == var) {
/* If this opcode doesn't keep the operand alive, we're done. Check
@@ -641,7 +635,7 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
Z_TRY_ADDREF_P(val);
if (!zend_optimizer_update_op1_const(op_array, opline, val)) {
zval_ptr_dtor(val);
- return 0;
+ return false;
}
if (is_last) {
break;
@@ -650,13 +644,13 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
opline++;
}
zval_ptr_dtor_nogc(val);
- return 1;
+ return true;
}
case ZEND_VERIFY_RETURN_TYPE: {
- zend_arg_info *ret_info = op_array->arg_info - 1;
+ const zend_arg_info *ret_info = op_array->arg_info - 1;
if (!ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val))
|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
- return 0;
+ return false;
}
MAKE_NOP(opline);
@@ -681,11 +675,11 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
opline++;
}
- return 1;
+ return true;
}
/* Update jump offsets after a jump was migrated to another opline */
-void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
+void zend_optimizer_migrate_jump(const zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
switch (new_opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
@@ -718,7 +712,7 @@ void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, z
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ const HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
@@ -730,7 +724,7 @@ void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, z
}
/* Shift jump offsets based on shiftlist */
-void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
+void zend_optimizer_shift_jump(const zend_op_array *op_array, zend_op *opline, const uint32_t *shiftlist) {
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
@@ -763,7 +757,7 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
- HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
+ const HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]);
@@ -774,15 +768,15 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_
}
}
-static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename)
+static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename)
{
- zend_class_entry *ce = Z_PTR_P(ce_zv);
+ const zend_class_entry *ce = Z_PTR_P(ce_zv);
if (ce->ce_flags & ZEND_ACC_PRELOADED) {
if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) {
return true;
}
- Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val));
+ const Bucket *ce_bucket = ZEND_CONTAINER_OF(ce_zv, Bucket, val);
size_t offset = ce_bucket - EG(class_table)->arData;
if (offset < EG(persistent_classes_count)) {
return false;
@@ -792,9 +786,9 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename)
&& (!ce->info.user.filename || ce->info.user.filename != filename);
}
-static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename)
+static bool zend_optimizer_ignore_function(zval *fbc_zv, const zend_string *filename)
{
- zend_function *fbc = Z_PTR_P(fbc_zv);
+ const zend_function *fbc = Z_PTR_P(fbc_zv);
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
return false;
@@ -803,7 +797,7 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename)
if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) {
return true;
}
- Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val));
+ const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val);
size_t offset = fbc_bucket - EG(function_table)->arData;
if (offset < EG(persistent_functions_count)) {
return false;
@@ -838,7 +832,7 @@ zend_class_entry *zend_optimizer_get_class_entry(
zend_class_entry *zend_optimizer_get_class_entry_from_op1(
const zend_script *script, const zend_op_array *op_array, const zend_op *opline) {
if (opline->op1_type == IS_CONST) {
- zval *op1 = CRT_CONSTANT(opline->op1);
+ const zval *op1 = CRT_CONSTANT(opline->op1);
if (Z_TYPE_P(op1) == IS_STRING) {
return zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1));
}
@@ -861,7 +855,7 @@ const zend_class_constant *zend_fetch_class_const_info(
return NULL;
}
if (opline->op1_type == IS_CONST) {
- zval *op1 = CRT_CONSTANT(opline->op1);
+ const zval *op1 = CRT_CONSTANT(opline->op1);
if (Z_TYPE_P(op1) == IS_STRING) {
if (script) {
ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1));
@@ -875,7 +869,7 @@ const zend_class_constant *zend_fetch_class_const_info(
} else if (opline->op1_type == IS_UNUSED
&& op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
&& !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
- int fetch_type = opline->op1.num & ZEND_FETCH_CLASS_MASK;
+ uint32_t fetch_type = opline->op1.num & ZEND_FETCH_CLASS_MASK;
if (fetch_type == ZEND_FETCH_CLASS_SELF) {
ce = op_array->scope;
} else if (fetch_type == ZEND_FETCH_CLASS_STATIC) {
@@ -905,9 +899,9 @@ const zend_class_constant *zend_fetch_class_const_info(
}
zend_function *zend_optimizer_get_called_func(
- zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype)
+ const zend_script *script, const zend_op_array *op_array, zend_op *opline, bool *is_prototype)
{
- *is_prototype = 0;
+ *is_prototype = false;
switch (opline->opcode) {
case ZEND_INIT_FCALL:
{
@@ -926,7 +920,7 @@ zend_function *zend_optimizer_get_called_func(
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
- zval *function_name = CRT_CONSTANT(opline->op2) + 1;
+ const zval *function_name = CRT_CONSTANT(opline->op2) + 1;
zend_function *func;
zval *func_zv;
if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
@@ -940,7 +934,7 @@ zend_function *zend_optimizer_get_called_func(
break;
case ZEND_INIT_STATIC_METHOD_CALL:
if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
- zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
+ const zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
script, op_array, opline);
if (ce) {
zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
@@ -984,13 +978,13 @@ zend_function *zend_optimizer_get_called_func(
}
break;
case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: {
- zend_class_entry *scope = op_array->scope;
+ const zend_class_entry *scope = op_array->scope;
ZEND_ASSERT(scope != NULL);
if ((scope->ce_flags & ZEND_ACC_LINKED) && scope->parent) {
- zend_class_entry *parent_scope = scope->parent;
+ const zend_class_entry *parent_scope = scope->parent;
zend_string *prop_name = Z_STR_P(CRT_CONSTANT(opline->op1));
zend_property_hook_kind hook_kind = opline->op2.num;
- zend_property_info *prop_info = zend_get_property_info(parent_scope, prop_name, /* silent */ true);
+ const zend_property_info *prop_info = zend_get_property_info(parent_scope, prop_name, /* silent */ true);
if (prop_info
&& prop_info != ZEND_WRONG_PROPERTY_INFO
@@ -1007,7 +1001,7 @@ zend_function *zend_optimizer_get_called_func(
}
case ZEND_NEW:
{
- zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
+ const zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
script, op_array, opline);
if (ce && ce->type == ZEND_USER_CLASS) {
return ce->constructor;
@@ -1018,7 +1012,7 @@ zend_function *zend_optimizer_get_called_func(
return NULL;
}
-uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
+uint32_t zend_optimizer_classify_function(const zend_string *name, uint32_t num_args) {
if (zend_string_equals_literal(name, "extract")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "compact")) {
@@ -1170,12 +1164,12 @@ static void zend_optimize(zend_op_array *op_array,
static void zend_revert_pass_two(zend_op_array *op_array)
{
- zend_op *opline, *end;
+ zend_op *opline;
ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0);
opline = op_array->opcodes;
- end = opline + op_array->last;
+ const zend_op *end = opline + op_array->last;
while (opline < end) {
if (opline->op1_type == IS_CONST) {
ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1);
@@ -1308,7 +1302,7 @@ static void zend_redo_pass_two(zend_op_array *op_array)
op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
}
-static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
+static void zend_redo_pass_two_ex(zend_op_array *op_array, const zend_ssa *ssa)
{
zend_op *opline, *end;
#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
@@ -1337,7 +1331,7 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
- zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
+ const zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
uint32_t res_info =
@@ -1467,13 +1461,13 @@ static void zend_optimize_op_array(zend_op_array *op_array,
}
}
-static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
+static void zend_adjust_fcall_stack_size(const zend_op_array *op_array, const zend_optimizer_ctx *ctx)
{
zend_function *func;
- zend_op *opline, *end;
+ zend_op *opline;
opline = op_array->opcodes;
- end = opline + op_array->last;
+ const zend_op* end = opline + op_array->last;
while (opline < end) {
if (opline->opcode == ZEND_INIT_FCALL) {
func = zend_hash_find_ptr(
@@ -1487,12 +1481,12 @@ static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer
}
}
-static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
+static void zend_adjust_fcall_stack_size_graph(const zend_op_array *op_array)
{
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ const zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
if (func_info) {
- zend_call_info *call_info =func_info->callee_info;
+ const zend_call_info *call_info =func_info->callee_info;
while (call_info) {
zend_op *opline = call_info->caller_init_opline;
@@ -1506,13 +1500,13 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
}
}
-static bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) {
- zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
- zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes];
+static bool needs_live_range(const zend_op_array *op_array, const zend_op *def_opline) {
+ const zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
+ const zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes];
int ssa_var = ssa_op->result_def;
if (ssa_var < 0) {
/* Be conservative. */
- return 1;
+ return true;
}
/* If the variable is used by a PHI, this may be the assignment of the final branch of a
@@ -1549,7 +1543,7 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
continue;
}
- zend_class_entry *ce = Z_CE_P(zv);
+ const zend_class_entry *ce = Z_CE_P(zv);
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
if (op_array->scope == ce
&& op_array->type == ZEND_USER_FUNCTION
@@ -1564,7 +1558,7 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
zend_function **hooks = property->hooks;
if (property->ce == ce && property->hooks) {
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
- zend_function *hook = hooks[i];
+ const zend_function *hook = hooks[i];
if (hook && hook->common.scope == ce && !(hooks[i]->op_array.fn_flags & ZEND_ACC_TRAIT_CLONE)) {
zend_foreach_op_array_helper(&hooks[i]->op_array, func, context);
}
@@ -1615,7 +1609,7 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
zend_call_graph call_graph;
zend_build_call_graph(&ctx.arena, script, &call_graph);
- int i;
+ uint32_t i;
zend_func_info *func_info;
for (i = 0; i < call_graph.op_arrays_count; i++) {
@@ -1724,20 +1718,22 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
continue;
}
- zend_class_entry *ce = Z_CE_P(zv);
+ const zend_class_entry *ce = Z_CE_P(zv);
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
- zend_op_array *orig_op_array =
+ const zend_op_array *orig_op_array =
zend_hash_find_ptr(&op_array->scope->function_table, name);
ZEND_ASSERT(orig_op_array != NULL);
if (orig_op_array != op_array) {
uint32_t fn_flags = op_array->fn_flags;
+ uint32_t fn_flags2 = op_array->fn_flags2;
zend_function *prototype = op_array->prototype;
HashTable *ht = op_array->static_variables;
*op_array = *orig_op_array;
op_array->fn_flags = fn_flags;
+ op_array->fn_flags2 = fn_flags2;
op_array->prototype = prototype;
op_array->static_variables = ht;
}
diff --git a/Zend/Optimizer/zend_optimizer.h b/Zend/Optimizer/zend_optimizer.h
index 43a0f60a2321..d2847c92869c 100644
--- a/Zend/Optimizer/zend_optimizer.h
+++ b/Zend/Optimizer/zend_optimizer.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
diff --git a/Zend/Optimizer/zend_optimizer_internal.h b/Zend/Optimizer/zend_optimizer_internal.h
index 896fe8fb4728..d01df56260bc 100644
--- a/Zend/Optimizer/zend_optimizer_internal.h
+++ b/Zend/Optimizer/zend_optimizer_internal.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -78,17 +76,17 @@ static inline bool zend_optimizer_is_loop_var_free(const zend_op *opline) {
|| (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH);
}
-void zend_optimizer_convert_to_free_op1(zend_op_array *op_array, zend_op *opline);
-int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv);
-bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy);
-void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value);
-bool zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value);
+void zend_optimizer_convert_to_free_op1(const zend_op_array *op_array, zend_op *opline);
+uint32_t zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv);
+bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, bool copy);
+void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, const zval *name, zval* value);
+bool zend_optimizer_get_collected_constant(const HashTable *constants, const zval *name, zval* value);
zend_result zend_optimizer_eval_binary_op(zval *result, uint8_t opcode, zval *op1, zval *op2);
zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1);
zend_result zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1);
zend_result zend_optimizer_eval_strlen(zval *result, const zval *op1);
zend_result zend_optimizer_eval_special_func_call(
- zval *result, zend_string *name, zend_string *arg);
+ zval *result, const zend_string *name, zend_string *arg);
bool zend_optimizer_update_op1_const(zend_op_array *op_array,
zend_op *opline,
zval *val);
@@ -120,11 +118,11 @@ void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_compact_vars(zend_op_array *op_array);
zend_function *zend_optimizer_get_called_func(
- zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype);
-uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
-void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
-void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);
-int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map);
+ const zend_script *script, const zend_op_array *op_array, zend_op *opline, bool *is_prototype);
+uint32_t zend_optimizer_classify_function(const zend_string *name, uint32_t num_args);
+void zend_optimizer_migrate_jump(const zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
+void zend_optimizer_shift_jump(const zend_op_array *op_array, zend_op *opline, const uint32_t *shiftlist);
+uint32_t sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map);
int dce_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *optimizer_ctx, zend_ssa *ssa, bool reorder_dtor_effects);
zend_result zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa);
diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c
index 886bb467b444..fa2901b89421 100644
--- a/Zend/Optimizer/zend_ssa.c
+++ b/Zend/Optimizer/zend_ssa.c
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, SSA - Static Single Assignment Form |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
| Nikita Popov |
@@ -35,8 +33,7 @@ static bool dominates(const zend_basic_block *blocks, int a, int b) {
static bool will_rejoin(
const zend_cfg *cfg, const zend_dfg *dfg, const zend_basic_block *block,
int other_successor, int exclude, int var) {
- int i;
- for (i = 0; i < block->predecessors_count; i++) {
+ for (uint32_t i = 0; i < block->predecessors_count; i++) {
int predecessor = cfg->predecessors[block->predecessor_offset + i];
if (predecessor == exclude) {
continue;
@@ -52,10 +49,10 @@ static bool will_rejoin(
/* The other successor dominates this predecessor,
* so we will get the original value from it. */
if (dominates(cfg->blocks, other_successor, predecessor)) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
static bool needs_pi(const zend_op_array *op_array, const zend_dfg *dfg, const zend_ssa *ssa, int from, int to, int var) /* {{{ */
@@ -65,7 +62,7 @@ static bool needs_pi(const zend_op_array *op_array, const zend_dfg *dfg, const z
if (!DFG_ISSET(dfg->in, dfg->size, to, var)) {
/* Variable is not live, certainly won't benefit from pi */
- return 0;
+ return false;
}
/* Make sure that both successors of the from block aren't the same. Pi nodes are associated
@@ -73,13 +70,13 @@ static bool needs_pi(const zend_op_array *op_array, const zend_dfg *dfg, const z
from_block = &ssa->cfg.blocks[from];
ZEND_ASSERT(from_block->successors_count == 2);
if (from_block->successors[0] == from_block->successors[1]) {
- return 0;
+ return false;
}
to_block = &ssa->cfg.blocks[to];
if (to_block->predecessors_count == 1) {
/* Always place pi if one predecessor (an if branch) */
- return 1;
+ return true;
}
/* Check whether we will rejoin with the original value coming from the other successor,
@@ -91,7 +88,7 @@ static bool needs_pi(const zend_op_array *op_array, const zend_dfg *dfg, const z
/* }}} */
static zend_ssa_phi *add_pi(
- zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa,
+ zend_arena **arena, const zend_op_array *op_array, const zend_dfg *dfg, const zend_ssa *ssa,
int from, int to, int var) /* {{{ */
{
zend_ssa_phi *phi;
@@ -131,7 +128,7 @@ static zend_ssa_phi *add_pi(
static void pi_range(
zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max,
- char underflow, char overflow, char negative) /* {{{ */
+ bool underflow, bool overflow, bool negative) /* {{{ */
{
zend_ssa_range_constraint *constraint = &phi->constraint.range;
constraint->min_var = min_var;
@@ -148,16 +145,16 @@ static void pi_range(
/* }}} */
static inline void pi_range_equals(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, var, var, val, val, 0, 0, 0);
+ pi_range(phi, var, var, val, val, false, false, false);
}
static inline void pi_range_not_equals(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, var, var, val, val, 0, 0, 1);
+ pi_range(phi, var, var, val, val, false, false, true);
}
static inline void pi_range_min(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, var, -1, val, ZEND_LONG_MAX, 0, 1, 0);
+ pi_range(phi, var, -1, val, ZEND_LONG_MAX, false, true, false);
}
static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) {
- pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0);
+ pi_range(phi, -1, var, ZEND_LONG_MIN, val, true, false, false);
}
static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) {
@@ -241,10 +238,10 @@ static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_f
*/
static void place_essa_pis(
zend_arena **arena, const zend_script *script, const zend_op_array *op_array,
- uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ {
- zend_basic_block *blocks = ssa->cfg.blocks;
- int j, blocks_count = ssa->cfg.blocks_count;
- for (j = 0; j < blocks_count; j++) {
+ uint32_t build_flags, const zend_ssa *ssa, const zend_dfg *dfg) /* {{{ */ {
+ const zend_basic_block *blocks = ssa->cfg.blocks;
+ uint32_t blocks_count = ssa->cfg.blocks_count;
+ for (uint32_t j = 0; j < blocks_count; j++) {
zend_ssa_phi *pi;
zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
int bt; /* successor block number if a condition is true */
@@ -819,12 +816,11 @@ ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *op
static void zend_ssa_rename_in_block(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */
{
- zend_basic_block *blocks = ssa->cfg.blocks;
- zend_ssa_block *ssa_blocks = ssa->blocks;
+ const zend_basic_block *blocks = ssa->cfg.blocks;
+ const zend_ssa_block *ssa_blocks = ssa->blocks;
zend_ssa_op *ssa_ops = ssa->ops;
int ssa_vars_count = ssa->vars_count;
- int i, j;
- zend_op *opline, *end;
+ const zend_op *opline, *end;
if (ssa_blocks[n].phis) {
zend_ssa_phi *phi = ssa_blocks[n].phis;
@@ -849,11 +845,11 @@ static void zend_ssa_rename_in_block(const zend_op_array *op_array, uint32_t bui
}
}
- zend_ssa_op *fe_fetch_ssa_op = blocks[n].len != 0
+ const zend_ssa_op *fe_fetch_ssa_op = blocks[n].len != 0
&& ((end-1)->opcode == ZEND_FE_FETCH_R || (end-1)->opcode == ZEND_FE_FETCH_RW)
&& (end-1)->op2_type == IS_CV
? &ssa_ops[blocks[n].start + blocks[n].len - 1] : NULL;
- for (i = 0; i < blocks[n].successors_count; i++) {
+ for (uint32_t i = 0; i < blocks[n].successors_count; i++) {
int succ = blocks[n].successors[i];
zend_ssa_phi *p;
for (p = ssa_blocks[succ].phis; p; p = p->next) {
@@ -867,7 +863,7 @@ static void zend_ssa_rename_in_block(const zend_op_array *op_array, uint32_t bui
p->constraint.range.max_ssa_var = var[p->constraint.range.max_var];
}
}
- for (j = 0; j < blocks[succ].predecessors_count; j++) {
+ for (uint32_t j = 0; j < blocks[succ].predecessors_count; j++) {
p->sources[j] = var[p->var];
}
if (p->ssa_var < 0) {
@@ -876,6 +872,7 @@ static void zend_ssa_rename_in_block(const zend_op_array *op_array, uint32_t bui
}
} else if (p->pi < 0) {
/* Normal Phi */
+ uint32_t j;
for (j = 0; j < blocks[succ].predecessors_count; j++)
if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
break;
@@ -893,6 +890,7 @@ static void zend_ssa_rename_in_block(const zend_op_array *op_array, uint32_t bui
zend_ssa_phi *q = p->next;
while (q) {
if (q->pi < 0 && q->var == p->var) {
+ uint32_t j;
for (j = 0; j < blocks[succ].predecessors_count; j++) {
if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) {
break;
@@ -995,13 +993,13 @@ backtrack:;
ZEND_API zend_result zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa) /* {{{ */
{
- zend_basic_block *blocks = ssa->cfg.blocks;
+ const zend_basic_block *blocks = ssa->cfg.blocks;
zend_ssa_block *ssa_blocks;
- int blocks_count = ssa->cfg.blocks_count;
+ uint32_t blocks_count = ssa->cfg.blocks_count;
uint32_t set_size;
zend_bitset def, in, phi;
int *var = NULL;
- int i, j, k, changed;
+ int i, j, changed;
zend_dfg dfg;
ALLOCA_FLAG(dfg_use_heap)
ALLOCA_FLAG(var_use_heap)
@@ -1056,7 +1054,7 @@ ZEND_API zend_result zend_build_ssa(zend_arena **arena, const zend_script *scrip
register allocator depends on this property. */
zend_bitset_union(phi_j, in + (j * set_size), set_size);
} else {
- for (k = 0; k < blocks[j].predecessors_count; k++) {
+ for (uint32_t k = 0; k < blocks[j].predecessors_count; k++) {
i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k];
while (i != -1 && i != blocks[j].idom) {
zend_bitset_union_with_intersection(
@@ -1223,9 +1221,7 @@ ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_
}
}
} else {
- int j;
-
- for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
+ for (uint32_t j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) {
zend_ssa_phi *p;
ZEND_ASSERT(phi->sources[j] >= 0);
@@ -1259,7 +1255,7 @@ ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_
}
/* }}} */
-void zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */
+void zend_ssa_unlink_use_chain(const zend_ssa *ssa, int op, int var) /* {{{ */
{
if (ssa->vars[var].use_chain == op) {
ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op);
@@ -1298,7 +1294,7 @@ void zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */
}
/* }}} */
-void zend_ssa_replace_use_chain(zend_ssa *ssa, int op, int new_op, int var) /* {{{ */
+void zend_ssa_replace_use_chain(const zend_ssa *ssa, int op, int new_op, int var) /* {{{ */
{
if (ssa->vars[var].use_chain == op) {
ssa->vars[var].use_chain = new_op;
@@ -1338,7 +1334,7 @@ void zend_ssa_replace_use_chain(zend_ssa *ssa, int op, int new_op, int var) /* {
}
/* }}} */
-void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op) /* {{{ */
+void zend_ssa_remove_instr(const zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op) /* {{{ */
{
if (ssa_op->result_use >= 0) {
zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->result_use);
@@ -1369,13 +1365,12 @@ void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op)
}
/* }}} */
-static inline zend_ssa_phi **zend_ssa_next_use_phi_ptr(zend_ssa *ssa, int var, zend_ssa_phi *p) /* {{{ */
+static inline zend_ssa_phi **zend_ssa_next_use_phi_ptr(const zend_ssa *ssa, int var, zend_ssa_phi *p) /* {{{ */
{
if (p->pi >= 0) {
return &p->use_chains[0];
} else {
- int j;
- for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ for (uint32_t j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
if (p->sources[j] == var) {
return &p->use_chains[j];
}
@@ -1388,7 +1383,7 @@ static inline zend_ssa_phi **zend_ssa_next_use_phi_ptr(zend_ssa *ssa, int var, z
/* May be called even if source is not used in the phi (useful when removing uses in a phi
* with multiple identical operands) */
-static inline void zend_ssa_remove_use_of_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int source, zend_ssa_phi *next_use_phi) /* {{{ */
+static inline void zend_ssa_remove_use_of_phi_source(const zend_ssa *ssa, const zend_ssa_phi *phi, int source, zend_ssa_phi *next_use_phi) /* {{{ */
{
zend_ssa_phi **cur = &ssa->vars[source].phi_use_chain;
while (*cur && *cur != phi) {
@@ -1400,7 +1395,7 @@ static inline void zend_ssa_remove_use_of_phi_source(zend_ssa *ssa, zend_ssa_phi
}
/* }}} */
-static void zend_ssa_remove_uses_of_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
+static void zend_ssa_remove_uses_of_phi_sources(const zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
{
int source;
FOREACH_PHI_SOURCE(phi, source) {
@@ -1409,7 +1404,7 @@ static void zend_ssa_remove_uses_of_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi
}
/* }}} */
-static void zend_ssa_remove_phi_from_block(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
+static void zend_ssa_remove_phi_from_block(const zend_ssa *ssa, const zend_ssa_phi *phi) /* {{{ */
{
zend_ssa_block *block = &ssa->blocks[phi->block];
zend_ssa_phi **cur = &block->phis;
@@ -1438,9 +1433,9 @@ void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) /* {{{ */
}
/* }}} */
-static inline void zend_ssa_remove_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int pred_offset, int predecessors_count) /* {{{ */
+static inline void zend_ssa_remove_phi_source(const zend_ssa *ssa, const zend_ssa_phi *phi, int pred_offset, uint32_t predecessors_count) /* {{{ */
{
- int j, var_num = phi->sources[pred_offset];
+ int var_num = phi->sources[pred_offset];
zend_ssa_phi *next_phi = phi->use_chains[pred_offset];
predecessors_count--;
@@ -1451,7 +1446,7 @@ static inline void zend_ssa_remove_phi_source(zend_ssa *ssa, zend_ssa_phi *phi,
/* Check if they same var is used in a different phi operand as well, in this case we don't
* need to adjust the use chain (but may have to move the next pointer). */
- for (j = 0; j < predecessors_count; j++) {
+ for (uint32_t j = 0; j < predecessors_count; j++) {
if (phi->sources[j] == var_num) {
if (j < pred_offset) {
ZEND_ASSERT(next_phi == NULL);
@@ -1467,7 +1462,7 @@ static inline void zend_ssa_remove_phi_source(zend_ssa *ssa, zend_ssa_phi *phi,
}
/* }}} */
-void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
+void zend_ssa_remove_phi(const zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
{
ZEND_ASSERT(phi->ssa_var >= 0);
ZEND_ASSERT(ssa->vars[phi->ssa_var].use_chain < 0
@@ -1479,14 +1474,14 @@ void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */
}
/* }}} */
-void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num) /* {{{ */
+void zend_ssa_remove_uses_of_var(const zend_ssa *ssa, int var_num) /* {{{ */
{
zend_ssa_var *var = &ssa->vars[var_num];
zend_ssa_phi *phi;
int use;
FOREACH_PHI_USE(var, phi) {
- int i, end = NUM_PHI_SOURCES(phi);
- for (i = 0; i < end; i++) {
+ uint32_t end = NUM_PHI_SOURCES(phi);
+ for (uint32_t i = 0; i < end; i++) {
if (phi->sources[i] == var_num) {
phi->use_chains[i] = NULL;
}
@@ -1515,15 +1510,14 @@ void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num) /* {{{ */
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */
{
zend_basic_block *next_block = &ssa->cfg.blocks[to];
- zend_ssa_block *next_ssa_block = &ssa->blocks[to];
+ const zend_ssa_block *next_ssa_block = &ssa->blocks[to];
zend_ssa_phi *phi;
- int j;
/* Find at which predecessor offset this block is referenced */
int pred_offset = -1;
int *predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset];
- for (j = 0; j < next_block->predecessors_count; j++) {
+ for (uint32_t j = 0; j < next_block->predecessors_count; j++) {
if (predecessors[j] == from) {
pred_offset = j;
break;
@@ -1540,7 +1534,7 @@ void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */
for (phi = next_ssa_block->phis; phi; phi = phi->next) {
if (phi->pi >= 0) {
if (phi->pi == from) {
- zend_ssa_rename_var_uses(ssa, phi->ssa_var, phi->sources[0], /* update_types */ 0);
+ zend_ssa_rename_var_uses(ssa, phi->ssa_var, phi->sources[0], /* update_types */ false);
zend_ssa_remove_phi(ssa, phi);
}
} else {
@@ -1558,10 +1552,10 @@ void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */
}
/* }}} */
-void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ */
+void zend_ssa_remove_block(const zend_op_array *op_array, zend_ssa *ssa, uint32_t i) /* {{{ */
{
zend_basic_block *block = &ssa->cfg.blocks[i];
- zend_ssa_block *ssa_block = &ssa->blocks[i];
+ const zend_ssa_block *ssa_block = &ssa->blocks[i];
zend_ssa_phi *phi;
int j;
@@ -1591,7 +1585,8 @@ void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int i) /* {{{ */
{
zend_basic_block *block = &ssa->cfg.blocks[i];
int *predecessors;
- int j, s;
+ int j;
+ uint32_t s;
for (s = 0; s < block->successors_count; s++) {
zend_ssa_remove_predecessor(ssa, i, block->successors[s]);
@@ -1671,15 +1666,15 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types
/* If the op already uses the new var, don't add the op to the use
* list again. Instead move the use_chain to the correct operand. */
- bool add_to_use_chain = 1;
+ bool add_to_use_chain = true;
if (ssa_op->result_use == new) {
- add_to_use_chain = 0;
+ add_to_use_chain = false;
} else if (ssa_op->op1_use == new) {
if (ssa_op->result_use == old) {
ssa_op->res_use_chain = ssa_op->op1_use_chain;
ssa_op->op1_use_chain = -1;
}
- add_to_use_chain = 0;
+ add_to_use_chain = false;
} else if (ssa_op->op2_use == new) {
if (ssa_op->result_use == old) {
ssa_op->res_use_chain = ssa_op->op2_use_chain;
@@ -1688,7 +1683,7 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types
ssa_op->op1_use_chain = ssa_op->op2_use_chain;
ssa_op->op2_use_chain = -1;
}
- add_to_use_chain = 0;
+ add_to_use_chain = false;
}
/* Perform the actual renaming */
@@ -1722,8 +1717,8 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types
/* Update phi use chains */
FOREACH_PHI_USE(old_var, phi) {
- int j;
- bool after_first_new_source = 0;
+ uint32_t j;
+ bool after_first_new_source = false;
/* If the phi already uses the new var, find its use chain, as we may
* need to move it to a different source operand. */
@@ -1737,7 +1732,7 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types
for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) {
if (phi->sources[j] == new) {
- after_first_new_source = 1;
+ after_first_new_source = true;
} else if (phi->sources[j] == old) {
phi->sources[j] = new;
@@ -1751,7 +1746,7 @@ void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types
phi->use_chains[j] = new_var->phi_use_chain;
new_var->phi_use_chain = phi;
}
- after_first_new_source = 1;
+ after_first_new_source = true;
} else {
phi->use_chains[j] = NULL;
}
diff --git a/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h
index 5995adcb1498..ee9faf9bfd23 100644
--- a/Zend/Optimizer/zend_ssa.h
+++ b/Zend/Optimizer/zend_ssa.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine, SSA - Static Single Assignment Form |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
@@ -66,7 +64,7 @@ struct _zend_ssa_phi {
zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
int var; /* Original CV, VAR or TMP variable index */
int ssa_var; /* SSA variable index */
- int block; /* current BB index */
+ uint32_t block; /* current BB index */
bool has_range_constraint;
zend_ssa_phi **use_chains;
zend_ssa_phi *sym_use_chain;
@@ -147,15 +145,15 @@ BEGIN_EXTERN_C()
ZEND_API zend_result zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa);
ZEND_API void zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var);
-void zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
-void zend_ssa_replace_use_chain(zend_ssa *ssa, int op, int new_op, int var);
+void zend_ssa_unlink_use_chain(const zend_ssa *ssa, int op, int var);
+void zend_ssa_replace_use_chain(const zend_ssa *ssa, int op, int new_op, int var);
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op);
-void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
-void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
-void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
-void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b);
+void zend_ssa_remove_instr(const zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
+void zend_ssa_remove_phi(const zend_ssa *ssa, zend_ssa_phi *phi);
+void zend_ssa_remove_uses_of_var(const zend_ssa *ssa, int var_num);
+void zend_ssa_remove_block(const zend_op_array *op_array, zend_ssa *ssa, uint32_t b);
void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types);
void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int b);
@@ -207,8 +205,7 @@ static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ss
if (p->pi >= 0) {
return p->use_chains[0];
} else {
- int j;
- for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
+ for (uint32_t j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
if (p->sources[j] == var) {
return p->use_chains[j];
}
@@ -240,21 +237,21 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend
/* Rename def to use if possible. Mark variable as not defined otherwise. */
if (ssa_op->op1_def >= 0) {
if (ssa_op->op1_use >= 0) {
- zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
+ zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, true);
}
ssa->vars[ssa_op->op1_def].definition = -1;
ssa_op->op1_def = -1;
}
if (ssa_op->op2_def >= 0) {
if (ssa_op->op2_use >= 0) {
- zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, 1);
+ zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, true);
}
ssa->vars[ssa_op->op2_def].definition = -1;
ssa_op->op2_def = -1;
}
if (ssa_op->result_def >= 0) {
if (ssa_op->result_use >= 0) {
- zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, 1);
+ zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, true);
}
ssa->vars[ssa_op->result_def].definition = -1;
ssa_op->result_def = -1;
@@ -285,7 +282,7 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend
#define FOREACH_PHI_SOURCE(phi, source) do { \
zend_ssa_phi *_phi = (phi); \
- int _i, _end = NUM_PHI_SOURCES(phi); \
+ uint32_t _i, _end = NUM_PHI_SOURCES(phi); \
for (_i = 0; _i < _end; _i++) { \
ZEND_ASSERT(_phi->sources[_i] >= 0); \
source = _phi->sources[_i];
@@ -294,8 +291,7 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend
} while (0)
#define FOREACH_PHI(phi) do { \
- int _i; \
- for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
+ for (uint32_t _i = 0; _i < ssa->cfg.blocks_count; _i++) { \
phi = ssa->blocks[_i].phis; \
for (; phi; phi = phi->next) {
#define FOREACH_PHI_END() \
@@ -304,8 +300,7 @@ static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend
} while (0)
#define FOREACH_BLOCK(block) do { \
- int _i; \
- for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
+ for (uint32_t _i = 0; _i < ssa->cfg.blocks_count; _i++) { \
(block) = &ssa->cfg.blocks[_i]; \
if (!((block)->flags & ZEND_BB_REACHABLE)) { \
continue; \
diff --git a/Zend/Optimizer/zend_worklist.h b/Zend/Optimizer/zend_worklist.h
index f47d01bd1579..ea03048e9756 100644
--- a/Zend/Optimizer/zend_worklist.h
+++ b/Zend/Optimizer/zend_worklist.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Andy Wingo |
+----------------------------------------------------------------------+
@@ -97,12 +95,12 @@ static inline bool zend_worklist_push(zend_worklist *worklist, int i)
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
if (zend_bitset_in(worklist->visited, i)) {
- return 0;
+ return false;
}
zend_bitset_incl(worklist->visited, i);
zend_worklist_stack_push(&worklist->stack, i);
- return 1;
+ return true;
}
static inline int zend_worklist_peek(const zend_worklist *worklist)
diff --git a/Zend/Zend.m4 b/Zend/Zend.m4
index f1644b3737b0..5e69a97db197 100644
--- a/Zend/Zend.m4
+++ b/Zend/Zend.m4
@@ -118,7 +118,7 @@ dnl Ugly hack to check if dlsym() requires a leading underscore in symbol name.
dnl
AC_DEFUN([ZEND_DLSYM_CHECK], [dnl
AC_MSG_CHECKING([whether dlsym() requires a leading underscore in symbol names])
-_LT_AC_TRY_DLOPEN_SELF([AC_MSG_RESULT([no])], [
+_LT_TRY_DLOPEN_SELF([AC_MSG_RESULT([no])], [
AC_MSG_RESULT([yes])
AC_DEFINE([DLSYM_NEEDS_UNDERSCORE], [1],
[Define to 1 if 'dlsym()' requires a leading underscore in symbol names.])
diff --git a/Zend/asm/jump_arm64_aapcs_elf_gas.S b/Zend/asm/jump_arm64_aapcs_elf_gas.S
index cefd1830d718..b8abb2ef6bc4 100644
--- a/Zend/asm/jump_arm64_aapcs_elf_gas.S
+++ b/Zend/asm/jump_arm64_aapcs_elf_gas.S
@@ -53,10 +53,29 @@
.file "jump_arm64_aapcs_elf_gas.S"
.text
+#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1)
+/* Mark this object as requiring BTI, enabling the linker to set BTI on the output */
+ .pushsection .note.gnu.property, "a", %note
+ .p2align 3
+ .long 4 /* namesz */
+ .long 16 /* descsz (16 bytes) */
+ .long 5 /* type = NT_GNU_PROPERTY_TYPE_0 */
+ .asciz "GNU"
+ .p2align 3
+ .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */
+ .long 4 /* datasz = 4 */
+ .long 1 /* GNU_PROPERTY_AARCH64_FEATURE_1_BTI */
+ .long 0 /* pad to 8-byte alignment */
+ .popsection
+#endif
.align 2
.global jump_fcontext
+.hidden jump_fcontext
.type jump_fcontext, %function
jump_fcontext:
+#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1)
+ hint #34 /* bti c: valid indirect-entry target */
+#endif
# prepare stack for GP + FPU
sub sp, sp, #0xb0
diff --git a/Zend/asm/jump_arm64_aapcs_macho_gas.S b/Zend/asm/jump_arm64_aapcs_macho_gas.S
index 31738f745314..12cc02158685 100644
--- a/Zend/asm/jump_arm64_aapcs_macho_gas.S
+++ b/Zend/asm/jump_arm64_aapcs_macho_gas.S
@@ -52,6 +52,7 @@
*******************************************************/
.text
+.private_extern _jump_fcontext
.globl _jump_fcontext
.balign 16
_jump_fcontext:
diff --git a/Zend/asm/jump_arm64_aapcs_pe_armasm.asm b/Zend/asm/jump_arm64_aapcs_pe_armasm.asm
index 3100243d68de..afb6d2b2e703 100644
--- a/Zend/asm/jump_arm64_aapcs_pe_armasm.asm
+++ b/Zend/asm/jump_arm64_aapcs_pe_armasm.asm
@@ -45,7 +45,7 @@
;* ------------------------------------------------- *
;* | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc| *
;* ------------------------------------------------- *
-;* | fiber data| base | limit | dealloc | *
+;* | base | limit | dealloc | fiber data| *
;* ------------------------------------------------- *
;* ------------------------------------------------- *
;* | 48 | 49 | 50 | 51 | | | *
@@ -130,4 +130,4 @@ jump_fcontext proc
ret x4
ENDP
- END
\ No newline at end of file
+ END
diff --git a/Zend/asm/jump_arm_aapcs_elf_gas.S b/Zend/asm/jump_arm_aapcs_elf_gas.S
index 86efe9d82146..4415a541fd3e 100644
--- a/Zend/asm/jump_arm_aapcs_elf_gas.S
+++ b/Zend/asm/jump_arm_aapcs_elf_gas.S
@@ -41,6 +41,7 @@
.file "jump_arm_aapcs_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.align 2
.type jump_fcontext,%function
.syntax unified
@@ -48,8 +49,38 @@ jump_fcontext:
@ save LR as PC
push {lr}
@ save hidden,V1-V8,LR
+
+ #if __ARM_ARCH >= 7
+
push {a1,v1-v8,lr}
+ #else
+
+ sub sp, #40
+
+ str a1, [sp, #0]
+ str v1, [sp, #4]
+ str v2, [sp, #8]
+ str v3, [sp, #12]
+ str v4, [sp, #16]
+
+ mov v4, v5
+ str v4, [sp, #20]
+
+ mov v4, v6
+ str v4, [sp, #24]
+
+ mov v4, v7
+ str v4, [sp, #28]
+
+ mov v4, v8
+ str v4, [sp, #32]
+
+ mov v4, lr
+ str v4, [sp, #36]
+
+ #endif
+
@ prepare stack for FPU
sub sp, sp, #64
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
@@ -71,7 +102,34 @@ jump_fcontext:
add sp, sp, #64
@ restore hidden,V1-V8,LR
+ #if __ARM_ARCH >= 7
pop {a4,v1-v8,lr}
+ #else
+
+ ldr v4, [sp, #20]
+ mov v5, v4
+
+ ldr v4, [sp, #24]
+ mov v6, v4
+
+ ldr v4, [sp, #28]
+ mov v7, v4
+
+ ldr v4, [sp, #32]
+ mov v8, v4
+
+ ldr v4, [sp, #36]
+ mov lr, v4
+
+ ldr a4, [sp, #0]
+ ldr v1, [sp, #4]
+ ldr v2, [sp, #8]
+ ldr v3, [sp, #12]
+ ldr v4, [sp, #16]
+
+ add sp, #40
+
+ #endif
@ return transfer_t from jump
str a1, [a4, #0]
diff --git a/Zend/asm/jump_arm_aapcs_macho_gas.S b/Zend/asm/jump_arm_aapcs_macho_gas.S
index 077c36409e83..44e7f2a42274 100644
--- a/Zend/asm/jump_arm_aapcs_macho_gas.S
+++ b/Zend/asm/jump_arm_aapcs_macho_gas.S
@@ -39,6 +39,7 @@
*******************************************************/
.text
+.private_extern _jump_fcontext
.globl _jump_fcontext
.align 2
_jump_fcontext:
diff --git a/Zend/asm/jump_i386_ms_pe_masm.asm b/Zend/asm/jump_i386_ms_pe_masm.asm
index 7a9e848f1c3f..a0450d0532f8 100644
--- a/Zend/asm/jump_i386_ms_pe_masm.asm
+++ b/Zend/asm/jump_i386_ms_pe_masm.asm
@@ -55,17 +55,17 @@ ENDIF
mov eax, [edx]
mov [esp+018h], eax
- mov [esp+01ch], edi ; save EDI
- mov [esp+020h], esi ; save ESI
- mov [esp+024h], ebx ; save EBX
- mov [esp+028h], ebp ; save EBP
+ mov [esp+01ch], edi ; save EDI
+ mov [esp+020h], esi ; save ESI
+ mov [esp+024h], ebx ; save EBX
+ mov [esp+028h], ebp ; save EBP
; store ESP (pointing to context-data) in EAX
mov eax, esp
; firstarg of jump_fcontext() == fcontext to jump to
mov ecx, [esp+030h]
-
+
; restore ESP (pointing to context-data) from ECX
mov esp, ecx
@@ -98,10 +98,10 @@ ENDIF
mov ecx, [esp+02ch] ; restore EIP
- mov edi, [esp+01ch] ; restore EDI
- mov esi, [esp+020h] ; restore ESI
- mov ebx, [esp+024h] ; restore EBX
- mov ebp, [esp+028h] ; restore EBP
+ mov edi, [esp+01ch] ; restore EDI
+ mov esi, [esp+020h] ; restore ESI
+ mov ebx, [esp+024h] ; restore EBX
+ mov ebp, [esp+028h] ; restore EBP
; prepare stack
lea esp, [esp+030h]
diff --git a/Zend/asm/jump_i386_sysv_elf_gas.S b/Zend/asm/jump_i386_sysv_elf_gas.S
index ed83717ce2b8..d00ac4fffd42 100644
--- a/Zend/asm/jump_i386_sysv_elf_gas.S
+++ b/Zend/asm/jump_i386_sysv_elf_gas.S
@@ -31,6 +31,7 @@
.file "jump_i386_sysv_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.align 2
.type jump_fcontext,@function
jump_fcontext:
diff --git a/Zend/asm/jump_i386_sysv_macho_gas.S b/Zend/asm/jump_i386_sysv_macho_gas.S
index 8ab7c6f29c66..4cae3252d171 100644
--- a/Zend/asm/jump_i386_sysv_macho_gas.S
+++ b/Zend/asm/jump_i386_sysv_macho_gas.S
@@ -25,6 +25,7 @@
****************************************************************************************/
.text
+.private_extern _jump_fcontext
.globl _jump_fcontext
.align 2
_jump_fcontext:
@@ -54,8 +55,8 @@ _jump_fcontext:
/* return parent fcontext_t */
movl %ecx, %eax
- /* returned data is stored in EDX */
-
+ /* returned data is stored in EDX */
+
movl 0x18(%esp), %ecx /* restore EIP */
#if !defined(BOOST_USE_TSX)
diff --git a/Zend/asm/jump_loongarch64_sysv_elf_gas.S b/Zend/asm/jump_loongarch64_sysv_elf_gas.S
index 74c081e07faa..6f99e719ec00 100644
--- a/Zend/asm/jump_loongarch64_sysv_elf_gas.S
+++ b/Zend/asm/jump_loongarch64_sysv_elf_gas.S
@@ -41,6 +41,7 @@
.file "jump_loongarch64_sysv_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.align 2
.type jump_fcontext,@function
jump_fcontext:
diff --git a/Zend/asm/jump_mips32_o32_elf_gas.S b/Zend/asm/jump_mips32_o32_elf_gas.S
index f2b8034d8c5b..38d22d26f6d7 100644
--- a/Zend/asm/jump_mips32_o32_elf_gas.S
+++ b/Zend/asm/jump_mips32_o32_elf_gas.S
@@ -41,6 +41,7 @@
.file "jump_mips32_o32_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.align 2
.type jump_fcontext,@function
.ent jump_fcontext
@@ -102,13 +103,13 @@ jump_fcontext:
# adjust stack
addiu $sp, $sp, 96
-
+
# return transfer_t from jump
sw $a0, ($v0) # fctx of transfer_t
sw $a2, 4($v0) # data of transfer_t
# pass transfer_t as first arg in context function
# A0 == fctx, A1 == data
- move $a1, $a2
+ move $a1, $a2
# jump to context
jr $t9
diff --git a/Zend/asm/jump_mips64_n64_elf_gas.S b/Zend/asm/jump_mips64_n64_elf_gas.S
index e338912bb6e8..60027fe61734 100644
--- a/Zend/asm/jump_mips64_n64_elf_gas.S
+++ b/Zend/asm/jump_mips64_n64_elf_gas.S
@@ -48,6 +48,7 @@
.file "jump_mips64_n64_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.align 3
.type jump_fcontext,@function
.ent jump_fcontext
diff --git a/Zend/asm/jump_ppc32_sysv_elf_gas.S b/Zend/asm/jump_ppc32_sysv_elf_gas.S
index 48e09c935e09..e31336640065 100644
--- a/Zend/asm/jump_ppc32_sysv_elf_gas.S
+++ b/Zend/asm/jump_ppc32_sysv_elf_gas.S
@@ -53,6 +53,7 @@
.file "jump_ppc32_sysv_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.align 2
.type jump_fcontext,@function
jump_fcontext:
diff --git a/Zend/asm/jump_ppc32_sysv_macho_gas.S b/Zend/asm/jump_ppc32_sysv_macho_gas.S
index fef90c295f8c..c2a2061e8774 100644
--- a/Zend/asm/jump_ppc32_sysv_macho_gas.S
+++ b/Zend/asm/jump_ppc32_sysv_macho_gas.S
@@ -68,12 +68,13 @@
* ------------------------------------------------- *
* | 256 | | *
* ------------------------------------------------- *
- * | DATA| | *
+ * | DATA| | *
* ------------------------------------------------- *
* *
*******************************************************/
.text
+.private_extern _jump_fcontext
.globl _jump_fcontext
.align 2
_jump_fcontext:
@@ -193,7 +194,7 @@ _jump_fcontext:
; adjust stack
addi r1, r1, 244
- ; return transfer_t
+ ; return transfer_t
stw r6, 0(r3)
stw r5, 4(r3)
diff --git a/Zend/asm/jump_ppc64_sysv_elf_gas.S b/Zend/asm/jump_ppc64_sysv_elf_gas.S
index 28907db32b8c..debeb3466404 100644
--- a/Zend/asm/jump_ppc64_sysv_elf_gas.S
+++ b/Zend/asm/jump_ppc64_sysv_elf_gas.S
@@ -68,6 +68,7 @@
.file "jump_ppc64_sysv_elf_gas.S"
.globl jump_fcontext
+.hidden jump_fcontext
#if _CALL_ELF == 2
.text
.align 2
@@ -97,7 +98,7 @@ jump_fcontext:
# endif
#endif
# reserve space on stack
- subi %r1, %r1, 184
+ subi %r1, %r1, 200
#if _CALL_ELF != 2
std %r2, 0(%r1) # save TOC
@@ -133,6 +134,10 @@ jump_fcontext:
# save LR as PC
std %r0, 176(%r1)
+ # Save VS63
+ li %r31, 184
+ stvx %v31, %r1, %r31
+
# store RSP (pointing to context-data) in R6
mr %r6, %r1
@@ -145,6 +150,11 @@ jump_fcontext:
ld %r2, 0(%r1) # restore TOC
#endif
+
+ # Restore VS63
+ li %r31, 184
+ lvx %v31, %r1, %r31
+
ld %r14, 8(%r1) # restore R14
ld %r15, 16(%r1) # restore R15
ld %r16, 24(%r1) # restore R16
@@ -180,7 +190,7 @@ jump_fcontext:
mtctr %r12
# adjust stack
- addi %r1, %r1, 184
+ addi %r1, %r1, 200
#if _CALL_ELF == 2
# copy transfer_t into transfer_fn arg registers
diff --git a/Zend/asm/jump_ppc64_sysv_macho_gas.S b/Zend/asm/jump_ppc64_sysv_macho_gas.S
index dcc6c645db61..e4d8944b563d 100644
--- a/Zend/asm/jump_ppc64_sysv_macho_gas.S
+++ b/Zend/asm/jump_ppc64_sysv_macho_gas.S
@@ -68,6 +68,7 @@
.text
.align 2
+.private_extern _jump_fcontext
.globl _jump_fcontext
_jump_fcontext:
diff --git a/Zend/asm/jump_riscv64_sysv_elf_gas.S b/Zend/asm/jump_riscv64_sysv_elf_gas.S
index 5417e5d5e349..879eb7efee62 100644
--- a/Zend/asm/jump_riscv64_sysv_elf_gas.S
+++ b/Zend/asm/jump_riscv64_sysv_elf_gas.S
@@ -61,6 +61,7 @@
.text
.align 1
.global jump_fcontext
+.hidden jump_fcontext
.type jump_fcontext, %function
jump_fcontext:
# prepare stack for GP + FPU
diff --git a/Zend/asm/jump_s390x_sysv_elf_gas.S b/Zend/asm/jump_s390x_sysv_elf_gas.S
index fa71467756d2..59bc58bf370b 100644
--- a/Zend/asm/jump_s390x_sysv_elf_gas.S
+++ b/Zend/asm/jump_s390x_sysv_elf_gas.S
@@ -46,6 +46,7 @@
.text
.align 8
.global jump_fcontext
+.hidden jump_fcontext
.type jump_fcontext, @function
#define ARG_OFFSET 0
diff --git a/Zend/asm/jump_x86_64_sysv_elf_gas.S b/Zend/asm/jump_x86_64_sysv_elf_gas.S
index be264bdc2e2b..2eff59a380cb 100644
--- a/Zend/asm/jump_x86_64_sysv_elf_gas.S
+++ b/Zend/asm/jump_x86_64_sysv_elf_gas.S
@@ -44,6 +44,7 @@
.file "jump_x86_64_sysv_elf_gas.S"
.text
.globl jump_fcontext
+.hidden jump_fcontext
.type jump_fcontext,@function
.align 16
jump_fcontext:
@@ -67,14 +68,6 @@ jump_fcontext:
movq %rbx, 0x30(%rsp) /* save RBX */
movq %rbp, 0x38(%rsp) /* save RBP */
-#if BOOST_CONTEXT_SHADOW_STACK
- /* grow the stack to reserve space for shadow stack pointer(SSP) */
- leaq -0x8(%rsp), %rsp
- /* read the current SSP and store it */
- rdsspq %rcx
- movq %rcx, (%rsp)
-#endif
-
#if BOOST_CONTEXT_SHADOW_STACK
/* grow the stack to reserve space for shadow stack pointer(SSP) */
leaq -0x8(%rsp), %rsp
diff --git a/Zend/asm/jump_x86_64_sysv_macho_gas.S b/Zend/asm/jump_x86_64_sysv_macho_gas.S
index afc3e5c126f9..673daa61dfaa 100644
--- a/Zend/asm/jump_x86_64_sysv_macho_gas.S
+++ b/Zend/asm/jump_x86_64_sysv_macho_gas.S
@@ -25,6 +25,7 @@
****************************************************************************************/
.text
+.private_extern _jump_fcontext
.globl _jump_fcontext
.align 8
_jump_fcontext:
diff --git a/Zend/asm/make_arm64_aapcs_elf_gas.S b/Zend/asm/make_arm64_aapcs_elf_gas.S
index 66cfb2da17ea..246525402f5c 100644
--- a/Zend/asm/make_arm64_aapcs_elf_gas.S
+++ b/Zend/asm/make_arm64_aapcs_elf_gas.S
@@ -53,10 +53,29 @@
.file "make_arm64_aapcs_elf_gas.S"
.text
+#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1)
+/* Mark this object as requiring BTI, enabling the linker to set BTI on the output */
+ .pushsection .note.gnu.property, "a", %note
+ .p2align 3
+ .long 4 /* namesz */
+ .long 16 /* descsz (16 bytes) */
+ .long 5 /* type = NT_GNU_PROPERTY_TYPE_0 */
+ .asciz "GNU"
+ .p2align 3
+ .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */
+ .long 4 /* datasz = 4 */
+ .long 1 /* GNU_PROPERTY_AARCH64_FEATURE_1_BTI */
+ .long 0 /* pad to 8-byte alignment */
+ .popsection
+#endif
.align 2
.global make_fcontext
+.hidden make_fcontext
.type make_fcontext, %function
make_fcontext:
+#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1)
+ hint #34 /* bti c */
+#endif
# shift address in x0 (allocated stack) to lower 16 byte boundary
and x0, x0, ~0xF
diff --git a/Zend/asm/make_arm64_aapcs_macho_gas.S b/Zend/asm/make_arm64_aapcs_macho_gas.S
index b30b1e3e5bbb..a6a1314c0dcd 100644
--- a/Zend/asm/make_arm64_aapcs_macho_gas.S
+++ b/Zend/asm/make_arm64_aapcs_macho_gas.S
@@ -52,6 +52,7 @@
*******************************************************/
.text
+.private_extern _make_fcontext
.globl _make_fcontext
.balign 16
diff --git a/Zend/asm/make_arm64_aapcs_pe_armasm.asm b/Zend/asm/make_arm64_aapcs_pe_armasm.asm
index 50f9b69876be..c384af039fc4 100644
--- a/Zend/asm/make_arm64_aapcs_pe_armasm.asm
+++ b/Zend/asm/make_arm64_aapcs_pe_armasm.asm
@@ -73,7 +73,7 @@ make_fcontext proc
; save top address of context_stack as 'base'
str x3, [x0, #0xa0]
- ; save bottom address of context-stack as 'limit' and 'dealloction stack'
+ ; save bottom address of context-stack as 'limit' and 'deallocation stack'
sub x3, x3, x1
stp x3, x3, [x0, #0xa8]
; save 0 as 'fiber data'
diff --git a/Zend/asm/make_arm_aapcs_elf_gas.S b/Zend/asm/make_arm_aapcs_elf_gas.S
index 98ae64b43f9d..2367c75a6a77 100644
--- a/Zend/asm/make_arm_aapcs_elf_gas.S
+++ b/Zend/asm/make_arm_aapcs_elf_gas.S
@@ -41,26 +41,47 @@
.file "make_arm_aapcs_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.align 2
.type make_fcontext,%function
.syntax unified
make_fcontext:
@ shift address in A1 to lower 16 byte boundary
+
+ #if __ARM_ARCH >= 7
bic a1, a1, #15
+ #else
+ lsrs a1, a1, #4
+ lsls a1, a1, #4
+ #endif
@ reserve space for context-data on context-stack
+ #if __ARM_ARCH >= 7
sub a1, a1, #124
+ #else
+ subs a1, #124
+ #endif
@ third arg of make_fcontext() == address of context-function
str a3, [a1, #104]
@ compute address of returned transfer_t
+ #if __ARM_ARCH >= 7
add a2, a1, #108
+ #else
+ mov a2, a1
+ adds a2, #108
+ #endif
mov a3, a2
str a3, [a1, #64]
+
@ compute abs address of label finish
+ #if __ARM_ARCH >= 7
adr a2, finish
+ #else
+ ldr a2, =finish
+ #endif
@ save address of finish as return-address for context-function
@ will be entered after context-function returns
str a2, [a1, #100]
@@ -72,7 +93,13 @@ make_fcontext:
finish:
@ exit code is zero
+ #if __ARM_ARCH >=7
mov a1, #0
+ #else
+ movs r3, #0
+ movs r0, r3
+ #endif
+
@ exit application
bl _exit@PLT
.size make_fcontext,.-make_fcontext
diff --git a/Zend/asm/make_arm_aapcs_macho_gas.S b/Zend/asm/make_arm_aapcs_macho_gas.S
index c909ae9d43a2..de934075d247 100644
--- a/Zend/asm/make_arm_aapcs_macho_gas.S
+++ b/Zend/asm/make_arm_aapcs_macho_gas.S
@@ -39,6 +39,7 @@
*******************************************************/
.text
+.private_extern _make_fcontext
.globl _make_fcontext
.align 2
_make_fcontext:
diff --git a/Zend/asm/make_i386_ms_pe_masm.asm b/Zend/asm/make_i386_ms_pe_masm.asm
index 5246465cb975..3879396390f2 100644
--- a/Zend/asm/make_i386_ms_pe_masm.asm
+++ b/Zend/asm/make_i386_ms_pe_masm.asm
@@ -53,7 +53,7 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT
mov [eax+014h], ecx
; second arg of make_fcontext() == size of context-stack
mov edx, [esp+08h]
- ; negate stack size for LEA instruction (== substraction)
+ ; negate stack size for LEA instruction (== subtraction)
neg edx
; compute bottom address of context stack (limit)
lea ecx, [ecx+edx]
diff --git a/Zend/asm/make_i386_sysv_elf_gas.S b/Zend/asm/make_i386_sysv_elf_gas.S
index c6e0b36558a6..992bae019af2 100644
--- a/Zend/asm/make_i386_sysv_elf_gas.S
+++ b/Zend/asm/make_i386_sysv_elf_gas.S
@@ -31,6 +31,7 @@
.file "make_i386_sysv_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.align 2
.type make_fcontext,@function
make_fcontext:
diff --git a/Zend/asm/make_i386_sysv_macho_gas.S b/Zend/asm/make_i386_sysv_macho_gas.S
index 519e406248bb..01076129bcf5 100644
--- a/Zend/asm/make_i386_sysv_macho_gas.S
+++ b/Zend/asm/make_i386_sysv_macho_gas.S
@@ -25,6 +25,7 @@
****************************************************************************************/
.text
+.private_extern _make_fcontext
.globl _make_fcontext
.align 2
_make_fcontext:
@@ -69,7 +70,7 @@ _make_fcontext:
addl $finish-2b, %ecx
/* save address of finish as return-address for context-function */
/* will be entered after context-function returns */
- movl %ecx, 0x14(%eax)
+ movl %ecx, 0x14(%eax)
ret /* return pointer to context-data */
diff --git a/Zend/asm/make_loongarch64_sysv_elf_gas.S b/Zend/asm/make_loongarch64_sysv_elf_gas.S
index 55062702f18d..a067baded56f 100644
--- a/Zend/asm/make_loongarch64_sysv_elf_gas.S
+++ b/Zend/asm/make_loongarch64_sysv_elf_gas.S
@@ -41,6 +41,7 @@
.file "make_loongarch64_sysv_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.align 2
.type make_fcontext,@function
make_fcontext:
diff --git a/Zend/asm/make_mips32_o32_elf_gas.S b/Zend/asm/make_mips32_o32_elf_gas.S
index 4e11e3d05825..54742f3a8be2 100644
--- a/Zend/asm/make_mips32_o32_elf_gas.S
+++ b/Zend/asm/make_mips32_o32_elf_gas.S
@@ -41,6 +41,7 @@
.file "make_mips32_o32_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.align 2
.type make_fcontext,@function
.ent make_fcontext
diff --git a/Zend/asm/make_mips64_n64_elf_gas.S b/Zend/asm/make_mips64_n64_elf_gas.S
index 7bb30b14dee7..c43f2ff368f9 100644
--- a/Zend/asm/make_mips64_n64_elf_gas.S
+++ b/Zend/asm/make_mips64_n64_elf_gas.S
@@ -48,6 +48,7 @@
.file "make_mips64_n64_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.align 3
.type make_fcontext,@function
.ent make_fcontext
@@ -69,7 +70,7 @@ make_fcontext:
# save global pointer in context-data
sd $gp, 136($v0)
- # psudo instruction compute abs address of label finish based on GP
+ # pseudo instruction compute abs address of label finish based on GP
dla $t9, finish
# save address of finish as return-address for context-function
diff --git a/Zend/asm/make_ppc32_sysv_elf_gas.S b/Zend/asm/make_ppc32_sysv_elf_gas.S
index 9616c4ca9b46..0dd77719354b 100644
--- a/Zend/asm/make_ppc32_sysv_elf_gas.S
+++ b/Zend/asm/make_ppc32_sysv_elf_gas.S
@@ -53,6 +53,7 @@
.file "make_ppc32_sysv_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.align 2
.type make_fcontext,@function
make_fcontext:
diff --git a/Zend/asm/make_ppc32_sysv_macho_gas.S b/Zend/asm/make_ppc32_sysv_macho_gas.S
index 1102ee90ef07..75d1c80e5263 100644
--- a/Zend/asm/make_ppc32_sysv_macho_gas.S
+++ b/Zend/asm/make_ppc32_sysv_macho_gas.S
@@ -68,12 +68,13 @@
* ------------------------------------------------- *
* | 256 | | *
* ------------------------------------------------- *
- * | DATA| | *
+ * | DATA| | *
* ------------------------------------------------- *
* *
*******************************************************/
.text
+.private_extern _make_fcontext
.globl _make_fcontext
.align 2
_make_fcontext:
@@ -101,8 +102,8 @@ _make_fcontext:
; compute address of returned transfer_t
addi r0, r3, 252
- mr r4, r0
- stw r4, 228(r3)
+ mr r4, r0
+ stw r4, 228(r3)
; load LR
mflr r0
diff --git a/Zend/asm/make_ppc64_sysv_elf_gas.S b/Zend/asm/make_ppc64_sysv_elf_gas.S
index c4d7ee59826a..f513d3639ea9 100644
--- a/Zend/asm/make_ppc64_sysv_elf_gas.S
+++ b/Zend/asm/make_ppc64_sysv_elf_gas.S
@@ -68,6 +68,7 @@
.file "make_ppc64_sysv_elf_gas.S"
.globl make_fcontext
+.hidden make_fcontext
#if _CALL_ELF == 2
.text
.align 2
diff --git a/Zend/asm/make_ppc64_sysv_macho_gas.S b/Zend/asm/make_ppc64_sysv_macho_gas.S
index dd7bf2c5c0c0..d8cdd2d080db 100644
--- a/Zend/asm/make_ppc64_sysv_macho_gas.S
+++ b/Zend/asm/make_ppc64_sysv_macho_gas.S
@@ -67,6 +67,7 @@
*******************************************************/
.text
+.private_extern _make_fcontext
.globl _make_fcontext
_make_fcontext:
; save return address into R6
diff --git a/Zend/asm/make_riscv64_sysv_elf_gas.S b/Zend/asm/make_riscv64_sysv_elf_gas.S
index 5322e0fdbdec..6d97c59e877e 100644
--- a/Zend/asm/make_riscv64_sysv_elf_gas.S
+++ b/Zend/asm/make_riscv64_sysv_elf_gas.S
@@ -61,6 +61,7 @@
.text
.align 1
.global make_fcontext
+.hidden make_fcontext
.type make_fcontext, %function
make_fcontext:
# shift address in a0 (allocated stack) to lower 16 byte boundary
diff --git a/Zend/asm/make_s390x_sysv_elf_gas.S b/Zend/asm/make_s390x_sysv_elf_gas.S
index 4dd423e2a44c..4d6aa62c0ae1 100644
--- a/Zend/asm/make_s390x_sysv_elf_gas.S
+++ b/Zend/asm/make_s390x_sysv_elf_gas.S
@@ -46,6 +46,7 @@
.text
.align 8
.global make_fcontext
+.hidden make_fcontext
.type make_fcontext, @function
#define ARG_OFFSET 0
diff --git a/Zend/asm/make_sparc64_sysv_elf_gas.S b/Zend/asm/make_sparc64_sysv_elf_gas.S
index 3e7ee809c918..3d504471afd5 100644
--- a/Zend/asm/make_sparc64_sysv_elf_gas.S
+++ b/Zend/asm/make_sparc64_sysv_elf_gas.S
@@ -45,7 +45,7 @@ make_fcontext:
add %l0, (trampoline - 1b - 8), %l0
stx %l0, [%i0 + I7]
- # Save framepointer to first stack frame but first substract the BIAS
+ # Save framepointer to first stack frame but first subtract the BIAS
add %i0, CC64FSZ - BIAS, %l0
stx %l0, [%i0 + SP]
diff --git a/Zend/asm/make_x86_64_ms_pe_gas.S b/Zend/asm/make_x86_64_ms_pe_gas.S
index 958a2a7b6d0e..7146637c0319 100644
--- a/Zend/asm/make_x86_64_ms_pe_gas.S
+++ b/Zend/asm/make_x86_64_ms_pe_gas.S
@@ -113,7 +113,7 @@ make_fcontext:
/* save top address of context stack as 'base' */
movq %rcx, 0xc8(%rax)
/* second arg of make_fcontext() == size of context-stack */
- /* negate stack size for LEA instruction (== substraction) */
+ /* negate stack size for LEA instruction (== subtraction) */
negq %rdx
/* compute bottom address of context stack (limit) */
leaq (%rcx,%rdx), %rcx
@@ -151,7 +151,7 @@ make_fcontext:
trampoline:
/* store return address on stack */
- /* fix stack alignment */
+ /* fix stack alignment */
pushq %rbp
/* jump to context-function */
jmp *%rbx
diff --git a/Zend/asm/make_x86_64_ms_pe_masm.asm b/Zend/asm/make_x86_64_ms_pe_masm.asm
index 8f6c959a8376..a6c2b147e446 100644
--- a/Zend/asm/make_x86_64_ms_pe_masm.asm
+++ b/Zend/asm/make_x86_64_ms_pe_masm.asm
@@ -110,7 +110,7 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME
; save top address of context stack as 'base'
mov [rax+0c8h], rcx
; second arg of make_fcontext() == size of context-stack
- ; negate stack size for LEA instruction (== substraction)
+ ; negate stack size for LEA instruction (== subtraction)
neg rdx
; compute bottom address of context stack (limit)
lea rcx, [rcx+rdx]
@@ -141,7 +141,7 @@ make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME
; compute abs address of label finish
lea rcx, finish
; save address of finish as return-address for context-function in RBP
- ; will be entered after context-function returns
+ ; will be entered after context-function returns
mov [rax+0108h], rcx
ret ; return pointer to context-data
diff --git a/Zend/asm/make_x86_64_sysv_elf_gas.S b/Zend/asm/make_x86_64_sysv_elf_gas.S
index b0d0c0341ef3..cabebff6e626 100644
--- a/Zend/asm/make_x86_64_sysv_elf_gas.S
+++ b/Zend/asm/make_x86_64_sysv_elf_gas.S
@@ -44,6 +44,7 @@
.file "make_x86_64_sysv_elf_gas.S"
.text
.globl make_fcontext
+.hidden make_fcontext
.type make_fcontext,@function
.align 16
make_fcontext:
@@ -76,7 +77,7 @@ make_fcontext:
#if defined(BOOST_CONTEXT_TLS_STACK_PROTECTOR)
/* save stack guard */
movq %fs:0x28, %rcx /* read stack guard from TLS record */
- movq %rcx, 0x8(%rsp) /* save stack guard */
+ movq %rcx, 0x8(%rax) /* save stack guard */
#endif
/* compute abs address of label trampoline */
@@ -91,35 +92,6 @@ make_fcontext:
/* will be entered after context-function returns */
movq %rcx, 0x38(%rax)
-#if BOOST_CONTEXT_SHADOW_STACK
- /* Populate the shadow stack and normal stack */
- /* get original SSP */
- rdsspq %r8
- /* restore new shadow stack */
- rstorssp -0x8(%r9)
- /* save the restore token on the original shadow stack */
- saveprevssp
- /* push the address of "jmp trampoline" to the new shadow stack */
- /* as well as the stack */
- call 1f
- jmp trampoline
-1:
- /* save address of "jmp trampoline" as return-address */
- /* for context-function */
- pop 0x38(%rax)
- /* Get the new SSP. */
- rdsspq %r9
- /* restore original shadow stack */
- rstorssp -0x8(%r8)
- /* save the restore token on the new shadow stack. */
- saveprevssp
-
- /* reserve space for the new SSP */
- leaq -0x8(%rax), %rax
- /* save the new SSP to this fcontext */
- movq %r9, (%rax)
-#endif
-
#if BOOST_CONTEXT_SHADOW_STACK
/* Populate the shadow stack */
@@ -161,6 +133,8 @@ make_fcontext:
ret /* return pointer to context-data */
trampoline:
+ .cfi_startproc
+ .cfi_undefined rip
/* store return address on stack */
/* fix stack alignment */
_CET_ENDBR
@@ -175,6 +149,7 @@ trampoline:
#endif
/* jump to context-function */
jmp *%rbx
+ .cfi_endproc
finish:
_CET_ENDBR
diff --git a/Zend/asm/make_x86_64_sysv_macho_gas.S b/Zend/asm/make_x86_64_sysv_macho_gas.S
index 5d6c5431c598..06357f678ac9 100644
--- a/Zend/asm/make_x86_64_sysv_macho_gas.S
+++ b/Zend/asm/make_x86_64_sysv_macho_gas.S
@@ -25,6 +25,7 @@
****************************************************************************************/
.text
+.private_extern _make_fcontext
.globl _make_fcontext
.align 8
_make_fcontext:
diff --git a/Zend/tests/GHSA-wm6j-2649-pv75.phpt b/Zend/tests/GHSA-wm6j-2649-pv75.phpt
index 7257af27cb87..c1035938bd7b 100644
--- a/Zend/tests/GHSA-wm6j-2649-pv75.phpt
+++ b/Zend/tests/GHSA-wm6j-2649-pv75.phpt
@@ -15,6 +15,8 @@ mb_regex_encoding('iso-8859-11');
mb_ereg_search_init('x');
?>
--EXPECTF--
+Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d
+
Fatal error: Uncaught ValueError: mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "iso-8859-11" given in %s:%d
Stack trace:
#0 %s(%d): mb_regex_encoding('iso-8859-11')
diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt
index 9f8e9b77003b..3bb08504f581 100644
--- a/Zend/tests/assert/expect_015.phpt
+++ b/Zend/tests/assert/expect_015.phpt
@@ -201,7 +201,7 @@ assert(0 && ($a = function &(array &$a, ?X $b = null) use($c, &$d): ?X {
$s[$i] = $a[$j];
}
foreach ($a as $key => &$val) {
- print "$key => $val\n";
+ print "{$key} => {$val}\n";
}
while ($s[$i]) {
$i++;
@@ -299,12 +299,12 @@ assert(0 && ($a = function (): ?static {
echo 1;
}
$x = '\'"`$a';
- $x = "'\"`$a";
- $x = `'"\`$a`;
+ $x = "'\"`{$a}";
+ $x = `'"\`{$a}`;
$x = "{$a}b";
$x = "{$a}b";
$x = " {$foo->bar} {${$foo->bar}} ";
- $x = " ${---} ";
+ $x = " {${'---'}} ";
foo();
\foo();
namespace\foo();
diff --git a/Zend/tests/assert/expect_020.phpt b/Zend/tests/assert/expect_020.phpt
index d305667a28d7..2146abc96045 100644
--- a/Zend/tests/assert/expect_020.phpt
+++ b/Zend/tests/assert/expect_020.phpt
@@ -16,5 +16,5 @@ assert(0 && ($a = function () {
--EXPECT--
assert(): assert(0 && ($a = function () {
$var = 'test';
- $str = "$var, {$var[1]}, {$var}[], {$var[1]}[], {$var}[], {$var[1]}[]";
+ $str = "{$var}, {$var[1]}, {$var}[], {$var[1]}[], {$var}[], {$var[1]}[]";
})) failed
diff --git a/Zend/tests/asymmetric_visibility/virtual_get_only.phpt b/Zend/tests/asymmetric_visibility/virtual_get_only.phpt
index 3eaada70329d..a3f6f980bdfe 100644
--- a/Zend/tests/asymmetric_visibility/virtual_get_only.phpt
+++ b/Zend/tests/asymmetric_visibility/virtual_get_only.phpt
@@ -11,4 +11,4 @@ class Foo {
?>
--EXPECTF--
-Fatal error: Read-only virtual property Foo::$bar must not specify asymmetric visibility in %s on line %d
+Fatal error: get-only virtual property Foo::$bar must not specify asymmetric visibility in %s on line %d
diff --git a/Zend/tests/asymmetric_visibility/virtual_set_only.phpt b/Zend/tests/asymmetric_visibility/virtual_set_only.phpt
index 18abb7ac046a..7d1d2bc7cb99 100644
--- a/Zend/tests/asymmetric_visibility/virtual_set_only.phpt
+++ b/Zend/tests/asymmetric_visibility/virtual_set_only.phpt
@@ -11,4 +11,4 @@ class Foo {
?>
--EXPECTF--
-Fatal error: Write-only virtual property Foo::$bar must not specify asymmetric visibility in %s on line %d
+Fatal error: set-only virtual property Foo::$bar must not specify asymmetric visibility in %s on line %d
diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt
new file mode 100644
index 000000000000..9ebb8e4c0202
--- /dev/null
+++ b/Zend/tests/attributes/delayed_target_validation/with_Override_error_constant.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\DelayedTargetValidation] with #[\Override]: non-overrides still error (class constant)
+--FILE--
+
+--EXPECTF--
+Fatal error: DemoClass::CLASS_CONSTANT has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt
new file mode 100644
index 000000000000..53d75412a849
--- /dev/null
+++ b/Zend/tests/attributes/delayed_target_validation/with_Override_error_enum_case.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\DelayedTargetValidation] with #[\Override]: non-overrides still error (enum case)
+--FILE--
+
+--EXPECTF--
+Fatal error: DemoEnum::MyCase has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt b/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
index dd077f4b9cbd..494d85eaea9c 100644
--- a/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
+++ b/Zend/tests/attributes/delayed_target_validation/with_Override_okay.phpt
@@ -12,6 +12,8 @@ class Base {
set => $value;
}
+ public const CLASS_CONST = '';
+
public function printVal() {
echo __METHOD__ . "\n";
}
@@ -34,7 +36,7 @@ class DemoClass extends Base {
}
#[DelayedTargetValidation]
- #[Override] // Does nothing here
+ #[Override] // Does something here
public const CLASS_CONST = 'FOO';
public function __construct(
diff --git a/Zend/tests/attributes/nodiscard/007.phpt b/Zend/tests/attributes/nodiscard/007.phpt
index 1b72de8c22a0..de4604e15e48 100644
--- a/Zend/tests/attributes/nodiscard/007.phpt
+++ b/Zend/tests/attributes/nodiscard/007.phpt
@@ -12,6 +12,8 @@ zend_test_nodiscard();
?>
--EXPECTF--
+
Warning: The return value of function zend_test_nodiscard() should either be used or intentionally ignored by casting it as (void), custom message in %s on line %d
+
diff --git a/Zend/tests/attributes/override/constants/anon_failure.phpt b/Zend/tests/attributes/override/constants/anon_failure.phpt
new file mode 100644
index 000000000000..c43cedda81d7
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/anon_failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - anonymous class, no interface or parent class
+--FILE--
+
+--EXPECTF--
+Fatal error: class@anonymous::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/anon_interface.phpt b/Zend/tests/attributes/override/constants/anon_interface.phpt
new file mode 100644
index 000000000000..b19abfbf4d3a
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/anon_interface.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - anonymous class overrides interface
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/anon_parent.phpt b/Zend/tests/attributes/override/constants/anon_parent.phpt
new file mode 100644
index 000000000000..b430705c3c9d
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/anon_parent.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - anonymous class overrides parent class
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/basic.phpt b/Zend/tests/attributes/override/constants/basic.phpt
new file mode 100644
index 000000000000..c89007022051
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/basic.phpt
@@ -0,0 +1,57 @@
+--TEST--
+#[\Override]: Constants - basic
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/enum_failure.phpt b/Zend/tests/attributes/override/constants/enum_failure.phpt
new file mode 100644
index 000000000000..77ba225e9b9e
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/enum_failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no interface for enum constant
+--FILE--
+
+--EXPECTF--
+Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/enum_failure_case.phpt b/Zend/tests/attributes/override/constants/enum_failure_case.phpt
new file mode 100644
index 000000000000..2f2c6fc84a08
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/enum_failure_case.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no interface for enum case
+--FILE--
+
+--EXPECTF--
+Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/failure.phpt b/Zend/tests/attributes/override/constants/failure.phpt
new file mode 100644
index 000000000000..be621915d927
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no interface or parent class
+--FILE--
+
+--EXPECTF--
+Fatal error: Demo::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/interface_failure.phpt b/Zend/tests/attributes/override/constants/interface_failure.phpt
new file mode 100644
index 000000000000..0aa4d33d1301
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/interface_failure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - no parent interface
+--FILE--
+
+--EXPECTF--
+Fatal error: IFace::I has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/trait_failure.phpt b/Zend/tests/attributes/override/constants/trait_failure.phpt
new file mode 100644
index 000000000000..8d4af0e57977
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_failure.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - on a trait, no interface or parent class
+--FILE--
+
+--EXPECTF--
+Fatal error: UsesTrait::T has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/trait_interface.phpt b/Zend/tests/attributes/override/constants/trait_interface.phpt
new file mode 100644
index 000000000000..3a2ba799ae4e
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_interface.phpt
@@ -0,0 +1,23 @@
+--TEST--
+#[\Override]: Constants - on a trait, overrides interface
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_parent.phpt b/Zend/tests/attributes/override/constants/trait_parent.phpt
new file mode 100644
index 000000000000..2753aaf546a2
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_parent.phpt
@@ -0,0 +1,23 @@
+--TEST--
+#[\Override]: Constants - on a trait, overrides parent class
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_redeclared.phpt b/Zend/tests/attributes/override/constants/trait_redeclared.phpt
new file mode 100644
index 000000000000..984f9eedb668
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_redeclared.phpt
@@ -0,0 +1,21 @@
+--TEST--
+#[\Override]: Constants - trait constant redeclared, not overridden
+--FILE--
+
+--EXPECTF--
+Fatal error: UsesTrait::T has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt b/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt
new file mode 100644
index 000000000000..21a1aa532249
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_redeclared_interface.phpt
@@ -0,0 +1,25 @@
+--TEST--
+#[\Override]: Constants - trait constant redeclared, overrides interface
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt b/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt
new file mode 100644
index 000000000000..c3d1e43d2534
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_redeclared_parent.phpt
@@ -0,0 +1,25 @@
+--TEST--
+#[\Override]: Constants - trait constant redeclared, overrides parent class
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/trait_unused.phpt b/Zend/tests/attributes/override/constants/trait_unused.phpt
new file mode 100644
index 000000000000..785f2888e90b
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/trait_unused.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\Override]: Constants - on a trait, unused
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/visibility_01.phpt b/Zend/tests/attributes/override/constants/visibility_01.phpt
new file mode 100644
index 000000000000..49671471f758
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_01.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - private constant not overridden (by public constant)
+--FILE--
+
+--EXPECTF--
+Fatal error: Child::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/visibility_02.phpt b/Zend/tests/attributes/override/constants/visibility_02.phpt
new file mode 100644
index 000000000000..c160a29a6180
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_02.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - private constant not overridden (by private constant)
+--FILE--
+
+--EXPECTF--
+Fatal error: Child::C has #[\Override] attribute, but no matching parent constant exists in %s on line %d
diff --git a/Zend/tests/attributes/override/constants/visibility_03.phpt b/Zend/tests/attributes/override/constants/visibility_03.phpt
new file mode 100644
index 000000000000..db460f24375e
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_03.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - protected constant is overridden (by public constant)
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/override/constants/visibility_04.phpt b/Zend/tests/attributes/override/constants/visibility_04.phpt
new file mode 100644
index 000000000000..0ad46b022f43
--- /dev/null
+++ b/Zend/tests/attributes/override/constants/visibility_04.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Override]: Constants - protected constant is overridden (by protected constant)
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/backtrace/debug_backtrace_with_include_and_this.phpt b/Zend/tests/backtrace/debug_backtrace_with_include_and_this.phpt
index 49796a9b9103..18f155611f42 100644
--- a/Zend/tests/backtrace/debug_backtrace_with_include_and_this.phpt
+++ b/Zend/tests/backtrace/debug_backtrace_with_include_and_this.phpt
@@ -30,7 +30,7 @@ try {
}
?>
--EXPECTF--
-ERR#2: include(class://non.existent.Class): Failed to open stream: "CLWrapper::stream_open" call failed @ include
+ERR#2: include(): Failed to open stream: "CLWrapper::stream_open" call failed @ include
ERR#2: include(): Failed opening 'class://non.existent.Class' for inclusion (include_path='%s') @ include
Fatal error: Uncaught Exception: Failed loading class://non.existent.Class in %s
diff --git a/Zend/tests/bug30998.phpt b/Zend/tests/bug30998.phpt
index 9d8435121671..a4cd6de8f50e 100644
--- a/Zend/tests/bug30998.phpt
+++ b/Zend/tests/bug30998.phpt
@@ -14,6 +14,6 @@ set_error_handler('my_error');
$f = fopen("/tmp/blah", "r");
?>
--EXPECTF--
-fopen(/tmp/blah): Failed to open stream: %s (2) in %s:%d
+fopen(): Failed to open stream: %s (2) in %s:%d
-Warning: fopen(/tmp/blah): Failed to open stream: %s in %s on line %d
+Warning: fopen(): Failed to open stream: %s in %s on line %d
diff --git a/Zend/tests/bug60909_1.phpt b/Zend/tests/bug60909_1.phpt
index 4404adfd54a3..bde5a381ba47 100644
--- a/Zend/tests/bug60909_1.phpt
+++ b/Zend/tests/bug60909_1.phpt
@@ -11,10 +11,10 @@ set_error_handler(function($errno, $errstr, $errfile, $errline){
require 'notfound.php';
?>
--EXPECTF--
-error(require(notfound.php): Failed to open stream: %s)
+error(require(): Failed to open stream: %s)
Fatal error: Uncaught Exception: Foo in %sbug60909_1.php:5
Stack trace:
-#0 %s(%d): {closure:%s:%d}(2, 'require(notfoun...', '%s', 8)
+#0 %s(%d): {closure:%s:%d}(2, 'require(): Fail...', '%s', 8)
#1 %sbug60909_1.php(8): require()
#2 {main}
thrown in %sbug60909_1.php on line 5
diff --git a/Zend/tests/closures/fcc-cache.phpt b/Zend/tests/closures/fcc-cache.phpt
new file mode 100644
index 000000000000..3b47a2410d14
--- /dev/null
+++ b/Zend/tests/closures/fcc-cache.phpt
@@ -0,0 +1,8 @@
+--TEST--
+FCCs are cached and shared
+--FILE--
+
+--EXPECT--
+bool(true)
diff --git a/Zend/tests/closures/gh19653_3.phpt b/Zend/tests/closures/gh19653_3.phpt
new file mode 100644
index 000000000000..7a9ac589182c
--- /dev/null
+++ b/Zend/tests/closures/gh19653_3.phpt
@@ -0,0 +1,21 @@
+--TEST--
+GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - temporary method variation
+--EXTENSIONS--
+zend_test
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Unknown named parameter $tmpMethodParamName in %s:%d
+Stack trace:
+#0 %s(%d): usage1(Object(Closure))
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/comparison/spaceship_type_compare.phpt b/Zend/tests/comparison/spaceship_type_compare.phpt
new file mode 100644
index 000000000000..7382a593cc84
--- /dev/null
+++ b/Zend/tests/comparison/spaceship_type_compare.phpt
@@ -0,0 +1,536 @@
+--TEST--
+Compare types via <=> operator
+--FILE--
+ $r) {
+ 0 => "EQUAL",
+ 1 => match ($r <=> $l) {
+ -1 => "GREATER THAN",
+ 1 => "UNCOMPARABLE",
+ },
+ -1 => "LESS THAN",
+ };
+}
+
+class test {
+}
+
+$values = [
+ 'null' => null,
+ 'false' => false,
+ 'true' => true,
+ 'i-5' => -5,
+ 'i0' => 0,
+ 'i10' => 10,
+ 'f-5' => -5.0,
+ 'f0' => 0.0,
+ 'f2.5' => 2.5,
+ 'f10' => 10.0,
+ 's""' => "",
+ 's"str"' => "str",
+ 's"-5"' => "-5",
+ 's"0"' => "0",
+ 's"2.5"' => "2.5",
+ 's"10"' => "10",
+ 'a[]' => [],
+ 'a[1, 2, 3]' => [1, 2, 3],
+ 'c"stdClass"' => new stdClass(),
+ 'c"test"' => new test(),
+ 'r"STDOUT"' => STDOUT,
+ 'r"STDERR"' => STDERR,
+];
+
+foreach($values as $l_name => $l) {
+ foreach($values as $r_name => $r) {
+ echo $l_name, ' ', cmp($l, $r), ' ', $r_name, "\n";
+ }
+}
+
+?>
+--EXPECT--
+null EQUAL null
+null EQUAL false
+null LESS THAN true
+null LESS THAN i-5
+null EQUAL i0
+null LESS THAN i10
+null LESS THAN f-5
+null EQUAL f0
+null LESS THAN f2.5
+null LESS THAN f10
+null EQUAL s""
+null LESS THAN s"str"
+null LESS THAN s"-5"
+null LESS THAN s"0"
+null LESS THAN s"2.5"
+null LESS THAN s"10"
+null EQUAL a[]
+null LESS THAN a[1, 2, 3]
+null LESS THAN c"stdClass"
+null LESS THAN c"test"
+null LESS THAN r"STDOUT"
+null LESS THAN r"STDERR"
+false EQUAL null
+false EQUAL false
+false LESS THAN true
+false LESS THAN i-5
+false EQUAL i0
+false LESS THAN i10
+false LESS THAN f-5
+false EQUAL f0
+false LESS THAN f2.5
+false LESS THAN f10
+false EQUAL s""
+false LESS THAN s"str"
+false LESS THAN s"-5"
+false EQUAL s"0"
+false LESS THAN s"2.5"
+false LESS THAN s"10"
+false EQUAL a[]
+false LESS THAN a[1, 2, 3]
+false LESS THAN c"stdClass"
+false LESS THAN c"test"
+false LESS THAN r"STDOUT"
+false LESS THAN r"STDERR"
+true GREATER THAN null
+true GREATER THAN false
+true EQUAL true
+true EQUAL i-5
+true GREATER THAN i0
+true EQUAL i10
+true EQUAL f-5
+true GREATER THAN f0
+true EQUAL f2.5
+true EQUAL f10
+true GREATER THAN s""
+true EQUAL s"str"
+true EQUAL s"-5"
+true GREATER THAN s"0"
+true EQUAL s"2.5"
+true EQUAL s"10"
+true GREATER THAN a[]
+true EQUAL a[1, 2, 3]
+true EQUAL c"stdClass"
+true EQUAL c"test"
+true EQUAL r"STDOUT"
+true EQUAL r"STDERR"
+i-5 GREATER THAN null
+i-5 GREATER THAN false
+i-5 EQUAL true
+i-5 EQUAL i-5
+i-5 LESS THAN i0
+i-5 LESS THAN i10
+i-5 EQUAL f-5
+i-5 LESS THAN f0
+i-5 LESS THAN f2.5
+i-5 LESS THAN f10
+i-5 GREATER THAN s""
+i-5 LESS THAN s"str"
+i-5 EQUAL s"-5"
+i-5 LESS THAN s"0"
+i-5 LESS THAN s"2.5"
+i-5 LESS THAN s"10"
+i-5 LESS THAN a[]
+i-5 LESS THAN a[1, 2, 3]
+i-5 LESS THAN c"stdClass"
+i-5 LESS THAN c"test"
+i-5 LESS THAN r"STDOUT"
+i-5 LESS THAN r"STDERR"
+i0 EQUAL null
+i0 EQUAL false
+i0 LESS THAN true
+i0 GREATER THAN i-5
+i0 EQUAL i0
+i0 LESS THAN i10
+i0 GREATER THAN f-5
+i0 EQUAL f0
+i0 LESS THAN f2.5
+i0 LESS THAN f10
+i0 GREATER THAN s""
+i0 LESS THAN s"str"
+i0 GREATER THAN s"-5"
+i0 EQUAL s"0"
+i0 LESS THAN s"2.5"
+i0 LESS THAN s"10"
+i0 LESS THAN a[]
+i0 LESS THAN a[1, 2, 3]
+i0 LESS THAN c"stdClass"
+i0 LESS THAN c"test"
+i0 LESS THAN r"STDOUT"
+i0 LESS THAN r"STDERR"
+i10 GREATER THAN null
+i10 GREATER THAN false
+i10 EQUAL true
+i10 GREATER THAN i-5
+i10 GREATER THAN i0
+i10 EQUAL i10
+i10 GREATER THAN f-5
+i10 GREATER THAN f0
+i10 GREATER THAN f2.5
+i10 EQUAL f10
+i10 GREATER THAN s""
+i10 LESS THAN s"str"
+i10 GREATER THAN s"-5"
+i10 GREATER THAN s"0"
+i10 GREATER THAN s"2.5"
+i10 EQUAL s"10"
+i10 LESS THAN a[]
+i10 LESS THAN a[1, 2, 3]
+i10 GREATER THAN c"stdClass"
+i10 GREATER THAN c"test"
+i10 GREATER THAN r"STDOUT"
+i10 GREATER THAN r"STDERR"
+f-5 GREATER THAN null
+f-5 GREATER THAN false
+f-5 EQUAL true
+f-5 EQUAL i-5
+f-5 LESS THAN i0
+f-5 LESS THAN i10
+f-5 EQUAL f-5
+f-5 LESS THAN f0
+f-5 LESS THAN f2.5
+f-5 LESS THAN f10
+f-5 GREATER THAN s""
+f-5 LESS THAN s"str"
+f-5 EQUAL s"-5"
+f-5 LESS THAN s"0"
+f-5 LESS THAN s"2.5"
+f-5 LESS THAN s"10"
+f-5 LESS THAN a[]
+f-5 LESS THAN a[1, 2, 3]
+f-5 LESS THAN c"stdClass"
+f-5 LESS THAN c"test"
+f-5 LESS THAN r"STDOUT"
+f-5 LESS THAN r"STDERR"
+f0 EQUAL null
+f0 EQUAL false
+f0 LESS THAN true
+f0 GREATER THAN i-5
+f0 EQUAL i0
+f0 LESS THAN i10
+f0 GREATER THAN f-5
+f0 EQUAL f0
+f0 LESS THAN f2.5
+f0 LESS THAN f10
+f0 GREATER THAN s""
+f0 LESS THAN s"str"
+f0 GREATER THAN s"-5"
+f0 EQUAL s"0"
+f0 LESS THAN s"2.5"
+f0 LESS THAN s"10"
+f0 LESS THAN a[]
+f0 LESS THAN a[1, 2, 3]
+f0 LESS THAN c"stdClass"
+f0 LESS THAN c"test"
+f0 LESS THAN r"STDOUT"
+f0 LESS THAN r"STDERR"
+f2.5 GREATER THAN null
+f2.5 GREATER THAN false
+f2.5 EQUAL true
+f2.5 GREATER THAN i-5
+f2.5 GREATER THAN i0
+f2.5 LESS THAN i10
+f2.5 GREATER THAN f-5
+f2.5 GREATER THAN f0
+f2.5 EQUAL f2.5
+f2.5 LESS THAN f10
+f2.5 GREATER THAN s""
+f2.5 LESS THAN s"str"
+f2.5 GREATER THAN s"-5"
+f2.5 GREATER THAN s"0"
+f2.5 EQUAL s"2.5"
+f2.5 LESS THAN s"10"
+f2.5 LESS THAN a[]
+f2.5 LESS THAN a[1, 2, 3]
+f2.5 GREATER THAN c"stdClass"
+f2.5 GREATER THAN c"test"
+f2.5 GREATER THAN r"STDOUT"
+f2.5 LESS THAN r"STDERR"
+f10 GREATER THAN null
+f10 GREATER THAN false
+f10 EQUAL true
+f10 GREATER THAN i-5
+f10 GREATER THAN i0
+f10 EQUAL i10
+f10 GREATER THAN f-5
+f10 GREATER THAN f0
+f10 GREATER THAN f2.5
+f10 EQUAL f10
+f10 GREATER THAN s""
+f10 LESS THAN s"str"
+f10 GREATER THAN s"-5"
+f10 GREATER THAN s"0"
+f10 GREATER THAN s"2.5"
+f10 EQUAL s"10"
+f10 LESS THAN a[]
+f10 LESS THAN a[1, 2, 3]
+f10 GREATER THAN c"stdClass"
+f10 GREATER THAN c"test"
+f10 GREATER THAN r"STDOUT"
+f10 GREATER THAN r"STDERR"
+s"" EQUAL null
+s"" EQUAL false
+s"" LESS THAN true
+s"" LESS THAN i-5
+s"" LESS THAN i0
+s"" LESS THAN i10
+s"" LESS THAN f-5
+s"" LESS THAN f0
+s"" LESS THAN f2.5
+s"" LESS THAN f10
+s"" EQUAL s""
+s"" LESS THAN s"str"
+s"" LESS THAN s"-5"
+s"" LESS THAN s"0"
+s"" LESS THAN s"2.5"
+s"" LESS THAN s"10"
+s"" LESS THAN a[]
+s"" LESS THAN a[1, 2, 3]
+s"" LESS THAN c"stdClass"
+s"" LESS THAN c"test"
+s"" LESS THAN r"STDOUT"
+s"" LESS THAN r"STDERR"
+s"str" GREATER THAN null
+s"str" GREATER THAN false
+s"str" EQUAL true
+s"str" GREATER THAN i-5
+s"str" GREATER THAN i0
+s"str" GREATER THAN i10
+s"str" GREATER THAN f-5
+s"str" GREATER THAN f0
+s"str" GREATER THAN f2.5
+s"str" GREATER THAN f10
+s"str" GREATER THAN s""
+s"str" EQUAL s"str"
+s"str" GREATER THAN s"-5"
+s"str" GREATER THAN s"0"
+s"str" GREATER THAN s"2.5"
+s"str" GREATER THAN s"10"
+s"str" LESS THAN a[]
+s"str" LESS THAN a[1, 2, 3]
+s"str" LESS THAN c"stdClass"
+s"str" LESS THAN c"test"
+s"str" LESS THAN r"STDOUT"
+s"str" LESS THAN r"STDERR"
+s"-5" GREATER THAN null
+s"-5" GREATER THAN false
+s"-5" EQUAL true
+s"-5" EQUAL i-5
+s"-5" LESS THAN i0
+s"-5" LESS THAN i10
+s"-5" EQUAL f-5
+s"-5" LESS THAN f0
+s"-5" LESS THAN f2.5
+s"-5" LESS THAN f10
+s"-5" GREATER THAN s""
+s"-5" LESS THAN s"str"
+s"-5" EQUAL s"-5"
+s"-5" LESS THAN s"0"
+s"-5" LESS THAN s"2.5"
+s"-5" LESS THAN s"10"
+s"-5" LESS THAN a[]
+s"-5" LESS THAN a[1, 2, 3]
+s"-5" LESS THAN c"stdClass"
+s"-5" LESS THAN c"test"
+s"-5" LESS THAN r"STDOUT"
+s"-5" LESS THAN r"STDERR"
+s"0" GREATER THAN null
+s"0" EQUAL false
+s"0" LESS THAN true
+s"0" GREATER THAN i-5
+s"0" EQUAL i0
+s"0" LESS THAN i10
+s"0" GREATER THAN f-5
+s"0" EQUAL f0
+s"0" LESS THAN f2.5
+s"0" LESS THAN f10
+s"0" GREATER THAN s""
+s"0" LESS THAN s"str"
+s"0" GREATER THAN s"-5"
+s"0" EQUAL s"0"
+s"0" LESS THAN s"2.5"
+s"0" LESS THAN s"10"
+s"0" LESS THAN a[]
+s"0" LESS THAN a[1, 2, 3]
+s"0" LESS THAN c"stdClass"
+s"0" LESS THAN c"test"
+s"0" LESS THAN r"STDOUT"
+s"0" LESS THAN r"STDERR"
+s"2.5" GREATER THAN null
+s"2.5" GREATER THAN false
+s"2.5" EQUAL true
+s"2.5" GREATER THAN i-5
+s"2.5" GREATER THAN i0
+s"2.5" LESS THAN i10
+s"2.5" GREATER THAN f-5
+s"2.5" GREATER THAN f0
+s"2.5" EQUAL f2.5
+s"2.5" LESS THAN f10
+s"2.5" GREATER THAN s""
+s"2.5" LESS THAN s"str"
+s"2.5" GREATER THAN s"-5"
+s"2.5" GREATER THAN s"0"
+s"2.5" EQUAL s"2.5"
+s"2.5" LESS THAN s"10"
+s"2.5" LESS THAN a[]
+s"2.5" LESS THAN a[1, 2, 3]
+s"2.5" LESS THAN c"stdClass"
+s"2.5" LESS THAN c"test"
+s"2.5" GREATER THAN r"STDOUT"
+s"2.5" LESS THAN r"STDERR"
+s"10" GREATER THAN null
+s"10" GREATER THAN false
+s"10" EQUAL true
+s"10" GREATER THAN i-5
+s"10" GREATER THAN i0
+s"10" EQUAL i10
+s"10" GREATER THAN f-5
+s"10" GREATER THAN f0
+s"10" GREATER THAN f2.5
+s"10" EQUAL f10
+s"10" GREATER THAN s""
+s"10" LESS THAN s"str"
+s"10" GREATER THAN s"-5"
+s"10" GREATER THAN s"0"
+s"10" GREATER THAN s"2.5"
+s"10" EQUAL s"10"
+s"10" LESS THAN a[]
+s"10" LESS THAN a[1, 2, 3]
+s"10" LESS THAN c"stdClass"
+s"10" LESS THAN c"test"
+s"10" GREATER THAN r"STDOUT"
+s"10" GREATER THAN r"STDERR"
+a[] EQUAL null
+a[] EQUAL false
+a[] LESS THAN true
+a[] GREATER THAN i-5
+a[] GREATER THAN i0
+a[] GREATER THAN i10
+a[] GREATER THAN f-5
+a[] GREATER THAN f0
+a[] GREATER THAN f2.5
+a[] GREATER THAN f10
+a[] GREATER THAN s""
+a[] GREATER THAN s"str"
+a[] GREATER THAN s"-5"
+a[] GREATER THAN s"0"
+a[] GREATER THAN s"2.5"
+a[] GREATER THAN s"10"
+a[] EQUAL a[]
+a[] LESS THAN a[1, 2, 3]
+a[] LESS THAN c"stdClass"
+a[] LESS THAN c"test"
+a[] GREATER THAN r"STDOUT"
+a[] GREATER THAN r"STDERR"
+a[1, 2, 3] GREATER THAN null
+a[1, 2, 3] GREATER THAN false
+a[1, 2, 3] EQUAL true
+a[1, 2, 3] GREATER THAN i-5
+a[1, 2, 3] GREATER THAN i0
+a[1, 2, 3] GREATER THAN i10
+a[1, 2, 3] GREATER THAN f-5
+a[1, 2, 3] GREATER THAN f0
+a[1, 2, 3] GREATER THAN f2.5
+a[1, 2, 3] GREATER THAN f10
+a[1, 2, 3] GREATER THAN s""
+a[1, 2, 3] GREATER THAN s"str"
+a[1, 2, 3] GREATER THAN s"-5"
+a[1, 2, 3] GREATER THAN s"0"
+a[1, 2, 3] GREATER THAN s"2.5"
+a[1, 2, 3] GREATER THAN s"10"
+a[1, 2, 3] GREATER THAN a[]
+a[1, 2, 3] EQUAL a[1, 2, 3]
+a[1, 2, 3] LESS THAN c"stdClass"
+a[1, 2, 3] LESS THAN c"test"
+a[1, 2, 3] GREATER THAN r"STDOUT"
+a[1, 2, 3] GREATER THAN r"STDERR"
+c"stdClass" GREATER THAN null
+c"stdClass" GREATER THAN false
+c"stdClass" EQUAL true
+c"stdClass" GREATER THAN i-5
+c"stdClass" GREATER THAN i0
+c"stdClass" LESS THAN i10
+c"stdClass" GREATER THAN f-5
+c"stdClass" GREATER THAN f0
+c"stdClass" LESS THAN f2.5
+c"stdClass" LESS THAN f10
+c"stdClass" GREATER THAN s""
+c"stdClass" GREATER THAN s"str"
+c"stdClass" GREATER THAN s"-5"
+c"stdClass" GREATER THAN s"0"
+c"stdClass" GREATER THAN s"2.5"
+c"stdClass" GREATER THAN s"10"
+c"stdClass" GREATER THAN a[]
+c"stdClass" GREATER THAN a[1, 2, 3]
+c"stdClass" EQUAL c"stdClass"
+c"stdClass" UNCOMPARABLE c"test"
+c"stdClass" GREATER THAN r"STDOUT"
+c"stdClass" GREATER THAN r"STDERR"
+c"test" GREATER THAN null
+c"test" GREATER THAN false
+c"test" EQUAL true
+c"test" GREATER THAN i-5
+c"test" GREATER THAN i0
+c"test" LESS THAN i10
+c"test" GREATER THAN f-5
+c"test" GREATER THAN f0
+c"test" LESS THAN f2.5
+c"test" LESS THAN f10
+c"test" GREATER THAN s""
+c"test" GREATER THAN s"str"
+c"test" GREATER THAN s"-5"
+c"test" GREATER THAN s"0"
+c"test" GREATER THAN s"2.5"
+c"test" GREATER THAN s"10"
+c"test" GREATER THAN a[]
+c"test" GREATER THAN a[1, 2, 3]
+c"test" UNCOMPARABLE c"stdClass"
+c"test" EQUAL c"test"
+c"test" GREATER THAN r"STDOUT"
+c"test" GREATER THAN r"STDERR"
+r"STDOUT" GREATER THAN null
+r"STDOUT" GREATER THAN false
+r"STDOUT" EQUAL true
+r"STDOUT" GREATER THAN i-5
+r"STDOUT" GREATER THAN i0
+r"STDOUT" LESS THAN i10
+r"STDOUT" GREATER THAN f-5
+r"STDOUT" GREATER THAN f0
+r"STDOUT" LESS THAN f2.5
+r"STDOUT" LESS THAN f10
+r"STDOUT" GREATER THAN s""
+r"STDOUT" GREATER THAN s"str"
+r"STDOUT" GREATER THAN s"-5"
+r"STDOUT" GREATER THAN s"0"
+r"STDOUT" LESS THAN s"2.5"
+r"STDOUT" LESS THAN s"10"
+r"STDOUT" LESS THAN a[]
+r"STDOUT" LESS THAN a[1, 2, 3]
+r"STDOUT" LESS THAN c"stdClass"
+r"STDOUT" LESS THAN c"test"
+r"STDOUT" EQUAL r"STDOUT"
+r"STDOUT" LESS THAN r"STDERR"
+r"STDERR" GREATER THAN null
+r"STDERR" GREATER THAN false
+r"STDERR" EQUAL true
+r"STDERR" GREATER THAN i-5
+r"STDERR" GREATER THAN i0
+r"STDERR" LESS THAN i10
+r"STDERR" GREATER THAN f-5
+r"STDERR" GREATER THAN f0
+r"STDERR" GREATER THAN f2.5
+r"STDERR" LESS THAN f10
+r"STDERR" GREATER THAN s""
+r"STDERR" GREATER THAN s"str"
+r"STDERR" GREATER THAN s"-5"
+r"STDERR" GREATER THAN s"0"
+r"STDERR" GREATER THAN s"2.5"
+r"STDERR" LESS THAN s"10"
+r"STDERR" LESS THAN a[]
+r"STDERR" LESS THAN a[1, 2, 3]
+r"STDERR" LESS THAN c"stdClass"
+r"STDERR" LESS THAN c"test"
+r"STDERR" GREATER THAN r"STDOUT"
+r"STDERR" EQUAL r"STDERR"
diff --git a/Zend/tests/debug_info/debug_info-error-0.0.phpt b/Zend/tests/debug_info/debug_info-error-0.0.phpt
index ab41b440fc88..4df25c4c95af 100644
--- a/Zend/tests/debug_info/debug_info-error-0.0.phpt
+++ b/Zend/tests/debug_info/debug_info-error-0.0.phpt
@@ -17,4 +17,4 @@ $c = new C(0.0);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-0.phpt b/Zend/tests/debug_info/debug_info-error-0.phpt
index 37289c6468ff..c0cedf3b88e5 100644
--- a/Zend/tests/debug_info/debug_info-error-0.phpt
+++ b/Zend/tests/debug_info/debug_info-error-0.phpt
@@ -17,4 +17,4 @@ $c = new C(0);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-1.0.phpt b/Zend/tests/debug_info/debug_info-error-1.0.phpt
index 9b168621b5ef..6af945cb0e37 100644
--- a/Zend/tests/debug_info/debug_info-error-1.0.phpt
+++ b/Zend/tests/debug_info/debug_info-error-1.0.phpt
@@ -17,4 +17,4 @@ $c = new C(1.0);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-1.phpt b/Zend/tests/debug_info/debug_info-error-1.phpt
index ae01a6055c00..7cadd485118d 100644
--- a/Zend/tests/debug_info/debug_info-error-1.phpt
+++ b/Zend/tests/debug_info/debug_info-error-1.phpt
@@ -17,4 +17,4 @@ $c = new C(1);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt b/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt
new file mode 100644
index 000000000000..c2a6f4357a5c
--- /dev/null
+++ b/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Testing __debugInfo() magic method declared with non-canonical case
+--FILE--
+val;
+ }
+ public function __construct($val) {
+ $this->val = $val;
+ }
+}
+
+$c = new C(1);
+var_dump($c);
+?>
+--EXPECTF--
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-empty_str.phpt b/Zend/tests/debug_info/debug_info-error-empty_str.phpt
index bbab78cd8202..21611cc9bde5 100644
--- a/Zend/tests/debug_info/debug_info-error-empty_str.phpt
+++ b/Zend/tests/debug_info/debug_info-error-empty_str.phpt
@@ -17,4 +17,4 @@ $c = new C("");
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-false.phpt b/Zend/tests/debug_info/debug_info-error-false.phpt
index 3e48372c420c..fb5a35c2d1e3 100644
--- a/Zend/tests/debug_info/debug_info-error-false.phpt
+++ b/Zend/tests/debug_info/debug_info-error-false.phpt
@@ -17,4 +17,4 @@ $c = new C(false);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-object.phpt b/Zend/tests/debug_info/debug_info-error-object.phpt
index 42e073999908..5cdc6b289e62 100644
--- a/Zend/tests/debug_info/debug_info-error-object.phpt
+++ b/Zend/tests/debug_info/debug_info-error-object.phpt
@@ -17,4 +17,4 @@ $c = new C(new stdClass);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-resource.phpt b/Zend/tests/debug_info/debug_info-error-resource.phpt
index ccacce7a74b4..1fc84fe83d87 100644
--- a/Zend/tests/debug_info/debug_info-error-resource.phpt
+++ b/Zend/tests/debug_info/debug_info-error-resource.phpt
@@ -19,4 +19,4 @@ $c = new C(fopen("data:text/plain,Foo", 'r'));
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-str.phpt b/Zend/tests/debug_info/debug_info-error-str.phpt
index 85d3f63b97e0..fe4201de1c9f 100644
--- a/Zend/tests/debug_info/debug_info-error-str.phpt
+++ b/Zend/tests/debug_info/debug_info-error-str.phpt
@@ -17,4 +17,4 @@ $c = new C("foo");
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-true.phpt b/Zend/tests/debug_info/debug_info-error-true.phpt
index 3843c6a7a14a..12bb7329a856 100644
--- a/Zend/tests/debug_info/debug_info-error-true.phpt
+++ b/Zend/tests/debug_info/debug_info-error-true.phpt
@@ -17,4 +17,4 @@ $c = new C(true);
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info.phpt b/Zend/tests/debug_info/debug_info.phpt
index bff5876777ba..e7bb4d48aec6 100644
--- a/Zend/tests/debug_info/debug_info.phpt
+++ b/Zend/tests/debug_info/debug_info.phpt
@@ -21,6 +21,12 @@ class Bar {
}
}
+class Baz {
+ public function __debugInfo(): ?array {
+ return null;
+ }
+}
+
$f = new Foo;
var_dump($f);
@@ -28,6 +34,7 @@ $b = new Bar;
var_dump($b);
?>
--EXPECTF--
+Deprecated: Returning null from Baz::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d
object(Foo)#%d (3) {
["a"]=>
int(1)
diff --git a/Zend/tests/display_error_function_args.phpt b/Zend/tests/display_error_function_args.phpt
new file mode 100644
index 000000000000..03530eb98189
--- /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/enum/__debugInfo.phpt b/Zend/tests/enum/__debugInfo.phpt
deleted file mode 100644
index 33e0f49851f9..000000000000
--- a/Zend/tests/enum/__debugInfo.phpt
+++ /dev/null
@@ -1,16 +0,0 @@
---TEST--
-Enum __debugInfo
---FILE--
-cases();
- }
-}
-
-?>
---EXPECTF--
-Fatal error: Enum Foo cannot include magic method __debugInfo in %s on line %d
diff --git a/Zend/tests/enum/debugInfo/backed_enum_value.phpt b/Zend/tests/enum/debugInfo/backed_enum_value.phpt
new file mode 100644
index 000000000000..2bf56eb3a59e
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/backed_enum_value.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum with __debugInfo() magic method - backed enum value accessible
+--FILE--
+name . ' = ' . $this->value];
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECT--
+enum(Foo::Bar) (1) {
+ [0]=>
+ string(14) "Foo::Bar = Baz"
+}
diff --git a/Zend/tests/enum/debugInfo/magic_method.phpt b/Zend/tests/enum/debugInfo/magic_method.phpt
new file mode 100644
index 000000000000..4c5963f73b26
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/magic_method.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum supports __debugInfo() magic method
+--FILE--
+name . ' is a case of the ' . __CLASS__ . ' enum'];
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECT--
+enum(Foo::Bar) (1) {
+ [0]=>
+ string(29) "Bar is a case of the Foo enum"
+}
diff --git a/Zend/tests/enum/debugInfo/missing_magic.phpt b/Zend/tests/enum/debugInfo/missing_magic.phpt
new file mode 100644
index 000000000000..9cb5b0c69e52
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/missing_magic.phpt
@@ -0,0 +1,14 @@
+--TEST--
+When an enum does not have __debugInfo() it is printed nicely
+--FILE--
+
+--EXPECT--
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/debugInfo/param_validation.phpt b/Zend/tests/enum/debugInfo/param_validation.phpt
new file mode 100644
index 000000000000..84c60730822e
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/param_validation.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum with __debugInfo() magic method - param validation
+--FILE--
+
+--EXPECTF--
+Fatal error: Method Foo::__debugInfo() cannot take arguments in %s on line %d
diff --git a/Zend/tests/enum/debugInfo/return_validation.phpt b/Zend/tests/enum/debugInfo/return_validation.phpt
new file mode 100644
index 000000000000..5583dcadf409
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/return_validation.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum with __debugInfo() magic method - return validation
+--FILE--
+
+--EXPECTF--
+Fatal error: Foo::__debugInfo(): Return type must be ?array when declared in %s on line %d
diff --git a/Zend/tests/enum/debugInfo/visibility_validation.phpt b/Zend/tests/enum/debugInfo/visibility_validation.phpt
new file mode 100644
index 000000000000..97e2f2c7456b
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/visibility_validation.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Enum with __debugInfo() magic method - visibility validation
+--FILE--
+
+--EXPECTF--
+Warning: The magic method Foo::__debugInfo() must have public visibility in %s on line %d
+enum(Foo::Bar) (0) {
+}
diff --git a/Zend/tests/enum/no-class-implements-backed-enum.phpt b/Zend/tests/enum/no-class-implements-backed-enum.phpt
index f6f37818da5a..f8d4fd13a423 100644
--- a/Zend/tests/enum/no-class-implements-backed-enum.phpt
+++ b/Zend/tests/enum/no-class-implements-backed-enum.phpt
@@ -7,4 +7,4 @@ class Foo implements BackedEnum {}
?>
--EXPECTF--
-Fatal error: Non-enum class Foo cannot implement interface BackedEnum in %s on line %d
+Fatal error: Non-enum class Foo cannot implement interface UnitEnum in %s on line %d
diff --git a/Zend/tests/exit/exit_as_function.phpt b/Zend/tests/exit/exit_as_function.phpt
index fb95b9f28817..726cf1f0d7a9 100644
--- a/Zend/tests/exit/exit_as_function.phpt
+++ b/Zend/tests/exit/exit_as_function.phpt
@@ -19,10 +19,10 @@ foreach ($values as $value) {
}
?>
---EXPECT--
+--EXPECTF--
string(4) "exit"
string(3) "die"
-object(Closure)#1 (2) {
+object(Closure)#%d (2) {
["function"]=>
string(4) "exit"
["parameter"]=>
@@ -31,7 +31,7 @@ object(Closure)#1 (2) {
string(10) ""
}
}
-object(Closure)#2 (2) {
+object(Closure)#%d (2) {
["function"]=>
string(4) "exit"
["parameter"]=>
diff --git a/Zend/tests/first_class_callable/constexpr/gh22782.inc b/Zend/tests/first_class_callable/constexpr/gh22782.inc
new file mode 100644
index 000000000000..d67ba4eab14e
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/gh22782.inc
@@ -0,0 +1,18 @@
+
+--FILE--
+getAttributes(A::class)[0]->newInstance()->fn)('hello'));
+echo "# Method attr\n";
+var_dump((new ReflectionMethod(C::class, 'f')->getAttributes(A::class)[0]->newInstance()->fn)('hello'));
+echo "# Method default value\n";
+var_dump((new C()->f())('hello'));
+
+?>
+--EXPECT--
+# Class const
+int(5)
+# Class attr
+int(5)
+# Method attr
+int(5)
+# Method default value
+int(5)
diff --git a/Zend/tests/first_class_callable/constexpr/namespace_004.phpt b/Zend/tests/first_class_callable/constexpr/namespace_004.phpt
index 0fc23422199d..6fe99593bf95 100644
--- a/Zend/tests/first_class_callable/constexpr/namespace_004.phpt
+++ b/Zend/tests/first_class_callable/constexpr/namespace_004.phpt
@@ -26,7 +26,7 @@ foo();
?>
--EXPECTF--
-object(Closure)#1 (2) {
+object(Closure)#%d (2) {
["function"]=>
string(6) "strrev"
["parameter"]=>
@@ -36,7 +36,7 @@ object(Closure)#1 (2) {
}
}
string(3) "cba"
-object(Closure)#2 (2) {
+object(Closure)#%d (2) {
["function"]=>
string(6) "strrev"
["parameter"]=>
@@ -46,7 +46,7 @@ object(Closure)#2 (2) {
}
}
string(3) "cba"
-object(Closure)#2 (2) {
+object(Closure)#%d (2) {
["function"]=>
string(6) "strrev"
["parameter"]=>
@@ -56,7 +56,7 @@ object(Closure)#2 (2) {
}
}
string(3) "cba"
-object(Closure)#1 (2) {
+object(Closure)#%d (2) {
["function"]=>
string(6) "strrev"
["parameter"]=>
diff --git a/Zend/tests/first_class_callable/first_class_callable_optimization.phpt b/Zend/tests/first_class_callable/first_class_callable_optimization.phpt
index 707b6a7299a4..169f6dfd62cc 100644
--- a/Zend/tests/first_class_callable/first_class_callable_optimization.phpt
+++ b/Zend/tests/first_class_callable/first_class_callable_optimization.phpt
@@ -10,12 +10,12 @@ var_dump(test1(...));
var_dump(test2(...));
?>
---EXPECT--
-object(Closure)#1 (1) {
+--EXPECTF--
+object(Closure)#%d (1) {
["function"]=>
string(5) "test1"
}
-object(Closure)#1 (1) {
+object(Closure)#%d (1) {
["function"]=>
string(5) "test2"
}
diff --git a/Zend/tests/functions/zend_call_function_deprecated_frame.phpt b/Zend/tests/functions/zend_call_function_deprecated_frame.phpt
new file mode 100644
index 000000000000..83d05144c0bd
--- /dev/null
+++ b/Zend/tests/functions/zend_call_function_deprecated_frame.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Deprecated function notice promoted to exception within zend_call_function()
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Exception: Function foo() is deprecated in %s:%d
+Stack trace:
+#0 %s(%d): {closure:%s:%d}(16384, 'Function foo() ...', '%s', %d)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/gh11735_2.phpt b/Zend/tests/gh11735_2.phpt
index b568b6f6df37..521862b48237 100644
--- a/Zend/tests/gh11735_2.phpt
+++ b/Zend/tests/gh11735_2.phpt
@@ -13,5 +13,5 @@ stream_wrapper_register('foo', 'FooWrapper');
var_dump(fopen('foo://bar', 'r'));
?>
--EXPECTF--
-Warning: fopen(foo://bar): Failed to open stream: "FooWrapper::stream_open" call failed in %s on line %d
+Warning: fopen(): Failed to open stream: "FooWrapper::stream_open" call failed in %s on line %d
bool(false)
diff --git a/Zend/tests/gh14003.phpt b/Zend/tests/gh14003.phpt
index 92a6c5919ab5..8d25cad863c1 100644
--- a/Zend/tests/gh14003.phpt
+++ b/Zend/tests/gh14003.phpt
@@ -18,7 +18,6 @@ array_filter(
--EXPECTF--
Fatal error: Uncaught Exception: Test in %s:%d
Stack trace:
-#0 [internal function]: foo('a')
-#1 %s(%d): array_map(Object(Closure), Array)
-#2 {main}
+#0 %s(%d): foo('a')
+#1 {main}
thrown in %s on line %d
diff --git a/Zend/tests/gh20564.phpt b/Zend/tests/gh20564.phpt
new file mode 100644
index 000000000000..53311d952de7
--- /dev/null
+++ b/Zend/tests/gh20564.phpt
@@ -0,0 +1,24 @@
+--TEST--
+GH-20564: Don't call autoloaders with pending exception
+--CREDITS--
+Viet Hoang Luu (@vi3tL0u1s)
+--FILE--
+test();
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+array_map(): Argument #1 ($callback) must be a valid callback or null, class "B" not found
diff --git a/Zend/tests/gh20628_001.phpt b/Zend/tests/gh20628_001.phpt
new file mode 100644
index 000000000000..7b9534c9713b
--- /dev/null
+++ b/Zend/tests/gh20628_001.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Nullsafe operator does not support BP_VAR_W
+--FILE--
+bar();
+}
+
+?>
+--EXPECTF--
+Fatal error: Cannot take reference of a nullsafe chain in %s on line %d
diff --git a/Zend/tests/gh20628_002.phpt b/Zend/tests/gh20628_002.phpt
new file mode 100644
index 000000000000..509db5ff3d6c
--- /dev/null
+++ b/Zend/tests/gh20628_002.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Pipes support return-by-reference
+--FILE--
+ foo(...);
+}
+
+$xRef = &bar();
+$xRef++;
+var_dump($x);
+
+?>
+--EXPECT--
+int(43)
diff --git a/Zend/tests/gh20628_003.phpt b/Zend/tests/gh20628_003.phpt
new file mode 100644
index 000000000000..ef23c8276b06
--- /dev/null
+++ b/Zend/tests/gh20628_003.phpt
@@ -0,0 +1,71 @@
+--TEST--
+Missing separation for ASSIGN_DIM with ref OP_DATA
+--FILE--
+
+--EXPECT--
+array(1) {
+ [0]=>
+ NULL
+}
+array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ NULL
+ }
+}
+array(1) {
+ [0]=>
+ array(0) {
+ }
+}
+array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ array(0) {
+ }
+ }
+}
+array(1) {
+ [0]=>
+ NULL
+}
+array(1) {
+ [0]=>
+ NULL
+}
+NULL
+array(1) {
+ [0]=>
+ &NULL
+}
diff --git a/Zend/tests/gh20628_004.inc b/Zend/tests/gh20628_004.inc
new file mode 100644
index 000000000000..970b43ec1933
--- /dev/null
+++ b/Zend/tests/gh20628_004.inc
@@ -0,0 +1,3 @@
+bar();
diff --git a/Zend/tests/gh20628_004.phpt b/Zend/tests/gh20628_004.phpt
new file mode 100644
index 000000000000..29851a717bc3
--- /dev/null
+++ b/Zend/tests/gh20628_004.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Invalid opcode for method call with FETCH_THIS
+--FILE--
+test();
+
+?>
+--EXPECTF--
+object(Foo)#%d (0) {
+}
+string(8) "Foo::bar"
diff --git a/Zend/tests/gh20628_005.phpt b/Zend/tests/gh20628_005.phpt
new file mode 100644
index 000000000000..43b49e0c18f2
--- /dev/null
+++ b/Zend/tests/gh20628_005.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Failed assertion for assignment in expression context
+--CREDITS--
+Matteo Beccati (mbeccati)
+--FILE--
+ 'baz'];
+}
+
+var_dump(($v = foo())['bar'], $v);
+
+?>
+--EXPECT--
+string(3) "baz"
+array(1) {
+ ["bar"]=>
+ string(3) "baz"
+}
diff --git a/Zend/tests/gh22257.phpt b/Zend/tests/gh22257.phpt
new file mode 100644
index 000000000000..9bbf3b1f8d35
--- /dev/null
+++ b/Zend/tests/gh22257.phpt
@@ -0,0 +1,38 @@
+--TEST--
+GH-22257 (Type confusion / OOB read unserializing an Exception with a non-array trace)
+--CREDITS--
+Igor Sak-Sakovskiy (Positive Technologies)
+--FILE--
+getPrevious()) {
+ printf("%s: %s\n", $e::class, $e->getMessage());
+ }
+}
+
+/* By-ref type violation: the slot is reset to the property default. */
+class Test { public int $i; public array $a; }
+try {
+ var_dump(unserialize('O:4:"Test":2:{s:1:"i";N;s:1:"a";R:2;}'));
+} catch (\Throwable $e) {
+ printf("%s: %s\n", $e::class, $e->getMessage());
+}
+echo "OK\n";
+?>
+--EXPECTF--
+Warning: unserialize(): Error at offset %d of %d bytes in %s on line %d
+TypeError: Cannot assign null to property Test::$i of type int
+OK
diff --git a/Zend/tests/gh22849.phpt b/Zend/tests/gh22849.phpt
new file mode 100644
index 000000000000..a38c64baf542
--- /dev/null
+++ b/Zend/tests/gh22849.phpt
@@ -0,0 +1,31 @@
+--TEST--
+PHP 8.6 error_include_args=1 mishandled for include()
+--INI--
+error_include_args=On
+--FILE--
+
+--EXPECTF--
+Warning: include('does not exist'): Failed to open stream: No such file or directory in %s on line %d
+
+Warning: include('does not exist'): Failed opening 'does not exist' for inclusion (include_path='%s') in %s on line %d
+
+Warning: include_once('does not exist'): Failed to open stream: No such file or directory in %s on line %d
+
+Warning: include_once('does not exist'): Failed opening 'does not exist' for inclusion (include_path='%s') in %s on line %d
+
+Warning: require('does not exist'): Failed to open stream: No such file or directory in %s on line %d
+
+Fatal error: Uncaught Error: Failed opening required 'does not exist' (include_path='%s') in %s:%d
+Stack trace:
+#0 %s(%d): foo(1, 2)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/include_stat_is_quiet.phpt b/Zend/tests/include_stat_is_quiet.phpt
index 75ba8f3d6273..39eb3053fbe4 100644
--- a/Zend/tests/include_stat_is_quiet.phpt
+++ b/Zend/tests/include_stat_is_quiet.phpt
@@ -26,7 +26,7 @@ try {
?>
--EXPECTF--
-Warning: require_once(doesnt_exist.php): Failed to open stream: No such file or directory in %s on line %d
+Warning: require_once(): Failed to open stream: No such file or directory in %s on line %d
Fatal error: Uncaught Error: Failed opening required 'doesnt_exist.php' (include_path='test://foo:test://bar') in %s:%d
Stack trace:
diff --git a/Zend/tests/inheritance/argument_restriction_001.phpt b/Zend/tests/inheritance/argument_restriction_001.phpt
index 2c54636a5817..6eb3ff8d2706 100644
--- a/Zend/tests/inheritance/argument_restriction_001.phpt
+++ b/Zend/tests/inheritance/argument_restriction_001.phpt
@@ -13,4 +13,4 @@ class Sub extends Base {
}
?>
--EXPECTF--
-Fatal error: Declaration of & Sub::test() must be compatible with & Base::test($foo, array $bar, $option = null, $extra = 'llllllllll...') in %s on line %d
+Fatal error: Declaration of &Sub::test() must be compatible with &Base::test($foo, array $bar, $option = null, $extra = 'llllllllll...') in %s on line %d
diff --git a/Zend/tests/inheritance/enum_only_interfaces_error_via_indirect_interface.phpt b/Zend/tests/inheritance/enum_only_interfaces_error_via_indirect_interface.phpt
new file mode 100644
index 000000000000..66a921532055
--- /dev/null
+++ b/Zend/tests/inheritance/enum_only_interfaces_error_via_indirect_interface.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Interface that is extended from Enum only interface shouldn't be implementable by non-enum class
+--FILE--
+
+--EXPECTF--
+Fatal error: Non-enum class C cannot implement interface UnitEnum in %s on line %d
diff --git a/Zend/tests/lazy_objects/gh20174.phpt b/Zend/tests/lazy_objects/gh20174.phpt
new file mode 100644
index 000000000000..2bce09b4dc71
--- /dev/null
+++ b/Zend/tests/lazy_objects/gh20174.phpt
@@ -0,0 +1,33 @@
+--TEST--
+GH-20174: Assertion failure in ReflectionProperty::skipLazyInitialization after failed LazyProxy skipLazyInitialization
+--CREDITS--
+vi3tL0u1s
+--FILE--
+newLazyProxy(function ($obj) {
+ $obj->b = 4;
+ throw new Exception();
+});
+
+try {
+ $reflector->initializeLazyObject($obj);
+} catch (Exception $e) {
+ $reflector->getProperty('b')->skipLazyInitialization($obj);
+}
+
+var_dump($obj);
+
+?>
+--EXPECTF--
+lazy proxy object(C)#%d (1) {
+ ["b"]=>
+ NULL
+}
diff --git a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes.phpt b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes.phpt
index 4edf9481ebc2..695da60229c7 100644
--- a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes.phpt
+++ b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes.phpt
@@ -46,7 +46,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});
-// Initializer effects on the proxy are not reverted
test('Proxy', $obj);
--EXPECTF--
@@ -63,12 +62,10 @@ Is lazy: 1
# Proxy:
string(11) "initializer"
initializer exception
-lazy proxy object(C)#%d (3) {
- ["a"]=>
- int(3)
+lazy proxy object(C)#%d (1) {
["b"]=>
- int(4)
+ uninitialized(int)
["c"]=>
- int(5)
+ int(0)
}
Is lazy: 1
diff --git a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props.phpt b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props.phpt
index ce94fc8b2ab7..19d3eac7a9f0 100644
--- a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props.phpt
+++ b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props.phpt
@@ -49,7 +49,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});
-// Initializer effects on the proxy are not reverted
test('Proxy', $obj);
--EXPECTF--
@@ -66,14 +65,10 @@ Is lazy: 1
# Proxy:
string(11) "initializer"
initializer exception
-lazy proxy object(C)#%d (4) {
- ["a"]=>
- int(3)
+lazy proxy object(C)#%d (1) {
["b"]=>
- int(4)
+ uninitialized(int)
["c"]=>
- int(5)
- ["d"]=>
- int(6)
+ int(0)
}
Is lazy: 1
diff --git a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props_and_ht.phpt b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props_and_ht.phpt
index 1bc3eb2cea8e..c11a0eda7aaa 100644
--- a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props_and_ht.phpt
+++ b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_dyn_props_and_ht.phpt
@@ -52,7 +52,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});
-// Initializer effects on the proxy are not reverted
test('Proxy', $obj);
--EXPECTF--
@@ -73,14 +72,10 @@ array(0) {
}
string(11) "initializer"
initializer exception
-lazy proxy object(C)#%d (4) {
- ["a"]=>
- int(3)
+lazy proxy object(C)#%d (1) {
["b"]=>
- int(4)
+ uninitialized(int)
["c"]=>
- int(5)
- ["d"]=>
- int(6)
+ int(0)
}
Is lazy: 1
diff --git a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht.phpt b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht.phpt
index c4f0bd98773f..7419a4a31da9 100644
--- a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht.phpt
+++ b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht.phpt
@@ -49,7 +49,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});
-// Initializer effects on the proxy are not reverted
test('Proxy', $obj);
--EXPECTF--
@@ -74,12 +73,10 @@ array(1) {
}
string(11) "initializer"
initializer exception
-lazy proxy object(C)#%d (3) {
- ["a"]=>
- int(3)
+lazy proxy object(C)#%d (1) {
["b"]=>
- int(4)
+ uninitialized(int)
["c"]=>
- int(5)
+ int(0)
}
Is lazy: 1
diff --git a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht_ref.phpt b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht_ref.phpt
index 094f5c9b8094..cea95c3c8a7b 100644
--- a/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht_ref.phpt
+++ b/Zend/tests/lazy_objects/init_exception_reverts_initializer_changes_props_ht_ref.phpt
@@ -55,7 +55,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});
-// Initializer effects on the proxy are not reverted
test('Proxy', $obj);
--EXPECTF--
@@ -83,13 +82,11 @@ array(1) {
}
string(11) "initializer"
initializer exception
-lazy proxy object(C)#%d (3) {
- ["a"]=>
- int(3)
+lazy proxy object(C)#%d (1) {
["b"]=>
- int(4)
+ uninitialized(int)
["c"]=>
- int(5)
+ int(0)
}
Is lazy: 1
diff --git a/Zend/tests/magic_methods/constructor_destructor_return.phpt b/Zend/tests/magic_methods/constructor_destructor_return.phpt
new file mode 100644
index 000000000000..635548bb6f47
--- /dev/null
+++ b/Zend/tests/magic_methods/constructor_destructor_return.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Returning values from constructors and destructors is deprecated
+--FILE--
+voidMethod(); }
+ public function __destruct() { return $this->voidMethod(); }
+
+ public function voidMethod(): void { }
+}
+
+class Gen {
+ public function __construct() { yield ''; }
+ public function __destruct() { yield ''; }
+}
+
+?>
+--EXPECTF--
+Deprecated: Returning a value from a constructor is deprecated in %s on line %d
+
+Deprecated: Returning a value from a destructor is deprecated in %s on line %d
+
+Deprecated: Returning a value from a constructor is deprecated in %s on line %d
+
+Deprecated: Returning a value from a destructor is deprecated in %s on line %d
+
+Deprecated: Making a constructor a Generator is deprecated in %s on line %d
+
+Deprecated: Making a destructor a Generator is deprecated in %s on line %d
diff --git a/Zend/tests/magic_methods/gh12695.phpt b/Zend/tests/magic_methods/gh12695.phpt
new file mode 100644
index 000000000000..957f474fb6a1
--- /dev/null
+++ b/Zend/tests/magic_methods/gh12695.phpt
@@ -0,0 +1,65 @@
+--TEST--
+GH-12695: ?? on unset property does not call __get() when __isset() materialized the property
+--FILE--
+$n = 123;
+ return true;
+ }
+}
+
+echo "Dynamic property materialised in __isset, then `??`:\n";
+$a = new A;
+var_dump($a->foo ?? 'fallback');
+
+echo "\nSame on a declared (unset) property:\n";
+class B {
+ public int $x = 99;
+ public function __get($n) {
+ throw new Exception("__get must not be called when __isset materialised the property");
+ }
+ public function __isset($n) {
+ echo " __isset($n)\n";
+ $this->$n = 7;
+ return true;
+ }
+}
+$b = new B;
+unset($b->x);
+var_dump($b->x ?? 'fallback');
+
+echo "\nWhen __isset() materialises the property to null, `??` falls back:\n";
+#[AllowDynamicProperties]
+class D {
+ public function __get($n) {
+ throw new Exception("__get must not be called when __isset materialised the property");
+ }
+ public function __isset($n) {
+ echo " __isset($n)\n";
+ $this->$n = null;
+ return true;
+ }
+}
+$d = new D;
+var_dump($d->foo ?? 'fallback');
+
+?>
+--EXPECT--
+Dynamic property materialised in __isset, then `??`:
+ __isset(foo)
+int(123)
+
+Same on a declared (unset) property:
+ __isset(x)
+int(7)
+
+When __isset() materialises the property to null, `??` falls back:
+ __isset(foo)
+string(8) "fallback"
diff --git a/Zend/tests/magic_methods/gh12695_empty.phpt b/Zend/tests/magic_methods/gh12695_empty.phpt
new file mode 100644
index 000000000000..99806820cc6f
--- /dev/null
+++ b/Zend/tests/magic_methods/gh12695_empty.phpt
@@ -0,0 +1,55 @@
+--TEST--
+GH-12695: empty() on unset property does not call __get() when __isset() materialized the property
+--FILE--
+$n = $GLOBALS['next_value'];
+ return true;
+ }
+}
+
+echo "empty() when __isset materialised a truthy value: __get is not called, empty=false:\n";
+$GLOBALS['next_value'] = 7;
+$a = new A;
+var_dump(empty($a->foo));
+
+echo "\nempty() when __isset materialised a falsy value: __get is not called, empty=true:\n";
+$GLOBALS['next_value'] = 0;
+$a = new A;
+var_dump(empty($a->bar));
+
+echo "\nempty() with no materialization: __get is still called (legacy path preserved):\n";
+class B {
+ public function __get($n) {
+ echo " __get($n)\n";
+ return 'value';
+ }
+ public function __isset($n) {
+ echo " __isset($n)\n";
+ return true;
+ }
+}
+$b = new B;
+var_dump(empty($b->any));
+
+?>
+--EXPECT--
+empty() when __isset materialised a truthy value: __get is not called, empty=false:
+ __isset(foo)
+bool(false)
+
+empty() when __isset materialised a falsy value: __get is not called, empty=true:
+ __isset(bar)
+bool(true)
+
+empty() with no materialization: __get is still called (legacy path preserved):
+ __isset(any)
+ __get(any)
+bool(false)
diff --git a/Zend/tests/magic_methods/gh12695_no_materialization.phpt b/Zend/tests/magic_methods/gh12695_no_materialization.phpt
new file mode 100644
index 000000000000..700b605e2984
--- /dev/null
+++ b/Zend/tests/magic_methods/gh12695_no_materialization.phpt
@@ -0,0 +1,65 @@
+--TEST--
+GH-12695: __get() invocation is based on __isset()'s return value
+--FILE--
+any ?? 'fallback');
+
+echo "\n`??` when __isset=true and __get returns null: __get is called and fallback is used:\n";
+class D {
+ public function __get($n) {
+ echo " __get($n)\n";
+ return null;
+ }
+ public function __isset($n) {
+ echo " __isset($n)\n";
+ return true;
+ }
+}
+$d = new D;
+var_dump($d->any ?? 'fallback');
+
+echo "\n`??` when __isset returns false: __get is not called:\n";
+class E {
+ public function __get($n) {
+ throw new Exception("__get must not be called when __isset returned false");
+ }
+ public function __isset($n) {
+ echo " __isset($n)\n";
+ return false;
+ }
+}
+$e = new E;
+var_dump($e->any ?? 'fallback');
+
+?>
+--EXPECT--
+`??` when __isset=true and __get returns a value: __get is called:
+ __isset(any)
+ __get(any)
+string(8) "from-get"
+
+`??` when __isset=true and __get returns null: __get is called and fallback is used:
+ __isset(any)
+ __get(any)
+string(8) "fallback"
+
+`??` when __isset returns false: __get is not called:
+ __isset(any)
+string(8) "fallback"
diff --git a/Zend/tests/magic_methods/gh12695_object_released_in_isset.phpt b/Zend/tests/magic_methods/gh12695_object_released_in_isset.phpt
new file mode 100644
index 000000000000..911ffde637a9
--- /dev/null
+++ b/Zend/tests/magic_methods/gh12695_object_released_in_isset.phpt
@@ -0,0 +1,28 @@
+--TEST--
+GH-12695: Object freed by __isset() during materialization
+--FILE--
+prop = 'materialised';
+ return true;
+ }
+}
+
+$obj = new C();
+unset($obj->prop);
+var_dump($obj->prop ?? 'fb');
+
+?>
+--EXPECT--
+string(12) "materialised"
diff --git a/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt
index 6bdcafb1bfb5..a4076ee00ce2 100644
--- a/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt
+++ b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt
@@ -66,5 +66,6 @@ class WidenedArgumentType extends NarrowedReturnType {
echo 'No problems!';
?>
---EXPECT--
+--EXPECTF--
+Deprecated: Returning null from ValidMagicMethods::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d
No problems!
diff --git a/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt b/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt
new file mode 100644
index 000000000000..7f4564794452
--- /dev/null
+++ b/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Stringable is automatically implemented for __toString() declared with non-canonical case
+--FILE--
+getInterfaceNames());
+var_dump((string) new Test);
+
+?>
+--EXPECT--
+bool(true)
+array(1) {
+ [0]=>
+ string(10) "Stringable"
+}
+string(3) "foo"
diff --git a/Zend/tests/magic_methods/trampoline_closure_named_arguments.phpt b/Zend/tests/magic_methods/trampoline_closure_named_arguments.phpt
index e4ccaf16e63a..3be14f6145ac 100644
--- a/Zend/tests/magic_methods/trampoline_closure_named_arguments.phpt
+++ b/Zend/tests/magic_methods/trampoline_closure_named_arguments.phpt
@@ -42,7 +42,7 @@ var_dump($type);
var_dump($type->getName());
?>
---EXPECT--
+--EXPECTF--
-- Non-static cases --
string(4) "test"
array(3) {
@@ -69,7 +69,7 @@ array(4) {
["a"]=>
int(123)
["b"]=>
- object(Test)#1 (0) {
+ object(Test)#%d (0) {
}
}
string(4) "test"
@@ -77,7 +77,7 @@ array(2) {
["a"]=>
int(123)
["b"]=>
- object(Test)#1 (0) {
+ object(Test)#%d (0) {
}
}
string(4) "test"
@@ -114,7 +114,7 @@ array(4) {
["a"]=>
int(123)
["b"]=>
- object(Test)#1 (0) {
+ object(Test)#%d (0) {
}
}
string(10) "testStatic"
@@ -122,7 +122,7 @@ array(2) {
["a"]=>
int(123)
["b"]=>
- object(Test)#1 (0) {
+ object(Test)#%d (0) {
}
}
string(10) "testStatic"
@@ -136,12 +136,12 @@ array(1) {
-- Reflection tests --
array(1) {
[0]=>
- object(ReflectionParameter)#4 (1) {
+ object(ReflectionParameter)#%d (1) {
["name"]=>
string(9) "arguments"
}
}
bool(true)
-object(ReflectionNamedType)#5 (0) {
+object(ReflectionNamedType)#%d (0) {
}
string(5) "mixed"
diff --git a/Zend/tests/named_params/internal_variadics.phpt b/Zend/tests/named_params/internal_variadics.phpt
index 3599f644ac97..8312bde406d2 100644
--- a/Zend/tests/named_params/internal_variadics.phpt
+++ b/Zend/tests/named_params/internal_variadics.phpt
@@ -15,7 +15,14 @@ try {
echo $e->getMessage(), "\n";
}
+try {
+ $array = [1, 2];
+ array_push($array, ...['values' => 3]);
+} catch (ArgumentCountError $e) {
+ echo $e->getMessage(), "\n";
+}
?>
--EXPECT--
-array_merge() does not accept unknown named parameters
-array_diff_key() does not accept unknown named parameters
+Internal function array_merge() does not accept named variadic arguments
+Internal function array_diff_key() does not accept named variadic arguments
+Internal function array_push() does not accept named variadic arguments
diff --git a/Zend/tests/objects/objects_005.phpt b/Zend/tests/objects/objects_005.phpt
index 9b9a41465f95..7749a39d986f 100644
--- a/Zend/tests/objects/objects_005.phpt
+++ b/Zend/tests/objects/objects_005.phpt
@@ -19,4 +19,4 @@ class test3 extends test {
?>
--EXPECTF--
-Fatal error: Declaration of test3::foo() must be compatible with & test::foo() in %s on line %d
+Fatal error: Declaration of test3::foo() must be compatible with &test::foo() in %s on line %d
diff --git a/Zend/tests/operator_unsupported_types.phpt b/Zend/tests/operator_unsupported_types.phpt
index a08032226e5d..904ac402b372 100644
--- a/Zend/tests/operator_unsupported_types.phpt
+++ b/Zend/tests/operator_unsupported_types.phpt
@@ -287,7 +287,7 @@ Unsupported operand types: stdClass * stdClass
Unsupported operand types: stdClass * resource
Unsupported operand types: stdClass * string
Unsupported operand types: resource * array
-Unsupported operand types: stdClass * resource
+Unsupported operand types: resource * stdClass
Unsupported operand types: resource * resource
Unsupported operand types: resource * string
Unsupported operand types: string * array
@@ -753,7 +753,7 @@ Unsupported operand types: stdClass & stdClass
Unsupported operand types: stdClass & resource
Unsupported operand types: stdClass & string
Unsupported operand types: resource & array
-Unsupported operand types: stdClass & resource
+Unsupported operand types: resource & stdClass
Unsupported operand types: resource & resource
Unsupported operand types: resource & string
Unsupported operand types: string & array
@@ -828,7 +828,7 @@ Unsupported operand types: stdClass | stdClass
Unsupported operand types: stdClass | resource
Unsupported operand types: stdClass | string
Unsupported operand types: resource | array
-Unsupported operand types: stdClass | resource
+Unsupported operand types: resource | stdClass
Unsupported operand types: resource | resource
Unsupported operand types: resource | string
Unsupported operand types: string | array
@@ -903,7 +903,7 @@ Unsupported operand types: stdClass ^ stdClass
Unsupported operand types: stdClass ^ resource
Unsupported operand types: stdClass ^ string
Unsupported operand types: resource ^ array
-Unsupported operand types: stdClass ^ resource
+Unsupported operand types: resource ^ stdClass
Unsupported operand types: resource ^ resource
Unsupported operand types: resource ^ string
Unsupported operand types: string ^ array
diff --git a/Zend/tests/oss-fuzz-480111866.phpt b/Zend/tests/oss-fuzz-480111866.phpt
new file mode 100644
index 000000000000..ba956f0f9ac6
--- /dev/null
+++ b/Zend/tests/oss-fuzz-480111866.phpt
@@ -0,0 +1,10 @@
+--TEST--
+OSS-Fuzz #480111866: Assignment to assignment through list operator
+--FILE--
+
+--EXPECTF--
+Fatal error: Assignments can only happen to writable values in %s on line %d
diff --git a/Zend/tests/oss-fuzz-481017027.phpt b/Zend/tests/oss-fuzz-481017027.phpt
new file mode 100644
index 000000000000..472133cfe845
--- /dev/null
+++ b/Zend/tests/oss-fuzz-481017027.phpt
@@ -0,0 +1,23 @@
+--TEST--
+OSS-Fuzz #481017027: Missing zend_fe_fetch_object_helper deref
+--FILE--
+y = &$y;
+test($obj, '');
+
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/tests/oss_fuzz_429429090.phpt b/Zend/tests/oss_fuzz_429429090.phpt
new file mode 100644
index 000000000000..d5279c2806ac
--- /dev/null
+++ b/Zend/tests/oss_fuzz_429429090.phpt
@@ -0,0 +1,20 @@
+--TEST--
+OSS-Fuzz #429429090: FETCH_OBJ_UNSET IS_UNDEF result
+--FILE--
+x[0]->prop);
+unset($c->x[0]->prop);
+isset(C::$y[0]->prop);
+unset(C::$y[0]->prop);
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/oss_fuzz_454273637.phpt b/Zend/tests/oss_fuzz_454273637.phpt
new file mode 100644
index 000000000000..fbcdb57ed2a1
--- /dev/null
+++ b/Zend/tests/oss_fuzz_454273637.phpt
@@ -0,0 +1,8 @@
+--TEST--
+OSS-Fuzz #454273637 (UAF with printf optimization and const output)
+--FILE--
+
+--EXPECT--
+%
diff --git a/Zend/tests/oss_fuzz_64209.phpt b/Zend/tests/oss_fuzz_64209.phpt
index 599ae258e5b2..56b9dcfc7a24 100644
--- a/Zend/tests/oss_fuzz_64209.phpt
+++ b/Zend/tests/oss_fuzz_64209.phpt
@@ -5,7 +5,7 @@ oss-fuzz #64209: Fix in-place modification of filename in php_message_handler_fo
require '://@';
?>
--EXPECTF--
-Warning: require(://@): Failed to open stream: No such file or directory in %s on line %d
+Warning: require(): Failed to open stream: No such file or directory in %s on line %d
Fatal error: Uncaught Error: Failed opening required '://@' (include_path='%s') in %s:%d
Stack trace:
diff --git a/Zend/tests/partial_application/assert.phpt b/Zend/tests/partial_application/assert.phpt
new file mode 100644
index 000000000000..166cc5022eb2
--- /dev/null
+++ b/Zend/tests/partial_application/assert.phpt
@@ -0,0 +1,45 @@
+--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";
+}
+
+try {
+ echo "# Upper-case assert():\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)
+# Upper-case assert():
+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/bound_arg_strict_types.phpt b/Zend/tests/partial_application/bound_arg_strict_types.phpt
new file mode 100644
index 000000000000..d7e2ddcae5c4
--- /dev/null
+++ b/Zend/tests/partial_application/bound_arg_strict_types.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Partial application checks bound arguments with the creating scope's strict_types
+--FILE--
+getMessage(), "\n";
+}
+
+function make() {
+ try {
+ f("3", ?);
+ } catch (\TypeError $e) {
+ echo $e::class, ": ", $e->getMessage(), "\n";
+ }
+}
+make();
+?>
+--EXPECT--
+TypeError: f(): Argument #1 ($a) must be of type int, string given
+TypeError: f(): Argument #1 ($a) must be of type int, string given
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/closure_rt_cache_lifetime.phpt b/Zend/tests/partial_application/closure_rt_cache_lifetime.phpt
new file mode 100644
index 000000000000..7eb4810cf00a
--- /dev/null
+++ b/Zend/tests/partial_application/closure_rt_cache_lifetime.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Partial application over a Closure::fromCallable() must not double-free the run_time_cache
+--FILE--
+
+--EXPECT--
+int(15)
+OK
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/construct_throw_arg_leak.phpt b/Zend/tests/partial_application/construct_throw_arg_leak.phpt
new file mode 100644
index 000000000000..1291901f0cc8
--- /dev/null
+++ b/Zend/tests/partial_application/construct_throw_arg_leak.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Partial application must not leak bound arguments when construction throws
+--FILE--
+
+--EXPECT--
+TypeError
+TypeError
+ArgumentCountError
+OK
diff --git a/Zend/tests/partial_application/default_const_expr.phpt b/Zend/tests/partial_application/default_const_expr.phpt
new file mode 100644
index 000000000000..12d25adedeff
--- /dev/null
+++ b/Zend/tests/partial_application/default_const_expr.phpt
@@ -0,0 +1,116 @@
+--TEST--
+PFA: constant-expression default for a skipped optional parameter
+--FILE--
+name, $c];
+}
+
+$p = f(a: 10, c: ?);
+var_dump($p(99));
+
+$v = variadic(a: 10, c: ?, foo: 1);
+var_dump($v(99));
+
+$q = C::m(a: 1, c: ?);
+var_dump($q(9));
+
+$r = g(a: 1, c: ?);
+var_dump($r(9));
+
+$l = late(a: 1, c: ?);
+define('App\LATE', 'defined after the partial was created');
+var_dump($l(9));
+
+define('GLOBAL_ONLY', 'global fallback');
+$s = fallback(a: 1, c: ?);
+var_dump($s(9));
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ int(10)
+ [1]=>
+ int(7)
+ [2]=>
+ int(99)
+}
+array(4) {
+ [0]=>
+ int(10)
+ [1]=>
+ int(7)
+ [2]=>
+ int(99)
+ [3]=>
+ array(1) {
+ ["foo"]=>
+ int(1)
+ }
+}
+array(3) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(42)
+ [2]=>
+ int(9)
+}
+array(3) {
+ [0]=>
+ int(1)
+ [1]=>
+ string(1) "A"
+ [2]=>
+ int(9)
+}
+array(3) {
+ [0]=>
+ int(1)
+ [1]=>
+ string(37) "defined after the partial was created"
+ [2]=>
+ int(9)
+}
+array(3) {
+ [0]=>
+ int(1)
+ [1]=>
+ string(15) "global fallback"
+ [2]=>
+ int(9)
+}
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..bdcc10675785
--- /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 [ $arguments2 ]
+ }
+
+ - 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..1d5efaea7c63
--- /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 [ $arguments2 ]
+ }
+
+ - 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)
+
+
+ {closure:%s:%d}>
+
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..cfead54bbd81
--- /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::class, ": ", $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::class, ": ", $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
+Error: 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
+Error: 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..e5163c710da2
--- /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 [ $args2 ]
+ }
+
+ - 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..04b63f3c4010
--- /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)
+ ["c2"]=>
+ 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..a7c4c2d76e49
--- /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)
+ }
+ ["arrays2"]=>
+ 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..850f0eda149d
--- /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 [ $b2 ]
+ }
+
+ - 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..21d8169fc42c
--- /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 [ $values2 ]
+ }
+
+ - 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/tests/pipe_operator/gh22490.phpt b/Zend/tests/pipe_operator/gh22490.phpt
new file mode 100644
index 000000000000..c801d938c275
--- /dev/null
+++ b/Zend/tests/pipe_operator/gh22490.phpt
@@ -0,0 +1,60 @@
+--TEST--
+GH-22490: Null pointer dereference compiling a pipe on the lhs of ??=
+--FILE--
+p = 7;
+ return $o;
+}
+
+echo "property unset, assigns and returns default: ";
+var_dump((1 |> unset_prop(...))->p ??= 99);
+echo "pipe evaluated once: ";
+var_dump($calls);
+
+$calls = 0;
+echo "property set, returns existing value: ";
+var_dump((1 |> set_prop(...))->p ??= 99);
+echo "pipe evaluated once: ";
+var_dump($calls);
+
+function new_array($x) {
+ global $calls;
+ $calls++;
+ return [];
+}
+
+$calls = 0;
+echo "dim target on a pipe result: ";
+var_dump((1 |> new_array(...))[0] ??= 42);
+echo "pipe evaluated once: ";
+var_dump($calls);
+
+function identity($x) {
+ return $x;
+}
+
+$calls = 0;
+echo "nested pipe on the lhs: ";
+var_dump((1 |> identity(...) |> unset_prop(...))->p ??= 5);
+echo "pipe evaluated once: ";
+var_dump($calls);
+?>
+--EXPECT--
+property unset, assigns and returns default: int(99)
+pipe evaluated once: int(1)
+property set, returns existing value: int(7)
+pipe evaluated once: int(1)
+dim target on a pipe result: int(42)
+pipe evaluated once: int(1)
+nested pipe on the lhs: int(5)
+pipe evaluated once: int(1)
diff --git a/Zend/tests/prop_const_expr/non_enums_catchable.phpt b/Zend/tests/prop_const_expr/non_enums_catchable.phpt
index 6f410ac7acbe..702bc024122d 100644
--- a/Zend/tests/prop_const_expr/non_enums_catchable.phpt
+++ b/Zend/tests/prop_const_expr/non_enums_catchable.phpt
@@ -18,6 +18,7 @@ const A_prop = (new A)->{new Printer ? 'printer' : null};
?>
--EXPECTF--
+Deprecated: Returning a value from a constructor is deprecated in %s on line %d
Printer
Fatal error: Uncaught Error: Fetching properties on non-enums in constant expressions is not allowed in %s:%d
diff --git a/Zend/tests/property_hooks/bug001.phpt b/Zend/tests/property_hooks/bug001.phpt
index 5328506d476c..62291019b333 100644
--- a/Zend/tests/property_hooks/bug001.phpt
+++ b/Zend/tests/property_hooks/bug001.phpt
@@ -27,4 +27,4 @@ try {
?>
--EXPECT--
bool(true)
-Property C::$x is read-only
+Cannot write to get-only virtual property C::$x
diff --git a/Zend/tests/property_hooks/bug008.phpt b/Zend/tests/property_hooks/bug008.phpt
index 9c321cfe16e1..38fe709a38a3 100644
--- a/Zend/tests/property_hooks/bug008.phpt
+++ b/Zend/tests/property_hooks/bug008.phpt
@@ -18,7 +18,7 @@ $foo->bar = 'bar';
?>
--EXPECTF--
-Fatal error: Uncaught Error: Property Foo::$bar is read-only in %s:%d
+Fatal error: Uncaught Error: Cannot write to get-only virtual property Foo::$bar in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
diff --git a/Zend/tests/property_hooks/get.phpt b/Zend/tests/property_hooks/get.phpt
index 0cb0a6b7c76b..6d4b005843ea 100644
--- a/Zend/tests/property_hooks/get.phpt
+++ b/Zend/tests/property_hooks/get.phpt
@@ -21,4 +21,4 @@ try {
?>
--EXPECT--
int(42)
-Property Test::$prop is read-only
+Cannot write to get-only virtual property Test::$prop
diff --git a/Zend/tests/property_hooks/get_by_ref_implemented_by_val.phpt b/Zend/tests/property_hooks/get_by_ref_implemented_by_val.phpt
index 84eb96826354..2c507d862be6 100644
--- a/Zend/tests/property_hooks/get_by_ref_implemented_by_val.phpt
+++ b/Zend/tests/property_hooks/get_by_ref_implemented_by_val.phpt
@@ -15,4 +15,4 @@ class A implements I {
?>
--EXPECTF--
-Fatal error: Declaration of A::$prop::get() must be compatible with & I::$prop::get() in %s on line %d
+Fatal error: Declaration of A::$prop::get() must be compatible with &I::$prop::get() in %s on line %d
diff --git a/Zend/tests/property_hooks/override_add_get.phpt b/Zend/tests/property_hooks/override_add_get.phpt
index 28dbd047c071..df2739e6086c 100644
--- a/Zend/tests/property_hooks/override_add_get.phpt
+++ b/Zend/tests/property_hooks/override_add_get.phpt
@@ -31,7 +31,7 @@ var_dump($b->prop);
?>
--EXPECT--
A::A::$prop::set
-Property A::$prop is write-only
+Cannot read from set-only virtual property A::$prop
B::B::$prop::set
B::B::$prop::get
int(42)
diff --git a/Zend/tests/property_hooks/override_add_set.phpt b/Zend/tests/property_hooks/override_add_set.phpt
index 6b490d4d87f9..e13de15f0b82 100644
--- a/Zend/tests/property_hooks/override_add_set.phpt
+++ b/Zend/tests/property_hooks/override_add_set.phpt
@@ -30,7 +30,7 @@ var_dump($b->prop);
?>
--EXPECT--
-Property A::$prop is read-only
+Cannot write to get-only virtual property A::$prop
A::A::$prop::get
int(42)
B::B::$prop::set
diff --git a/Zend/tests/property_hooks/set.phpt b/Zend/tests/property_hooks/set.phpt
index 04e36ae85411..2928aee6cede 100644
--- a/Zend/tests/property_hooks/set.phpt
+++ b/Zend/tests/property_hooks/set.phpt
@@ -28,5 +28,5 @@ try {
?>
--EXPECT--
int(42)
-Property Test::$prop is write-only
-Property Test::$prop is write-only
+Cannot read from set-only virtual property Test::$prop
+Cannot read from set-only virtual property Test::$prop
diff --git a/Zend/tests/property_hooks/type_compatibility.phpt b/Zend/tests/property_hooks/type_compatibility.phpt
index 8bfb7b0cadd5..def1fc77dc9c 100644
--- a/Zend/tests/property_hooks/type_compatibility.phpt
+++ b/Zend/tests/property_hooks/type_compatibility.phpt
@@ -1,5 +1,5 @@
--TEST--
-Relaxed type compatibility for read-only and write-only properties
+Relaxed type compatibility for get-only and set-only properties
--FILE--
--EXPECT--
-require_once(does-not-exist.php): Failed to open stream: No such file or directory
+require_once(): Failed to open stream: No such file or directory
diff --git a/Zend/tests/return_types/042.phpt b/Zend/tests/return_types/042.phpt
index 069e0228ff99..21483047d4f2 100644
--- a/Zend/tests/return_types/042.phpt
+++ b/Zend/tests/return_types/042.phpt
@@ -20,5 +20,10 @@ class JustAnArray {
echo 'No problems!';
?>
---EXPECT--
+--EXPECTF--
+Deprecated: Returning null from UnionType::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d
+
+Deprecated: Returning null from UnionType2::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d
+
+Deprecated: Returning null from UnionTypeOldStyle::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d
No problems!
diff --git a/Zend/tests/stack_limit/stack_limit_010.phpt b/Zend/tests/stack_limit/stack_limit_010.phpt
index 4f8358fb41e7..85736423e41e 100644
--- a/Zend/tests/stack_limit/stack_limit_010.phpt
+++ b/Zend/tests/stack_limit/stack_limit_010.phpt
@@ -27,7 +27,9 @@ $expectedMaxSize = match(php_uname('s')) {
'true' => 16*1024*1024, // https://github.com/actions/runner-images/pull/3328
default => 8*1024*1024,
},
- 'SunOS' => 10 * 1024 * 1024,
+ 'SunOS' => preg_match('/(omnios|illumos|smartos|oi-|openindiana|joyent)/i', php_uname('v'))
+ ? 10 * 1024 * 1024
+ : 8 * 1024 * 1024,
'Windows NT' => 67108864 - 4*4096, // Set by sapi/cli/config.w32
};
diff --git a/Zend/tests/traits/bug60536_001.phpt b/Zend/tests/traits/bug60536_001.phpt
index a58098ca3112..712600a4b1d1 100644
--- a/Zend/tests/traits/bug60536_001.phpt
+++ b/Zend/tests/traits/bug60536_001.phpt
@@ -23,5 +23,9 @@ $a->__construct();
echo "DONE";
?>
--EXPECTF--
+Deprecated: Returning a value from a constructor is deprecated in %s on line %d
+
+Deprecated: Returning a value from a constructor is deprecated in %s on line %d
+
Warning: Undefined property: Z::$x in %s on line %d
DONE
diff --git a/Zend/tests/unified_arg_infos_001.phpt b/Zend/tests/unified_arg_infos_001.phpt
new file mode 100644
index 000000000000..d6699aa8d8cb
--- /dev/null
+++ b/Zend/tests/unified_arg_infos_001.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Declaring non persistent method with arg info
+--EXTENSIONS--
+zend_test
+--FILE--
+testTmpMethodWithArgInfo(null);
+
+echo new ReflectionFunction($o->testTmpMethodWithArgInfo(...));
+
+?>
+--EXPECT--
+Closure [ public method testTmpMethodWithArgInfo ] {
+
+ - Parameters [2] {
+ Parameter #0 [ Foo|Bar|null $tmpMethodParamName = null ]
+ Parameter #1 [ string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue" ]
+ }
+}
diff --git a/Zend/tests/unserialize_typed_prop_reset_on_failure.phpt b/Zend/tests/unserialize_typed_prop_reset_on_failure.phpt
new file mode 100644
index 000000000000..2380c8e5550e
--- /dev/null
+++ b/Zend/tests/unserialize_typed_prop_reset_on_failure.phpt
@@ -0,0 +1,37 @@
+--TEST--
+unserialize() resets a typed property to its default on every failure path
+--FILE--
+getPrevious()) {
+ printf("%s: %s\n", $e::class, $e->getMessage());
+ }
+}
+
+/* By-ref type violation: the slot is reset to its default. */
+class C { public array $a; }
+try {
+ var_dump(unserialize('O:1:"C":1:{s:1:"a";R:1;}'));
+} catch (\Throwable $e) {
+ printf("%s: %s\n", $e::class, $e->getMessage());
+}
+echo "OK\n";
+?>
+--EXPECTF--
+Warning: unserialize(): Error at offset %d of %d bytes in %s on line %d
+TypeError: %s
+OK
diff --git a/Zend/tests/vm_kind_tailcall_clang_windows.phpt b/Zend/tests/vm_kind_tailcall_clang_windows.phpt
new file mode 100644
index 000000000000..f66a17ae0daf
--- /dev/null
+++ b/Zend/tests/vm_kind_tailcall_clang_windows.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Tailcall VM is selected when compiled with Clang >= 19 on Windows x64
+--SKIPIF--
+ clang version (\d+)/', $info, $m)) {
+ die('skip not compiled with clang');
+}
+
+if ((int)$m[1] < 19) {
+ die('skip requires clang >= 19');
+}
+?>
+--FILE--
+
+--EXPECT--
+string(21) "ZEND_VM_KIND_TAILCALL"
diff --git a/Zend/tests/weakrefs/gh17442_1.phpt b/Zend/tests/weakrefs/gh17442_1.phpt
index fc7f60174ed9..d535c307e3be 100644
--- a/Zend/tests/weakrefs/gh17442_1.phpt
+++ b/Zend/tests/weakrefs/gh17442_1.phpt
@@ -17,6 +17,6 @@ headers_sent($obj,$generator);
Fatal error: Uncaught Exception: Test in %s:%d
Stack trace:
#0 [internal function]: class@anonymous->__destruct()
-#1 %s(%d): headers_sent(NULL, 0)
+#1 %s(%d): headers_sent(NULL, NULL)
#2 {main}
thrown in %s on line %d
diff --git a/Zend/tests/weakrefs/weakmap_by_ref_dimension_assign.phpt b/Zend/tests/weakrefs/weakmap_by_ref_dimension_assign.phpt
new file mode 100644
index 000000000000..6444de7eea6b
--- /dev/null
+++ b/Zend/tests/weakrefs/weakmap_by_ref_dimension_assign.phpt
@@ -0,0 +1,13 @@
+--TEST--
+By-ref assign of WeakMap dimension
+--FILE--
+
+--EXPECT--
+int(1)
diff --git a/Zend/zend.c b/Zend/zend.c
index 59a1a1a3ba88..07692db85196 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -38,6 +37,8 @@
#include "zend_call_stack.h"
#include "zend_max_execution_timer.h"
#include "zend_hrtime.h"
+#include "zend_enum.h"
+#include "zend_closures.h"
#include "Optimizer/zend_optimizer.h"
#include "php.h"
#include "php_globals.h"
@@ -53,6 +54,9 @@ ZEND_API int compiler_globals_id;
ZEND_API int executor_globals_id;
ZEND_API size_t compiler_globals_offset;
ZEND_API size_t executor_globals_offset;
+/* ts_allocate_tls_id takes a callback so each thread resolves its own block.
+ * A plain &language_scanner_globals would capture only the registering thread's address. */
+static void *language_scanner_globals_tls_addr(void) { return &language_scanner_globals; }
static HashTable *global_function_table = NULL;
static HashTable *global_class_table = NULL;
static HashTable *global_constants_table = NULL;
@@ -76,7 +80,7 @@ ZEND_API bool zend_dtrace_enabled;
/* version information */
static char *zend_version_info;
static uint32_t zend_version_info_length;
-#define ZEND_CORE_VERSION_INFO "Zend Engine v" ZEND_VERSION ", Copyright (c) Zend Technologies\n"
+#define ZEND_CORE_VERSION_INFO "Zend Engine v" ZEND_VERSION ", Copyright © Zend by Perforce\n"
#define PRINT_ZVAL_INDENT 4
/* true multithread-shared globals */
@@ -155,7 +159,7 @@ static ZEND_INI_MH(OnUpdateScriptEncoding) /* {{{ */
static ZEND_INI_MH(OnUpdateAssertions) /* {{{ */
{
- zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
+ zend_long *p = ZEND_INI_GET_ADDR();
zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
@@ -553,7 +557,7 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
}
GC_PROTECT_RECURSION(Z_ARRVAL_P(expr));
}
- print_hash(buf, Z_ARRVAL_P(expr), indent, 0);
+ print_hash(buf, Z_ARRVAL_P(expr), indent, false);
GC_TRY_UNPROTECT_RECURSION(Z_ARRVAL_P(expr));
break;
case IS_OBJECT:
@@ -563,6 +567,7 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
zend_object *zobj = Z_OBJ_P(expr);
uint32_t *guard = zend_get_recursion_guard(zobj);
zend_string *class_name = Z_OBJ_HANDLER_P(expr, get_class_name)(zobj);
+ /* cut off on NULL byte ... class@anonymous */
smart_str_appends(buf, ZSTR_VAL(class_name));
zend_string_release_ex(class_name, 0);
@@ -583,12 +588,12 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
}
if ((properties = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_DEBUG)) == NULL) {
- print_hash(buf, (HashTable*) &zend_empty_array, indent, 1);
+ print_hash(buf, (HashTable*) &zend_empty_array, indent, true);
break;
}
ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
- print_hash(buf, properties, indent, 1);
+ print_hash(buf, properties, indent, true);
ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
zend_release_properties(properties);
@@ -641,7 +646,7 @@ static FILE *zend_fopen_wrapper(zend_string *filename, zend_string **opened_path
/* }}} */
#ifdef ZTS
-static bool short_tags_default = 1;
+static bool short_tags_default = true;
static uint32_t compiler_options_default = ZEND_COMPILE_DEFAULT;
#else
# define short_tags_default 1
@@ -810,7 +815,6 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
ZVAL_UNDEF(&executor_globals->user_error_handler);
ZVAL_UNDEF(&executor_globals->user_exception_handler);
ZVAL_UNDEF(&executor_globals->last_fatal_error_backtrace);
- executor_globals->in_autoload = NULL;
executor_globals->current_execute_data = NULL;
executor_globals->current_module = NULL;
executor_globals->exit_status = 0;
@@ -818,7 +822,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
executor_globals->saved_fpu_cw = 0;
#endif
executor_globals->saved_fpu_cw_ptr = NULL;
- executor_globals->active = 0;
+ executor_globals->active = false;
executor_globals->bailout = NULL;
executor_globals->error_handling = EH_NORMAL;
executor_globals->exception_class = NULL;
@@ -832,8 +836,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
#endif
executor_globals->flags = EG_FLAGS_INITIAL;
executor_globals->record_errors = false;
- executor_globals->num_errors = 0;
- executor_globals->errors = NULL;
+ memset(&executor_globals->errors, 0, sizeof(executor_globals->errors));
executor_globals->filename_override = NULL;
executor_globals->lineno_override = -1;
#ifdef ZEND_CHECK_STACK_LIMIT
@@ -911,7 +914,7 @@ static bool php_auto_globals_create_globals(zend_string *name) /* {{{ */
{
/* While we keep registering $GLOBALS as an auto-global, we do not create an
* actual variable for it. Access to it handled specially by the compiler. */
- return 0;
+ return false;
}
/* }}} */
@@ -1019,15 +1022,17 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
zend_init_rsrc_list_dtors();
#ifdef ZTS
- ts_allocate_fast_id(&compiler_globals_id, &compiler_globals_offset, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor);
- ts_allocate_fast_id(&executor_globals_id, &executor_globals_offset, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor);
- ts_allocate_fast_id(&language_scanner_globals_id, &language_scanner_globals_offset, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL);
+ ts_allocate_fast_id_at(&compiler_globals_id, &compiler_globals_offset, ZEND_CG_OFFSET, sizeof(zend_compiler_globals), (ts_allocate_ctor) compiler_globals_ctor, (ts_allocate_dtor) compiler_globals_dtor);
+ ts_allocate_fast_id_at(&executor_globals_id, &executor_globals_offset, ZEND_EG_OFFSET, sizeof(zend_executor_globals), (ts_allocate_ctor) executor_globals_ctor, (ts_allocate_dtor) executor_globals_dtor);
+ ts_allocate_tls_id(&language_scanner_globals_id, language_scanner_globals_tls_addr, sizeof(zend_php_scanner_globals), (ts_allocate_ctor) php_scanner_globals_ctor, NULL);
+ ZEND_ASSERT(compiler_globals_offset == ZEND_CG_OFFSET);
+ ZEND_ASSERT(executor_globals_offset == ZEND_EG_OFFSET);
ts_allocate_fast_id(&ini_scanner_globals_id, &ini_scanner_globals_offset, sizeof(zend_ini_scanner_globals), (ts_allocate_ctor) ini_scanner_globals_ctor, NULL);
compiler_globals = ts_resource(compiler_globals_id);
executor_globals = ts_resource(executor_globals_id);
compiler_globals_dtor(compiler_globals);
- compiler_globals->in_compilation = 0;
+ compiler_globals->in_compilation = false;
compiler_globals->function_table = (HashTable *) malloc(sizeof(HashTable));
compiler_globals->class_table = (HashTable *) malloc(sizeof(HashTable));
@@ -1055,6 +1060,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
zend_interned_strings_init();
+ zend_object_handlers_startup();
zend_startup_builtin_functions();
zend_register_standard_constants();
zend_register_auto_global(zend_string_init_interned("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals);
@@ -1078,6 +1084,8 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
#endif
+
+ zend_enum_startup();
}
/* }}} */
@@ -1301,7 +1309,7 @@ ZEND_API void zend_append_version_info(const zend_extension *extension) /* {{{ *
snprintf(new_info, new_info_length, " with %s v%s, %s, by %s\n", extension->name, extension->version, extension->copyright, extension->author);
- zend_version_info = (char *) realloc(zend_version_info, zend_version_info_length+new_info_length + 1);
+ zend_version_info = (char *) perealloc(zend_version_info, zend_version_info_length+new_info_length + 1, true);
strncat(zend_version_info, new_info, new_info_length);
zend_version_info_length += new_info_length;
free(new_info);
@@ -1442,8 +1450,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
zend_stack delayed_oplines_stack;
int type = orig_type & E_ALL;
bool orig_record_errors;
- uint32_t orig_num_errors;
- zend_error_info **orig_errors;
+ zend_err_buf orig_errors_buf;
zend_result res;
/* If we're executing a function during SCCP, count any warnings that may be emitted,
@@ -1455,11 +1462,9 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
}
/* Emit any delayed error before handling fatal error */
- if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(num_errors)) {
- uint32_t num_errors = EG(num_errors);
- zend_error_info **errors = EG(errors);
- EG(num_errors) = 0;
- EG(errors) = NULL;
+ if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(errors).size) {
+ zend_err_buf errors_buf = EG(errors);
+ EG(errors).size = 0;
bool orig_record_errors = EG(record_errors);
EG(record_errors) = false;
@@ -1469,12 +1474,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
EG(user_error_handler_error_reporting) = 0;
- zend_emit_recorded_errors_ex(num_errors, errors);
+ zend_emit_recorded_errors_ex(errors_buf.size, errors_buf.errors);
EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting;
EG(record_errors) = orig_record_errors;
- EG(num_errors) = num_errors;
- EG(errors) = errors;
+ EG(errors) = errors_buf;
}
if (EG(record_errors)) {
@@ -1483,12 +1487,13 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
info->lineno = error_lineno;
info->filename = zend_string_copy(error_filename);
info->message = zend_string_copy(message);
-
- /* This is very inefficient for a large number of errors.
- * Use pow2 realloc if it becomes a problem. */
- EG(num_errors)++;
- EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors));
- EG(errors)[EG(num_errors)-1] = info;
+ EG(errors).size++;
+ if (EG(errors).size > EG(errors).capacity) {
+ uint32_t capacity = EG(errors).capacity ? EG(errors).capacity + (EG(errors).capacity >> 1) : 2;
+ EG(errors).errors = erealloc(EG(errors).errors, sizeof(zend_error_info *) * capacity);
+ EG(errors).capacity = capacity;
+ }
+ EG(errors).errors[EG(errors).size - 1] = info;
/* Do not process non-fatal recorded error */
if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) {
@@ -1502,12 +1507,10 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
/* Report about uncaught exception in case of fatal errors */
if (EG(exception)) {
- zend_execute_data *ex;
- const zend_op *opline;
-
if (type & E_FATAL_ERRORS) {
- ex = EG(current_execute_data);
- opline = NULL;
+ zend_execute_data *ex = EG(current_execute_data);
+ const zend_op *opline = NULL;
+
while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) {
ex = ex->prev_execute_data;
}
@@ -1573,17 +1576,15 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
}
orig_record_errors = EG(record_errors);
- orig_num_errors = EG(num_errors);
- orig_errors = EG(errors);
EG(record_errors) = false;
- EG(num_errors) = 0;
- EG(errors) = NULL;
+
+ orig_errors_buf = EG(errors);
+ memset(&EG(errors), 0, sizeof(EG(errors)));
res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params);
EG(record_errors) = orig_record_errors;
- EG(num_errors) = orig_num_errors;
- EG(errors) = orig_errors;
+ EG(errors) = orig_errors_buf;
if (res == SUCCESS) {
if (Z_TYPE(retval) != IS_UNDEF) {
@@ -1778,8 +1779,7 @@ ZEND_API void zend_begin_record_errors(void)
{
ZEND_ASSERT(!EG(record_errors) && "Error recording already enabled");
EG(record_errors) = true;
- EG(num_errors) = 0;
- EG(errors) = NULL;
+ EG(errors).size = 0;
}
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors)
@@ -1793,24 +1793,23 @@ ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info
ZEND_API void zend_emit_recorded_errors(void)
{
EG(record_errors) = false;
- zend_emit_recorded_errors_ex(EG(num_errors), EG(errors));
+ zend_emit_recorded_errors_ex(EG(errors).size, EG(errors).errors);
}
ZEND_API void zend_free_recorded_errors(void)
{
- if (!EG(num_errors)) {
+ if (!EG(errors).size) {
return;
}
- for (uint32_t i = 0; i < EG(num_errors); i++) {
- zend_error_info *info = EG(errors)[i];
+ for (uint32_t i = 0; i < EG(errors).size; i++) {
+ zend_error_info *info = EG(errors).errors[i];
zend_string_release(info->filename);
zend_string_release(info->message);
- efree(info);
+ efree_size(info, sizeof(zend_error_info));
}
- efree(EG(errors));
- EG(errors) = NULL;
- EG(num_errors) = 0;
+ efree(EG(errors).errors);
+ memset(&EG(errors), 0, sizeof(EG(errors)));
}
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format, ...) /* {{{ */
@@ -1976,7 +1975,6 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl
zend_result ret = SUCCESS;
if (op_array) {
zend_execute(op_array, retval);
- zend_exception_restore();
if (UNEXPECTED(EG(exception))) {
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
zend_user_exception_handler();
diff --git a/Zend/zend.h b/Zend/zend.h
index 40b3d7825a72..0d5303192b57 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -20,7 +19,7 @@
#ifndef ZEND_H
#define ZEND_H
-#define ZEND_VERSION "4.5.9-dev"
+#define ZEND_VERSION "4.6.0-dev"
#define ZEND_ENGINE_3
@@ -144,19 +143,25 @@ struct _zend_inheritance_cache_entry {
zend_class_entry *traits_and_interfaces[1];
};
+C23_ENUM(zend_class_type, uint8_t) {
+ ZEND_INTERNAL_CLASS = 1,
+ ZEND_USER_CLASS = 2,
+};
+
struct _zend_class_entry {
- char type;
+ zend_class_type type;
zend_string *name;
/* class_entry or string depending on ZEND_ACC_LINKED */
union {
zend_class_entry *parent;
zend_string *parent_name;
};
- int refcount;
+ uint32_t refcount;
uint32_t ce_flags;
+ uint32_t ce_flags2;
int default_properties_count;
- int default_static_members_count;
+ uint32_t default_static_members_count;
zval *default_properties_table;
zval *default_static_members_table;
ZEND_MAP_PTR_DEF(zval *, static_members_table);
@@ -443,7 +448,7 @@ typedef struct {
BEGIN_EXTERN_C()
ZEND_API void zend_save_error_handling(zend_error_handling *current);
ZEND_API void zend_replace_error_handling(zend_error_handling_t error_handling, zend_class_entry *exception_class, zend_error_handling *current);
-ZEND_API void zend_restore_error_handling(zend_error_handling *saved);
+ZEND_API void zend_restore_error_handling(const zend_error_handling *saved);
ZEND_API void zend_begin_record_errors(void);
ZEND_API void zend_emit_recorded_errors(void);
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors);
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index e874c4a5bbd7..72f4727eeddf 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -20,6 +19,7 @@
*/
#include "zend.h"
+#include "zend_compile.h"
#include "zend_execute.h"
#include "zend_API.h"
#include "zend_hash.h"
@@ -76,28 +76,6 @@ ZEND_API zend_result zend_get_parameters_array_ex(uint32_t param_count, zval *ar
}
/* }}} */
-ZEND_API zend_result zend_copy_parameters_array(uint32_t param_count, zval *argument_array) /* {{{ */
-{
- zval *param_ptr;
- uint32_t arg_count;
-
- param_ptr = ZEND_CALL_ARG(EG(current_execute_data), 1);
- arg_count = ZEND_CALL_NUM_ARGS(EG(current_execute_data));
-
- if (param_count>arg_count) {
- return FAILURE;
- }
-
- while (param_count-->0) {
- Z_TRY_ADDREF_P(param_ptr);
- zend_hash_next_index_insert_new(Z_ARRVAL_P(argument_array), param_ptr);
- param_ptr++;
- }
-
- return SUCCESS;
-}
-/* }}} */
-
ZEND_API ZEND_COLD void zend_wrong_param_count(void) /* {{{ */
{
const char *space;
@@ -107,7 +85,7 @@ ZEND_API ZEND_COLD void zend_wrong_param_count(void) /* {{{ */
}
/* }}} */
-ZEND_API ZEND_COLD void zend_wrong_property_read(zval *object, zval *property)
+ZEND_API ZEND_COLD void zend_wrong_property_read(const zval *object, zval *property)
{
zend_string *tmp_property_name;
zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name);
@@ -147,7 +125,7 @@ ZEND_API const char *zend_get_type_by_const(int type) /* {{{ */
return "mixed";
case _IS_NUMBER:
return "int|float";
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
@@ -247,7 +225,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code, uint32_t num, char *name, zend_expected_type expected_type, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg) /* {{{ */
{
switch (error_code) {
case ZPP_ERROR_WRONG_CALLBACK:
@@ -283,12 +261,12 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code,
case ZPP_ERROR_FAILURE:
ZEND_ASSERT(EG(exception) && "Should have produced an error already");
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, const zval *arg) /* {{{ */
{
static const char * const expected_error[] = {
Z_EXPECTED_TYPES(Z_EXPECTED_TYPE_STR)
@@ -309,7 +287,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t n
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, const zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
@@ -319,7 +297,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, const zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
@@ -329,7 +307,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(u
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(uint32_t num, const char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(uint32_t num, const char *name, const zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
@@ -339,7 +317,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(u
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null_error(uint32_t num, const char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null_error(uint32_t num, const char *name, const zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
@@ -349,7 +327,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, const zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
@@ -359,7 +337,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error
}
/* }}} */
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_null_error(uint32_t num, const char *name, zval *arg) /* {{{ */
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_null_error(uint32_t num, const char *name, const zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
@@ -391,11 +369,13 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void)
{
const char *space;
const char *class_name = get_active_class_name(&space);
- zend_argument_count_error("%s%s%s() does not accept unknown named parameters",
- class_name, space, get_active_function_name());
+ zend_argument_count_error("Internal function %s%s%s() does not accept named variadic arguments",
+ class_name, space, get_active_function_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_API ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(
+ zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, const char *format, va_list va)
{
zend_string *func_name;
const char *arg_name;
@@ -404,8 +384,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",
@@ -415,34 +395,65 @@ 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_argument_error_ex(zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ zend_argument_error_variadic(error_ce, function, arg_num, 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;
+ const zend_function *function = zend_active_function();
va_start(va, format);
- zend_argument_error_variadic(error_ce, arg_num, format, va);
+ zend_argument_error_variadic(error_ce, function, arg_num, format, va);
va_end(va);
}
/* }}} */
+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(zend_ce_type_error, function, arg_num, format, va);
+ va_end(va);
+}
+
ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format, ...) /* {{{ */
{
va_list va;
+ const zend_function *function = zend_active_function();
va_start(va, format);
- zend_argument_error_variadic(zend_ce_type_error, arg_num, format, va);
+ zend_argument_error_variadic(zend_ce_type_error, function, arg_num, format, va);
va_end(va);
}
/* }}} */
+ZEND_API ZEND_COLD void zend_argument_value_error_ex(
+ const zend_function *function, uint32_t arg_num, const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ zend_argument_error_variadic(zend_ce_value_error, function, arg_num, format, va);
+ va_end(va);
+}
+
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format, ...) /* {{{ */
{
va_list va;
+ const zend_function *function = zend_active_function();
va_start(va, format);
- zend_argument_error_variadic(zend_ce_value_error, arg_num, format, va);
+ zend_argument_error_variadic(zend_ce_value_error, function, arg_num, format, va);
va_end(va);
}
/* }}} */
@@ -452,7 +463,7 @@ ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num)
zend_argument_value_error(arg_num, "must not be empty");
}
-ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string *new_name, zend_class_entry *old_ce)
+ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string *new_name, const zend_class_entry *old_ce)
{
if (old_ce->type == ZEND_INTERNAL_CLASS) {
zend_error(type, "Cannot redeclare %s %s",
@@ -467,7 +478,7 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string
}
}
-ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, zend_class_entry *old_ce)
+ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, const zend_class_entry *old_ce)
{
zend_class_redeclaration_error_ex(type, old_ce->name, old_ce);
}
@@ -502,7 +513,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p
/* }}} */
static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32_t arg_num) {
- zend_function *func = zend_active_function();
+ const zend_function *func = zend_active_function();
ZEND_ASSERT(arg_num > 0);
uint32_t arg_offset = arg_num - 1;
if (arg_offset >= func->common.num_args) {
@@ -510,7 +521,7 @@ static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32
arg_offset = func->common.num_args;
}
- zend_arg_info *arg_info = &func->common.arg_info[arg_offset];
+ const zend_arg_info *arg_info = &func->common.arg_info[arg_offset];
zend_string *func_name = get_active_function_or_method_name();
const char *arg_name = get_active_function_arg_name(arg_num);
@@ -530,38 +541,37 @@ static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32
return !EG(exception);
}
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, bool *dest, uint32_t arg_num) /* {{{ */
+ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) <= IS_STRING)) {
if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("bool", arg_num)) {
- return 0;
+ return ZPP_PARSE_BOOL_STATUS_ERROR;
}
- *dest = zend_is_true(arg);
+ bool result = zend_is_true(arg);
if (UNEXPECTED(EG(exception))) {
- return 0;
+ return ZPP_PARSE_BOOL_STATUS_ERROR;
}
- } else {
- return 0;
+ return result;
}
- return 1;
+ return ZPP_PARSE_BOOL_STATUS_ERROR;
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num) /* {{{ */
+ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
- return 0;
+ return ZPP_PARSE_BOOL_STATUS_ERROR;
}
- return zend_parse_arg_bool_weak(arg, dest, arg_num);
+ return zend_parse_arg_bool_weak(arg, arg_num);
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num)
+ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, uint32_t arg_num)
{
if (UNEXPECTED(ZEND_FLF_ARG_USES_STRICT_TYPES())) {
- return 0;
+ return ZPP_PARSE_BOOL_STATUS_ERROR;
}
- return zend_parse_arg_bool_weak(arg, dest, arg_num);
+ return zend_parse_arg_bool_weak(arg, arg_num);
}
ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num) /* {{{ */
@@ -617,9 +627,6 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long
return 0;
}
}
- if (UNEXPECTED(EG(exception))) {
- return 0;
- }
} else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) {
if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("int", arg_num)) {
return 0;
@@ -651,47 +658,46 @@ ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_l
return zend_parse_arg_long_weak(arg, dest, arg_num);
}
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num) /* {{{ */
+ZEND_API double ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) {
- *dest = (double)Z_LVAL_P(arg);
+ return (double)Z_LVAL_P(arg);
} else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) {
zend_long l;
+ double dval;
uint8_t type;
- if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, dest)) != IS_DOUBLE)) {
+ if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, &dval)) != IS_DOUBLE)) {
if (EXPECTED(type != 0)) {
- *dest = (double)(l);
+ return (double)(l);
} else {
- return 0;
+ return NAN;
}
- }
- if (UNEXPECTED(EG(exception))) {
- return 0;
+ } else {
+ return dval;
}
} else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) {
if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("float", arg_num)) {
- return 0;
+ return NAN;
}
- *dest = 0.0;
+ return 0.0;
} else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) {
- *dest = 1.0;
+ return 1.0;
} else {
- return 0;
+ return NAN;
}
- return 1;
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num) /* {{{ */
+ZEND_API double ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) {
/* SSTH Exception: IS_LONG may be accepted instead as IS_DOUBLE */
- *dest = (double)Z_LVAL_P(arg);
+ return (double)Z_LVAL_P(arg);
} else if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
- return 0;
+ return NAN;
}
- return zend_parse_arg_double_weak(arg, dest, arg_num);
+ return zend_parse_arg_double_weak(arg, arg_num);
}
/* }}} */
@@ -758,61 +764,61 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **
return true;
}
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */
+ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num) /* {{{ */
{
if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) {
if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("string", arg_num)) {
- return 0;
+ return NULL;
}
convert_to_string(arg);
if (UNEXPECTED(EG(exception))) {
- return 0;
+ return NULL;
}
- *dest = Z_STR_P(arg);
+ return Z_STR_P(arg);
} else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
zend_object *zobj = Z_OBJ_P(arg);
zval obj;
if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) {
OBJ_RELEASE(zobj);
ZVAL_COPY_VALUE(arg, &obj);
- *dest = Z_STR_P(arg);
- return 1;
+ return Z_STR_P(arg);
}
- return 0;
+ return NULL;
} else {
- return 0;
+ return NULL;
}
- return 1;
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */
+ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num) /* {{{ */
{
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
- return 0;
+ return NULL;
}
- return zend_parse_arg_str_weak(arg, dest, arg_num);
+ return zend_parse_arg_str_weak(arg, arg_num);
}
/* }}} */
-ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num)
+ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num)
{
if (UNEXPECTED(ZEND_FLF_ARG_USES_STRICT_TYPES())) {
- return 0;
+ return NULL;
}
- return zend_parse_arg_str_weak(arg, dest, arg_num);
+ return zend_parse_arg_str_weak(arg, arg_num);
}
ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num) /* {{{ */
{
+ zend_string *str;
if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) {
return 0;
}
if (zend_parse_arg_long_weak(arg, dest_long, arg_num)) {
*dest_str = NULL;
return 1;
- } else if (zend_parse_arg_str_weak(arg, dest_str, arg_num)) {
+ } else if ((str = zend_parse_arg_str_weak(arg, arg_num)) != NULL) {
*dest_long = 0;
+ *dest_str = str;
return 1;
} else {
return 0;
@@ -824,8 +830,8 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
{
const char *spec_walk = *spec;
char c = *spec_walk++;
- bool check_null = 0;
- bool separate = 0;
+ bool check_null = false;
+ bool separate = false;
zval *real_arg = arg;
/* scan through modifiers */
@@ -834,9 +840,9 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec
if (*spec_walk == '/') {
SEPARATE_ZVAL_NOREF(arg);
real_arg = arg;
- separate = 1;
+ separate = true;
} else if (*spec_walk == '!') {
- check_null = 1;
+ check_null = true;
} else {
break;
}
@@ -1146,7 +1152,7 @@ ZEND_API zend_result zend_parse_parameter(int flags, uint32_t arg_num, zval *arg
}
static ZEND_COLD void zend_parse_parameters_debug_error(const char *msg) {
- zend_function *active_function = EG(current_execute_data)->func;
+ const zend_function *active_function = EG(current_execute_data)->func;
const char *class_name = active_function->common.scope
? ZSTR_VAL(active_function->common.scope->name) : "";
zend_error_noreturn(E_CORE_ERROR, "%s%s%s(): %s",
@@ -1163,8 +1169,8 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
uint32_t max_num_args = 0;
uint32_t post_varargs = 0;
zval *arg;
- bool have_varargs = 0;
- bool have_optional_args = 0;
+ bool have_varargs = false;
+ bool have_optional_args = false;
zval **varargs = NULL;
uint32_t *n_varargs = NULL;
@@ -1186,7 +1192,7 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
case '|':
min_num_args = max_num_args;
- have_optional_args = 1;
+ have_optional_args = true;
break;
case '/':
@@ -1201,7 +1207,7 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
"only one varargs specifier (* or +) is permitted");
return FAILURE;
}
- have_varargs = 1;
+ have_varargs = true;
/* we expect at least one parameter in varargs */
if (c == '+') {
max_num_args++;
@@ -1402,7 +1408,7 @@ ZEND_API zend_result zend_parse_method_parameters_ex(int flags, uint32_t num_arg
/* This function should be called after the constructor has been called
* because it may call __set from the uninitialized object otherwise. */
-ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */
+ZEND_API void zend_merge_properties(const zval *obj, const HashTable *properties) /* {{{ */
{
zend_object *zobj = Z_OBJ_P(obj);
zend_object_write_property_t write_property = zobj->handlers->write_property;
@@ -1424,7 +1430,7 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */
}
/* }}} */
-static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *class_type) /* {{{ */
+static zend_class_mutable_data *zend_allocate_mutable_data(const zend_class_entry *class_type) /* {{{ */
{
zend_class_mutable_data *mutable_data;
@@ -1440,7 +1446,7 @@ static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *cla
}
/* }}} */
-ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type) /* {{{ */
+ZEND_API HashTable *zend_separate_class_constants_table(const zend_class_entry *class_type) /* {{{ */
{
zend_class_mutable_data *mutable_data;
HashTable *constants_table;
@@ -1480,7 +1486,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
return constants_table;
}
-static zend_result update_property(zval *val, zend_property_info *prop_info) {
+static zend_result update_property(zval *val, const zend_property_info *prop_info) {
if (ZEND_TYPE_IS_SET(prop_info->type)) {
zval tmp;
@@ -1715,10 +1721,9 @@ ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properti
if (object->ce->default_properties_count) {
zval *prop;
zend_string *key;
- zend_property_info *property_info;
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(properties, key, prop) {
- property_info = zend_get_property_info(object->ce, key, 1);
+ const zend_property_info *property_info = zend_get_property_info(object->ce, key, 1);
if (property_info != ZEND_WRONG_PROPERTY_INFO &&
property_info &&
(property_info->flags & ZEND_ACC_STATIC) == 0) {
@@ -1742,12 +1747,12 @@ ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properti
}
/* }}} */
-ZEND_API void object_properties_load(zend_object *object, HashTable *properties) /* {{{ */
+ZEND_API void object_properties_load(zend_object *object, const HashTable *properties) /* {{{ */
{
zval *prop, tmp;
zend_string *key;
zend_long h;
- zend_property_info *property_info;
+ const zend_property_info *property_info;
ZEND_HASH_FOREACH_KEY_VAL(properties, h, key, prop) {
if (key) {
@@ -2463,8 +2468,6 @@ ZEND_API zend_result zend_startup_module_ex(zend_module_entry *module) /* {{{ */
EG(current_module) = module;
if (module->module_startup_func(module->type, module->module_number)==FAILURE) {
zend_error_noreturn(E_CORE_ERROR,"Unable to start %s module", module->name);
- EG(current_module) = NULL;
- return FAILURE;
}
EG(current_module) = NULL;
}
@@ -2540,19 +2543,19 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */
dl_loaded_count++;
}
} ZEND_HASH_FOREACH_END();
- module_request_startup_handlers = (zend_module_entry**)realloc(
+ module_request_startup_handlers = (zend_module_entry**)perealloc(
module_request_startup_handlers,
sizeof(zend_module_entry*) *
(startup_count + 1 +
shutdown_count + 1 +
- post_deactivate_count + 1));
+ post_deactivate_count + 1), true);
module_request_startup_handlers[startup_count] = NULL;
module_request_shutdown_handlers = module_request_startup_handlers + startup_count + 1;
module_request_shutdown_handlers[shutdown_count] = NULL;
module_post_deactivate_handlers = module_request_shutdown_handlers + shutdown_count + 1;
module_post_deactivate_handlers[post_deactivate_count] = NULL;
/* Cannot reuse module_request_startup_handlers because it is freed in zend_destroy_modules, which happens before zend_unload_modules. */
- modules_dl_loaded = realloc(modules_dl_loaded, sizeof(zend_module_entry*) * (dl_loaded_count + 1));
+ modules_dl_loaded = perealloc(modules_dl_loaded, sizeof(zend_module_entry*) * (dl_loaded_count + 1), true);
modules_dl_loaded[dl_loaded_count] = NULL;
startup_count = 0;
@@ -2579,10 +2582,10 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */
}
} ZEND_HASH_FOREACH_END();
- class_cleanup_handlers = (zend_class_entry**)realloc(
+ class_cleanup_handlers = (zend_class_entry**)perealloc(
class_cleanup_handlers,
sizeof(zend_class_entry*) *
- (class_count + 1));
+ (class_count + 1), true);
class_cleanup_handlers[class_count] = NULL;
if (class_count) {
@@ -2776,7 +2779,7 @@ static void zend_check_magic_method_static(
}
static void zend_check_magic_method_public(
- const zend_class_entry *ce, const zend_function *fptr, int error_type)
+ const zend_class_entry *ce, const zend_function *fptr)
{
// TODO: Remove this warning after adding proper visibility handling.
if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
@@ -2798,7 +2801,7 @@ static void zend_check_magic_method_no_return_type(
}
}
-ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type) /* {{{ */
+ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, const zend_string *lcname, int error_type) /* {{{ */
{
if (ZSTR_VAL(lcname)[0] != '_'
|| ZSTR_VAL(lcname)[1] != '_') {
@@ -2819,83 +2822,87 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce,
} else if (zend_string_equals_literal(lcname, ZEND_GET_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
} else if (zend_string_equals_literal(lcname, ZEND_SET_FUNC_NAME)) {
zend_check_magic_method_args(2, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID);
} else if (zend_string_equals_literal(lcname, ZEND_UNSET_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID);
} else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_BOOL);
} else if (zend_string_equals_literal(lcname, ZEND_CALL_FUNC_NAME)) {
zend_check_magic_method_args(2, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY);
- } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_LCNAME)) {
zend_check_magic_method_args(2, ce, fptr, error_type);
zend_check_magic_method_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY);
- } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_STRING);
- } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_LCNAME)) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, (MAY_BE_ARRAY | MAY_BE_NULL));
- } else if (zend_string_equals_literal(lcname, "__serialize")) {
+ if ((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && ZEND_TYPE_PURE_MASK(fptr->common.arg_info[-1].type) & MAY_BE_NULL) {
+ zend_error(E_DEPRECATED, "Returning null from %s::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead",
+ ZSTR_VAL(ce->name));
+ }
+ } else if (zend_string_equals_literal(lcname, ZEND_SERIALIZE_FUNC_NAME)) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_ARRAY);
- } else if (zend_string_equals_literal(lcname, "__unserialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_UNSERIALIZE_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ARRAY);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID);
- } else if (zend_string_equals_literal(lcname, "__set_state")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_SET_STATE_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ARRAY);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_OBJECT);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) {
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_SLEEP))) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_ARRAY);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_WAKEUP))) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
- zend_check_magic_method_public(ce, fptr, error_type);
+ zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID);
}
}
/* }}} */
-ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, zend_string *lcname)
+ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, const zend_string *lcname)
{
if (ZSTR_VAL(lcname)[0] != '_' || ZSTR_VAL(lcname)[1] != '_') {
/* pass */
@@ -2920,16 +2927,16 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, z
} else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) {
ce->__isset = fptr;
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
- } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_LCNAME)) {
ce->__callstatic = fptr;
- } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)) {
ce->__tostring = fptr;
- } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_LCNAME)) {
ce->__debugInfo = fptr;
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
- } else if (zend_string_equals_literal(lcname, "__serialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_SERIALIZE_FUNC_NAME)) {
ce->__serialize = fptr;
- } else if (zend_string_equals_literal(lcname, "__unserialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_UNSERIALIZE_FUNC_NAME)) {
ce->__unserialize = fptr;
}
}
@@ -2962,6 +2969,81 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) {
} ZEND_TYPE_FOREACH_END();
}
+static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent)
+{
+ if (ZEND_TYPE_HAS_LITERAL_NAME(*type)) {
+ // gen_stubs.php does not support codegen for compound types. As a
+ // temporary workaround, we support union types by splitting
+ // the type name on `|` characters if necessary.
+ const char *class_name = ZEND_TYPE_LITERAL_NAME(*type);
+ type->type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
+
+ size_t num_types = 1;
+ const char *p = class_name;
+ while ((p = strchr(p, '|'))) {
+ num_types++;
+ p++;
+ }
+
+ if (num_types == 1) {
+ /* Simple class type */
+ zend_string *str = zend_string_init_interned(class_name, strlen(class_name), persistent);
+ zend_alloc_ce_cache(str);
+ ZEND_TYPE_SET_PTR(*type, str);
+ type->type_mask |= _ZEND_TYPE_NAME_BIT;
+ } else {
+ /* Union type */
+ zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), persistent);
+ list->num_types = num_types;
+ ZEND_TYPE_SET_LIST(*type, list);
+ ZEND_TYPE_FULL_MASK(*type) |= _ZEND_TYPE_UNION_BIT;
+
+ const char *start = class_name;
+ uint32_t j = 0;
+ while (true) {
+ const char *end = strchr(start, '|');
+ zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), persistent);
+ zend_alloc_ce_cache(str);
+ list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
+ if (!end) {
+ break;
+ }
+ start = end + 1;
+ j++;
+ }
+ }
+ }
+ if (ZEND_TYPE_IS_ITERABLE_FALLBACK(*type)) {
+ /* Warning generated an extension load warning which is emitted for every test
+ zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
+ " regenerate the argument info via the php-src gen_stub build script");
+ */
+ zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
+ ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
+ (type->type_mask | MAY_BE_ARRAY)
+ );
+ *type = legacy_iterable;
+ }
+}
+
+ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info, bool persistent)
+{
+ if (!is_return_info) {
+ new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), persistent);
+ if (arg_info->default_value) {
+ new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), persistent);
+ } else {
+ new_arg_info->default_value = NULL;
+ }
+ } else {
+ new_arg_info->name = NULL;
+ new_arg_info->default_value = NULL;
+ }
+ new_arg_info->doc_comment = NULL;
+ new_arg_info->type = arg_info->type;
+ zend_convert_internal_arg_info_type(&new_arg_info->type, persistent);
+}
+
/* registers all functions in *library_functions in the function hash */
ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type) /* {{{ */
{
@@ -2973,6 +3055,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
int error_type;
zend_string *lowercase_name;
size_t fname_len;
+ const zend_internal_arg_info *internal_arg_info;
if (type==MODULE_PERSISTENT) {
error_type = E_CORE_WARNING;
@@ -3014,7 +3097,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
ZEND_MAP_PTR_INIT(internal_function->run_time_cache, NULL);
#endif
}
- if (ptr->flags) {
+ if (ptr->flags & UINT32_MAX) {
if (!(ptr->flags & ZEND_ACC_PPP_MASK)) {
if (ptr->flags != ZEND_ACC_DEPRECATED && scope) {
zend_error(error_type, "Invalid access level for %s::%s() - access must be exactly one of public, protected or private", ZSTR_VAL(scope->name), ptr->fname);
@@ -3026,10 +3109,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
} else {
internal_function->fn_flags = ZEND_ACC_PUBLIC;
}
+ internal_function->fn_flags2 = ptr->flags >> 32;
if (ptr->arg_info) {
zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info;
- internal_function->arg_info = (zend_internal_arg_info*)ptr->arg_info+1;
+ internal_arg_info = ptr->arg_info+1;
internal_function->num_args = ptr->num_args;
/* Currently you cannot denote that the function can accept less arguments than num_args */
if (info->required_num_args == (uintptr_t)-1) {
@@ -3059,24 +3143,22 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
zend_error(E_CORE_WARNING, "Missing arginfo for %s%s%s()",
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
- internal_function->arg_info = NULL;
+ internal_arg_info = NULL;
internal_function->num_args = 0;
internal_function->required_num_args = 0;
}
/* If not specified, add __toString() return type for compatibility with Stringable
* interface. */
- if (scope && zend_string_equals_literal_ci(internal_function->function_name, "__tostring") &&
+ if (scope && zend_string_equals_literal_ci(internal_function->function_name, ZEND_TOSTRING_FUNC_LCNAME) &&
!(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type",
ZSTR_VAL(scope->name));
- internal_function->arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
+ internal_arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
internal_function->num_args = internal_function->required_num_args = 0;
}
-
- zend_set_function_arg_flags((zend_function*)internal_function);
if (ptr->flags & ZEND_ACC_ABSTRACT) {
if (scope) {
/* This is a class that must be abstract itself. Here we set the check info. */
@@ -3104,7 +3186,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
lowercase_name = zend_string_tolower_ex(internal_function->function_name, type == MODULE_PERSISTENT);
lowercase_name = zend_new_interned_string(lowercase_name);
- reg_function = malloc(sizeof(zend_internal_function));
+ reg_function = pemalloc(sizeof(zend_internal_function), true);
memcpy(reg_function, &function, sizeof(zend_internal_function));
if (zend_hash_add_ptr(target_function_table, lowercase_name, reg_function) == NULL) {
unload=1;
@@ -3122,8 +3204,8 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
zend_flf_capacity *= 2;
}
/* +1 for NULL terminator */
- zend_flf_handlers = realloc(zend_flf_handlers, (zend_flf_capacity + 1) * sizeof(void *));
- zend_flf_functions = realloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *));
+ zend_flf_handlers = perealloc(zend_flf_handlers, (zend_flf_capacity + 1) * sizeof(void *), true);
+ zend_flf_functions = perealloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *), true);
}
zend_flf_handlers[zend_flf_count] = flf_info->handler;
zend_flf_functions[zend_flf_count] = (zend_function *)reg_function;
@@ -3141,17 +3223,17 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
/* If types of arguments have to be checked */
- if (reg_function->arg_info && num_args) {
+ if (internal_arg_info && num_args) {
uint32_t i;
for (i = 0; i < num_args; i++) {
- zend_internal_arg_info *arg_info = ®_function->arg_info[i];
+ const zend_internal_arg_info *arg_info = &internal_arg_info[i];
ZEND_ASSERT(arg_info->name && "Parameter must have a name");
if (ZEND_TYPE_IS_SET(arg_info->type)) {
reg_function->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
}
#if ZEND_DEBUG
for (uint32_t j = 0; j < i; j++) {
- if (!strcmp(arg_info->name, reg_function->arg_info[j].name)) {
+ if (!strcmp(arg_info->name, internal_arg_info[j].name)) {
zend_error_noreturn(E_CORE_ERROR,
"Duplicate parameter name $%s for function %s%s%s()", arg_info->name,
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
@@ -3161,78 +3243,24 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
}
- /* Rebuild arginfos if parameter/property types and/or a return type are used */
- if (reg_function->arg_info &&
- (reg_function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
- /* convert "const char*" class type names into "zend_string*" */
+ /* Convert zend_internal_arg_info to zend_arg_info */
+ if (internal_arg_info) {
uint32_t i;
- zend_internal_arg_info *arg_info = reg_function->arg_info - 1;
- zend_internal_arg_info *new_arg_info;
+ const zend_internal_arg_info *arg_info = internal_arg_info - 1;
+ zend_arg_info *new_arg_info;
/* Treat return type as an extra argument */
num_args++;
- new_arg_info = malloc(sizeof(zend_internal_arg_info) * num_args);
- memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args);
+ new_arg_info = pemalloc(sizeof(zend_arg_info) * num_args, true);
reg_function->arg_info = new_arg_info + 1;
for (i = 0; i < num_args; i++) {
- if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) {
- // gen_stubs.php does not support codegen for DNF types in arg infos.
- // As a temporary workaround, we split the type name on `|` characters,
- // converting it to an union type if necessary.
- const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
- new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
-
- size_t num_types = 1;
- const char *p = class_name;
- while ((p = strchr(p, '|'))) {
- num_types++;
- p++;
- }
-
- if (num_types == 1) {
- /* Simple class type */
- zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1);
- zend_alloc_ce_cache(str);
- ZEND_TYPE_SET_PTR(new_arg_info[i].type, str);
- new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT;
- } else {
- /* Union type */
- zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
- list->num_types = num_types;
- ZEND_TYPE_SET_LIST(new_arg_info[i].type, list);
- ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT;
-
- const char *start = class_name;
- uint32_t j = 0;
- while (true) {
- const char *end = strchr(start, '|');
- zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1);
- zend_alloc_ce_cache(str);
- list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
- if (!end) {
- break;
- }
- start = end + 1;
- j++;
- }
- }
- }
- if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) {
- /* Warning generated an extension load warning which is emitted for every test
- zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
- " regenerate the argument info via the php-src gen_stub build script");
- */
- zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
- ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
- (new_arg_info[i].type.type_mask | MAY_BE_ARRAY)
- );
- new_arg_info[i].type = legacy_iterable;
- }
-
- zend_normalize_internal_type(&new_arg_info[i].type);
+ zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i],
+ i == 0, true);
}
}
+ zend_set_function_arg_flags((zend_function*)reg_function);
+
if (scope) {
zend_check_magic_method_implementation(
scope, (zend_function *)reg_function, lowercase_name, E_CORE_ERROR);
@@ -3312,7 +3340,7 @@ static void clean_module_classes(int module_number) /* {{{ */
/* Child classes may reuse structures from parent classes, so destroy in reverse order. */
Bucket *bucket;
ZEND_HASH_REVERSE_FOREACH_BUCKET(EG(class_table), bucket) {
- zend_class_entry *ce = Z_CE(bucket->val);
+ const zend_class_entry *ce = Z_CE(bucket->val);
if (ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module->module_number == module_number) {
zend_hash_del_bucket(EG(class_table), bucket);
}
@@ -3323,8 +3351,8 @@ static void clean_module_classes(int module_number) /* {{{ */
static int clean_module_function(zval *el, void *arg) /* {{{ */
{
- zend_function *fe = (zend_function *) Z_PTR_P(el);
- zend_module_entry *module = (zend_module_entry *) arg;
+ const zend_function *fe = (zend_function *) Z_PTR_P(el);
+ const zend_module_entry *module = arg;
if (fe->common.type == ZEND_INTERNAL_FUNCTION && fe->internal_function.module == module) {
return ZEND_HASH_APPLY_REMOVE;
} else {
@@ -3412,7 +3440,7 @@ ZEND_API void zend_activate_modules(void) /* {{{ */
zend_module_entry **p = module_request_startup_handlers;
while (*p) {
- zend_module_entry *module = *p;
+ const zend_module_entry *module = *p;
if (module->request_startup_func(module->type, module->module_number)==FAILURE) {
zend_error(E_WARNING, "request_startup() for %s module failed", module->name);
@@ -3441,7 +3469,7 @@ ZEND_API void zend_deactivate_modules(void) /* {{{ */
zend_module_entry **p = module_request_shutdown_handlers;
while (*p) {
- zend_module_entry *module = *p;
+ const zend_module_entry *module = *p;
zend_try {
module->request_shutdown_func(module->type, module->module_number);
} zend_end_try();
@@ -3490,7 +3518,7 @@ ZEND_API void zend_post_deactivate_modules(void) /* {{{ */
zend_module_entry **p = module_post_deactivate_handlers;
while (*p) {
- zend_module_entry *module = *p;
+ const zend_module_entry *module = *p;
module->post_deactivate_func();
p++;
@@ -3506,9 +3534,9 @@ ZEND_API int zend_next_free_module(void) /* {{{ */
}
/* }}} */
-static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */
+static zend_class_entry *do_register_internal_class(const zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */
{
- zend_class_entry *class_entry = malloc(sizeof(zend_class_entry));
+ zend_class_entry *class_entry = pemalloc(sizeof(zend_class_entry), true);
zend_string *lowercase_name;
*class_entry = *orig_class_entry;
@@ -3542,14 +3570,14 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class
* If both parent_ce and parent_name are NULL it does a regular class registration
* If parent_name is specified but not found NULL is returned
*/
-ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *class_entry, zend_class_entry *parent_ce) /* {{{ */
+ZEND_API zend_class_entry *zend_register_internal_class_ex(const zend_class_entry *class_entry, zend_class_entry *parent_ce) /* {{{ */
{
return zend_register_internal_class_with_flags(class_entry, parent_ce, 0);
}
/* }}} */
ZEND_API zend_class_entry *zend_register_internal_class_with_flags(
- zend_class_entry *class_entry,
+ const zend_class_entry *class_entry,
zend_class_entry *parent_ce,
uint32_t ce_flags
) {
@@ -3587,13 +3615,13 @@ ZEND_API void zend_class_implements(zend_class_entry *class_entry, int num_inter
/* A class that contains at least one abstract method automatically becomes an abstract class.
*/
-ZEND_API zend_class_entry *zend_register_internal_class(zend_class_entry *orig_class_entry) /* {{{ */
+ZEND_API zend_class_entry *zend_register_internal_class(const zend_class_entry *orig_class_entry) /* {{{ */
{
return do_register_internal_class(orig_class_entry, 0);
}
/* }}} */
-ZEND_API zend_class_entry *zend_register_internal_interface(zend_class_entry *orig_class_entry) /* {{{ */
+ZEND_API zend_class_entry *zend_register_internal_interface(const zend_class_entry *orig_class_entry) /* {{{ */
{
return do_register_internal_class(orig_class_entry, ZEND_ACC_INTERFACE);
}
@@ -3606,7 +3634,7 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
/* TODO: Move this out of here in 7.4. */
if (persistent && EG(current_module) && EG(current_module)->type == MODULE_TEMPORARY) {
- persistent = 0;
+ persistent = false;
}
if (name[0] == '\\') {
@@ -3640,29 +3668,6 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
}
/* }}} */
-// TODO num_symbol_tables as unsigned int?
-ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...) /* {{{ */
-{
- HashTable *symbol_table;
- va_list symbol_table_list;
-
- if (num_symbol_tables <= 0) return FAILURE;
-
- if (is_ref) {
- ZVAL_MAKE_REF(symbol);
- }
-
- va_start(symbol_table_list, num_symbol_tables);
- while (num_symbol_tables-- > 0) {
- symbol_table = va_arg(symbol_table_list, HashTable *);
- zend_hash_str_update(symbol_table, name, name_length, symbol);
- Z_TRY_ADDREF_P(symbol);
- }
- va_end(symbol_table_list);
- return SUCCESS;
-}
-/* }}} */
-
/* Disabled functions support */
static void zend_disable_function(const char *function_name, size_t function_name_length)
@@ -3713,12 +3718,12 @@ ZEND_API void zend_disable_functions(const char *function_list) /* {{{ */
}
/* }}} */
-static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame)
+static zend_always_inline zend_class_entry *get_scope(const zend_execute_data *frame)
{
return frame && frame->func ? frame->func->common.scope : NULL;
}
-static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *scope, zend_execute_data *frame, zend_fcall_info_cache *fcc, bool *strict_class, char **error, bool suppress_deprecation) /* {{{ */
+static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *scope, const zend_execute_data *frame, zend_fcall_info_cache *fcc, bool *strict_class, char **error, bool suppress_deprecation) /* {{{ */
{
bool ret = false;
zend_class_entry *ce;
@@ -3729,7 +3734,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
ZSTR_ALLOCA_ALLOC(lcname, name_len, use_heap);
zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(name), name_len);
- *strict_class = 0;
+ *strict_class = false;
if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_SELF))) {
if (!scope) {
if (error) *error = estrdup("cannot access \"self\" when no class scope is active");
@@ -3764,7 +3769,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
if (!fcc->object) {
fcc->object = zend_get_this_object(frame);
}
- *strict_class = 1;
+ *strict_class = true;
ret = true;
}
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_STATIC))) {
@@ -3781,18 +3786,18 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
if (!fcc->object) {
fcc->object = zend_get_this_object(frame);
}
- *strict_class = 1;
+ *strict_class = true;
ret = true;
}
} else if ((ce = zend_lookup_class(name)) != NULL) {
- zend_class_entry *scope = get_scope(frame);
+ const zend_class_entry *frame_scope = get_scope(frame);
fcc->calling_scope = ce;
- if (scope && !fcc->object) {
+ if (frame_scope && !fcc->object) {
zend_object *object = zend_get_this_object(frame);
if (object &&
- instanceof_function(object->ce, scope) &&
- instanceof_function(scope, ce)) {
+ instanceof_function(object->ce, frame_scope) &&
+ instanceof_function(frame_scope, ce)) {
fcc->object = object;
fcc->called_scope = object->ce;
} else {
@@ -3801,7 +3806,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
} else {
fcc->called_scope = fcc->object ? fcc->object->ce : ce;
}
- *strict_class = 1;
+ *strict_class = true;
ret = true;
} else {
if (error) zend_spprintf(error, 0, "class \"%.*s\" not found", (int)name_len, ZSTR_VAL(name));
@@ -3826,10 +3831,10 @@ ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc) {
}
}
-static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_execute_data *frame, zend_fcall_info_cache *fcc, bool strict_class, char **error, bool suppress_deprecation) /* {{{ */
+static zend_always_inline bool zend_is_callable_check_func(const zval *callable, const zend_execute_data *frame, zend_fcall_info_cache *fcc, bool strict_class, char **error, bool suppress_deprecation) /* {{{ */
{
zend_class_entry *ce_org = fcc->calling_scope;
- bool retval = 0;
+ bool retval = false;
zend_string *mname, *cname;
zend_string *lmname;
const char *colon;
@@ -3911,7 +3916,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
} else {
fcc->called_scope = fcc->object ? fcc->object->ce : fcc->calling_scope;
}
- strict_class = 1;
+ strict_class = true;
} else if (!zend_is_callable_check_class(cname, scope, frame, fcc, &strict_class, error, suppress_deprecation || ce_org != NULL)) {
zend_string_release_ex(cname, 0);
return 0;
@@ -3949,11 +3954,11 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
zend_string_equals_literal(lmname, ZEND_CONSTRUCTOR_FUNC_NAME)) {
fcc->function_handler = fcc->calling_scope->constructor;
if (fcc->function_handler) {
- retval = 1;
+ retval = true;
}
} else if ((zv = zend_hash_find(ftable, lmname)) != NULL) {
fcc->function_handler = Z_PTR_P(zv);
- retval = 1;
+ retval = true;
if ((fcc->function_handler->op_array.fn_flags & ZEND_ACC_CHANGED) &&
!strict_class) {
scope = get_scope(frame);
@@ -3978,7 +3983,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
scope = get_scope(frame);
ZEND_ASSERT(!(fcc->function_handler->common.fn_flags & ZEND_ACC_PUBLIC));
if (!zend_check_method_accessible(fcc->function_handler, scope)) {
- retval = 0;
+ retval = false;
fcc->function_handler = NULL;
goto get_function_via_handler;
}
@@ -3987,9 +3992,9 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
get_function_via_handler:
if (fcc->object && fcc->calling_scope == ce_org) {
if (strict_class && ce_org->__call) {
- fcc->function_handler = zend_get_call_trampoline_func(ce_org, mname, 0);
+ fcc->function_handler = zend_get_call_trampoline_func(ce_org->__call, mname);
call_via_handler = 1;
- retval = 1;
+ retval = true;
} else {
fcc->function_handler = fcc->object->handlers->get_method(&fcc->object, mname, NULL);
if (fcc->function_handler) {
@@ -3998,7 +4003,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
!instanceof_function(ce_org, fcc->function_handler->common.scope))) {
zend_release_fcall_info_cache(fcc);
} else {
- retval = 1;
+ retval = true;
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
}
}
@@ -4010,7 +4015,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
fcc->function_handler = zend_std_get_static_method(fcc->calling_scope, mname, NULL);
}
if (fcc->function_handler) {
- retval = 1;
+ retval = true;
call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
if (call_via_handler && !fcc->object) {
zend_object *object = zend_get_this_object(frame);
@@ -4026,12 +4031,12 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
if (retval) {
if (fcc->calling_scope && !call_via_handler) {
if (fcc->function_handler->common.fn_flags & ZEND_ACC_ABSTRACT) {
- retval = 0;
+ retval = false;
if (error) {
zend_spprintf(error, 0, "cannot call abstract method %s::%s()", ZSTR_VAL(fcc->calling_scope->name), ZSTR_VAL(fcc->function_handler->common.function_name));
}
} else if (!fcc->object && !(fcc->function_handler->common.fn_flags & ZEND_ACC_STATIC)) {
- retval = 0;
+ retval = false;
if (error) {
zend_spprintf(error, 0, "non-static method %s::%s() cannot be called statically", ZSTR_VAL(fcc->calling_scope->name), ZSTR_VAL(fcc->function_handler->common.function_name));
}
@@ -4047,7 +4052,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
}
zend_spprintf(error, 0, "cannot access %s method %s::%s()", zend_visibility_string(fcc->function_handler->common.fn_flags), ZSTR_VAL(fcc->calling_scope->name), ZSTR_VAL(fcc->function_handler->common.function_name));
}
- retval = 0;
+ retval = false;
}
}
}
@@ -4072,7 +4077,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, zend_
}
/* }}} */
-ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object) /* {{{ */
+ZEND_API zend_string *zend_get_callable_name_ex(const zval *callable, const zend_object *object) /* {{{ */
{
try_again:
switch (Z_TYPE_P(callable)) {
@@ -4084,8 +4089,8 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj
case IS_ARRAY:
{
- zval *method = NULL;
- zval *obj = NULL;
+ const zval *method = NULL;
+ const zval *obj = NULL;
if (zend_hash_num_elements(Z_ARRVAL_P(callable)) == 2) {
obj = zend_hash_index_find_deref(Z_ARRVAL_P(callable), 0);
@@ -4106,7 +4111,7 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj
}
case IS_OBJECT:
{
- zend_class_entry *ce = Z_OBJCE_P(callable);
+ const zend_class_entry *ce = Z_OBJCE_P(callable);
if (ce == zend_ce_closure) {
const zend_function *fn = zend_get_closure_method_def(Z_OBJ_P(callable));
@@ -4130,19 +4135,65 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj
}
/* }}} */
-ZEND_API zend_string *zend_get_callable_name(zval *callable) /* {{{ */
+static bool zend_fcc_function_handler_equals(const zend_function *func1, const zend_function *func2) /* {{{ */
+{
+ if (func1 == func2) {
+ return true;
+ }
+
+ const bool fake_closure1 = (func1->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
+ const bool fake_closure2 = (func2->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
+
+ if (!fake_closure1 && !fake_closure2) {
+ return false;
+ }
+ if (((func1->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure1) ||
+ ((func2->common.fn_flags & ZEND_ACC_CLOSURE) && !fake_closure2)) {
+ return false;
+ }
+ if (func1->type != func2->type ||
+ func1->common.scope != func2->common.scope ||
+ !zend_string_equals(func1->common.function_name, func2->common.function_name)) {
+ return false;
+ }
+
+ if (func1->type == ZEND_USER_FUNCTION) {
+ return func1->op_array.opcodes == func2->op_array.opcodes;
+ }
+
+ return func1->internal_function.handler == func2->internal_function.handler;
+}
+/* }}} */
+
+ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b) /* {{{ */
+{
+ const zend_function *func1 = a->function_handler;
+ const zend_function *func2 = b->function_handler;
+
+ if (a->closure && a->closure->ce == zend_ce_closure) {
+ func1 = zend_get_closure_method_def(a->closure);
+ }
+ if (b->closure && b->closure->ce == zend_ce_closure) {
+ func2 = zend_get_closure_method_def(b->closure);
+ }
+
+ return zend_fcc_function_handler_equals(func1, func2);
+}
+/* }}} */
+
+ZEND_API zend_string *zend_get_callable_name(const zval *callable) /* {{{ */
{
return zend_get_callable_name_ex(callable, NULL);
}
/* }}} */
ZEND_API bool zend_is_callable_at_frame(
- zval *callable, zend_object *object, zend_execute_data *frame,
+ const zval *callable, zend_object *object, const zend_execute_data *frame,
uint32_t check_flags, zend_fcall_info_cache *fcc, char **error) /* {{{ */
{
bool ret;
zend_fcall_info_cache fcc_local;
- bool strict_class = 0;
+ bool strict_class = false;
if (fcc == NULL) {
fcc = &fcc_local;
@@ -4247,10 +4298,10 @@ ZEND_API bool zend_is_callable_at_frame(
}
/* }}} */
-ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */
+ZEND_API bool zend_is_callable_ex(const zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */
{
/* Determine callability at the first parent user frame. */
- zend_execute_data *frame = EG(current_execute_data);
+ const zend_execute_data *frame = EG(current_execute_data);
while (frame && (!frame->func || !ZEND_USER_CODE(frame->func->type))) {
frame = frame->prev_execute_data;
}
@@ -4262,31 +4313,13 @@ ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t
return ret;
}
-ZEND_API bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */
+ZEND_API bool zend_is_callable(const zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */
{
return zend_is_callable_ex(callable, NULL, check_flags, callable_name, NULL, NULL);
}
/* }}} */
-ZEND_API bool zend_make_callable(zval *callable, zend_string **callable_name) /* {{{ */
-{
- zend_fcall_info_cache fcc;
-
- if (zend_is_callable_ex(callable, NULL, IS_CALLABLE_SUPPRESS_DEPRECATIONS, callable_name, &fcc, NULL)) {
- if (Z_TYPE_P(callable) == IS_STRING && fcc.calling_scope) {
- zval_ptr_dtor_str(callable);
- array_init(callable);
- add_next_index_str(callable, zend_string_copy(fcc.calling_scope->name));
- add_next_index_str(callable, zend_string_copy(fcc.function_handler->common.function_name));
- }
- zend_release_fcall_info_cache(&fcc);
- return 1;
- }
- return 0;
-}
-/* }}} */
-
-ZEND_API zend_result zend_fcall_info_init(zval *callable, uint32_t check_flags, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zend_string **callable_name, char **error) /* {{{ */
+ZEND_API zend_result zend_fcall_info_init(const zval *callable, uint32_t check_flags, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zend_string **callable_name, char **error) /* {{{ */
{
if (!zend_is_callable_ex(callable, NULL, check_flags, callable_name, fcc, error)) {
return FAILURE;
@@ -4299,6 +4332,7 @@ ZEND_API zend_result zend_fcall_info_init(zval *callable, uint32_t check_flags,
fci->param_count = 0;
fci->params = NULL;
fci->named_params = NULL;
+ fci->consumed_args = 0;
return SUCCESS;
}
@@ -4334,7 +4368,7 @@ ZEND_API void zend_fcall_info_args_save(zend_fcall_info *fci, uint32_t *param_co
ZEND_API void zend_fcall_info_args_restore(zend_fcall_info *fci, uint32_t param_count, zval *params) /* {{{ */
{
- zend_fcall_info_args_clear(fci, 1);
+ zend_fcall_info_args_clear(fci, true);
fci->param_count = param_count;
fci->params = params;
}
@@ -4464,21 +4498,14 @@ ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc,
ZEND_API const char *zend_get_module_version(const char *module_name) /* {{{ */
{
- zend_string *lname;
size_t name_len = strlen(module_name);
- zend_module_entry *module;
-
- lname = zend_string_alloc(name_len, 0);
- zend_str_tolower_copy(ZSTR_VAL(lname), module_name, name_len);
- module = zend_hash_find_ptr(&module_registry, lname);
- zend_string_efree(lname);
+ zend_module_entry *module = zend_hash_str_find_ptr_lc(&module_registry, module_name, name_len);
return module ? module->version : NULL;
}
/* }}} */
-static zend_always_inline bool is_persistent_class(zend_class_entry *ce) {
- return (ce->type & ZEND_INTERNAL_CLASS)
- && ce->info.internal.module->type == MODULE_PERSISTENT;
+static zend_always_inline bool is_persistent_class(const zend_class_entry *ce) {
+ return ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module->type == MODULE_PERSISTENT;
}
ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */
@@ -4601,7 +4628,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
Z_PROP_FLAG_P(property_default_ptr) = Z_ISUNDEF_P(property) ? IS_PROP_UNINIT : 0;
}
skip_property_storage:
- if (ce->type & ZEND_INTERNAL_CLASS) {
+ if (ce->type == ZEND_INTERNAL_CLASS) {
/* Must be interned to avoid ZTS data races */
if (is_persistent_class(ce)) {
name = zend_new_interned_string(zend_string_copy(name));
@@ -4820,7 +4847,7 @@ ZEND_API void zend_declare_property_string(zend_class_entry *ce, const char *nam
{
zval property;
- ZVAL_NEW_STR(&property, zend_string_init(value, strlen(value), ce->type & ZEND_INTERNAL_CLASS));
+ ZVAL_NEW_STR(&property, zend_string_init(value, strlen(value), ce->type == ZEND_INTERNAL_CLASS));
zend_declare_property(ce, name, name_length, &property, access_type);
}
/* }}} */
@@ -4829,7 +4856,7 @@ ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *na
{
zval property;
- ZVAL_NEW_STR(&property, zend_string_init(value, value_len, ce->type & ZEND_INTERNAL_CLASS));
+ ZVAL_NEW_STR(&property, zend_string_init(value, value_len, ce->type == ZEND_INTERNAL_CLASS));
zend_declare_property(ce, name, name_length, &property, access_type);
}
/* }}} */
@@ -4943,7 +4970,7 @@ ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const ch
{
zval constant;
- ZVAL_NEW_STR(&constant, zend_string_init(value, value_length, ce->type & ZEND_INTERNAL_CLASS));
+ ZVAL_NEW_STR(&constant, zend_string_init(value, value_length, ce->type == ZEND_INTERNAL_CLASS));
zend_declare_class_constant(ce, name, name_length, &constant);
}
/* }}} */
@@ -5162,7 +5189,7 @@ ZEND_API zend_result zend_update_static_property_stringl(zend_class_entry *scope
}
/* }}} */
-ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv) /* {{{ */
+ZEND_API zval *zend_read_property_ex(const zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv) /* {{{ */
{
zval *value;
const zend_class_entry *old_scope = EG(fake_scope);
@@ -5176,7 +5203,7 @@ ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *objec
}
/* }}} */
-ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv) /* {{{ */
+ZEND_API zval *zend_read_property(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv) /* {{{ */
{
zval *value;
zend_string *str;
@@ -5228,7 +5255,7 @@ ZEND_API void zend_replace_error_handling(zend_error_handling_t error_handling,
}
/* }}} */
-ZEND_API void zend_restore_error_handling(zend_error_handling *saved) /* {{{ */
+ZEND_API void zend_restore_error_handling(const zend_error_handling *saved) /* {{{ */
{
EG(error_handling) = saved->handling;
EG(exception_class) = saved->exception;
@@ -5328,48 +5355,43 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) {
}
ZEND_API zend_result zend_get_default_from_internal_arg_info(
- zval *default_value_zval, zend_internal_arg_info *arg_info)
+ zval *default_value_zval, const zend_arg_info *arg_info)
{
- const char *default_value = arg_info->default_value;
+ const zend_string *default_value = arg_info->default_value;
if (!default_value) {
return FAILURE;
}
/* Avoid going through the full AST machinery for some simple and common cases. */
- size_t default_value_len = strlen(default_value);
zend_ulong lval;
- if (default_value_len == sizeof("null")-1
- && !memcmp(default_value, "null", sizeof("null")-1)) {
+ if (zend_string_equals_literal(default_value, "null")) {
ZVAL_NULL(default_value_zval);
return SUCCESS;
- } else if (default_value_len == sizeof("true")-1
- && !memcmp(default_value, "true", sizeof("true")-1)) {
+ } else if (zend_string_equals_literal(default_value, "true")) {
ZVAL_TRUE(default_value_zval);
return SUCCESS;
- } else if (default_value_len == sizeof("false")-1
- && !memcmp(default_value, "false", sizeof("false")-1)) {
+ } else if (zend_string_equals_literal(default_value, "false")) {
ZVAL_FALSE(default_value_zval);
return SUCCESS;
- } else if (default_value_len >= 2
- && (default_value[0] == '\'' || default_value[0] == '"')
- && default_value[default_value_len - 1] == default_value[0]) {
+ } else if (ZSTR_LEN(default_value) >= 2
+ && (ZSTR_VAL(default_value)[0] == '\'' || ZSTR_VAL(default_value)[0] == '"')
+ && ZSTR_VAL(default_value)[ZSTR_LEN(default_value) - 1] == ZSTR_VAL(default_value)[0]) {
zend_string *str = try_parse_string(
- default_value + 1, default_value_len - 2, default_value[0]);
+ ZSTR_VAL(default_value) + 1, ZSTR_LEN(default_value) - 2, ZSTR_VAL(default_value)[0]);
if (str) {
ZVAL_STR(default_value_zval, str);
return SUCCESS;
}
- } else if (default_value_len == sizeof("[]")-1
- && !memcmp(default_value, "[]", sizeof("[]")-1)) {
+ } else if (zend_string_equals_literal(default_value, "[]")) {
ZVAL_EMPTY_ARRAY(default_value_zval);
return SUCCESS;
- } else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) {
+ } else if (ZEND_HANDLE_NUMERIC(default_value, lval)) {
ZVAL_LONG(default_value_zval, lval);
return SUCCESS;
}
#if 0
- fprintf(stderr, "Evaluating %s via AST\n", default_value);
+ fprintf(stderr, "Evaluating %s via AST\n", ZSTR_VAL(default_value));
#endif
- return get_default_via_ast(default_value_zval, default_value);
+ return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value));
}
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 8cde8317530e..2c0b892d1941 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -37,7 +36,7 @@ typedef struct _zend_function_entry {
zif_handler handler;
const struct _zend_internal_arg_info *arg_info;
uint32_t num_args;
- uint32_t flags;
+ uint64_t flags;
const zend_frameless_function_info *frameless_function_infos;
const char *doc_comment;
} zend_function_entry;
@@ -49,6 +48,7 @@ typedef struct _zend_fcall_info {
zval *params;
zend_object *object;
uint32_t param_count;
+ uint32_t consumed_args;
/* This hashtable can also contain positional arguments (with integer keys),
* which will be appended to the normal params[]. This makes it easier to
* integrate APIs like call_user_func_array(). The usual restriction that
@@ -75,6 +75,8 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(zif_##name)
#define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(zim_##classname##_##name)
+#define ZEND_FENTRY_FLAGS(flags, flags2) (((uint64_t)flags) | ((uint64_t)flags2 << 32))
+
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags, NULL, NULL },
#define ZEND_RAW_FENTRY(zend_name, name, arg_info, flags, frameless_function_infos, doc_comment) { zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags, frameless_function_infos, doc_comment },
@@ -340,6 +342,13 @@ typedef struct _zend_fcall_info_cache {
#define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0)
#define ZEND_FCC_INITIALIZED(fcc) ((fcc).function_handler != NULL)
+static zend_always_inline uint32_t zend_fci_consumed_arg(uint32_t arg_index) {
+ return arg_index < 32 ? (UINT32_C(1) << arg_index) : UINT32_C(0);
+}
+static zend_always_inline bool zend_fci_is_consumed_arg(uint32_t consumed_args, uint32_t arg_index) {
+ return arg_index < 32 && (consumed_args & (UINT32_C(1) << arg_index));
+}
+
ZEND_API int zend_next_free_module(void);
BEGIN_EXTERN_C()
@@ -347,25 +356,16 @@ ZEND_API void zend_set_dl_use_deepbind(bool use_deepbind);
ZEND_API zend_result zend_get_parameters_array_ex(uint32_t param_count, zval *argument_array);
-/* internal function to efficiently copy parameters when executing __call() */
-ZEND_API zend_result zend_copy_parameters_array(uint32_t param_count, zval *argument_array);
-
#define zend_get_parameters_array(ht, param_count, argument_array) \
zend_get_parameters_array_ex(param_count, argument_array)
#define zend_parse_parameters_none() \
(EXPECTED(ZEND_NUM_ARGS() == 0) ? SUCCESS : (zend_wrong_parameters_none_error(), FAILURE))
-#define zend_parse_parameters_none_throw() \
- zend_parse_parameters_none()
/* Parameter parsing API -- andrei */
-#define ZEND_PARSE_PARAMS_THROW 0 /* No longer used, zpp always uses exceptions */
#define ZEND_PARSE_PARAMS_QUIET (1<<1)
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec, ...);
ZEND_API zend_result zend_parse_parameters_ex(int flags, uint32_t num_args, const char *type_spec, ...);
-/* NOTE: This must have at least one value in __VA_ARGS__ for the expression to be valid */
-#define zend_parse_parameters_throw(num_args, ...) \
- zend_parse_parameters(num_args, __VA_ARGS__)
ZEND_API const char *zend_zval_type_name(const zval *arg);
ZEND_API const char *zend_zval_value_name(const zval *arg);
ZEND_API zend_string *zend_zval_get_legacy_type(const zval *arg);
@@ -387,14 +387,15 @@ ZEND_API void zend_startup_modules(void);
ZEND_API void zend_collect_module_handlers(void);
ZEND_API void zend_destroy_modules(void);
ZEND_API void zend_check_magic_method_implementation(
- const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type);
-ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, zend_string *lcname);
+ const zend_class_entry *ce, const zend_function *fptr, const zend_string *lcname, int error_type);
+ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, const zend_string *lcname);
-ZEND_API zend_class_entry *zend_register_internal_class(zend_class_entry *class_entry);
-ZEND_API zend_class_entry *zend_register_internal_class_ex(zend_class_entry *class_entry, zend_class_entry *parent_ce);
-ZEND_API zend_class_entry *zend_register_internal_class_with_flags(zend_class_entry *class_entry, zend_class_entry *parent_ce, uint32_t flags);
-ZEND_API zend_class_entry *zend_register_internal_interface(zend_class_entry *orig_class_entry);
+ZEND_API zend_class_entry *zend_register_internal_class(const zend_class_entry *class_entry);
+ZEND_API zend_class_entry *zend_register_internal_class_ex(const zend_class_entry *class_entry, zend_class_entry *parent_ce);
+ZEND_API zend_class_entry *zend_register_internal_class_with_flags(const zend_class_entry *class_entry, zend_class_entry *parent_ce, uint32_t flags);
+ZEND_API zend_class_entry *zend_register_internal_interface(const zend_class_entry *orig_class_entry);
ZEND_API void zend_class_implements(zend_class_entry *class_entry, int num_interfaces, ...);
+ZEND_API void zend_class_use_internal_traits(zend_class_entry *class_entry, int num_traits, ...);
ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_len, zend_class_entry *ce, bool persistent);
@@ -407,20 +408,19 @@ static zend_always_inline zend_result zend_register_class_alias(const char *name
ZEND_API void zend_disable_functions(const char *function_list);
ZEND_API ZEND_COLD void zend_wrong_param_count(void);
-ZEND_API ZEND_COLD void zend_wrong_property_read(zval *object, zval *property);
+ZEND_API ZEND_COLD void zend_wrong_property_read(const zval *object, zval *property);
#define IS_CALLABLE_CHECK_SYNTAX_ONLY (1<<0)
#define IS_CALLABLE_SUPPRESS_DEPRECATIONS (1<<1)
ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc);
-ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object);
-ZEND_API zend_string *zend_get_callable_name(zval *callable);
+ZEND_API zend_string *zend_get_callable_name_ex(const zval *callable, const zend_object *object);
+ZEND_API zend_string *zend_get_callable_name(const zval *callable);
ZEND_API bool zend_is_callable_at_frame(
- zval *callable, zend_object *object, zend_execute_data *frame,
+ const zval *callable, zend_object *object, const zend_execute_data *frame,
uint32_t check_flags, zend_fcall_info_cache *fcc, char **error);
-ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error);
-ZEND_API bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name);
-ZEND_API bool zend_make_callable(zval *callable, zend_string **callable_name);
+ZEND_API bool zend_is_callable_ex(const zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error);
+ZEND_API bool zend_is_callable(const zval *callable, uint32_t check_flags, zend_string **callable_name);
ZEND_API const char *zend_get_module_version(const char *module_name);
ZEND_API zend_result zend_get_module_started(const char *module_name);
@@ -447,12 +447,12 @@ ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const cha
ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, const zend_string *name, zend_class_entry *scope);
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
-ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
+ZEND_API HashTable *zend_separate_class_constants_table(const zend_class_entry *class_type);
-static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry *ce) {
+static zend_always_inline const HashTable *zend_class_constants_table(const zend_class_entry *ce) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) && ZEND_MAP_PTR(ce->mutable_data)) {
- zend_class_mutable_data *mutable_data =
- (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+ const zend_class_mutable_data *mutable_data =
+ (const zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
if (mutable_data && mutable_data->constants_table) {
return mutable_data->constants_table;
} else {
@@ -463,10 +463,10 @@ static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry
}
}
-static zend_always_inline zval *zend_class_default_properties_table(zend_class_entry *ce) {
+static zend_always_inline zval *zend_class_default_properties_table(const zend_class_entry *ce) {
if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES) && ZEND_MAP_PTR(ce->mutable_data)) {
- zend_class_mutable_data *mutable_data =
- (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+ const zend_class_mutable_data *mutable_data =
+ (const zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
return mutable_data->default_properties_table;
} else {
return ce->default_properties_table;
@@ -483,10 +483,10 @@ static zend_always_inline void zend_class_set_backed_enum_table(zend_class_entry
}
}
-static zend_always_inline HashTable *zend_class_backed_enum_table(zend_class_entry *ce)
+static zend_always_inline HashTable *zend_class_backed_enum_table(const zend_class_entry *ce)
{
if (ZEND_MAP_PTR(ce->mutable_data) && ce->type == ZEND_USER_CLASS) {
- zend_class_mutable_data *mutable_data = (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
+ const zend_class_mutable_data *mutable_data = (const zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
return mutable_data->backed_enum_table;
} else {
return ce->backed_enum_table;
@@ -513,8 +513,8 @@ ZEND_API zend_result zend_update_static_property_double(zend_class_entry *scope,
ZEND_API zend_result zend_update_static_property_string(zend_class_entry *scope, const char *name, size_t name_length, const char *value);
ZEND_API zend_result zend_update_static_property_stringl(zend_class_entry *scope, const char *name, size_t name_length, const char *value, size_t value_length);
-ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv);
-ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv);
+ZEND_API zval *zend_read_property_ex(const zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv);
+ZEND_API zval *zend_read_property(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv);
ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, bool silent);
ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, bool silent);
@@ -527,25 +527,31 @@ ZEND_API const char *zend_get_type_by_const(int type);
#define getThis() (hasThis() ? ZEND_THIS : NULL)
#define ZEND_IS_METHOD_CALL() (EX(func)->common.scope != NULL)
-#define WRONG_PARAM_COUNT ZEND_WRONG_PARAM_COUNT()
#define ZEND_NUM_ARGS() EX_NUM_ARGS()
-#define ZEND_WRONG_PARAM_COUNT() { zend_wrong_param_count(); return; }
#ifndef ZEND_WIN32
#define DLEXPORT
#endif
-#define array_init(arg) ZVAL_ARR((arg), zend_new_array(0))
-#define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size))
+static zend_always_inline void array_init_size(zval *arg, uint32_t size)
+{
+ ZVAL_ARR(arg, zend_new_array(size));
+}
+
+static zend_always_inline void array_init(zval *arg)
+{
+ array_init_size(arg, 0);
+}
+
ZEND_API void object_init(zval *arg);
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce);
ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params);
ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties);
ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type);
ZEND_API void object_properties_init_ex(zend_object *object, HashTable *properties);
-ZEND_API void object_properties_load(zend_object *object, HashTable *properties);
+ZEND_API void object_properties_load(zend_object *object, const HashTable *properties);
-ZEND_API void zend_merge_properties(zval *obj, HashTable *properties);
+ZEND_API void zend_merge_properties(const zval *obj, const HashTable *properties);
ZEND_API void add_assoc_long_ex(zval *arg, const char *key, size_t key_len, zend_long n);
ZEND_API void add_assoc_null_ex(zval *arg, const char *key, size_t key_len);
@@ -707,7 +713,7 @@ ZEND_API zend_result _call_user_function_impl(zval *object, zval *function_name,
* fci->params = NULL;
* The callable_name argument may be NULL.
*/
-ZEND_API zend_result zend_fcall_info_init(zval *callable, uint32_t check_flags, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zend_string **callable_name, char **error);
+ZEND_API zend_result zend_fcall_info_init(const zval *callable, uint32_t check_flags, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zend_string **callable_name, char **error);
/** Clear arguments connected with zend_fcall_info *fci
* If free_mem is not zero then the params array gets free'd as well
@@ -753,20 +759,27 @@ ZEND_API void zend_fcall_info_argn(zend_fcall_info *fci, uint32_t argc, ...);
ZEND_API zend_result zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *retval, zval *args);
/* Zend FCC API to store and handle PHP userland functions */
+ZEND_API bool zend_fcc_closure_equals_ex(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b);
+
static zend_always_inline bool zend_fcc_equals(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b)
{
+ if (a->closure || b->closure) {
+ return a->object == b->object
+ && a->calling_scope == b->calling_scope
+ && a->called_scope == b->called_scope
+ && (a->closure == b->closure || zend_fcc_closure_equals_ex(a, b))
+ ;
+ }
if (UNEXPECTED((a->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) &&
(b->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) {
return a->object == b->object
&& a->calling_scope == b->calling_scope
- && a->closure == b->closure
&& zend_string_equals(a->function_handler->common.function_name, b->function_handler->common.function_name)
;
}
return a->function_handler == b->function_handler
&& a->object == b->object
&& a->calling_scope == b->calling_scope
- && a->closure == b->closure
;
}
@@ -888,8 +901,6 @@ ZEND_API zend_result zend_call_method_if_exists(
zend_object *object, zend_string *method_name, zval *retval,
uint32_t param_count, zval *params);
-ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...);
-
ZEND_API zend_result zend_delete_global_variable(zend_string *name);
ZEND_API zend_array *zend_rebuild_symbol_table(void);
@@ -900,9 +911,11 @@ ZEND_API zend_result zend_set_local_var_str(const char *name, size_t len, zval *
static zend_always_inline zend_result zend_forbid_dynamic_call(void)
{
- zend_execute_data *ex = EG(current_execute_data);
+ const zend_execute_data *ex = EG(current_execute_data);
ZEND_ASSERT(ex != NULL && ex->func != NULL);
+ ZEND_ASSERT(ex->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS);
+
if (ZEND_CALL_INFO(ex) & ZEND_CALL_DYNAMIC) {
zend_string *function_or_method_name = get_active_function_or_method_name();
zend_throw_error(NULL, "Cannot call %.*s() dynamically",
@@ -930,8 +943,12 @@ ZEND_API bool zend_is_iterable(const zval *iterable);
ZEND_API bool zend_is_countable(const zval *countable);
+ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info,
+ const zend_internal_arg_info *arg_info, bool is_return_info,
+ bool permanent);
+
ZEND_API zend_result zend_get_default_from_internal_arg_info(
- zval *default_value_zval, zend_internal_arg_info *arg_info);
+ zval *default_value_zval, const zend_arg_info *arg_info);
END_EXTERN_C()
@@ -951,10 +968,6 @@ static zend_always_inline bool zend_char_has_nul_byte(const char *s, size_t know
return known_length != strlen(s);
}
-/* Compatibility with PHP 8.1 and below */
-#define CHECK_ZVAL_NULL_PATH(p) zend_str_has_nul_byte(Z_STR_P(p))
-#define CHECK_NULL_PATH(p, l) zend_char_has_nul_byte(p, l)
-
#define ZVAL_STRINGL(z, s, l) do { \
ZVAL_NEW_STR(z, zend_string_init(s, l, 0)); \
} while (0)
@@ -1066,7 +1079,6 @@ static zend_always_inline bool zend_char_has_nul_byte(const char *s, size_t know
#define RETURN_THROWS() do { ZEND_ASSERT(EG(exception)); (void) return_value; return; } while (0)
#define HASH_OF(p) (Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties(Z_OBJ_P(p)) : NULL)))
-#define ZVAL_IS_NULL(z) (Z_TYPE_P(z) == IS_NULL)
/* For compatibility */
#define ZEND_MINIT ZEND_MODULE_STARTUP_N
@@ -1557,24 +1569,27 @@ typedef enum _zend_expected_type {
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_none_error(void);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t min_num_args, uint32_t max_num_args);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code, uint32_t num, char *name, zend_expected_type expected_type, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(uint32_t num, const char *name, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null_error(uint32_t num, const char *name, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, zval *arg);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_null_error(uint32_t num, const char *name, zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code, uint32_t num, char *name, zend_expected_type expected_type, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(uint32_t num, const char *name, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null_error(uint32_t num, const char *name, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, const zval *arg);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_null_error(uint32_t num, const char *name, const zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error);
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(zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, 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(zend_class_entry *error_ce, const zend_function *function, uint32_t arg_num, 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_value_error_ex(const zend_function *function, 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, zend_class_entry *old_ce);
-ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string *new_name, zend_class_entry *old_ce);
+ZEND_API ZEND_COLD void zend_class_redeclaration_error(int type, const zend_class_entry *old_ce);
+ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string *new_name, const zend_class_entry *old_ce);
#define ZPP_ERROR_OK 0
#define ZPP_ERROR_FAILURE 1
@@ -2018,6 +2033,13 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string
#define Z_PARAM_OBJ_OF_CLASS_OR_LONG_OR_NULL(dest_obj, _ce, dest_long, is_null) \
Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, 1)
+#define Z_PARAM_ENUM(dest, _ce) \
+ { \
+ zend_object *_tmp = NULL; \
+ Z_PARAM_OBJ_OF_CLASS(_tmp, _ce); \
+ dest = zend_enum_fetch_case_id(_tmp); \
+ }
+
/* old "p" */
#define Z_PARAM_PATH_EX(dest, dest_len, check_null, deref) \
Z_PARAM_PROLOGUE(deref, 0); \
@@ -2176,21 +2198,27 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string
/* Inlined implementations shared by new and old parameter parsing APIs */
+typedef enum zpp_parse_bool_status {
+ ZPP_PARSE_BOOL_STATUS_FALSE = 0,
+ ZPP_PARSE_BOOL_STATUS_TRUE = 1,
+ ZPP_PARSE_BOOL_STATUS_ERROR = 2,
+} zpp_parse_bool_status;
+
ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, bool *dest, uint32_t arg_num);
+ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, uint32_t arg_num);
+ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num);
+ZEND_API double ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, uint32_t arg_num);
+ZEND_API double ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, uint32_t arg_num);
+ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num);
+ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **dest, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num);
-ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num);
+ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, uint32_t arg_num);
+ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num);
ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num);
static zend_always_inline bool zend_parse_arg_bool_ex(const zval *arg, bool *dest, bool *is_null, bool check_null, uint32_t arg_num, bool frameless)
@@ -2206,11 +2234,16 @@ static zend_always_inline bool zend_parse_arg_bool_ex(const zval *arg, bool *des
*is_null = 1;
*dest = 0;
} else {
+ zpp_parse_bool_status result;
if (frameless) {
- return zend_flf_parse_arg_bool_slow(arg, dest, arg_num);
+ result = zend_flf_parse_arg_bool_slow(arg, arg_num);
} else {
- return zend_parse_arg_bool_slow(arg, dest, arg_num);
+ result = zend_parse_arg_bool_slow(arg, arg_num);
}
+ if (UNEXPECTED(result == ZPP_PARSE_BOOL_STATUS_ERROR)) {
+ return false;
+ }
+ *dest = result;
}
return 1;
}
@@ -2256,7 +2289,8 @@ static zend_always_inline bool zend_parse_arg_double(const zval *arg, double *de
*is_null = 1;
*dest = 0.0;
} else {
- return zend_parse_arg_double_slow(arg, dest, arg_num);
+ *dest = zend_parse_arg_double_slow(arg, arg_num);
+ return !zend_isnan(*dest);
}
return 1;
}
@@ -2293,12 +2327,13 @@ static zend_always_inline bool zend_parse_arg_str_ex(zval *arg, zend_string **de
*dest = NULL;
} else {
if (frameless) {
- return zend_flf_parse_arg_str_slow(arg, dest, arg_num);
+ *dest = zend_flf_parse_arg_str_slow(arg, arg_num);
} else {
- return zend_parse_arg_str_slow(arg, dest, arg_num);
+ *dest = zend_parse_arg_str_slow(arg, arg_num);
}
+ return *dest != NULL;
}
- return 1;
+ return true;
}
static zend_always_inline bool zend_parse_arg_str(zval *arg, zend_string **dest, bool check_null, uint32_t arg_num)
@@ -2484,7 +2519,7 @@ static zend_always_inline bool zend_parse_arg_resource(zval *arg, zval **dest, b
return 1;
}
-static zend_always_inline bool zend_parse_arg_func(zval *arg, zend_fcall_info *dest_fci, zend_fcall_info_cache *dest_fcc, bool check_null, char **error, bool free_trampoline)
+static zend_always_inline bool zend_parse_arg_func(const zval *arg, zend_fcall_info *dest_fci, zend_fcall_info_cache *dest_fcc, bool check_null, char **error, bool free_trampoline)
{
if (check_null && UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
dest_fci->size = 0;
@@ -2529,7 +2564,8 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_str(
*dest_str = NULL;
} else {
*dest_ht = NULL;
- return zend_parse_arg_str_slow(arg, dest_str, arg_num);
+ *dest_str = zend_parse_arg_str_slow(arg, arg_num);
+ return *dest_str != NULL;
}
return 1;
}
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index f3dfe0f9df57..fc7bc1f4d9d4 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -198,7 +197,7 @@ typedef zend_mm_bitset zend_mm_page_map[ZEND_MM_PAGE_MAP_LEN]; /* 64B */
#define ZEND_MM_SRUN_BIN_NUM_MASK 0x0000001f
#define ZEND_MM_SRUN_BIN_NUM_OFFSET 0
-#define ZEND_MM_SRUN_FREE_COUNTER_MASK 0x01ff0000
+#define ZEND_MM_SRUN_FREE_COUNTER_MASK 0x03ff0000
#define ZEND_MM_SRUN_FREE_COUNTER_OFFSET 16
#define ZEND_MM_NRUN_OFFSET_MASK 0x01ff0000
@@ -1480,7 +1479,6 @@ static zend_always_inline void *zend_mm_alloc_heap(zend_mm_heap *heap, size_t si
size = ZEND_MM_ALIGNED_SIZE(size) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info));
if (UNEXPECTED(size < real_size)) {
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu + %zu)", ZEND_MM_ALIGNED_SIZE(real_size), ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info)));
- return NULL;
}
#endif
if (EXPECTED(size <= ZEND_MM_MAX_SMALL_SIZE)) {
@@ -2615,19 +2613,19 @@ typedef struct _zend_alloc_globals {
#ifdef ZTS
static int alloc_globals_id;
-static size_t alloc_globals_offset;
-# define AG(v) ZEND_TSRMG_FAST(alloc_globals_offset, zend_alloc_globals *, v)
+static TSRM_TLS TSRM_TLS_MODEL_ATTR zend_alloc_globals alloc_globals;
+static void *alloc_globals_tls_addr(void) { return &alloc_globals; }
#else
-# define AG(v) (alloc_globals.v)
static zend_alloc_globals alloc_globals;
#endif
+#define AG(v) (alloc_globals.v)
ZEND_API bool is_zend_mm(void)
{
#if ZEND_MM_CUSTOM
return !AG(mm_heap)->use_custom_heap;
#else
- return 1;
+ return true;
#endif
}
@@ -2975,7 +2973,7 @@ ZEND_API void refresh_memory_manager(void)
static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void)
{
fprintf(stderr, "Out of memory\n");
- exit(1);
+ abort();
}
#if ZEND_MM_CUSTOM
@@ -3337,7 +3335,7 @@ ZEND_API void start_memory_manager(void)
# endif
#endif
#ifdef ZTS
- ts_allocate_fast_id(&alloc_globals_id, &alloc_globals_offset, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor);
+ ts_allocate_tls_id(&alloc_globals_id, alloc_globals_tls_addr, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor);
#else
alloc_globals_ctor(&alloc_globals);
#endif
@@ -3466,12 +3464,14 @@ ZEND_API zend_mm_heap *zend_mm_startup(void)
ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void *data, size_t data_size)
{
#if ZEND_MM_STORAGE
- zend_mm_storage tmp_storage, *storage;
+ zend_mm_storage *storage;
+ zend_mm_storage tmp_storage = {
+ .handlers = *handlers,
+ .data = data,
+ };
zend_mm_chunk *chunk;
zend_mm_heap *heap;
- memcpy((zend_mm_handlers*)&tmp_storage.handlers, handlers, sizeof(zend_mm_handlers));
- tmp_storage.data = data;
chunk = (zend_mm_chunk*)handlers->chunk_alloc(&tmp_storage, ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
if (UNEXPECTED(chunk == NULL)) {
#if ZEND_MM_ERROR
@@ -3581,9 +3581,3 @@ ZEND_API char * __zend_strdup(const char *s)
zend_out_of_memory();
}
-#ifdef ZTS
-size_t zend_mm_globals_size(void)
-{
- return sizeof(zend_alloc_globals);
-}
-#endif
diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h
index 264e13848d1b..7e3ea031dc72 100644
--- a/Zend/zend_alloc.h
+++ b/Zend/zend_alloc.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -404,10 +403,6 @@ static void apc_init_heap(void)
*/
-#ifdef ZTS
-size_t zend_mm_globals_size(void);
-#endif
-
END_EXTERN_C()
#endif
diff --git a/Zend/zend_alloc_sizes.h b/Zend/zend_alloc_sizes.h
index 502b982a5052..f806056ad9d3 100644
--- a/Zend/zend_alloc_sizes.h
+++ b/Zend/zend_alloc_sizes.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
diff --git a/Zend/zend_arena.h b/Zend/zend_arena.h
index 37cda28f29db..9b917a47e927 100644
--- a/Zend/zend_arena.h
+++ b/Zend/zend_arena.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index bb405945849e..1b293c832c91 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Bob Weinand |
| Dmitry Stogov |
@@ -54,13 +53,14 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_znode(const znode *node) {
return (zend_ast *) ast;
}
-ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void) {
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args) {
zend_ast_fcc *ast;
ast = zend_ast_alloc(sizeof(zend_ast_fcc));
ast->kind = ZEND_AST_CALLABLE_CONVERT;
ast->attr = 0;
ast->lineno = CG(zend_lineno);
+ ast->args = args;
ZEND_MAP_PTR_INIT(ast->fptr, NULL);
return (zend_ast *) ast;
@@ -157,6 +157,12 @@ ZEND_API zend_ast *zend_ast_create_decl(
return (zend_ast *) ast;
}
+static bool zend_ast_is_placeholder_arg(zend_ast *arg) {
+ return arg->kind == ZEND_AST_PLACEHOLDER_ARG
+ || (arg->kind == ZEND_AST_NAMED_ARG
+ && arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG);
+}
+
#if ZEND_AST_SPEC
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_0(zend_ast_kind kind) {
zend_ast *ast;
@@ -400,6 +406,30 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zen
return ast;
}
+
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_0(zend_ast_kind kind) {
+ return zend_ast_create_list(0, kind);
+}
+
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_1(zend_ast_kind kind, zend_ast *arg) {
+ zend_ast *list = zend_ast_create_list(1, kind, arg);
+
+ if (zend_ast_is_placeholder_arg(arg)) {
+ return zend_ast_create_fcc(list);
+ }
+
+ return list;
+}
+
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_2(zend_ast_kind kind, zend_ast *arg1, zend_ast *arg2) {
+ zend_ast *list = zend_ast_create_list(2, kind, arg1, arg2);
+
+ if (zend_ast_is_placeholder_arg(arg1) || zend_ast_is_placeholder_arg(arg2)) {
+ return zend_ast_create_fcc(list);
+ }
+
+ return list;
+}
#else
static zend_ast *zend_ast_create_from_va_list(zend_ast_kind kind, zend_ast_attr attr, va_list va) {
uint32_t i, children = kind >> ZEND_AST_NUM_CHILDREN_SHIFT;
@@ -479,6 +509,41 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki
return ast;
}
+
+ZEND_API zend_ast *zend_ast_create_arg_list(uint32_t init_children, zend_ast_kind kind, ...) {
+ zend_ast *ast;
+ zend_ast_list *list;
+ bool has_placeholders = false;
+
+ ast = zend_ast_alloc(zend_ast_list_size(4));
+ list = (zend_ast_list *) ast;
+ list->kind = kind;
+ list->attr = 0;
+ list->lineno = CG(zend_lineno);
+ list->children = 0;
+
+ {
+ va_list va;
+ uint32_t i;
+ va_start(va, kind);
+ for (i = 0; i < init_children; ++i) {
+ zend_ast *child = va_arg(va, zend_ast *);
+ ast = zend_ast_list_add(ast, child);
+ uint32_t lineno = zend_ast_get_lineno(child);
+ if (lineno < ast->lineno) {
+ ast->lineno = lineno;
+ }
+ has_placeholders = has_placeholders || zend_ast_is_placeholder_arg(child);
+ }
+ va_end(va);
+ }
+
+ if (has_placeholders) {
+ return zend_ast_create_fcc(list);
+ }
+
+ return ast;
+}
#endif
zend_ast *zend_ast_create_concat_op(zend_ast *op0, zend_ast *op1) {
@@ -508,6 +573,23 @@ ZEND_ATTRIBUTE_NODISCARD ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zen
return (zend_ast *) list;
}
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg)
+{
+ if (list->kind == ZEND_AST_CALLABLE_CONVERT) {
+ zend_ast_fcc *fcc_ast = (zend_ast_fcc*)list;
+ fcc_ast->args = zend_ast_list_add(fcc_ast->args, arg);
+ return (zend_ast*)fcc_ast;
+ }
+
+ ZEND_ASSERT(list->kind == ZEND_AST_ARG_LIST);
+
+ if (zend_ast_is_placeholder_arg(arg)) {
+ return zend_ast_create_fcc(zend_ast_list_add(list, arg));
+ }
+
+ return zend_ast_list_add(list, arg);
+}
+
static zend_result zend_ast_add_array_element(const zval *result, zval *offset, zval *expr)
{
if (Z_TYPE_P(offset) == IS_UNDEF) {
@@ -558,7 +640,7 @@ static zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *s
return zend_fetch_class_with_scope(zend_ast_get_str(ast), (ast->attr >> ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT) | ZEND_FETCH_CLASS_EXCEPTION, scope);
}
-ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
+static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zval *result,
zend_ast *ast,
zend_class_entry *scope,
@@ -589,7 +671,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(
return r;
}
-ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
+static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zval *result,
zend_ast *ast,
zend_class_entry *scope,
@@ -731,7 +813,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
case IS_OBJECT:
zend_cast_zval_to_object(result, &op1, IS_VAR);
break;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
zval_ptr_dtor_nogc(&op1);
if (UNEXPECTED(EG(exception))) {
@@ -912,10 +994,13 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zend_ast *class_name_ast = ast->child[0];
zend_string *class_name = zend_ast_get_str(class_name_ast);
- zend_ast *case_name_ast = ast->child[1];
+ zend_ast *case_id_ast = ast->child[1];
+ int case_id = (int)Z_LVAL_P(zend_ast_get_zval(case_id_ast));
+
+ zend_ast *case_name_ast = ast->child[2];
zend_string *case_name = zend_ast_get_str(case_name_ast);
- zend_ast *case_value_ast = ast->child[2];
+ zend_ast *case_value_ast = ast->child[3];
zval case_value_zv;
ZVAL_UNDEF(&case_value_zv);
@@ -926,7 +1011,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
}
zend_class_entry *ce = zend_lookup_class(class_name);
- zend_enum_new(result, ce, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
+ zend_enum_new(result, ce, case_id, case_name, case_value_ast != NULL ? &case_value_zv : NULL);
zval_ptr_dtor_nogc(&case_value_zv);
break;
}
@@ -1054,12 +1139,29 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
case ZEND_AST_CALL:
case ZEND_AST_STATIC_CALL:
{
+ // Preloading will attempt to resolve constants but objects can't be stored in shm
+ // Aborting here to store the const AST instead
+ if (CG(in_compilation)) {
+ return FAILURE;
+ }
+
zend_function *fptr;
zend_class_entry *called_scope = NULL;
+
+ zend_ast *args_ast = zend_ast_call_get_args(ast);
+ ZEND_ASSERT(args_ast->kind == ZEND_AST_CALLABLE_CONVERT);
+
+ zend_ast_fcc *fcc_ast = (zend_ast_fcc*)args_ast;
+
+ zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
+ ZEND_ASSERT(args->children > 0);
+ if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) {
+ /* TODO: PFAs */
+ zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
+ }
+
switch (ast->kind) {
case ZEND_AST_CALL: {
- ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
- zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[1];
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
if (!fptr) {
@@ -1084,9 +1186,6 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
break;
}
case ZEND_AST_STATIC_CALL: {
- ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
- zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
-
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
if (!ce) {
return FAILURE;
@@ -1124,12 +1223,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
zend_non_static_method_call(fptr);
-
+
return FAILURE;
}
if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) {
zend_abstract_method_call(fptr);
-
+
return FAILURE;
} else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) {
zend_error(E_DEPRECATED,
@@ -1146,6 +1245,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
break;
}
+ default: ZEND_UNREACHABLE();
}
zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL);
@@ -1249,7 +1349,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
size = sizeof(zend_ast_op_array);
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
- size = sizeof(zend_ast_fcc);
+ zend_ast *args_ast = ((zend_ast_fcc*)ast)->args;
+ size = sizeof(zend_ast_fcc) + zend_ast_tree_size(args_ast);
} else if (zend_ast_is_list(ast)) {
uint32_t i;
const zend_ast_list *list = zend_ast_get_list(ast);
@@ -1326,6 +1427,8 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
new->lineno = old->lineno;
ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr));
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
+ new->args = buf;
+ buf = zend_ast_tree_copy(old->args, buf);
} else if (zend_ast_is_decl(ast)) {
/* Not implemented. */
ZEND_UNREACHABLE();
@@ -1362,6 +1465,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:
@@ -1409,6 +1522,11 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
zend_ast_destroy(decl->child[3]);
ast = decl->child[4];
goto tail_call;
+ } else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) {
+ zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
+
+ ast = fcc_ast->args;
+ goto tail_call;
}
}
@@ -1476,9 +1594,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
static ZEND_COLD void zend_ast_export_str(smart_str *str, const zend_string *s)
{
- size_t i;
-
- for (i = 0; i < ZSTR_LEN(s); i++) {
+ for (size_t i = 0; i < ZSTR_LEN(s); i++) {
unsigned char c = ZSTR_VAL(s)[i];
if (c == '\'' || c == '\\') {
smart_str_appendc(str, '\\');
@@ -1491,9 +1607,7 @@ static ZEND_COLD void zend_ast_export_str(smart_str *str, const zend_string *s)
static ZEND_COLD void zend_ast_export_qstr(smart_str *str, char quote, const zend_string *s)
{
- size_t i;
-
- for (i = 0; i < ZSTR_LEN(s); i++) {
+ for (size_t i = 0; i < ZSTR_LEN(s); i++) {
unsigned char c = ZSTR_VAL(s)[i];
if (c < ' ') {
switch (c) {
@@ -1534,12 +1648,11 @@ static ZEND_COLD void zend_ast_export_qstr(smart_str *str, char quote, const zen
}
}
-static ZEND_COLD void zend_ast_export_quoted_str(smart_str *str, zend_string *s)
+static ZEND_COLD void zend_ast_export_quoted_str(smart_str *str, const zend_string *s)
{
- size_t i;
-
- for (i = 0; i < ZSTR_LEN(s); i++) {
- if ((unsigned char) ZSTR_VAL(s)[i] < ' ') {
+ for (size_t i = 0; i < ZSTR_LEN(s); i++) {
+ unsigned char c = ZSTR_VAL(s)[i];
+ if (c < ' ') {
smart_str_appendc(str, '"');
zend_ast_export_qstr(str, '"', s);
smart_str_appendc(str, '"');
@@ -1589,18 +1702,18 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int
}
zend_ast_export_ex(str, ast, priority, indent);
}
-
-static ZEND_COLD bool zend_ast_valid_var_char(char ch)
+static ZEND_COLD void zend_ast_export_ns_name_or_expression(smart_str *str, zend_ast *ast, int priority, int indent)
{
- unsigned char c = (unsigned char)ch;
-
- if (c != '_' && c < 127 &&
- (c < '0' || c > '9') &&
- (c < 'A' || c > 'Z') &&
- (c < 'a' || c > 'z')) {
- return 0;
+ switch (ast->kind) {
+ case ZEND_AST_ZVAL:
+ case ZEND_AST_VAR:
+ zend_ast_export_ns_name(str, ast, priority, indent);
+ break;
+ default:
+ smart_str_appendc(str, '(');
+ zend_ast_export_ex(str, ast, priority, indent);
+ smart_str_appendc(str, ')');
}
- return 1;
}
static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len)
@@ -1609,13 +1722,13 @@ static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len)
size_t i;
if (len == 0) {
- return 0;
+ return false;
}
c = (unsigned char)s[0];
if (c != '_' && c < 127 &&
(c < 'A' || c > 'Z') &&
(c < 'a' || c > 'z')) {
- return 0;
+ return false;
}
for (i = 1; i < len; i++) {
c = (unsigned char)s[i];
@@ -1623,24 +1736,24 @@ static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len)
(c < '0' || c > '9') &&
(c < 'A' || c > 'Z') &&
(c < 'a' || c > 'z')) {
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
-static ZEND_COLD bool zend_ast_var_needs_braces(char ch)
-{
- return ch == '[' || zend_ast_valid_var_char(ch);
-}
-
-static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int priority, int indent)
+static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int indent)
{
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
- if (Z_TYPE_P(zv) == IS_STRING &&
- zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) {
- smart_str_append(str, Z_STR_P(zv));
+ if (Z_TYPE_P(zv) == IS_STRING) {
+ if (zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) {
+ smart_str_append(str, Z_STR_P(zv));
+ } else {
+ smart_str_appends(str, "{'");
+ zend_ast_export_str(str, Z_STR_P(zv));
+ smart_str_appends(str, "'}");
+ }
return;
}
} else if (ast->kind == ZEND_AST_VAR) {
@@ -1685,14 +1798,6 @@ static ZEND_COLD void zend_ast_export_encaps_list(smart_str *str, char quote, co
ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
zend_ast_export_qstr(str, quote, Z_STR_P(zv));
- } else if (ast->kind == ZEND_AST_VAR &&
- ast->child[0]->kind == ZEND_AST_ZVAL &&
- (i + 1 == list->children ||
- list->child[i + 1]->kind != ZEND_AST_ZVAL ||
- !zend_ast_var_needs_braces(
- *Z_STRVAL_P(
- zend_ast_get_zval(list->child[i + 1]))))) {
- zend_ast_export_ex(str, ast, 0, indent);
} else {
smart_str_appendc(str, '{');
zend_ast_export_ex(str, ast, 0, indent);
@@ -1859,11 +1964,10 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, const zval *zv, int p
}
if (key) {
zend_ast_export_quoted_str(str, key);
- smart_str_appends(str, " => ");
} else {
smart_str_append_long(str, idx);
- smart_str_appends(str, " => ");
}
+ smart_str_appends(str, " => ");
zend_ast_export_zval(str, val, 0, indent);
} ZEND_HASH_FOREACH_END();
smart_str_appendc(str, ']');
@@ -1872,7 +1976,7 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, const zval *zv, int p
case IS_CONSTANT_AST:
zend_ast_export_ex(str, Z_ASTVAL_P(zv), priority, indent);
break;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
}
@@ -2071,7 +2175,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
break;
case ZEND_AST_CONSTANT: {
zend_string *name = zend_ast_get_constant_name(ast);
- smart_str_appendl(str, ZSTR_VAL(name), ZSTR_LEN(name));
+ smart_str_append(str, name);
break;
}
case ZEND_AST_OP_ARRAY:
@@ -2121,7 +2225,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appendc(str, '&');
}
if (ast->kind != ZEND_AST_CLOSURE && ast->kind != ZEND_AST_ARROW_FUNC) {
- smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name));
+ smart_str_append(str, decl->name);
}
smart_str_appendc(str, '(');
zend_ast_export_ex(str, decl->child[0], 0, indent);
@@ -2159,7 +2263,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_CLASS:
decl = (const zend_ast_decl *) ast;
if (decl->child[3]) {
- zend_ast_export_attributes(str, decl->child[3], indent, 1);
+ zend_ast_export_attributes(str, decl->child[3], indent, true);
}
if (decl->flags & ZEND_ACC_INTERFACE) {
smart_str_appends(str, "interface ");
@@ -2179,7 +2283,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
}
smart_str_appends(str, "class ");
}
- smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name));
+ smart_str_append(str, decl->name);
if (decl->flags & ZEND_ACC_ENUM && decl->child[4]) {
smart_str_appends(str, ": ");
zend_ast_export_type(str, decl->child[4], indent);
@@ -2193,11 +2297,11 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_EXPR_LIST:
case ZEND_AST_PARAM_LIST:
simple_list:
- zend_ast_export_list(str, zend_ast_get_list(ast), 1, 20, indent);
+ zend_ast_export_list(str, zend_ast_get_list(ast), true, 20, indent);
break;
case ZEND_AST_ARRAY:
smart_str_appendc(str, '[');
- zend_ast_export_list(str, zend_ast_get_list(ast), 1, 20, indent);
+ zend_ast_export_list(str, zend_ast_get_list(ast), true, 20, indent);
smart_str_appendc(str, ']');
break;
case ZEND_AST_ENCAPS_LIST:
@@ -2215,7 +2319,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_SWITCH_LIST:
case ZEND_AST_CATCH_LIST:
case ZEND_AST_MATCH_ARM_LIST:
- zend_ast_export_list(str, zend_ast_get_list(ast), 0, 0, indent);
+ zend_ast_export_list(str, zend_ast_get_list(ast), false, 0, indent);
break;
case ZEND_AST_CLOSURE_USES:
smart_str_appends(str, " use(");
@@ -2227,7 +2331,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
zend_ast *prop_ast = ast->child[1];
if (ast->child[2]) {
- zend_ast_export_attributes(str, ast->child[2], indent, 1);
+ zend_ast_export_attributes(str, ast->child[2], indent, true);
}
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_PROPERTY);
@@ -2256,13 +2360,13 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
str,
ast_list->child[ast_list->children - 1],
indent,
- 1
+ true
);
/* So that the list printing doesn't try to print the attributes,
* use zend_ast_export_list_ex() to override the number of children
* to print. */
smart_str_appends(str, "const ");
- zend_ast_export_list_ex(str, ast_list, 1, 20, indent, ast_list->children - 1);
+ zend_ast_export_list_ex(str, ast_list, true, 20, indent, ast_list->children - 1);
break;
}
smart_str_appends(str, "const ");
@@ -2270,7 +2374,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
}
case ZEND_AST_CLASS_CONST_GROUP:
if (ast->child[1]) {
- zend_ast_export_attributes(str, ast->child[1], indent, 1);
+ zend_ast_export_attributes(str, ast->child[1], indent, true);
}
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CONSTANT);
@@ -2307,7 +2411,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case T_PROPERTY_C: APPEND_STR("__PROPERTY__");
case T_NS_C: APPEND_STR("__NAMESPACE__");
case T_CLASS_C: APPEND_STR("__CLASS__");
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
break;
case ZEND_AST_TYPE:
@@ -2316,14 +2420,21 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case IS_CALLABLE: APPEND_STR("callable");
case IS_STATIC: APPEND_STR("static");
case IS_MIXED: APPEND_STR("mixed");
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
+ }
+ break;
+ case ZEND_AST_PLACEHOLDER_ARG:
+ if (ast->attr == ZEND_PLACEHOLDER_VARIADIC) {
+ APPEND_STR("...");
+ } else {
+ APPEND_STR("?");
}
break;
/* 1 child node */
case ZEND_AST_VAR:
smart_str_appendc(str, '$');
- zend_ast_export_var(str, ast->child[0], 0, indent);
+ zend_ast_export_var(str, ast->child[0], indent);
break;
case ZEND_AST_CONST:
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
@@ -2343,7 +2454,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case IS_STRING: PREFIX_OP("(string)", 240, 241);
case IS_ARRAY: PREFIX_OP("(array)", 240, 241);
case IS_OBJECT: PREFIX_OP("(object)", 240, 241);
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
break;
case ZEND_AST_CAST_VOID:
@@ -2377,14 +2488,14 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_REQUIRE_ONCE: FUNC_OP("require_once");
case ZEND_REQUIRE: FUNC_OP("require");
case ZEND_EVAL: FUNC_OP("eval");
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
break;
case ZEND_AST_UNARY_OP:
switch (ast->attr) {
case ZEND_BW_NOT: PREFIX_OP("~", 240, 241);
case ZEND_BOOL_NOT: PREFIX_OP("!", 240, 241);
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
break;
case ZEND_AST_PRE_INC:
@@ -2438,38 +2549,27 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_NULLSAFE_PROP:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ast->kind == ZEND_AST_NULLSAFE_PROP ? "?->" : "->");
- zend_ast_export_var(str, ast->child[1], 0, indent);
+ zend_ast_export_var(str, ast->child[1], indent);
break;
case ZEND_AST_STATIC_PROP:
- zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent);
smart_str_appends(str, "::$");
- zend_ast_export_var(str, ast->child[1], 0, indent);
+ zend_ast_export_var(str, ast->child[1], indent);
break;
case ZEND_AST_CALL: {
- zend_ast *left = ast->child[0];
- if (left->kind == ZEND_AST_ARROW_FUNC || left->kind == ZEND_AST_CLOSURE) {
- smart_str_appendc(str, '(');
- zend_ast_export_ns_name(str, left, 0, indent);
- smart_str_appendc(str, ')');
- } else {
- zend_ast_export_ns_name(str, left, 0, indent);
- }
+ zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
break;
}
- case ZEND_AST_PARENT_PROPERTY_HOOK_CALL:
- smart_str_append(str, Z_STR_P(zend_ast_get_zval(ast->child[0])));
- smart_str_appendc(str, '(');
- zend_ast_export_ex(str, ast->child[1], 0, indent);
- smart_str_appendc(str, ')');
- break;
- case ZEND_AST_CALLABLE_CONVERT:
- smart_str_appends(str, "...");
- break;
+ case ZEND_AST_CALLABLE_CONVERT: {
+ zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast;
+ ast = fcc_ast->args;
+ goto simple_list;
+ }
case ZEND_AST_CLASS_CONST:
- zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent);
smart_str_appends(str, "::");
zend_ast_export_name(str, ast->child[1], 0, indent);
break;
@@ -2483,10 +2583,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_FETCH_CLASS_PARENT:
smart_str_append(str, ZSTR_KNOWN(ZEND_STR_PARENT));
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
} else {
- zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent);
}
smart_str_appends(str, "::class");
break;
@@ -2506,7 +2606,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_BW_AND: BINARY_OP(" &= ", 90, 91, 90);
case ZEND_BW_XOR: BINARY_OP(" ^= ", 90, 91, 90);
case ZEND_POW: BINARY_OP(" **= ", 90, 91, 90);
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
break;
case ZEND_AST_ASSIGN_COALESCE: BINARY_OP(" \?\?= ", 90, 91, 90);
@@ -2532,7 +2632,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_POW: BINARY_OP(" ** ", 250, 251, 250);
case ZEND_BOOL_XOR: BINARY_OP(" xor ", 40, 40, 41);
case ZEND_SPACESHIP: BINARY_OP(" <=> ", 180, 181, 181);
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
break;
case ZEND_AST_GREATER: BINARY_OP(" > ", 180, 181, 181);
@@ -2554,7 +2654,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
if (ast->child[0]->kind == ZEND_AST_CLASS) {
const zend_ast_decl *decl = (const zend_ast_decl *) ast->child[0];
if (decl->child[3]) {
- zend_ast_export_attributes(str, decl->child[3], indent, 0);
+ zend_ast_export_attributes(str, decl->child[3], indent, false);
}
smart_str_appends(str, "class");
if (!zend_ast_is_list(ast->child[1])
@@ -2565,7 +2665,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
}
zend_ast_export_class_no_header(str, decl, indent);
} else {
- zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
smart_str_appendc(str, ')');
@@ -2574,7 +2674,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_INSTANCEOF:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, " instanceof ");
- zend_ast_export_ns_name(str, ast->child[1], 0, indent);
+ zend_ast_export_ns_name_or_expression(str, ast->child[1], 0, indent);
break;
case ZEND_AST_YIELD:
if (priority > 70) smart_str_appendc(str, '(');
@@ -2655,7 +2755,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_MATCH_ARM:
zend_ast_export_indent(str, indent);
if (ast->child[0]) {
- zend_ast_export_list(str, zend_ast_get_list(ast->child[0]), 1, 0, indent);
+ zend_ast_export_list(str, zend_ast_get_list(ast->child[0]), true, 0, indent);
smart_str_appends(str, " => ");
} else {
smart_str_appends(str, "default => ");
@@ -2666,7 +2766,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_DECLARE:
smart_str_appends(str, "declare(");
ZEND_ASSERT(ast->child[0]->kind == ZEND_AST_CONST_DECL);
- zend_ast_export_list(str, zend_ast_get_list(ast->child[0]), 1, 0, indent);
+ zend_ast_export_list(str, zend_ast_get_list(ast->child[0]), true, 0, indent);
smart_str_appendc(str, ')');
if (ast->child[1]) {
smart_str_appends(str, " {\n");
@@ -2711,7 +2811,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appends(str, " insteadof ");
zend_ast_export_ex(str, ast->child[1], 0, indent);
break;
- case ZEND_AST_METHOD_REFERENCE:
+ case ZEND_AST_TRAIT_METHOD_REFERENCE:
if (ast->child[0]) {
zend_ast_export_name(str, ast->child[0], 0, indent);
smart_str_appends(str, "::");
@@ -2761,15 +2861,20 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_NULLSAFE_METHOD_CALL:
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL ? "?->" : "->");
- zend_ast_export_var(str, ast->child[1], 0, indent);
+ zend_ast_export_var(str, ast->child[1], indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[2], 0, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_STATIC_CALL:
- zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ if (zend_ast_is_parent_hook_call(ast)) {
+ zend_ast_export_ns_name(str, ast->child[0], 0, indent);
+ } else {
+ zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent);
+ }
+
smart_str_appends(str, "::");
- zend_ast_export_var(str, ast->child[1], 0, indent);
+ zend_ast_export_var(str, ast->child[1], indent);
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[2], 0, indent);
smart_str_appendc(str, ')');
@@ -2805,7 +2910,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent);
if (ast->child[1]) {
smart_str_appends(str, " $");
- zend_ast_export_var(str, ast->child[1], 0, indent);
+ zend_ast_export_var(str, ast->child[1], indent);
}
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[2], indent + 1);
@@ -2813,7 +2918,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
break;
case ZEND_AST_PARAM:
if (ast->child[3]) {
- zend_ast_export_attributes(str, ast->child[3], indent, 0);
+ zend_ast_export_attributes(str, ast->child[3], indent, false);
}
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CPP);
if (ast->attr & ZEND_ACC_FINAL) {
@@ -2841,7 +2946,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
break;
case ZEND_AST_ENUM_CASE:
if (ast->child[3]) {
- zend_ast_export_attributes(str, ast->child[3], indent, 1);
+ zend_ast_export_attributes(str, ast->child[3], indent, true);
}
smart_str_appends(str, "case ");
zend_ast_export_name(str, ast->child[0], 0, indent);
@@ -2884,7 +2989,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
return;
@@ -2981,8 +3086,39 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr)
* zend_compile_const_decl() checks the kind of the list elements. */
ast = zend_ast_list_add(ast, attr);
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
return ast;
}
+
+zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast)
+{
+ if (ast->kind == ZEND_AST_CALL) {
+ return ast->child[1];
+ } else if (ast->kind == ZEND_AST_STATIC_CALL || ast->kind == ZEND_AST_METHOD_CALL) {
+ return ast->child[2];
+ }
+
+ ZEND_UNREACHABLE();
+ return NULL;
+}
+
+bool zend_ast_is_parent_hook_call(const zend_ast *ast)
+{
+ ZEND_ASSERT(ast->kind == ZEND_AST_STATIC_CALL);
+
+ const zend_ast *class_ast = ast->child[0];
+ zend_ast *method_ast = ast->child[1];
+
+ return class_ast->kind == ZEND_AST_STATIC_PROP
+ && !(class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP)
+ && class_ast->child[0]->kind == ZEND_AST_ZVAL
+ && Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) == IS_STRING
+ && zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) == ZEND_FETCH_CLASS_PARENT
+ && class_ast->child[1]->kind == ZEND_AST_ZVAL
+ && method_ast->kind == ZEND_AST_ZVAL
+ && Z_TYPE_P(zend_ast_get_zval(method_ast)) == IS_STRING
+ && (zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get")
+ || zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set"));
+}
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index fb48b187252b..26f54102a143 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Bob Weinand |
| Dmitry Stogov |
@@ -76,6 +75,7 @@ enum _zend_ast_kind {
ZEND_AST_TYPE,
ZEND_AST_CONSTANT_CLASS,
ZEND_AST_CALLABLE_CONVERT,
+ ZEND_AST_PLACEHOLDER_ARG,
/* 1 child node */
ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT,
@@ -143,7 +143,7 @@ enum _zend_ast_kind {
ZEND_AST_DECLARE,
ZEND_AST_USE_TRAIT,
ZEND_AST_TRAIT_PRECEDENCE,
- ZEND_AST_METHOD_REFERENCE,
+ ZEND_AST_TRAIT_METHOD_REFERENCE,
ZEND_AST_NAMESPACE,
ZEND_AST_USE_ELEM,
ZEND_AST_TRAIT_ALIAS,
@@ -152,7 +152,6 @@ enum _zend_ast_kind {
ZEND_AST_MATCH,
ZEND_AST_MATCH_ARM,
ZEND_AST_NAMED_ARG,
- ZEND_AST_PARENT_PROPERTY_HOOK_CALL,
ZEND_AST_PIPE,
/* 3 child nodes */
@@ -167,15 +166,15 @@ enum _zend_ast_kind {
ZEND_AST_CONST_ELEM,
ZEND_AST_CLASS_CONST_GROUP,
- // Pseudo node for initializing enums
- ZEND_AST_CONST_ENUM_INIT,
-
/* 4 child nodes */
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
ZEND_AST_FOREACH,
ZEND_AST_ENUM_CASE,
ZEND_AST_PROP_ELEM,
+ // Pseudo node for initializing enums
+ ZEND_AST_CONST_ENUM_INIT,
+
/* 5 child nodes */
/* 6 child nodes */
@@ -229,10 +228,12 @@ typedef struct _zend_ast_decl {
zend_ast *child[5];
} zend_ast_decl;
+// TODO: rename
typedef struct _zend_ast_fcc {
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
zend_ast_attr attr; /* Additional attribute, use depending on node type */
uint32_t lineno; /* Line number */
+ zend_ast *args;
ZEND_MAP_PTR_DEF(zend_function *, fptr);
} zend_ast_fcc;
@@ -307,27 +308,39 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_0(zend_ast_kind kind);
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_1(zend_ast_kind kind, zend_ast *child);
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2);
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_0(zend_ast_kind kind);
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_1(zend_ast_kind kind, zend_ast *child);
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2);
+
# define zend_ast_create(...) \
ZEND_AST_SPEC_CALL(zend_ast_create, __VA_ARGS__)
# define zend_ast_create_ex(...) \
ZEND_AST_SPEC_CALL_EX(zend_ast_create_ex, __VA_ARGS__)
# define zend_ast_create_list(init_children, ...) \
ZEND_AST_SPEC_CALL(zend_ast_create_list, __VA_ARGS__)
+# define zend_ast_create_arg_list(init_children, ...) \
+ ZEND_AST_SPEC_CALL(zend_ast_create_arg_list, __VA_ARGS__)
#else
ZEND_API zend_ast *zend_ast_create(zend_ast_kind kind, ...);
ZEND_API zend_ast *zend_ast_create_ex(zend_ast_kind kind, zend_ast_attr attr, ...);
ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind kind, ...);
+ZEND_API zend_ast *zend_ast_create_arg_list(uint32_t init_children, zend_ast_kind kind, ...);
#endif
ZEND_ATTRIBUTE_NODISCARD ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op);
+/* Like zend_ast_list_add(), but wraps the list into a ZEND_AST_CALLABLE_CONVERT
+ * if any arg is a ZEND_AST_PLACEHOLDER_ARG. list can be a zend_ast_list, or a
+ * zend_ast_fcc. */
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg);
+
ZEND_API zend_ast *zend_ast_create_decl(
zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment,
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4
);
-ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void);
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args);
typedef struct {
bool had_side_effects;
@@ -337,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);
@@ -345,7 +361,7 @@ typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr, void *context);
ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *context);
static zend_always_inline size_t zend_ast_size(uint32_t children) {
- return XtOffsetOf(zend_ast, child) + (sizeof(zend_ast *) * children);
+ return offsetof(zend_ast, child) + (sizeof(zend_ast *) * children);
}
static zend_always_inline bool zend_ast_is_special(const zend_ast *ast) {
@@ -425,4 +441,9 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) {
zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr);
+zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast);
+
+/* Recognize parent::$prop::get() pattern. */
+bool zend_ast_is_parent_hook_call(const zend_ast *ast);
+
#endif
diff --git a/Zend/zend_atomic.c b/Zend/zend_atomic.c
index 5f6a7793beea..98a39c583437 100644
--- a/Zend/zend_atomic.c
+++ b/Zend/zend_atomic.c
@@ -1,12 +1,12 @@
/*
+----------------------------------------------------------------------+
- | 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. |
+ | Copyright © The PHP Group 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: Levi Morrison |
+----------------------------------------------------------------------+
@@ -57,7 +57,7 @@ ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired) {
zend_atomic_int_store_ex(obj, desired);
}
-#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
+#if (defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)) && !defined(HAVE_C11_ATOMICS)
/* On these platforms it is non-const due to underlying APIs. */
ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj) {
return zend_atomic_bool_load_ex(obj);
diff --git a/Zend/zend_atomic.h b/Zend/zend_atomic.h
index 56422a721fdb..31558996c3c3 100644
--- a/Zend/zend_atomic.h
+++ b/Zend/zend_atomic.h
@@ -1,12 +1,12 @@
/*
+----------------------------------------------------------------------+
- | 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. |
+ | Copyright © The PHP Group 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: Levi Morrison |
+----------------------------------------------------------------------+
@@ -39,7 +39,7 @@
* and alignment purposes.
*/
-#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
+#if (defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)) && !defined(HAVE_C11_ATOMICS)
typedef struct zend_atomic_bool_s {
volatile char value;
} zend_atomic_bool;
@@ -68,7 +68,7 @@ typedef struct zend_atomic_int_s {
BEGIN_EXTERN_C()
-#ifdef ZEND_WIN32
+#if defined(ZEND_WIN32) && !defined(HAVE_C11_ATOMICS)
#ifndef InterlockedExchange8
#define InterlockedExchange8 _InterlockedExchange8
@@ -123,7 +123,7 @@ static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_i
}
}
-/* On this platform it is non-const due to Iterlocked API*/
+/* On this platform it is non-const due to Interlocked API */
static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
/* Or'ing with false won't change the value. */
return InterlockedOr8(&obj->value, false);
@@ -376,7 +376,7 @@ ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expect
ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired);
ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired);
-#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
+#if (defined(ZEND_WIN32) && !defined(HAVE_C11_ATOMICS)) || defined(HAVE_SYNC_ATOMICS)
/* On these platforms it is non-const due to underlying APIs. */
ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj);
ZEND_API int zend_atomic_int_load(zend_atomic_int *obj);
diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c
index b69e192701e4..71f0d788e921 100644
--- a/Zend/zend_attributes.c
+++ b/Zend/zend_attributes.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Benjamin Eberlei |
| Martin Schröder |
@@ -38,7 +37,7 @@ static zend_object_handlers attributes_object_handlers_sensitive_parameter_value
static HashTable internal_attributes;
-uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope)
+uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope)
{
// TODO: More proper signature validation: Too many args, incorrect arg names.
if (attr->argc > 0) {
@@ -265,7 +264,7 @@ ZEND_METHOD(NoDiscard, __construct)
}
}
-static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
+static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset)
{
if (attributes) {
zend_attribute *attr;
@@ -280,7 +279,7 @@ static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname,
return NULL;
}
-static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
+static zend_attribute *get_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset)
{
if (attributes) {
zend_attribute *attr;
@@ -295,27 +294,27 @@ static zend_attribute *get_attribute_str(HashTable *attributes, const char *str,
return NULL;
}
-ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname)
+ZEND_API zend_attribute *zend_get_attribute(const HashTable *attributes, const zend_string *lcname)
{
return get_attribute(attributes, lcname, 0);
}
-ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len)
+ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, const char *str, size_t len)
{
return get_attribute_str(attributes, str, len, 0);
}
-ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
+ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset)
{
return get_attribute(attributes, lcname, offset + 1);
}
-ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
+ZEND_API zend_attribute *zend_get_parameter_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset)
{
return get_attribute_str(attributes, str, len, offset + 1);
}
-ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope)
+ZEND_API zend_result zend_get_attribute_value(zval *ret, const zend_attribute *attr, uint32_t i, zend_class_entry *scope)
{
if (i >= attr->argc) {
return FAILURE;
@@ -447,7 +446,7 @@ ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
return smart_str_extract(&str);
}
-ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
+ZEND_API bool zend_is_attribute_repeated(const HashTable *attributes, const zend_attribute *attr)
{
zend_attribute *other;
diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h
index 10227c2d1e8e..c044ef073cc8 100644
--- a/Zend/zend_attributes.h
+++ b/Zend/zend_attributes.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Benjamin Eberlei |
| Martin Schröder |
@@ -77,17 +76,17 @@ typedef struct _zend_internal_attribute {
zend_string* (*validator)(zend_attribute *attr, uint32_t target, zend_class_entry *scope);
} zend_internal_attribute;
-ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname);
-ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len);
+ZEND_API zend_attribute *zend_get_attribute(const HashTable *attributes, const zend_string *lcname);
+ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, const char *str, size_t len);
-ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset);
-ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset);
+ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset);
+ZEND_API zend_attribute *zend_get_parameter_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset);
-ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope);
+ZEND_API zend_result zend_get_attribute_value(zval *ret, const zend_attribute *attr, uint32_t i, zend_class_entry *scope);
ZEND_API zend_result zend_get_attribute_object(zval *out, zend_class_entry *attribute_ce, zend_attribute *attribute_data, zend_class_entry *scope, zend_string *filename);
ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets);
-ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr);
+ZEND_API bool zend_is_attribute_repeated(const HashTable *attributes, const zend_attribute *attr);
ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce);
ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags);
@@ -97,7 +96,7 @@ ZEND_API zend_attribute *zend_add_attribute(
HashTable **attributes, zend_string *name, uint32_t argc,
uint32_t flags, uint32_t offset, uint32_t lineno);
-uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope);
+uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope);
END_EXTERN_C()
@@ -119,13 +118,13 @@ static zend_always_inline zend_attribute *zend_add_parameter_attribute(zend_func
return zend_add_attribute(&func->common.attributes, name, argc, flags, offset + 1, 0);
}
-static zend_always_inline zend_attribute *zend_add_property_attribute(zend_class_entry *ce, zend_property_info *info, zend_string *name, uint32_t argc)
+static zend_always_inline zend_attribute *zend_add_property_attribute(const zend_class_entry *ce, zend_property_info *info, zend_string *name, uint32_t argc)
{
uint32_t flags = ce->type != ZEND_USER_CLASS ? ZEND_ATTRIBUTE_PERSISTENT : 0;
return zend_add_attribute(&info->attributes, name, argc, flags, 0, 0);
}
-static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc)
+static zend_always_inline zend_attribute *zend_add_class_constant_attribute(const zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc)
{
uint32_t flags = ce->type != ZEND_USER_CLASS ? ZEND_ATTRIBUTE_PERSISTENT : 0;
return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0);
diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php
index ded9c89593a3..05fd285d5b9f 100644
--- a/Zend/zend_attributes.stub.php
+++ b/Zend/zend_attributes.stub.php
@@ -68,7 +68,7 @@ public function __debugInfo(): array {}
/**
* @strict-properties
*/
-#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY)]
+#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_PROPERTY|Attribute::TARGET_CLASS_CONSTANT)]
final class Override
{
public function __construct() {}
diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h
index 05f7eeb3e5d4..8acac398426f 100644
--- a/Zend/zend_attributes_arginfo.h
+++ b/Zend/zend_attributes_arginfo.h
@@ -1,5 +1,5 @@
-/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: b868cb33f41d9442f42d0cec84e33fcc09f5d88c */
+/* This is a generated file, edit zend_attributes.stub.php instead.
+ * Stub hash: bae42fcb945360f90a1e2762c6c22177f25efb9e */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
@@ -95,67 +95,67 @@ static zend_class_entry *register_class_Attribute(void)
zval const_TARGET_CLASS_value;
ZVAL_LONG(&const_TARGET_CLASS_value, ZEND_ATTRIBUTE_TARGET_CLASS);
- zend_string *const_TARGET_CLASS_name = zend_string_init_interned("TARGET_CLASS", sizeof("TARGET_CLASS") - 1, 1);
+ zend_string *const_TARGET_CLASS_name = zend_string_init_interned("TARGET_CLASS", sizeof("TARGET_CLASS") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_CLASS_name, &const_TARGET_CLASS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_CLASS_name);
+ zend_string_release_ex(const_TARGET_CLASS_name, true);
zval const_TARGET_FUNCTION_value;
ZVAL_LONG(&const_TARGET_FUNCTION_value, ZEND_ATTRIBUTE_TARGET_FUNCTION);
- zend_string *const_TARGET_FUNCTION_name = zend_string_init_interned("TARGET_FUNCTION", sizeof("TARGET_FUNCTION") - 1, 1);
+ zend_string *const_TARGET_FUNCTION_name = zend_string_init_interned("TARGET_FUNCTION", sizeof("TARGET_FUNCTION") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_FUNCTION_name, &const_TARGET_FUNCTION_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_FUNCTION_name);
+ zend_string_release_ex(const_TARGET_FUNCTION_name, true);
zval const_TARGET_METHOD_value;
ZVAL_LONG(&const_TARGET_METHOD_value, ZEND_ATTRIBUTE_TARGET_METHOD);
- zend_string *const_TARGET_METHOD_name = zend_string_init_interned("TARGET_METHOD", sizeof("TARGET_METHOD") - 1, 1);
+ zend_string *const_TARGET_METHOD_name = zend_string_init_interned("TARGET_METHOD", sizeof("TARGET_METHOD") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_METHOD_name, &const_TARGET_METHOD_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_METHOD_name);
+ zend_string_release_ex(const_TARGET_METHOD_name, true);
zval const_TARGET_PROPERTY_value;
ZVAL_LONG(&const_TARGET_PROPERTY_value, ZEND_ATTRIBUTE_TARGET_PROPERTY);
- zend_string *const_TARGET_PROPERTY_name = zend_string_init_interned("TARGET_PROPERTY", sizeof("TARGET_PROPERTY") - 1, 1);
+ zend_string *const_TARGET_PROPERTY_name = zend_string_init_interned("TARGET_PROPERTY", sizeof("TARGET_PROPERTY") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_PROPERTY_name, &const_TARGET_PROPERTY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_PROPERTY_name);
+ zend_string_release_ex(const_TARGET_PROPERTY_name, true);
zval const_TARGET_CLASS_CONSTANT_value;
ZVAL_LONG(&const_TARGET_CLASS_CONSTANT_value, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
- zend_string *const_TARGET_CLASS_CONSTANT_name = zend_string_init_interned("TARGET_CLASS_CONSTANT", sizeof("TARGET_CLASS_CONSTANT") - 1, 1);
+ zend_string *const_TARGET_CLASS_CONSTANT_name = zend_string_init_interned("TARGET_CLASS_CONSTANT", sizeof("TARGET_CLASS_CONSTANT") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_CLASS_CONSTANT_name, &const_TARGET_CLASS_CONSTANT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_CLASS_CONSTANT_name);
+ zend_string_release_ex(const_TARGET_CLASS_CONSTANT_name, true);
zval const_TARGET_PARAMETER_value;
ZVAL_LONG(&const_TARGET_PARAMETER_value, ZEND_ATTRIBUTE_TARGET_PARAMETER);
- zend_string *const_TARGET_PARAMETER_name = zend_string_init_interned("TARGET_PARAMETER", sizeof("TARGET_PARAMETER") - 1, 1);
+ zend_string *const_TARGET_PARAMETER_name = zend_string_init_interned("TARGET_PARAMETER", sizeof("TARGET_PARAMETER") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_PARAMETER_name, &const_TARGET_PARAMETER_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_PARAMETER_name);
+ zend_string_release_ex(const_TARGET_PARAMETER_name, true);
zval const_TARGET_CONSTANT_value;
ZVAL_LONG(&const_TARGET_CONSTANT_value, ZEND_ATTRIBUTE_TARGET_CONST);
- zend_string *const_TARGET_CONSTANT_name = zend_string_init_interned("TARGET_CONSTANT", sizeof("TARGET_CONSTANT") - 1, 1);
+ zend_string *const_TARGET_CONSTANT_name = zend_string_init_interned("TARGET_CONSTANT", sizeof("TARGET_CONSTANT") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_CONSTANT_name, &const_TARGET_CONSTANT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_CONSTANT_name);
+ zend_string_release_ex(const_TARGET_CONSTANT_name, true);
zval const_TARGET_ALL_value;
ZVAL_LONG(&const_TARGET_ALL_value, ZEND_ATTRIBUTE_TARGET_ALL);
- zend_string *const_TARGET_ALL_name = zend_string_init_interned("TARGET_ALL", sizeof("TARGET_ALL") - 1, 1);
+ zend_string *const_TARGET_ALL_name = zend_string_init_interned("TARGET_ALL", sizeof("TARGET_ALL") - 1, true);
zend_declare_typed_class_constant(class_entry, const_TARGET_ALL_name, &const_TARGET_ALL_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_TARGET_ALL_name);
+ zend_string_release_ex(const_TARGET_ALL_name, true);
zval const_IS_REPEATABLE_value;
ZVAL_LONG(&const_IS_REPEATABLE_value, ZEND_ATTRIBUTE_IS_REPEATABLE);
- zend_string *const_IS_REPEATABLE_name = zend_string_init_interned("IS_REPEATABLE", sizeof("IS_REPEATABLE") - 1, 1);
+ zend_string *const_IS_REPEATABLE_name = zend_string_init_interned("IS_REPEATABLE", sizeof("IS_REPEATABLE") - 1, true);
zend_declare_typed_class_constant(class_entry, const_IS_REPEATABLE_name, &const_IS_REPEATABLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(const_IS_REPEATABLE_name);
+ zend_string_release_ex(const_IS_REPEATABLE_name, true);
zval property_flags_default_value;
ZVAL_UNDEF(&property_flags_default_value);
- zend_string *property_flags_name = zend_string_init("flags", sizeof("flags") - 1, 1);
+ zend_string *property_flags_name = zend_string_init("flags", sizeof("flags") - 1, true);
zend_declare_typed_property(class_entry, property_flags_name, &property_flags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(property_flags_name);
+ zend_string_release_ex(property_flags_name, true);
- zend_string *attribute_name_Attribute_class_Attribute_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_Attribute_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_Attribute_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Attribute_0, 1);
- zend_string_release(attribute_name_Attribute_class_Attribute_0);
+ zend_string_release_ex(attribute_name_Attribute_class_Attribute_0, true);
ZVAL_LONG(&attribute_Attribute_class_Attribute_0->args[0].value, ZEND_ATTRIBUTE_TARGET_CLASS);
return class_entry;
@@ -168,9 +168,9 @@ static zend_class_entry *register_class_ReturnTypeWillChange(void)
INIT_CLASS_ENTRY(ce, "ReturnTypeWillChange", class_ReturnTypeWillChange_methods);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL);
- zend_string *attribute_name_Attribute_class_ReturnTypeWillChange_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_ReturnTypeWillChange_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_ReturnTypeWillChange_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ReturnTypeWillChange_0, 1);
- zend_string_release(attribute_name_Attribute_class_ReturnTypeWillChange_0);
+ zend_string_release_ex(attribute_name_Attribute_class_ReturnTypeWillChange_0, true);
ZVAL_LONG(&attribute_Attribute_class_ReturnTypeWillChange_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD);
return class_entry;
@@ -183,9 +183,9 @@ static zend_class_entry *register_class_AllowDynamicProperties(void)
INIT_CLASS_ENTRY(ce, "AllowDynamicProperties", class_AllowDynamicProperties_methods);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL);
- zend_string *attribute_name_Attribute_class_AllowDynamicProperties_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_AllowDynamicProperties_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_AllowDynamicProperties_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_AllowDynamicProperties_0, 1);
- zend_string_release(attribute_name_Attribute_class_AllowDynamicProperties_0);
+ zend_string_release_ex(attribute_name_Attribute_class_AllowDynamicProperties_0, true);
ZVAL_LONG(&attribute_Attribute_class_AllowDynamicProperties_0->args[0].value, ZEND_ATTRIBUTE_TARGET_CLASS);
return class_entry;
@@ -198,9 +198,9 @@ static zend_class_entry *register_class_SensitiveParameter(void)
INIT_CLASS_ENTRY(ce, "SensitiveParameter", class_SensitiveParameter_methods);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES);
- zend_string *attribute_name_Attribute_class_SensitiveParameter_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_SensitiveParameter_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_SensitiveParameter_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_SensitiveParameter_0, 1);
- zend_string_release(attribute_name_Attribute_class_SensitiveParameter_0);
+ zend_string_release_ex(attribute_name_Attribute_class_SensitiveParameter_0, true);
ZVAL_LONG(&attribute_Attribute_class_SensitiveParameter_0->args[0].value, ZEND_ATTRIBUTE_TARGET_PARAMETER);
return class_entry;
@@ -227,10 +227,10 @@ static zend_class_entry *register_class_Override(void)
INIT_CLASS_ENTRY(ce, "Override", class_Override_methods);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES);
- zend_string *attribute_name_Attribute_class_Override_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_Override_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_Override_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Override_0, 1);
- zend_string_release(attribute_name_Attribute_class_Override_0);
- ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY);
+ zend_string_release_ex(attribute_name_Attribute_class_Override_0, true);
+ ZVAL_LONG(&attribute_Attribute_class_Override_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_PROPERTY | ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
return class_entry;
}
@@ -250,9 +250,9 @@ static zend_class_entry *register_class_Deprecated(void)
ZVAL_UNDEF(&property_since_default_value);
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SINCE), &property_since_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
- zend_string *attribute_name_Attribute_class_Deprecated_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_Deprecated_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_Deprecated_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Deprecated_0, 1);
- zend_string_release(attribute_name_Attribute_class_Deprecated_0);
+ zend_string_release_ex(attribute_name_Attribute_class_Deprecated_0, true);
ZVAL_LONG(&attribute_Attribute_class_Deprecated_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST | ZEND_ATTRIBUTE_TARGET_CONST | ZEND_ATTRIBUTE_TARGET_CLASS);
return class_entry;
@@ -269,9 +269,9 @@ static zend_class_entry *register_class_NoDiscard(void)
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|MAY_BE_NULL));
- zend_string *attribute_name_Attribute_class_NoDiscard_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_NoDiscard_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_NoDiscard_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_NoDiscard_0, 1);
- zend_string_release(attribute_name_Attribute_class_NoDiscard_0);
+ zend_string_release_ex(attribute_name_Attribute_class_NoDiscard_0, true);
ZVAL_LONG(&attribute_Attribute_class_NoDiscard_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION);
return class_entry;
@@ -284,9 +284,9 @@ static zend_class_entry *register_class_DelayedTargetValidation(void)
INIT_CLASS_ENTRY(ce, "DelayedTargetValidation", NULL);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES);
- zend_string *attribute_name_Attribute_class_DelayedTargetValidation_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ zend_string *attribute_name_Attribute_class_DelayedTargetValidation_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true);
zend_attribute *attribute_Attribute_class_DelayedTargetValidation_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_DelayedTargetValidation_0, 1);
- zend_string_release(attribute_name_Attribute_class_DelayedTargetValidation_0);
+ zend_string_release_ex(attribute_name_Attribute_class_DelayedTargetValidation_0, true);
ZVAL_LONG(&attribute_Attribute_class_DelayedTargetValidation_0->args[0].value, ZEND_ATTRIBUTE_TARGET_ALL);
return class_entry;
diff --git a/Zend/zend_autoload.c b/Zend/zend_autoload.c
new file mode 100644
index 000000000000..2ca3d7eea022
--- /dev/null
+++ b/Zend/zend_autoload.c
@@ -0,0 +1,165 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Gina Peter Banyard |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "zend_API.h"
+#include "zend_autoload.h"
+#include "zend_hash.h"
+#include "zend_types.h"
+#include "zend_exceptions.h"
+#include "zend_string.h"
+
+ZEND_TLS HashTable *zend_class_autoload_functions;
+
+static void zend_autoload_callback_zval_destroy(zval *element)
+{
+ zend_fcall_info_cache *fcc = Z_PTR_P(element);
+ zend_fcc_dtor(fcc);
+ efree(fcc);
+}
+
+static Bucket *autoload_find_registered_function(const HashTable *autoloader_table, const zend_fcall_info_cache *function_entry)
+{
+ zend_fcall_info_cache *current_function_entry;
+ ZEND_HASH_MAP_FOREACH_PTR(autoloader_table, current_function_entry) {
+ if (zend_fcc_equals(current_function_entry, function_entry)) {
+ return _p;
+ }
+ } ZEND_HASH_FOREACH_END();
+ return NULL;
+}
+
+ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name)
+{
+ if (!zend_class_autoload_functions) {
+ return NULL;
+ }
+
+ zval zname;
+ ZVAL_STR(&zname, class_name);
+
+ const HashTable *class_autoload_functions = zend_class_autoload_functions;
+
+ /* Cannot use ZEND_HASH_MAP_FOREACH_PTR here as autoloaders may be
+ * added/removed during autoloading. */
+ HashPosition pos;
+ zend_hash_internal_pointer_reset_ex(class_autoload_functions, &pos);
+ while (true) {
+ zend_fcall_info_cache *func_info = zend_hash_get_current_data_ptr_ex(class_autoload_functions, &pos);
+ if (!func_info) {
+ break;
+ }
+ zend_call_known_fcc(func_info, /* retval */ NULL, /* param_count */ 1, /* params */ &zname, /* named_params */ NULL);
+
+ if (EG(exception)) {
+ return NULL;
+ }
+ if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) {
+ return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name);
+ }
+
+ zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name);
+ if (ce) {
+ return ce;
+ }
+
+ zend_hash_move_forward_ex(class_autoload_functions, &pos);
+ }
+ return NULL;
+}
+
+/* Needed for compatibility with spl_register_autoload() */
+ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend)
+{
+ ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc));
+
+ if (!zend_class_autoload_functions) {
+ ALLOC_HASHTABLE(zend_class_autoload_functions);
+ zend_hash_init(zend_class_autoload_functions, 1, NULL, zend_autoload_callback_zval_destroy, false);
+ /* Initialize as non-packed hash table for prepend functionality. */
+ zend_hash_real_init_mixed(zend_class_autoload_functions);
+ }
+
+ ZEND_ASSERT(
+ fcc->function_handler->type != ZEND_INTERNAL_FUNCTION
+ || !zend_string_equals_literal(fcc->function_handler->common.function_name, "spl_autoload_call")
+ );
+
+ /* If function is already registered, don't do anything */
+ if (autoload_find_registered_function(zend_class_autoload_functions, fcc)) {
+ /* Release potential call trampoline */
+ zend_release_fcall_info_cache(fcc);
+ return;
+ }
+
+ zend_fcc_addref(fcc);
+ zend_hash_next_index_insert_mem(zend_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache));
+ if (prepend && zend_hash_num_elements(zend_class_autoload_functions) > 1) {
+ /* Move the newly created element to the head of the hashtable */
+ ZEND_ASSERT(!HT_IS_PACKED(zend_class_autoload_functions));
+ Bucket tmp = zend_class_autoload_functions->arData[zend_class_autoload_functions->nNumUsed-1];
+ memmove(zend_class_autoload_functions->arData + 1, zend_class_autoload_functions->arData, sizeof(Bucket) * (zend_class_autoload_functions->nNumUsed - 1));
+ zend_class_autoload_functions->arData[0] = tmp;
+ zend_hash_rehash(zend_class_autoload_functions);
+ }
+}
+
+ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc) {
+ if (zend_class_autoload_functions) {
+ Bucket *p = autoload_find_registered_function(zend_class_autoload_functions, fcc);
+ if (p) {
+ zend_hash_del_bucket(zend_class_autoload_functions, p);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* We do not return a HashTable* because zend_empty_array is not collectable,
+ * therefore the zval holding this value must do so. Something that ZVAL_EMPTY_ARRAY(); does. */
+ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value) {
+ if (zend_class_autoload_functions) {
+ zend_fcall_info_cache *fcc;
+
+ zend_array *map = zend_new_array(zend_hash_num_elements(zend_class_autoload_functions));
+ ZEND_HASH_MAP_FOREACH_PTR(zend_class_autoload_functions, fcc) {
+ zval tmp;
+ zend_get_callable_zval_from_fcc(fcc, &tmp);
+ zend_hash_next_index_insert(map, &tmp);
+ } ZEND_HASH_FOREACH_END();
+ RETURN_ARR(map);
+ }
+ RETURN_EMPTY_ARRAY();
+}
+
+/* Only for deprecated strange behaviour of spl_autoload_unregister() */
+ZEND_API void zend_autoload_clean_class_loaders(void)
+{
+ if (zend_class_autoload_functions) {
+ /* Don't destroy the hash table, as we might be iterating over it right now. */
+ zend_hash_clean(zend_class_autoload_functions);
+ }
+}
+
+void zend_autoload_shutdown(void)
+{
+ if (zend_class_autoload_functions) {
+ zend_hash_destroy(zend_class_autoload_functions);
+ FREE_HASHTABLE(zend_class_autoload_functions);
+ zend_class_autoload_functions = NULL;
+ }
+}
diff --git a/Zend/zend_autoload.h b/Zend/zend_autoload.h
new file mode 100644
index 000000000000..84e6ab80b5af
--- /dev/null
+++ b/Zend/zend_autoload.h
@@ -0,0 +1,34 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Gina Peter Banyard |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef _ZEND_AUTOLOAD_H
+#define _ZEND_AUTOLOAD_H
+
+#include "zend_string.h"
+#include "zend_hash.h"
+#include "zend_API.h"
+#include "zend.h"
+
+ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name);
+ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend);
+ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc);
+ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value);
+/* Only for deprecated strange behaviour of spl_autoload_unregister() */
+ZEND_API void zend_autoload_clean_class_loaders(void);
+void zend_autoload_shutdown(void);
+
+#endif
diff --git a/Zend/zend_bitset.h b/Zend/zend_bitset.h
index d990b6a2a871..877f2aaabc05 100644
--- a/Zend/zend_bitset.h
+++ b/Zend/zend_bitset.h
@@ -2,15 +2,13 @@
+----------------------------------------------------------------------+
| Zend OPcache JIT |
+----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
+ | Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
- | 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. |
+ | 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: Dmitry Stogov |
+----------------------------------------------------------------------+
diff --git a/Zend/zend_build.h b/Zend/zend_build.h
index c604fb311a74..8ba7064042e3 100644
--- a/Zend/zend_build.h
+++ b/Zend/zend_build.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Stanislav Malyshev |
+----------------------------------------------------------------------+
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 0d8be49af857..87acf073a2da 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -2,21 +2,21 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
+----------------------------------------------------------------------+
*/
+#include "php_version.h"
#include "zend.h"
#include "zend_API.h"
#include "zend_attributes.h"
@@ -29,12 +29,14 @@
#include "zend_extensions.h"
#include "zend_closures.h"
#include "zend_generators.h"
+#include "zend_autoload.h"
#include "zend_builtin_functions_arginfo.h"
#include "zend_smart_str.h"
/* }}} */
ZEND_MINIT_FUNCTION(core) { /* {{{ */
+ zend_autoload = zend_perform_class_autoload;
zend_register_default_classes();
zend_standard_class_def = register_class_stdClass();
@@ -82,10 +84,10 @@ ZEND_FUNCTION(clone)
/* clone() also exists as the ZEND_CLONE OPcode and both implementations must be kept in sync. */
- zend_class_entry *scope = zend_get_executed_scope();
+ const zend_class_entry *scope = zend_get_executed_scope();
- zend_class_entry *ce = zobj->ce;
- zend_function *clone = ce->clone;
+ const zend_class_entry *ce = zobj->ce;
+ const zend_function *clone = ce->clone;
if (UNEXPECTED(zobj->handlers->clone_obj == NULL)) {
zend_throw_error(NULL, "Trying to clone an uncloneable object of class %s", ZSTR_VAL(ce->name));
@@ -237,7 +239,7 @@ ZEND_FUNCTION(gc_status)
/* {{{ Get the number of arguments that were passed to the function */
ZEND_FUNCTION(func_num_args)
{
- zend_execute_data *ex = EX(prev_execute_data);
+ const zend_execute_data *ex = EX(prev_execute_data);
ZEND_PARSE_PARAMETERS_NONE();
@@ -247,7 +249,7 @@ ZEND_FUNCTION(func_num_args)
}
if (zend_forbid_dynamic_call() == FAILURE) {
- RETURN_LONG(-1);
+ RETURN_THROWS();
}
RETURN_LONG(ZEND_CALL_NUM_ARGS(ex));
@@ -501,9 +503,9 @@ ZEND_FUNCTION(error_reporting)
}
/* }}} */
-static bool validate_constant_array_argument(HashTable *ht, int argument_number) /* {{{ */
+static bool validate_constant_array_argument(HashTable *ht, uint32_t argument_number) /* {{{ */
{
- bool ret = 1;
+ bool ret = true;
zval *val;
GC_PROTECT_RECURSION(ht);
@@ -512,10 +514,10 @@ static bool validate_constant_array_argument(HashTable *ht, int argument_number)
if (Z_TYPE_P(val) == IS_ARRAY && Z_REFCOUNTED_P(val)) {
if (Z_IS_RECURSIVE_P(val)) {
zend_argument_value_error(argument_number, "cannot be a recursive array");
- ret = 0;
+ ret = false;
break;
} else if (!validate_constant_array_argument(Z_ARRVAL_P(val), argument_number)) {
- ret = 0;
+ ret = false;
break;
}
}
@@ -525,7 +527,7 @@ static bool validate_constant_array_argument(HashTable *ht, int argument_number)
}
/* }}} */
-static void copy_constant_array(zval *dst, zval *src) /* {{{ */
+static void copy_constant_array(zval *dst, const zval *src) /* {{{ */
{
zend_string *key;
zend_ulong idx;
@@ -555,7 +557,7 @@ static void copy_constant_array(zval *dst, zval *src) /* {{{ */
ZEND_FUNCTION(define)
{
zend_string *name;
- zval *val, val_free;
+ zval *val;
bool non_cs = 0;
zend_constant c;
@@ -575,31 +577,20 @@ ZEND_FUNCTION(define)
zend_error(E_WARNING, "define(): Argument #3 ($case_insensitive) is ignored since declaration of case-insensitive constants is no longer supported");
}
- ZVAL_UNDEF(&val_free);
-
- if (Z_TYPE_P(val) == IS_ARRAY) {
- if (Z_REFCOUNTED_P(val)) {
- if (!validate_constant_array_argument(Z_ARRVAL_P(val), 2)) {
- RETURN_THROWS();
- } else {
- copy_constant_array(&c.value, val);
- goto register_constant;
- }
+ if (Z_TYPE_P(val) == IS_ARRAY && Z_REFCOUNTED_P(val)) {
+ if (!validate_constant_array_argument(Z_ARRVAL_P(val), 2)) {
+ RETURN_THROWS();
+ } else {
+ copy_constant_array(&c.value, val);
}
+ } else {
+ ZVAL_COPY(&c.value, val);
}
- ZVAL_COPY(&c.value, val);
- zval_ptr_dtor(&val_free);
-
-register_constant:
/* non persistent */
ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT);
c.name = zend_string_copy(name);
- if (zend_register_constant(&c) != NULL) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(zend_register_constant(&c) != NULL);
}
/* }}} */
@@ -613,11 +604,7 @@ ZEND_FUNCTION(defined)
Z_PARAM_STR(name)
ZEND_PARSE_PARAMETERS_END();
- if (zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT));
}
/* }}} */
@@ -631,7 +618,7 @@ ZEND_FUNCTION(get_class)
}
if (!obj) {
- zend_class_entry *scope = zend_get_executed_scope();
+ const zend_class_entry *scope = zend_get_executed_scope();
if (scope) {
zend_error(E_DEPRECATED, "Calling get_class() without arguments is deprecated");
@@ -652,11 +639,9 @@ ZEND_FUNCTION(get_class)
/* {{{ Retrieves the "Late Static Binding" class name */
ZEND_FUNCTION(get_called_class)
{
- zend_class_entry *called_scope;
-
ZEND_PARSE_PARAMETERS_NONE();
- called_scope = zend_get_called_scope(execute_data);
+ const zend_class_entry *called_scope = zend_get_called_scope(execute_data);
if (!called_scope) {
zend_throw_error(NULL, "get_called_class() must be called from within a class");
RETURN_THROWS();
@@ -696,10 +681,8 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ *
{
zval *obj;
zend_string *class_name;
- zend_class_entry *instance_ce;
- zend_class_entry *ce;
+ const zend_class_entry *instance_ce;
bool allow_string = only_subclass;
- bool retval;
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_ZVAL(obj)
@@ -726,21 +709,19 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ *
}
if (!only_subclass && EXPECTED(zend_string_equals(instance_ce->name, class_name))) {
- retval = 1;
- } else {
- ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
- if (!ce) {
- retval = 0;
- } else {
- if (only_subclass && instance_ce == ce) {
- retval = 0;
- } else {
- retval = instanceof_function(instance_ce, ce);
- }
- }
+ RETURN_TRUE;
+ }
+
+ const zend_class_entry *ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
+ if (!ce) {
+ RETURN_FALSE;
+ }
+
+ if (only_subclass && instance_ce == ce) {
+ RETURN_FALSE;
}
- RETURN_BOOL(retval);
+ RETURN_BOOL(instanceof_function(instance_ce, ce));
}
/* }}} */
@@ -759,7 +740,7 @@ ZEND_FUNCTION(is_a)
/* }}} */
/* {{{ add_class_vars */
-static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool statics, zval *return_value)
+static void add_class_vars(const zend_class_entry *scope, zend_class_entry *ce, bool statics, const zval *return_value)
{
zend_property_info *prop_info;
zval *prop, prop_copy;
@@ -810,7 +791,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s
/* {{{ Returns an array of default properties of the class. */
ZEND_FUNCTION(get_class_vars)
{
- zend_class_entry *ce = NULL, *scope;
+ zend_class_entry *ce = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "C", &ce) == FAILURE) {
RETURN_THROWS();
@@ -823,9 +804,9 @@ ZEND_FUNCTION(get_class_vars)
}
}
- scope = zend_get_executed_scope();
- add_class_vars(scope, ce, 0, return_value);
- add_class_vars(scope, ce, 1, return_value);
+ const zend_class_entry *scope = zend_get_executed_scope();
+ add_class_vars(scope, ce, false, return_value);
+ add_class_vars(scope, ce, true, return_value);
}
/* }}} */
@@ -949,7 +930,6 @@ ZEND_FUNCTION(get_class_methods)
{
zval method_name;
zend_class_entry *ce = NULL;
- zend_class_entry *scope;
zend_function *mptr;
ZEND_PARSE_PARAMETERS_START(1, 1)
@@ -957,7 +937,7 @@ ZEND_FUNCTION(get_class_methods)
ZEND_PARSE_PARAMETERS_END();
array_init(return_value);
- scope = zend_get_executed_scope();
+ const zend_class_entry *scope = zend_get_executed_scope();
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, mptr) {
if (zend_check_method_accessible(mptr, scope)) {
@@ -973,7 +953,6 @@ ZEND_FUNCTION(method_exists)
{
zval *klass;
zend_string *method_name;
- zend_string *lcname;
zend_class_entry *ce;
zend_function *func;
@@ -990,13 +969,11 @@ ZEND_FUNCTION(method_exists)
RETURN_FALSE;
}
} else {
- zend_argument_type_error(1, "must be of type object|string, %s given", zend_zval_value_name(klass));
+ zend_wrong_parameter_type_error(1, Z_EXPECTED_OBJECT_OR_STRING, klass);
RETURN_THROWS();
}
- lcname = zend_string_tolower(method_name);
- func = zend_hash_find_ptr(&ce->function_table, lcname);
- zend_string_release_ex(lcname, 0);
+ func = zend_hash_find_ptr_lc(&ce->function_table, method_name);
if (func) {
/* Exclude shadow properties when checking a method on a specific class. Include
@@ -1032,10 +1009,9 @@ ZEND_FUNCTION(method_exists)
}
/* }}} */
-static void _property_exists(zval *return_value, zval *object, zend_string *property)
+static void _property_exists(zval *return_value, const zval *object, zend_string *property)
{
zend_class_entry *ce;
- zend_property_info *property_info;
if (Z_TYPE_P(object) == IS_STRING) {
ce = zend_lookup_class(Z_STR_P(object));
@@ -1045,22 +1021,21 @@ static void _property_exists(zval *return_value, zval *object, zend_string *prop
} else if (Z_TYPE_P(object) == IS_OBJECT) {
ce = Z_OBJCE_P(object);
} else {
- zend_argument_type_error(1, "must be of type object|string, %s given", zend_zval_value_name(object));
+ zend_wrong_parameter_type_error(1, Z_EXPECTED_OBJECT_OR_STRING, object);
RETURN_THROWS();
}
- property_info = zend_hash_find_ptr(&ce->properties_info, property);
+ const zend_property_info *property_info = zend_hash_find_ptr(&ce->properties_info, property);
if (property_info != NULL
&& (!(property_info->flags & ZEND_ACC_PRIVATE)
|| property_info->ce == ce)) {
RETURN_TRUE;
}
- if (Z_TYPE_P(object) == IS_OBJECT &&
- Z_OBJ_HANDLER_P(object, has_property)(Z_OBJ_P(object), property, ZEND_PROPERTY_EXISTS, NULL)) {
- RETURN_TRUE;
- }
- RETURN_FALSE;
+ RETURN_BOOL(
+ Z_TYPE_P(object) == IS_OBJECT &&
+ Z_OBJ_HANDLER_P(object, has_property)(Z_OBJ_P(object), property, ZEND_PROPERTY_EXISTS, NULL)
+ );
}
/* {{{ Checks if the object or class has a property */
@@ -1093,10 +1068,10 @@ flf_clean:;
Z_FLF_PARAM_FREE_STR(2, property_tmp)
}
-static inline void _class_exists_impl(zval *return_value, zend_string *name, bool autoload, int flags, int skip_flags) /* {{{ */
+static zend_always_inline void _class_exists_impl(zval *return_value, zend_string *name, bool autoload, int flags, int skip_flags) /* {{{ */
{
zend_string *lcname;
- zend_class_entry *ce;
+ const zend_class_entry *ce;
if (ZSTR_HAS_CE_CACHE(name)) {
ce = ZSTR_GET_CE_CACHE(name);
@@ -1120,11 +1095,7 @@ static inline void _class_exists_impl(zval *return_value, zend_string *name, boo
ce = zend_lookup_class(name);
}
- if (ce) {
- RETURN_BOOL(((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags));
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(ce && ((ce->ce_flags & flags) == flags) && !(ce->ce_flags & skip_flags));
}
/* {{{ */
@@ -1430,7 +1401,6 @@ static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int fla
{
zend_string *key;
zval *zv;
- zend_class_entry *ce;
ZEND_PARSE_PARAMETERS_NONE();
@@ -1438,7 +1408,7 @@ static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int fla
zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) {
- ce = Z_PTR_P(zv);
+ const zend_class_entry *ce = Z_PTR_P(zv);
if ((ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == flags
&& key
&& ZSTR_VAL(key)[0] != 0) {
@@ -1556,7 +1526,7 @@ ZEND_FUNCTION(get_resource_type)
if (resource_type) {
RETURN_STRING(resource_type);
} else {
- RETURN_STRING("Unknown");
+ RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_UNKNOWN_CAPITALIZED));
}
}
/* }}} */
@@ -1621,7 +1591,7 @@ ZEND_FUNCTION(get_resources)
}
/* }}} */
-static void add_zendext_info(zend_extension *ext, void *arg) /* {{{ */
+static void add_zendext_info(const zend_extension *ext, void *arg) /* {{{ */
{
zval *name_array = (zval *)arg;
add_next_index_string(name_array, ext->name);
@@ -1631,7 +1601,7 @@ static void add_zendext_info(zend_extension *ext, void *arg) /* {{{ */
/* {{{ Return an array containing names of loaded extensions */
ZEND_FUNCTION(get_loaded_extensions)
{
- bool zendext = 0;
+ bool zendext = false;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &zendext) == FAILURE) {
RETURN_THROWS();
@@ -1654,7 +1624,7 @@ ZEND_FUNCTION(get_loaded_extensions)
/* {{{ Return an array containing the names and values of all defined constants */
ZEND_FUNCTION(get_defined_constants)
{
- bool categorize = 0;
+ bool categorize = false;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &categorize) == FAILURE) {
RETURN_THROWS();
@@ -1722,6 +1692,18 @@ ZEND_FUNCTION(get_defined_constants)
}
/* }}} */
+static bool backtrace_is_arg_sensitive(const zend_execute_data *call, uint32_t offset)
+{
+ const zend_attribute *attribute = zend_get_parameter_attribute_str(
+ call->func->common.attributes,
+ "sensitiveparameter",
+ sizeof("sensitiveparameter") - 1,
+ offset
+ );
+
+ return attribute != NULL;
+}
+
static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /* {{{ */
{
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
@@ -1745,14 +1727,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
zend_string *arg_name = call->func->op_array.vars[i];
zval original_arg;
zval *arg = zend_hash_find_ex_ind(call->symbol_table, arg_name, 1);
- zend_attribute *attribute = zend_get_parameter_attribute_str(
- call->func->common.attributes,
- "sensitiveparameter",
- sizeof("sensitiveparameter") - 1,
- i
- );
-
- bool is_sensitive = attribute != NULL;
+ bool is_sensitive = backtrace_is_arg_sensitive(call, i);
if (arg) {
ZVAL_DEREF(arg);
@@ -1763,8 +1738,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
if (is_sensitive) {
zval redacted_arg;
- object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value);
- zend_call_known_function(Z_OBJCE_P(&redacted_arg)->constructor, Z_OBJ_P(&redacted_arg), Z_OBJCE_P(&redacted_arg), NULL, 1, &original_arg, NULL);
+ object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, &original_arg, NULL);
ZEND_HASH_FILL_SET(&redacted_arg);
} else {
Z_TRY_ADDREF_P(&original_arg);
@@ -1777,13 +1751,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
} else {
while (i < first_extra_arg) {
zval original_arg;
- zend_attribute *attribute = zend_get_parameter_attribute_str(
- call->func->common.attributes,
- "sensitiveparameter",
- sizeof("sensitiveparameter") - 1,
- i
- );
- bool is_sensitive = attribute != NULL;
+ bool is_sensitive = backtrace_is_arg_sensitive(call, i);
if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) {
zval *arg = p;
@@ -1795,8 +1763,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
if (is_sensitive) {
zval redacted_arg;
- object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value);
- zend_call_known_function(Z_OBJCE_P(&redacted_arg)->constructor, Z_OBJ_P(&redacted_arg), Z_OBJCE_P(&redacted_arg), NULL, 1, &original_arg, NULL);
+ object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, &original_arg, NULL);
ZEND_HASH_FILL_SET(&redacted_arg);
} else {
Z_TRY_ADDREF_P(&original_arg);
@@ -1816,13 +1783,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
bool is_sensitive = 0;
if (i < call->func->common.num_args || call->func->common.fn_flags & ZEND_ACC_VARIADIC) {
- zend_attribute *attribute = zend_get_parameter_attribute_str(
- call->func->common.attributes,
- "sensitiveparameter",
- sizeof("sensitiveparameter") - 1,
- MIN(i, call->func->common.num_args)
- );
- is_sensitive = attribute != NULL;
+ is_sensitive = backtrace_is_arg_sensitive(call, MIN(i, call->func->common.num_args));
}
if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) {
@@ -1835,8 +1796,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
if (is_sensitive) {
zval redacted_arg;
- object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value);
- zend_call_known_function(Z_OBJCE_P(&redacted_arg)->constructor, Z_OBJ_P(&redacted_arg), Z_OBJCE_P(&redacted_arg), NULL, 1, &original_arg, NULL);
+ object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, &original_arg, NULL);
ZEND_HASH_FILL_SET(&redacted_arg);
} else {
Z_TRY_ADDREF_P(&original_arg);
@@ -1861,21 +1821,14 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
zend_string *name;
zval *arg;
- zend_attribute *attribute = zend_get_parameter_attribute_str(
- call->func->common.attributes,
- "sensitiveparameter",
- sizeof("sensitiveparameter") - 1,
- call->func->common.num_args
- );
- bool is_sensitive = attribute != NULL;
+ bool is_sensitive = backtrace_is_arg_sensitive(call, call->func->common.num_args);
SEPARATE_ARRAY(arg_array);
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) {
ZVAL_DEREF(arg);
if (is_sensitive) {
zval redacted_arg;
- object_init_ex(&redacted_arg, zend_ce_sensitive_parameter_value);
- zend_call_method_with_1_params(Z_OBJ_P(&redacted_arg), zend_ce_sensitive_parameter_value, &zend_ce_sensitive_parameter_value->constructor, "__construct", NULL, arg);
+ object_init_with_constructor(&redacted_arg, zend_ce_sensitive_parameter_value, 1, arg, NULL);
zend_hash_add_new(Z_ARRVAL_P(arg_array), name, &redacted_arg);
} else {
Z_TRY_ADDREF_P(arg);
@@ -1912,8 +1865,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
{
zend_execute_data *call, *last_call = NULL;
zend_object *object;
- bool fake_frame = 0;
- int lineno, frameno = 0;
+ bool fake_frame = false;
+ int frameno = 0;
zend_function *func;
zend_string *filename;
zend_string *include_filename = NULL;
@@ -1934,12 +1887,12 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
EG(filename_override) = NULL;
EG(lineno_override) = -1;
- zend_string *filename = zend_get_executed_filename_ex();
- zend_long lineno = zend_get_executed_lineno();
- if (filename && (!zend_string_equals(filename, filename_override) || lineno != lineno_override)) {
+ zend_string *executed_filename = zend_get_executed_filename_ex();
+ uint32_t lineno = zend_get_executed_lineno();
+ if (executed_filename && (!zend_string_equals(executed_filename, filename_override) || lineno != lineno_override)) {
stack_frame = zend_new_array(8);
zend_hash_real_init_mixed(stack_frame);
- ZVAL_STR_COPY(&tmp, filename);
+ ZVAL_STR_COPY(&tmp, executed_filename);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1);
ZVAL_LONG(&tmp, lineno);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1);
@@ -2002,19 +1955,21 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zval *arg = zend_get_zval_ptr(op_data, op_data->op1_type, &op_data->op1, call);
if (Z_TYPE_P(arg) == IS_UNDEF) goto not_frameless_call;
}
- zend_function *func = ZEND_FLF_FUNC(opline);
+ zend_function *frameless_func = ZEND_FLF_FUNC(opline);
/* Assume frameless functions are not recursive with themselves.
* This condition may be true when observers are enabled:
* Observers will put a call frame on top of the frameless opcode. */
- if (last_call && last_call->func == func) {
+ if (last_call && last_call->func == frameless_func) {
goto not_frameless_call;
}
stack_frame = zend_new_array(8);
zend_hash_real_init_mixed(stack_frame);
- ZVAL_STR_COPY(&tmp, func->common.function_name);
+ ZVAL_STR_COPY(&tmp, frameless_func->common.function_name);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1);
/* Steal file and line from the previous frame. */
if (call->func && ZEND_USER_CODE(call->func->common.type)) {
+ uint32_t lineno;
+
filename = call->func->op_array.filename;
if (call->opline->opcode == ZEND_HANDLE_EXCEPTION) {
if (EG(opline_before_exception)) {
@@ -2066,6 +2021,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zend_hash_real_init_mixed(stack_frame);
if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type)) {
+ uint32_t lineno;
+
filename = prev->func->op_array.filename;
if (prev->opline->opcode == ZEND_HANDLE_EXCEPTION) {
if (EG(opline_before_exception)) {
@@ -2148,7 +2105,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
}
} else {
/* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */
- bool build_filename_arg = 1;
+ bool build_filename_arg = true;
zend_string *pseudo_function_name;
uint32_t include_kind = 0;
if (prev && prev->func && ZEND_USER_CODE(prev->func->common.type) && prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) {
@@ -2158,7 +2115,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
switch (include_kind) {
case ZEND_EVAL:
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_EVAL);
- build_filename_arg = 0;
+ build_filename_arg = false;
break;
case ZEND_INCLUDE:
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_INCLUDE);
@@ -2180,7 +2137,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
}
pseudo_function_name = ZSTR_KNOWN(ZEND_STR_UNKNOWN);
- build_filename_arg = 0;
+ build_filename_arg = false;
break;
}
@@ -2214,9 +2171,9 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
&& prev->func
&& ZEND_USER_CODE(prev->func->common.type)
&& prev->opline->opcode == ZEND_INCLUDE_OR_EVAL) {
- fake_frame = 1;
+ fake_frame = true;
} else {
- fake_frame = 0;
+ fake_frame = false;
include_filename = filename;
last_call = call;
call = prev;
@@ -2250,11 +2207,7 @@ ZEND_FUNCTION(extension_loaded)
}
lcname = zend_string_tolower(extension_name);
- if (zend_hash_exists(&module_registry, lcname)) {
- RETVAL_TRUE;
- } else {
- RETVAL_FALSE;
- }
+ RETVAL_BOOL(zend_hash_exists(&module_registry, lcname));
zend_string_release_ex(lcname, 0);
}
/* }}} */
@@ -2263,7 +2216,6 @@ ZEND_FUNCTION(extension_loaded)
ZEND_FUNCTION(get_extension_funcs)
{
zend_string *extension_name;
- zend_string *lcname;
bool array;
zend_module_entry *module;
zend_function *zif;
@@ -2272,9 +2224,7 @@ ZEND_FUNCTION(get_extension_funcs)
RETURN_THROWS();
}
if (strncasecmp(ZSTR_VAL(extension_name), "zend", sizeof("zend"))) {
- lcname = zend_string_tolower(extension_name);
- module = zend_hash_find_ptr(&module_registry, lcname);
- zend_string_release_ex(lcname, 0);
+ module = zend_hash_find_ptr_lc(&module_registry, extension_name);
} else {
module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core") - 1);
}
@@ -2286,9 +2236,9 @@ ZEND_FUNCTION(get_extension_funcs)
if (module->functions) {
/* avoid BC break, if functions list is empty, will return an empty array */
array_init(return_value);
- array = 1;
+ array = true;
} else {
- array = 0;
+ array = false;
}
ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), zif) {
@@ -2296,7 +2246,7 @@ ZEND_FUNCTION(get_extension_funcs)
&& zif->internal_function.module == module) {
if (!array) {
array_init(return_value);
- array = 1;
+ array = true;
}
add_next_index_str(return_value, zend_string_copy(zif->common.function_name));
}
diff --git a/Zend/zend_builtin_functions.h b/Zend/zend_builtin_functions.h
index 237b34b2927b..018d0cdb1c1d 100644
--- a/Zend/zend_builtin_functions.h
+++ b/Zend/zend_builtin_functions.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php
index 9b2267b531eb..1d405587145d 100644
--- a/Zend/zend_builtin_functions.stub.php
+++ b/Zend/zend_builtin_functions.stub.php
@@ -18,11 +18,16 @@ function die(string|int $status = 0): never {}
/** @refcount 1 */
function zend_version(): string {}
+/** @forbid-dynamic-calls */
function func_num_args(): int {}
+/** @forbid-dynamic-calls */
function func_get_arg(int $position): mixed {}
-/** @return array */
+/**
+ * @return array
+ * @forbid-dynamic-calls
+ */
function func_get_args(): array {}
function strlen(string $string): int {}
@@ -156,6 +161,7 @@ function get_defined_functions(bool $exclude_disabled = true): array {}
/**
* @return array
* @refcount 1
+ * @forbid-dynamic-calls
*/
function get_defined_vars(): array {}
diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h
index cf349b551ac2..b3af43fef340 100644
--- a/Zend/zend_builtin_functions_arginfo.h
+++ b/Zend/zend_builtin_functions_arginfo.h
@@ -1,5 +1,5 @@
-/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 9b49f527064695c812cd204d9efc63c13681d942 */
+/* This is a generated file, edit zend_builtin_functions.stub.php instead.
+ * Stub hash: 64c61862de86d9968930893bf21b516119724064 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clone, 0, 1, IS_OBJECT, 0)
ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0)
@@ -316,9 +316,21 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(exit, arginfo_exit)
ZEND_RAW_FENTRY("die", zif_exit, arginfo_die, 0, NULL, NULL)
ZEND_FE(zend_version, arginfo_zend_version)
- ZEND_FE(func_num_args, arginfo_func_num_args)
- ZEND_FE(func_get_arg, arginfo_func_get_arg)
- ZEND_FE(func_get_args, arginfo_func_get_args)
+#if (PHP_VERSION_ID >= 80600)
+ ZEND_RAW_FENTRY("func_num_args", zif_func_num_args, arginfo_func_num_args, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL)
+#elif (PHP_VERSION_ID >= 80400)
+ ZEND_RAW_FENTRY("func_num_args", zif_func_num_args, arginfo_func_num_args, 0, NULL, NULL)
+#endif
+#if (PHP_VERSION_ID >= 80600)
+ ZEND_RAW_FENTRY("func_get_arg", zif_func_get_arg, arginfo_func_get_arg, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL)
+#elif (PHP_VERSION_ID >= 80400)
+ ZEND_RAW_FENTRY("func_get_arg", zif_func_get_arg, arginfo_func_get_arg, 0, NULL, NULL)
+#endif
+#if (PHP_VERSION_ID >= 80600)
+ ZEND_RAW_FENTRY("func_get_args", zif_func_get_args, arginfo_func_get_args, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL)
+#elif (PHP_VERSION_ID >= 80400)
+ ZEND_RAW_FENTRY("func_get_args", zif_func_get_args, arginfo_func_get_args, 0, NULL, NULL)
+#endif
ZEND_FE(strlen, arginfo_strlen)
ZEND_RAW_FENTRY("strcmp", zif_strcmp, arginfo_strcmp, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL)
ZEND_RAW_FENTRY("strncmp", zif_strncmp, arginfo_strncmp, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL)
@@ -358,7 +370,11 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(get_declared_traits, arginfo_get_declared_traits)
ZEND_FE(get_declared_interfaces, arginfo_get_declared_interfaces)
ZEND_FE(get_defined_functions, arginfo_get_defined_functions)
- ZEND_FE(get_defined_vars, arginfo_get_defined_vars)
+#if (PHP_VERSION_ID >= 80600)
+ ZEND_RAW_FENTRY("get_defined_vars", zif_get_defined_vars, arginfo_get_defined_vars, ZEND_FENTRY_FLAGS(0, ZEND_ACC2_FORBID_DYN_CALLS), NULL, NULL)
+#elif (PHP_VERSION_ID >= 80400)
+ ZEND_RAW_FENTRY("get_defined_vars", zif_get_defined_vars, arginfo_get_defined_vars, 0, NULL, NULL)
+#endif
ZEND_FE(get_resource_type, arginfo_get_resource_type)
ZEND_FE(get_resource_id, arginfo_get_resource_id)
ZEND_FE(get_resources, arginfo_get_resources)
@@ -387,9 +403,9 @@ static zend_class_entry *register_class_stdClass(void)
INIT_CLASS_ENTRY(ce, "stdClass", NULL);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES);
- zend_string *attribute_name_AllowDynamicProperties_class_stdClass_0 = zend_string_init_interned("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1);
+ zend_string *attribute_name_AllowDynamicProperties_class_stdClass_0 = zend_string_init_interned("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, true);
zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_stdClass_0, 0);
- zend_string_release(attribute_name_AllowDynamicProperties_class_stdClass_0);
+ zend_string_release_ex(attribute_name_AllowDynamicProperties_class_stdClass_0, true);
return class_entry;
}
diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c
index ed86ecc74a23..aa23c2e3e2fa 100644
--- a/Zend/zend_call_stack.c
+++ b/Zend/zend_call_stack.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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 |
+----------------------------------------------------------------------+
diff --git a/Zend/zend_call_stack.h b/Zend/zend_call_stack.h
index 58e91694f93f..c0b84334239d 100644
--- a/Zend/zend_call_stack.h
+++ b/Zend/zend_call_stack.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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 |
+----------------------------------------------------------------------+
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 4a6b5a521884..91f690c73ed0 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Christian Seiler |
| Dmitry Stogov |
@@ -28,6 +27,11 @@
#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
+
typedef struct _zend_closure {
zend_object std;
zend_function func;
@@ -41,6 +45,17 @@ 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);
+
+static inline uint32_t zend_closure_flags(const zend_closure *closure)
+{
+ return closure->std.extra_flags & (ZEND_PARTIAL|ZEND_PARTIAL_OF_CLOSURE);
+}
+
+static inline bool zend_closure_is_fake(const zend_closure *closure)
+{
+ return closure->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE;
+}
ZEND_METHOD(Closure, __invoke) /* {{{ */
{
@@ -78,11 +93,13 @@ 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");
- return 0;
+ return false;
}
if (is_fake_closure && func->common.scope &&
@@ -92,23 +109,23 @@ static bool zend_valid_closure_binding(
ZSTR_VAL(func->common.scope->name),
ZSTR_VAL(func->common.function_name),
ZSTR_VAL(Z_OBJCE_P(newthis)->name));
- return 0;
+ return false;
}
} else if (is_fake_closure && func->common.scope
&& !(func->common.fn_flags & ZEND_ACC_STATIC)) {
zend_error(E_WARNING, "Cannot unbind $this of method, this will be an error in PHP 9");
- return 0;
+ return false;
} else if (!is_fake_closure && !Z_ISUNDEF(closure->this_ptr)
&& (func->common.fn_flags & ZEND_ACC_USES_THIS)) {
zend_error(E_WARNING, "Cannot unbind $this of closure using $this, this will be an error in PHP 9");
- return 0;
+ return false;
}
if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) {
/* rebinding to internal class is not allowed */
zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s, this will be an error in PHP 9",
ZSTR_VAL(scope->name));
- return 0;
+ return false;
}
if (is_fake_closure && scope != func->common.scope) {
@@ -117,10 +134,10 @@ static bool zend_valid_closure_binding(
} else {
zend_error(E_WARNING, "Cannot rebind scope of closure created from method, this will be an error in PHP 9");
}
- return 0;
+ return false;
}
- return 1;
+ return true;
}
/* }}} */
@@ -155,13 +172,16 @@ ZEND_METHOD(Closure, call)
fci_cache.object = fci.object = newobj;
fci.size = sizeof(fci);
+ fci.consumed_args = 0;
ZVAL_OBJ(&fci.function_name, &closure->std);
ZVAL_UNDEF(&closure_result);
fci.retval = &closure_result;
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 +197,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 +244,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 +256,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 +273,34 @@ 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 */
+
+ zend_closure *new_closure = (zend_closure*)Z_OBJ_P(return_value);
+ HashTable *static_variables = ZEND_MAP_PTR_GET(new_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) {
+ /* Should not happen, as we have already validated arguments and the
+ * inner closure should have the same constraints. */
+ 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 */
@@ -287,6 +336,19 @@ ZEND_METHOD(Closure, bindTo)
do_closure_bind(return_value, ZEND_THIS, newthis, scope_obj, scope_str);
}
+static void zend_copy_parameters_array(const uint32_t param_count, HashTable *argument_array) /* {{{ */
+{
+ zval *param_ptr = ZEND_CALL_ARG(EG(current_execute_data), 1);
+
+ ZEND_ASSERT(param_count <= ZEND_CALL_NUM_ARGS(EG(current_execute_data)));
+
+ for (uint32_t i = 0; i < param_count; i++) {
+ Z_TRY_ADDREF_P(param_ptr);
+ zend_hash_next_index_insert_new(argument_array, param_ptr);
+ param_ptr++;
+ }
+}
+
static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
zend_fcall_info fci;
zend_fcall_info_cache fcc;
@@ -310,14 +372,14 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
array_init_size(&fci.params[1], ZEND_NUM_ARGS() + zend_hash_num_elements(EX(extra_named_params)));
/* Avoid conversion from packed to mixed later. */
zend_hash_real_init_mixed(Z_ARRVAL(fci.params[1]));
- zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
+ zend_copy_parameters_array(ZEND_NUM_ARGS(), Z_ARRVAL(fci.params[1]));
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, named_param_zval) {
Z_TRY_ADDREF_P(named_param_zval);
zend_hash_add_new(Z_ARRVAL(fci.params[1]), name, named_param_zval);
} ZEND_HASH_FOREACH_END();
} else if (ZEND_NUM_ARGS()) {
array_init_size(&fci.params[1], ZEND_NUM_ARGS());
- zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
+ zend_copy_parameters_array(ZEND_NUM_ARGS(), Z_ARRVAL(fci.params[1]));
} else {
ZVAL_EMPTY_ARRAY(&fci.params[1]);
}
@@ -326,7 +388,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
fcc.called_scope = zend_get_called_scope(EG(current_execute_data));
zend_call_function(&fci, &fcc);
-
+ zend_return_unwrap_ref(EG(current_execute_data), return_value);
zval_ptr_dtor(&fci.params[1]);
}
/* }}} */
@@ -497,7 +559,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
* ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
invoke->type = ZEND_INTERNAL_FUNCTION;
invoke->internal_function.fn_flags =
- ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
+ ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_NEVER_CACHE | (closure->func.common.fn_flags & keep_flags);
if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
invoke->internal_function.fn_flags |=
ZEND_ACC_USER_ARG_INFO;
@@ -575,8 +637,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);
}
/* }}} */
@@ -605,7 +668,6 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zval val;
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
HashTable *debug_info;
- bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
*is_temp = 1;
@@ -681,15 +743,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zend_string *name;
zval info;
ZEND_ASSERT(arg_info->name && "Argument should have name");
- if (zstr_args) {
- name = zend_strpprintf(0, "%s$%s",
- ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
- ZSTR_VAL(arg_info->name));
- } else {
- name = zend_strpprintf(0, "%s$%s",
- ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
- ((zend_internal_arg_info*)arg_info)->name);
- }
+ name = zend_strpprintf(0, "%s$%s",
+ ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
+ ZSTR_VAL(arg_info->name));
ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "" : ""));
zend_hash_update(Z_ARRVAL(val), name, &info);
zend_string_release_ex(name, 0);
@@ -744,13 +800,14 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
{
zend_closure *closure = (zend_closure*)ZEND_CLOSURE_OBJECT(EX(func));
closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+ ZEND_ASSERT(!(closure->func.common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS) || EG(exception));
// Assign to EX(this) so that it is released after observer checks etc.
ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_RELEASE_THIS);
Z_OBJ(EX(This)) = &closure->std;
}
/* }}} */
-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;
@@ -758,6 +815,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 */
@@ -823,7 +881,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
/* wrap internal function handler to avoid memory leak */
if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
/* avoid infinity recursion, by taking handler from nested closure */
- zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
+ zend_closure *nested = ZEND_CONTAINER_OF(func, zend_closure, func);
ZEND_ASSERT(nested->std.ce == zend_ce_closure);
closure->orig_internal_handler = nested->orig_internal_handler;
} else {
@@ -855,14 +913,16 @@ 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,
+ /* flags */ 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, /* flags */ 0);
closure = (zend_closure *)Z_OBJ_P(res);
closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
@@ -872,10 +932,17 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
}
/* }}} */
-/* __call and __callStatic name the arguments "$arguments" in the docs. */
-static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)};
+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)
+{
+ uint32_t 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, zend_execute_data *call) { /* {{{ */
+void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */
zval instance;
zend_internal_function trampoline;
zend_function *mptr = call->func;
@@ -899,9 +966,7 @@ void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {
trampoline.function_name = mptr->common.function_name;
trampoline.scope = mptr->common.scope;
trampoline.doc_comment = NULL;
- if (trampoline.fn_flags & ZEND_ACC_VARIADIC) {
- trampoline.arg_info = trampoline_arg_info;
- }
+ trampoline.arg_info = mptr->common.arg_info;
trampoline.attributes = mptr->common.attributes;
zend_free_trampoline(mptr);
diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h
index ced1b5ba48c1..305d82e5015a 100644
--- a/Zend/zend_closures.h
+++ b/Zend/zend_closures.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Christian Seiler |
| Dmitry Stogov |
@@ -31,12 +30,13 @@ BEGIN_EXTERN_C()
void zend_register_closure_ce(void);
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val);
-void zend_closure_from_frame(zval *closure_zv, zend_execute_data *frame);
+void zend_closure_from_frame(zval *closure_zv, const zend_execute_data *frame);
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_closures_arginfo.h b/Zend/zend_closures_arginfo.h
index 4ce02c40e55a..5bc983a97c2c 100644
--- a/Zend/zend_closures_arginfo.h
+++ b/Zend/zend_closures_arginfo.h
@@ -1,4 +1,4 @@
-/* This is a generated file, edit the .stub.php file instead.
+/* This is a generated file, edit zend_closures.stub.php instead.
* Stub hash: e0626e52adb2d38dad1140c1a28cc7774cc84500 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Closure___construct, 0, 0, 0)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 3bda0ffadc32..6a677e71a6b6 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -28,6 +27,9 @@
#include "zend_API.h"
#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"
@@ -100,7 +102,9 @@ static zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bo
static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref);
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);
+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)
@@ -187,10 +191,10 @@ static bool zend_get_unqualified_name(const zend_string *name, const char **resu
if (ns_separator != NULL) {
*result = ns_separator + 1;
*result_len = ZSTR_VAL(name) + ZSTR_LEN(name) - *result;
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/* }}} */
@@ -233,11 +237,11 @@ static bool zend_is_reserved_class_name(const zend_string *name) /* {{{ */
if (uqname_len == reserved->len
&& zend_binary_strcasecmp(uqname, uqname_len, reserved->name, reserved->len) == 0
) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
/* }}} */
@@ -348,7 +352,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_arra
}
/* }}} */
-void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */
+void zend_oparray_context_end(const zend_oparray_context *prev_context) /* {{{ */
{
if (CG(context).brk_cont_array) {
efree(CG(context).brk_cont_array);
@@ -411,7 +415,7 @@ void zend_file_context_begin(zend_file_context *prev_context) /* {{{ */
}
/* }}} */
-void zend_file_context_end(zend_file_context *prev_context) /* {{{ */
+void zend_file_context_end(const zend_file_context *prev_context) /* {{{ */
{
zend_end_namespace();
zend_hash_destroy(&FC(seen_symbols));
@@ -446,7 +450,7 @@ static void zend_register_seen_symbol(zend_string *name, uint32_t kind) {
}
static bool zend_have_seen_symbol(zend_string *name, uint32_t kind) {
- zval *zv = zend_hash_find(&FC(seen_symbols), name);
+ const zval *zv = zend_hash_find(&FC(seen_symbols), name);
return zv && (Z_LVAL_P(zv) & kind) != 0;
}
@@ -518,7 +522,7 @@ ZEND_API zend_string *zend_get_compiled_filename(void) /* {{{ */
}
/* }}} */
-ZEND_API int zend_get_compiled_lineno(void) /* {{{ */
+ZEND_API uint32_t zend_get_compiled_lineno(void) /* {{{ */
{
return CG(zend_lineno);
}
@@ -530,13 +534,19 @@ ZEND_API bool zend_is_compiling(void) /* {{{ */
}
/* }}} */
+static bool zend_is_constructor(const zend_string *name) /* {{{ */
+{
+ return zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME);
+}
+/* }}} */
+
static zend_always_inline uint32_t get_temporary_variable(void) /* {{{ */
{
return (uint32_t)CG(active_op_array)->T++;
}
/* }}} */
-static int lookup_cv(zend_string *name) /* {{{ */{
+static uint32_t lookup_cv(zend_string *name) /* {{{ */{
zend_op_array *op_array = CG(active_op_array);
int i = 0;
zend_ulong hash_value = zend_string_hash_val(name);
@@ -571,7 +581,7 @@ zend_string *zval_make_interned_string(zval *zv)
}
/* Common part of zend_add_literal and zend_append_individual_literal */
-static inline void zend_insert_literal(zend_op_array *op_array, zval *zv, int literal_position) /* {{{ */
+static inline void zend_insert_literal(const zend_op_array *op_array, zval *zv, int literal_position) /* {{{ */
{
zval *lit = CT_CONSTANT_EX(op_array, literal_position);
if (Z_TYPE_P(zv) == IS_STRING) {
@@ -588,7 +598,7 @@ static inline void zend_insert_literal(zend_op_array *op_array, zval *zv, int li
static int zend_add_literal(zval *zv) /* {{{ */
{
zend_op_array *op_array = CG(active_op_array);
- int i = op_array->last_literal;
+ uint32_t i = op_array->last_literal;
op_array->last_literal++;
if (i >= CG(context).literals_size) {
while (i >= CG(context).literals_size) {
@@ -667,12 +677,10 @@ static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {
int ret = zend_add_literal_string(&name);
- size_t ns_len = 0, after_ns_len = ZSTR_LEN(name);
- const char *after_ns = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
- if (after_ns) {
- after_ns += 1;
- ns_len = after_ns - ZSTR_VAL(name) - 1;
- after_ns_len = ZSTR_LEN(name) - ns_len - 1;
+ const char *after_ns;
+ size_t after_ns_len;
+ if (zend_get_unqualified_name(name, &after_ns, &after_ns_len)) {
+ size_t ns_len = after_ns - ZSTR_VAL(name) - 1;
/* lowercased namespace name & original constant name */
tmp_name = zend_string_init(ZSTR_VAL(name), ZSTR_LEN(name), 0);
@@ -684,6 +692,7 @@ static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {
}
} else {
after_ns = ZSTR_VAL(name);
+ after_ns_len = ZSTR_LEN(name);
}
/* original unqualified constant name */
@@ -752,6 +761,36 @@ static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */
}
/* }}} */
+bool zend_op_may_elide_result(uint8_t opcode)
+{
+ switch (opcode) {
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_OBJ:
+ case ZEND_ASSIGN_STATIC_PROP:
+ case ZEND_ASSIGN_OP:
+ case ZEND_ASSIGN_DIM_OP:
+ case ZEND_ASSIGN_OBJ_OP:
+ case ZEND_ASSIGN_STATIC_PROP_OP:
+ case ZEND_PRE_INC_STATIC_PROP:
+ case ZEND_PRE_DEC_STATIC_PROP:
+ case ZEND_PRE_INC_OBJ:
+ case ZEND_PRE_DEC_OBJ:
+ case ZEND_PRE_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_DO_FCALL:
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ case ZEND_YIELD:
+ case ZEND_YIELD_FROM:
+ case ZEND_INCLUDE_OR_EVAL:
+ return true;
+ default:
+ return false;
+ }
+}
+
static void zend_do_free(znode *op1) /* {{{ */
{
if (op1->op_type == IS_TMP_VAR) {
@@ -778,22 +817,12 @@ static void zend_do_free(znode *op1) /* {{{ */
opline->opcode -= 2;
SET_UNUSED(opline->result);
return;
- case ZEND_ASSIGN:
- case ZEND_ASSIGN_DIM:
- case ZEND_ASSIGN_OBJ:
- case ZEND_ASSIGN_STATIC_PROP:
- case ZEND_ASSIGN_OP:
- case ZEND_ASSIGN_DIM_OP:
- case ZEND_ASSIGN_OBJ_OP:
- case ZEND_ASSIGN_STATIC_PROP_OP:
- case ZEND_PRE_INC_STATIC_PROP:
- case ZEND_PRE_DEC_STATIC_PROP:
- case ZEND_PRE_INC_OBJ:
- case ZEND_PRE_DEC_OBJ:
- case ZEND_PRE_INC:
- case ZEND_PRE_DEC:
- SET_UNUSED(opline->result);
- return;
+ default:
+ if (zend_op_may_elide_result(opline->opcode)) {
+ SET_UNUSED(opline->result);
+ return;
+ }
+ break;
}
}
@@ -815,8 +844,6 @@ static void zend_do_free(znode *op1) /* {{{ */
} else {
/* Frameless calls usually use the return value, so always emit a free. This should be
* faster than checking RETURN_VALUE_USED inside the handler. */
- // FIXME: We may actually look at the function signature to determine whether a free
- // is necessary.
zend_emit_op(NULL, ZEND_FREE, op1, NULL);
}
} else {
@@ -872,7 +899,7 @@ static const char *zend_modifier_token_to_string(uint32_t token)
return "protected(set)";
case T_PRIVATE_SET:
return "private(set)";
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
@@ -951,7 +978,7 @@ uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token
uint32_t zend_modifier_list_to_flags(zend_modifier_target target, zend_ast *modifiers)
{
uint32_t flags = 0;
- zend_ast_list *modifier_list = zend_ast_get_list(modifiers);
+ const zend_ast_list *modifier_list = zend_ast_get_list(modifiers);
for (uint32_t i = 0; i < modifier_list->children; i++) {
uint32_t token = (uint32_t) Z_LVAL_P(zend_ast_get_zval(modifier_list->child[i]));
@@ -1050,20 +1077,20 @@ uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag, zend_modifi
}
/* }}} */
-ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name) {
+ZEND_API zend_string *zend_create_member_string(const zend_string *class_name, const zend_string *member_name) {
return zend_string_concat3(
ZSTR_VAL(class_name), ZSTR_LEN(class_name),
"::", sizeof("::") - 1,
ZSTR_VAL(member_name), ZSTR_LEN(member_name));
}
-static zend_string *zend_concat_names(char *name1, size_t name1_len, char *name2, size_t name2_len) {
+static zend_string *zend_concat_names(const char *name1, size_t name1_len, const char *name2, size_t name2_len) {
return zend_string_concat3(name1, name1_len, "\\", 1, name2, name2_len);
}
static zend_string *zend_prefix_with_ns(zend_string *name) {
if (FC(current_namespace)) {
- zend_string *ns = FC(current_namespace);
+ const zend_string *ns = FC(current_namespace);
return zend_concat_names(ZSTR_VAL(ns), ZSTR_LEN(ns), ZSTR_VAL(name), ZSTR_LEN(name));
} else {
return zend_string_copy(name);
@@ -1072,24 +1099,24 @@ static zend_string *zend_prefix_with_ns(zend_string *name) {
static zend_string *zend_resolve_non_class_name(
zend_string *name, uint32_t type, bool *is_fully_qualified,
- bool case_sensitive, HashTable *current_import_sub
+ bool case_sensitive, const HashTable *current_import_sub
) {
- char *compound;
- *is_fully_qualified = 0;
+ const char *compound;
+ *is_fully_qualified = false;
if (ZSTR_VAL(name)[0] == '\\') {
/* Remove \ prefix (only relevant if this is a string rather than a label) */
- *is_fully_qualified = 1;
+ *is_fully_qualified = true;
return zend_string_init(ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1, 0);
}
if (type == ZEND_NAME_FQ) {
- *is_fully_qualified = 1;
+ *is_fully_qualified = true;
return zend_string_copy(name);
}
if (type == ZEND_NAME_RELATIVE) {
- *is_fully_qualified = 1;
+ *is_fully_qualified = true;
return zend_prefix_with_ns(name);
}
@@ -1103,20 +1130,20 @@ static zend_string *zend_resolve_non_class_name(
}
if (import_name) {
- *is_fully_qualified = 1;
+ *is_fully_qualified = true;
return zend_string_copy(import_name);
}
}
compound = memchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
if (compound) {
- *is_fully_qualified = 1;
+ *is_fully_qualified = true;
}
if (compound && FC(imports)) {
/* If the first part of a qualified name is an alias, substitute it. */
size_t len = compound - ZSTR_VAL(name);
- zend_string *import_name = zend_hash_str_find_ptr_lc(FC(imports), ZSTR_VAL(name), len);
+ const zend_string *import_name = zend_hash_str_find_ptr_lc(FC(imports), ZSTR_VAL(name), len);
if (import_name) {
return zend_concat_names(
@@ -1131,18 +1158,18 @@ static zend_string *zend_resolve_non_class_name(
static zend_string *zend_resolve_function_name(zend_string *name, uint32_t type, bool *is_fully_qualified)
{
return zend_resolve_non_class_name(
- name, type, is_fully_qualified, 0, FC(imports_function));
+ name, type, is_fully_qualified, false, FC(imports_function));
}
static zend_string *zend_resolve_const_name(zend_string *name, uint32_t type, bool *is_fully_qualified)
{
return zend_resolve_non_class_name(
- name, type, is_fully_qualified, 1, FC(imports_const));
+ name, type, is_fully_qualified, true, FC(imports_const));
}
static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* {{{ */
{
- char *compound;
+ const char *compound;
if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) {
if (type == ZEND_NAME_FQ) {
@@ -1180,7 +1207,7 @@ static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /*
if (compound) {
/* If the first part of a qualified name is an alias, substitute it. */
size_t len = compound - ZSTR_VAL(name);
- zend_string *import_name =
+ const zend_string *import_name =
zend_hash_str_find_ptr_lc(FC(imports), ZSTR_VAL(name), len);
if (import_name) {
@@ -1205,7 +1232,7 @@ static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /*
static zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */
{
- zval *class_name = zend_ast_get_zval(ast);
+ const zval *class_name = zend_ast_get_zval(ast);
if (Z_TYPE_P(class_name) != IS_STRING) {
zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name");
}
@@ -1224,8 +1251,6 @@ static void str_dtor(zval *zv) /* {{{ */ {
}
/* }}} */
-static bool zend_is_call(zend_ast *ast);
-
static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
{
zend_op_array *op_array = CG(active_op_array);
@@ -1263,14 +1288,14 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
}
/* }}} */
-static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zend_string *lcname, zend_op_array *op_array, bool compile_time) /* {{{ */
+static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(const zend_string *lcname, const zend_op_array *op_array, bool compile_time) /* {{{ */
{
- zval *zv = zend_hash_find_known_hash(compile_time ? CG(function_table) : EG(function_table), lcname);
+ const zval *zv = zend_hash_find_known_hash(compile_time ? CG(function_table) : EG(function_table), lcname);
int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
- zend_function *old_function;
+ const zend_function *old_function;
ZEND_ASSERT(zv != NULL);
- old_function = (zend_function*)Z_PTR_P(zv);
+ old_function = Z_PTR_P(zv);
if (old_function->type == ZEND_USER_FUNCTION
&& old_function->op_array.last > 0) {
zend_error_noreturn(error_level, "Cannot redeclare function %s() (previously declared in %s:%d)",
@@ -1283,11 +1308,11 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen
}
}
-ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname) /* {{{ */
+ZEND_API zend_result do_bind_function(zend_function *func, const zval *lcname) /* {{{ */
{
zend_function *added_func = zend_hash_add_ptr(EG(function_table), Z_STR_P(lcname), func);
if (UNEXPECTED(!added_func)) {
- do_bind_function_error(Z_STR_P(lcname), &func->op_array, 0);
+ do_bind_function_error(Z_STR_P(lcname), &func->op_array, false);
return FAILURE;
}
@@ -1303,7 +1328,7 @@ ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname) /* {{{
/* }}} */
ZEND_API zend_class_entry *zend_bind_class_in_slot(
- zval *class_table_slot, zval *lcname, zend_string *lc_parent_name)
+ zval *class_table_slot, const zval *lcname, zend_string *lc_parent_name)
{
zend_class_entry *ce = Z_PTR_P(class_table_slot);
bool is_preloaded =
@@ -1345,7 +1370,6 @@ ZEND_API zend_class_entry *zend_bind_class_in_slot(
ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name) /* {{{ */
{
- zend_class_entry *ce;
zval *rtd_key, *zv;
rtd_key = lcname + 1;
@@ -1353,7 +1377,7 @@ ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name) /*
zv = zend_hash_find_known_hash(EG(class_table), Z_STR_P(rtd_key));
if (UNEXPECTED(!zv)) {
- ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(lcname));
+ const zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(lcname));
ZEND_ASSERT(ce);
zend_class_redeclaration_error(E_COMPILE_ERROR, ce);
return FAILURE;
@@ -1382,7 +1406,7 @@ static zend_string *add_type_string(zend_string *type, zend_string *new_type, bo
return result;
}
-static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scope) {
+static zend_string *resolve_class_name(zend_string *name, const zend_class_entry *scope) {
if (scope) {
if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_SELF))) {
name = scope->name;
@@ -1401,7 +1425,7 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop
}
static zend_string *add_intersection_type(zend_string *str,
- const zend_type_list *intersection_type_list, zend_class_entry *scope,
+ const zend_type_list *intersection_type_list,
bool is_bracketed)
{
const zend_type *single_type;
@@ -1426,19 +1450,19 @@ static zend_string *add_intersection_type(zend_string *str,
return str;
}
-zend_string *zend_type_to_string_resolved(const zend_type type, zend_class_entry *scope) {
+zend_string *zend_type_to_string_resolved(const zend_type type, const zend_class_entry *scope) {
zend_string *str = NULL;
/* Pure intersection type */
if (ZEND_TYPE_IS_INTERSECTION(type)) {
ZEND_ASSERT(!ZEND_TYPE_IS_UNION(type));
- str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false);
+ str = add_intersection_type(str, ZEND_TYPE_LIST(type), /* is_bracketed */ false);
} else if (ZEND_TYPE_HAS_LIST(type)) {
/* A union type might not be a list */
const zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
- str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), scope, /* is_bracketed */ true);
+ str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), /* is_bracketed */ true);
continue;
}
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
@@ -1464,7 +1488,7 @@ zend_string *zend_type_to_string_resolved(const zend_type type, zend_class_entry
zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC);
// During compilation of eval'd code the called scope refers to the scope calling the eval
if (scope && !zend_is_compiling()) {
- zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
+ const zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
if (called_scope) {
name = called_scope->name;
}
@@ -1542,7 +1566,7 @@ static void zend_mark_function_as_generator(void) /* {{{ */
ZEND_TYPE_FOREACH(return_type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)
&& is_generator_compatible_class_type(ZEND_TYPE_NAME(*single_type))) {
- valid_type = 1;
+ valid_type = true;
break;
}
} ZEND_TYPE_FOREACH_END();
@@ -1618,7 +1642,7 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con
}
/* }}} */
-static bool array_is_const_ex(zend_array *array, uint32_t *max_checks)
+static bool array_is_const_ex(const zend_array *array, uint32_t *max_checks)
{
if (zend_hash_num_elements(array) > *max_checks) {
return false;
@@ -1641,21 +1665,21 @@ static bool array_is_const_ex(zend_array *array, uint32_t *max_checks)
return true;
}
-static bool array_is_const(zend_array *array)
+static bool array_is_const(const zend_array *array)
{
uint32_t max_checks = 50;
return array_is_const_ex(array, &max_checks);
}
-static bool can_ct_eval_const(zend_constant *c) {
+static bool can_ct_eval_const(const zend_constant *c) {
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
- return 0;
+ return false;
}
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION)
&& !((ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
&& (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
- return 1;
+ return true;
}
if (Z_TYPE(c->value) < IS_ARRAY
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
@@ -1665,7 +1689,7 @@ static bool can_ct_eval_const(zend_constant *c) {
&& array_is_const(Z_ARR(c->value))) {
return 1;
}
- return 0;
+ return false;
}
static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qualified) /* {{{ */
@@ -1682,14 +1706,14 @@ static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qu
zend_constant *c;
if ((c = zend_get_special_const(lookup_name, lookup_len))) {
ZVAL_COPY_VALUE(zv, &c->value);
- return 1;
+ return true;
}
c = zend_hash_find_ptr(EG(zend_constants), name);
if (c && can_ct_eval_const(c)) {
ZVAL_COPY_OR_DUP(zv, &c->value);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/* }}} */
@@ -1697,12 +1721,12 @@ static inline bool zend_is_scope_known(void) /* {{{ */
{
if (!CG(active_op_array)) {
/* This can only happen when evaluating a default value string. */
- return 0;
+ return false;
}
if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) {
/* Closures can be rebound to a different scope */
- return 0;
+ return false;
}
if (!CG(active_class_entry)) {
@@ -1716,13 +1740,13 @@ static inline bool zend_is_scope_known(void) /* {{{ */
}
/* }}} */
-static inline bool class_name_refers_to_active_ce(zend_string *class_name, uint32_t fetch_type) /* {{{ */
+static inline bool class_name_refers_to_active_ce(const zend_string *class_name, uint32_t fetch_type) /* {{{ */
{
if (!CG(active_class_entry)) {
- return 0;
+ return false;
}
if (fetch_type == ZEND_FETCH_CLASS_SELF && zend_is_scope_known()) {
- return 1;
+ return true;
}
return fetch_type == ZEND_FETCH_CLASS_DEFAULT
&& zend_string_equals_ci(class_name, CG(active_class_entry)->name);
@@ -1784,10 +1808,10 @@ static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast) /* {{{ */
{
uint32_t fetch_type;
- zval *class_name;
+ const zval *class_name;
if (class_ast->kind != ZEND_AST_ZVAL) {
- return 0;
+ return false;
}
class_name = zend_ast_get_zval(class_ast);
@@ -1803,28 +1827,28 @@ static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *c
case ZEND_FETCH_CLASS_SELF:
if (CG(active_class_entry) && zend_is_scope_known()) {
ZVAL_STR_COPY(zv, CG(active_class_entry)->name);
- return 1;
+ return true;
}
- return 0;
+ return false;
case ZEND_FETCH_CLASS_PARENT:
if (CG(active_class_entry) && CG(active_class_entry)->parent_name
&& zend_is_scope_known()) {
ZVAL_STR_COPY(zv, CG(active_class_entry)->parent_name);
- return 1;
+ return true;
}
- return 0;
+ return false;
case ZEND_FETCH_CLASS_STATIC:
- return 0;
+ return false;
case ZEND_FETCH_CLASS_DEFAULT:
ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast));
- return 1;
- EMPTY_SWITCH_DEFAULT_CASE()
+ return true;
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
/* We don't use zend_verify_const_access because we need to deal with unlinked classes. */
-static bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry *scope)
+static bool zend_verify_ct_const_access(const zend_class_constant *c, const zend_class_entry *scope)
{
if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) {
return 0;
@@ -1869,7 +1893,7 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend
if (class_name_refers_to_active_ce(class_name, fetch_type)) {
cc = zend_hash_find_ptr(&CG(active_class_entry)->constants_table, name);
} else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
- zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), class_name);
+ const zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), class_name);
if (ce) {
cc = zend_hash_find_ptr(&ce->constants_table, name);
} else {
@@ -1880,11 +1904,11 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend
}
if (CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION) {
- return 0;
+ return false;
}
if (!cc || !zend_verify_ct_const_access(cc, CG(active_class_entry))) {
- return 0;
+ return false;
}
c = &cc->value;
@@ -1898,7 +1922,7 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend
return 1;
}
- return 0;
+ return false;
}
/* }}} */
@@ -2051,6 +2075,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand
ce->refcount = 1;
ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED;
+ ce->ce_flags2 = 0;
if (CG(compiler_options) & ZEND_COMPILE_GUARDS) {
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
@@ -2264,7 +2289,7 @@ static void zend_adjust_for_fetch_type(zend_op *opline, znode *result, uint32_t
case BP_VAR_UNSET:
opline->opcode += 5 * factor;
return;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
@@ -2427,7 +2452,7 @@ static inline void zend_update_jump_target(uint32_t opnum_jump, uint32_t opnum_t
case ZEND_JMP_FRAMELESS:
opline->op2.opline_num = opnum_target;
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
@@ -2496,9 +2521,9 @@ static bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind)
case ZEND_AST_METHOD_CALL:
case ZEND_AST_NULLSAFE_METHOD_CALL:
case ZEND_AST_STATIC_CALL:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -2513,9 +2538,9 @@ static bool zend_ast_is_short_circuited(const zend_ast *ast)
return zend_ast_is_short_circuited(ast->child[0]);
case ZEND_AST_NULLSAFE_PROP:
case ZEND_AST_NULLSAFE_METHOD_CALL:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -2543,7 +2568,7 @@ static uint32_t zend_short_circuiting_checkpoint(void)
return zend_stack_count(&CG(short_circuiting_opnums));
}
-static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zend_ast *ast)
+static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, const zend_ast *ast)
{
bool is_short_circuited = zend_ast_kind_is_short_circuited(ast->kind)
|| ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY;
@@ -2584,7 +2609,9 @@ static void zend_emit_jmp_null(znode *obj_node, uint32_t bp_type)
zend_stack_push(&CG(short_circuiting_opnums), &jmp_null_opnum);
}
-static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
+static inline bool zend_is_variable_or_call(const zend_ast *ast);
+
+static void zend_compile_memoized_expr(znode *result, zend_ast *expr, uint32_t type) /* {{{ */
{
const zend_memoize_mode memoize_mode = CG(memoize_mode);
if (memoize_mode == ZEND_MEMOIZE_COMPILE) {
@@ -2592,7 +2619,11 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
/* Go through normal compilation */
CG(memoize_mode) = ZEND_MEMOIZE_NONE;
- zend_compile_expr(result, expr);
+ if (zend_is_variable_or_call(expr)) {
+ zend_compile_var(result, expr, type, /* by_ref */ false);
+ } else {
+ zend_compile_expr(result, expr);
+ }
CG(memoize_mode) = ZEND_MEMOIZE_COMPILE;
if (result->op_type == IS_VAR) {
@@ -2609,7 +2640,7 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
zend_hash_index_update_mem(
CG(memoized_exprs), (uintptr_t) expr, &memoized_result, sizeof(znode));
} else if (memoize_mode == ZEND_MEMOIZE_FETCH) {
- znode *memoized_result = zend_hash_index_find_ptr(CG(memoized_exprs), (uintptr_t) expr);
+ const znode *memoized_result = zend_hash_index_find_ptr(CG(memoized_exprs), (uintptr_t) expr);
*result = *memoized_result;
if (result->op_type == IS_CONST) {
Z_TRY_ADDREF(result->u.constant);
@@ -2621,7 +2652,7 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
/* }}} */
static void zend_emit_return_type_check(
- znode *expr, zend_arg_info *return_info, bool implicit) /* {{{ */
+ znode *expr, const zend_arg_info *return_info, bool implicit) /* {{{ */
{
zend_type type = return_info->type;
if (ZEND_TYPE_IS_SET(type)) {
@@ -2650,7 +2681,6 @@ static void zend_emit_return_type_check(
ZEND_ASSERT(!implicit);
zend_error_noreturn(E_COMPILE_ERROR, "A never-returning %s must not return",
CG(active_class_entry) != NULL ? "method" : "function");
- return;
}
if (!expr && !implicit) {
@@ -2700,7 +2730,7 @@ void zend_emit_final_return(bool return_one) /* {{{ */
return;
}
- zend_emit_return_type_check(NULL, return_info, 1);
+ zend_emit_return_type_check(NULL, return_info, true);
}
zn.op_type = IS_CONST;
@@ -2715,7 +2745,7 @@ void zend_emit_final_return(bool return_one) /* {{{ */
}
/* }}} */
-static inline bool zend_is_variable(zend_ast *ast) /* {{{ */
+static inline bool zend_is_variable(const zend_ast *ast) /* {{{ */
{
return ast->kind == ZEND_AST_VAR
|| ast->kind == ZEND_AST_DIM
@@ -2725,7 +2755,22 @@ static inline bool zend_is_variable(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline bool zend_is_call(zend_ast *ast) /* {{{ */
+static bool zend_propagate_list_refs(zend_ast *ast);
+
+static inline bool zend_is_passable_by_ref(const zend_ast *ast)
+{
+ if (zend_is_variable(ast) || ast->kind == ZEND_AST_ASSIGN_REF) {
+ return true;
+ }
+ if (ast->kind == ZEND_AST_ASSIGN
+ && UNEXPECTED(ast->child[0]->kind == ZEND_AST_ARRAY)
+ && zend_propagate_list_refs(ast->child[0])) {
+ return true;
+ }
+ return false;
+}
+
+static inline bool zend_is_call(const zend_ast *ast) /* {{{ */
{
return ast->kind == ZEND_AST_CALL
|| ast->kind == ZEND_AST_METHOD_CALL
@@ -2735,13 +2780,13 @@ static inline bool zend_is_call(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */
+static inline bool zend_is_variable_or_call(const zend_ast *ast) /* {{{ */
{
return zend_is_variable(ast) || zend_is_call(ast);
}
/* }}} */
-static inline bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */
+static inline bool zend_is_unticked_stmt(const zend_ast *ast) /* {{{ */
{
return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL
|| ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_GROUP
@@ -2749,7 +2794,7 @@ static inline bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */
}
/* }}} */
-static inline bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */
+static inline bool zend_can_write_to_variable(const zend_ast *ast) /* {{{ */
{
while (
ast->kind == ZEND_AST_DIM
@@ -2765,7 +2810,7 @@ static inline bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */
static inline bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */
{
if (name_ast->kind != ZEND_AST_ZVAL) {
- return 0;
+ return false;
}
return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type_ast(name_ast);
@@ -2785,7 +2830,7 @@ static inline void zend_handle_numeric_op(znode *node) /* {{{ */
}
/* }}} */
-static inline void zend_handle_numeric_dim(zend_op *opline, znode *dim_node) /* {{{ */
+static inline void zend_handle_numeric_dim(const zend_op *opline, znode *dim_node) /* {{{ */
{
if (Z_TYPE(dim_node->u.constant) == IS_STRING) {
zend_ulong index;
@@ -2871,7 +2916,7 @@ static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t f
}
/* }}} */
-static zend_result zend_try_compile_cv(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
+static zend_result zend_try_compile_cv(znode *result, const zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
if (name_ast->kind == ZEND_AST_ZVAL) {
@@ -2912,7 +2957,7 @@ static zend_result zend_try_compile_cv(znode *result, zend_ast *ast, uint32_t ty
}
/* }}} */
-static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint32_t type, bool delayed) /* {{{ */
+static zend_op *zend_compile_simple_var_no_cv(znode *result, const zend_ast *ast, uint32_t type, bool delayed) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
znode name_node;
@@ -2934,7 +2979,6 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
opline->extended_value = ZEND_FETCH_GLOBAL;
} else {
- // TODO: Have a test case for this?
if (name_node.op_type == IS_CONST
&& type == BP_VAR_R
&& zend_string_equals_literal(Z_STR(name_node.u.constant), "http_response_header")) {
@@ -2950,39 +2994,39 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
}
/* }}} */
-static bool is_this_fetch(zend_ast *ast) /* {{{ */
+static bool is_this_fetch(const zend_ast *ast) /* {{{ */
{
if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
- zval *name = zend_ast_get_zval(ast->child[0]);
+ const zval *name = zend_ast_get_zval(ast->child[0]);
return Z_TYPE_P(name) == IS_STRING && zend_string_equals(Z_STR_P(name), ZSTR_KNOWN(ZEND_STR_THIS));
}
- return 0;
+ return false;
}
/* }}} */
static bool is_globals_fetch(const zend_ast *ast)
{
if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) {
- zval *name = zend_ast_get_zval(ast->child[0]);
+ const zval *name = zend_ast_get_zval(ast->child[0]);
return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "GLOBALS");
}
- return 0;
+ return false;
}
-static bool is_global_var_fetch(zend_ast *ast)
+static bool is_global_var_fetch(const zend_ast *ast)
{
return ast->kind == ZEND_AST_DIM && is_globals_fetch(ast->child[0]);
}
static bool this_guaranteed_exists(void) /* {{{ */
{
- zend_oparray_context *ctx = &CG(context);
+ const zend_oparray_context *ctx = &CG(context);
while (ctx) {
/* Instance methods always have a $this.
* This also includes closures that have a scope and use $this. */
- zend_op_array *op_array = ctx->op_array;
+ const zend_op_array *op_array = ctx->op_array;
if (op_array->fn_flags & ZEND_ACC_STATIC) {
return false;
} else if (op_array->scope) {
@@ -2996,7 +3040,7 @@ static bool this_guaranteed_exists(void) /* {{{ */
}
/* }}} */
-static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, bool delayed) /* {{{ */
+static zend_op *zend_compile_simple_var(znode *result, const zend_ast *ast, uint32_t type, bool delayed) /* {{{ */
{
if (is_this_fetch(ast)) {
zend_op *opline = zend_emit_op(result, ZEND_FETCH_THIS, NULL, NULL);
@@ -3020,7 +3064,7 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t
}
/* }}} */
-static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t type) /* {{{ */
+static void zend_separate_if_call_and_write(znode *node, const zend_ast *ast, uint32_t type) /* {{{ */
{
if (type != BP_VAR_R
&& type != BP_VAR_IS
@@ -3038,7 +3082,7 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t
}
/* }}} */
-static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
+static inline void zend_emit_assign_znode(zend_ast *var_ast, const znode *value_node) /* {{{ */
{
znode dummy_node;
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast,
@@ -3072,7 +3116,7 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
return opline;
} else {
zend_short_circuiting_mark_inner(var_ast);
- opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
+ opline = zend_delayed_compile_var(&var_node, var_ast, type, false);
if (opline) {
if (type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
opline->extended_value |= ZEND_FETCH_DIM_WRITE;
@@ -3132,7 +3176,11 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
if (this_guaranteed_exists()) {
obj_node.op_type = IS_UNUSED;
} else {
- zend_emit_op(&obj_node, ZEND_FETCH_THIS, NULL, NULL);
+ opline = zend_emit_op(&obj_node, ZEND_FETCH_THIS, NULL, NULL);
+ if ((type == BP_VAR_R) || (type == BP_VAR_IS)) {
+ opline->result_type = IS_TMP_VAR;
+ obj_node.op_type = IS_TMP_VAR;
+ }
}
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
@@ -3140,7 +3188,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
* check for a nullsafe access. */
} else {
zend_short_circuiting_mark_inner(obj_ast);
- opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
+ opline = zend_delayed_compile_var(&obj_node, obj_ast, type, false);
if (opline && (opline->opcode == ZEND_FETCH_DIM_W
|| opline->opcode == ZEND_FETCH_DIM_RW
|| opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
@@ -3246,7 +3294,7 @@ static zend_op *zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t
}
/* }}} */
-static void zend_verify_list_assign_target(zend_ast *var_ast, zend_ast_attr array_style) /* {{{ */ {
+static void zend_verify_list_assign_target(const zend_ast *var_ast, zend_ast_attr array_style) /* {{{ */ {
if (var_ast->kind == ZEND_AST_ARRAY) {
if (var_ast->attr == ZEND_ARRAY_SYNTAX_LONG) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot assign to array(), use [] instead");
@@ -3260,12 +3308,12 @@ static void zend_verify_list_assign_target(zend_ast *var_ast, zend_ast_attr arra
}
/* }}} */
-static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node);
+static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, const znode *value_node);
/* Propagate refs used on leaf elements to the surrounding list() structures. */
static bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */
- zend_ast_list *list = zend_ast_get_list(ast);
- bool has_refs = 0;
+ const zend_ast_list *list = zend_ast_get_list(ast);
+ bool has_refs = false;
uint32_t i;
for (i = 0; i < list->children; ++i) {
@@ -3284,10 +3332,10 @@ static bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */
}
/* }}} */
-static bool list_is_keyed(zend_ast_list *list)
+static bool list_is_keyed(const zend_ast_list *list)
{
for (uint32_t i = 0; i < list->children; i++) {
- zend_ast *child = list->child[i];
+ const zend_ast *child = list->child[i];
if (child) {
return child->kind == ZEND_AST_ARRAY_ELEM && child->child[1] != NULL;
}
@@ -3296,11 +3344,11 @@ static bool list_is_keyed(zend_ast_list *list)
}
static void zend_compile_list_assign(
- znode *result, zend_ast *ast, znode *expr_node, zend_ast_attr array_style) /* {{{ */
+ znode *result, zend_ast *ast, znode *expr_node, zend_ast_attr array_style, uint32_t type) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
- bool has_elems = 0;
+ bool has_elems = false;
bool is_keyed = list_is_keyed(list);
if (list->children && expr_node->op_type == IS_CONST && Z_TYPE(expr_node->u.constant) == IS_STRING) {
@@ -3329,7 +3377,7 @@ static void zend_compile_list_assign(
var_ast = elem_ast->child[0];
key_ast = elem_ast->child[1];
- has_elems = 1;
+ has_elems = true;
if (is_keyed) {
if (key_ast == NULL) {
@@ -3356,6 +3404,10 @@ static void zend_compile_list_assign(
opline = zend_emit_op(&fetch_result,
elem_ast->attr ? (expr_node->op_type == IS_CV ? ZEND_FETCH_DIM_W : ZEND_FETCH_LIST_W) : ZEND_FETCH_LIST_R, expr_node, &dim_node);
+ if (opline->opcode == ZEND_FETCH_LIST_R) {
+ opline->result_type = IS_TMP_VAR;
+ fetch_result.op_type = IS_TMP_VAR;
+ }
if (dim_node.op_type == IS_CONST) {
zend_handle_numeric_dim(opline, &dim_node);
@@ -3365,7 +3417,7 @@ static void zend_compile_list_assign(
zend_emit_op(&fetch_result, ZEND_MAKE_REF, &fetch_result, NULL);
}
if (var_ast->kind == ZEND_AST_ARRAY) {
- zend_compile_list_assign(NULL, var_ast, &fetch_result, var_ast->attr);
+ zend_compile_list_assign(NULL, var_ast, &fetch_result, var_ast->attr, type);
} else if (elem_ast->attr) {
zend_emit_assign_ref_znode(var_ast, &fetch_result);
} else {
@@ -3378,7 +3430,12 @@ static void zend_compile_list_assign(
}
if (result) {
- *result = *expr_node;
+ if ((type == BP_VAR_R || type == BP_VAR_IS) && expr_node->op_type == IS_VAR) {
+ /* Deref. */
+ zend_emit_op_tmp(result, ZEND_QM_ASSIGN, expr_node, NULL);
+ } else {
+ *result = *expr_node;
+ }
} else {
zend_do_free(expr_node);
}
@@ -3408,10 +3465,10 @@ static void zend_ensure_writable_variable(const zend_ast *ast) /* {{{ */
/* }}} */
/* Detects $a... = $a pattern */
-static bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ */
+static bool zend_is_assign_to_self(const zend_ast *var_ast, const zend_ast *expr_ast) /* {{{ */
{
if (expr_ast->kind != ZEND_AST_VAR || expr_ast->child[0]->kind != ZEND_AST_ZVAL) {
- return 0;
+ return false;
}
while (zend_is_variable(var_ast) && var_ast->kind != ZEND_AST_VAR) {
@@ -3419,7 +3476,7 @@ static bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{
}
if (var_ast->kind != ZEND_AST_VAR || var_ast->child[0]->kind != ZEND_AST_ZVAL) {
- return 0;
+ return false;
}
{
@@ -3434,13 +3491,13 @@ static bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{
/* }}} */
static void zend_compile_expr_with_potential_assign_to_self(
- znode *expr_node, zend_ast *expr_ast, zend_ast *var_ast) {
+ znode *expr_node, zend_ast *expr_ast, const zend_ast *var_ast) {
if (zend_is_assign_to_self(var_ast, expr_ast) && !is_this_fetch(expr_ast)) {
/* $a[0] = $a should evaluate the right $a first */
znode cv_node;
if (zend_try_compile_cv(&cv_node, expr_ast, BP_VAR_R) == FAILURE) {
- zend_compile_simple_var_no_cv(expr_node, expr_ast, BP_VAR_R, 0);
+ zend_compile_simple_var_no_cv(expr_node, expr_ast, BP_VAR_R, false);
} else {
zend_emit_op_tmp(expr_node, ZEND_QM_ASSIGN, &cv_node, NULL);
}
@@ -3449,7 +3506,7 @@ static void zend_compile_expr_with_potential_assign_to_self(
}
}
-static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_assign(znode *result, zend_ast *ast, bool stmt, uint32_t type) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
zend_ast *expr_ast = ast->child[1];
@@ -3468,7 +3525,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
switch (kind) {
case ZEND_AST_VAR:
offset = zend_delayed_compile_begin();
- zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0);
+ zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, false);
zend_compile_expr(&expr_node, expr_ast);
zend_delayed_compile_end(offset);
CG(zend_lineno) = zend_ast_get_lineno(var_ast);
@@ -3476,7 +3533,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
return;
case ZEND_AST_STATIC_PROP:
offset = zend_delayed_compile_begin();
- zend_delayed_compile_var(result, var_ast, BP_VAR_W, 0);
+ zend_delayed_compile_var(result, var_ast, BP_VAR_W, false);
zend_compile_expr(&expr_node, expr_ast);
opline = zend_delayed_compile_end(offset);
@@ -3520,7 +3577,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_assert_not_short_circuited(expr_ast);
}
- zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
+ zend_compile_var(&expr_node, expr_ast, BP_VAR_W, true);
/* MAKE_REF is usually not necessary for CVs. However, if there are
* self-assignments, this forces the RHS to evaluate first. */
zend_emit_op(&expr_node, ZEND_MAKE_REF, &expr_node, NULL);
@@ -3530,7 +3587,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
znode cv_node;
if (zend_try_compile_cv(&cv_node, expr_ast, BP_VAR_R) == FAILURE) {
- zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
+ zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, false);
} else {
zend_emit_op_tmp(&expr_node, ZEND_QM_ASSIGN, &cv_node, NULL);
}
@@ -3539,14 +3596,17 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
}
}
- zend_compile_list_assign(result, var_ast, &expr_node, var_ast->attr);
+ zend_compile_list_assign(!stmt ? result : NULL, var_ast, &expr_node, var_ast->attr, type);
+ if (stmt) {
+ result->op_type = IS_UNUSED;
+ }
return;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
-static void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_assign_ref(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *target_ast = ast->child[0];
zend_ast *source_ast = ast->child[1];
@@ -3565,8 +3625,8 @@ static void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
}
offset = zend_delayed_compile_begin();
- zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1);
- zend_compile_var(&source_node, source_ast, BP_VAR_W, 1);
+ zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, true);
+ zend_compile_var(&source_node, source_ast, BP_VAR_W, true);
if ((target_ast->kind != ZEND_AST_VAR
|| target_ast->child[0]->kind != ZEND_AST_ZVAL)
@@ -3593,28 +3653,40 @@ static void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
opline->opcode = ZEND_ASSIGN_OBJ_REF;
opline->extended_value &= ~ZEND_FETCH_REF;
opline->extended_value |= flags;
+ if (result) {
+ *result = target_node;
+ } else {
+ SET_UNUSED(opline->result);
+ }
zend_emit_op_data(&source_node);
- *result = target_node;
} else if (opline && opline->opcode == ZEND_FETCH_STATIC_PROP_W) {
opline->opcode = ZEND_ASSIGN_STATIC_PROP_REF;
opline->extended_value &= ~ZEND_FETCH_REF;
opline->extended_value |= flags;
+ if (result) {
+ *result = target_node;
+ } else {
+ SET_UNUSED(opline->result);
+ }
zend_emit_op_data(&source_node);
- *result = target_node;
} else {
opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node);
opline->extended_value = flags;
}
+
+ if (result && (type == BP_VAR_R || type == BP_VAR_IS)) {
+ /* Deref. */
+ znode tmp_result = *result;
+ zend_emit_op_tmp(result, ZEND_QM_ASSIGN, &tmp_result, NULL);
+ }
}
/* }}} */
-static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
+static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, const znode *value_node) /* {{{ */
{
- znode dummy_node;
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN_REF, var_ast,
zend_ast_create_znode(value_node));
- zend_compile_expr(&dummy_node, assign_ast);
- zend_do_free(&dummy_node);
+ zend_compile_stmt(assign_ast);
}
/* }}} */
@@ -3635,7 +3707,7 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
switch (kind) {
case ZEND_AST_VAR:
offset = zend_delayed_compile_begin();
- zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
+ zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, false);
zend_compile_expr(&expr_node, expr_ast);
zend_delayed_compile_end(offset);
opline = zend_emit_op_tmp(result, ZEND_ASSIGN_OP, &var_node, &expr_node);
@@ -3643,7 +3715,7 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
return;
case ZEND_AST_STATIC_PROP:
offset = zend_delayed_compile_begin();
- zend_delayed_compile_var(result, var_ast, BP_VAR_RW, 0);
+ zend_delayed_compile_var(result, var_ast, BP_VAR_RW, false);
zend_compile_expr(&expr_node, expr_ast);
opline = zend_delayed_compile_end(offset);
@@ -3685,28 +3757,17 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
opline = zend_emit_op_data(&expr_node);
opline->extended_value = cache_slot;
return;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
-static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) {
+static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg_name) {
// TODO: Caching?
- if (fn->type == ZEND_USER_FUNCTION) {
- for (uint32_t i = 0; i < fn->common.num_args; i++) {
- zend_arg_info *arg_info = &fn->op_array.arg_info[i];
- if (zend_string_equals(arg_info->name, arg_name)) {
- return i + 1;
- }
- }
- } else {
- ZEND_ASSERT(fn->common.num_args == 0 || fn->internal_function.arg_info);
- for (uint32_t i = 0; i < fn->common.num_args; i++) {
- zend_internal_arg_info *arg_info = &fn->internal_function.arg_info[i];
- size_t len = strlen(arg_info->name);
- if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
- return i + 1;
- }
+ for (uint32_t i = 0; i < fn->common.num_args; i++) {
+ zend_arg_info *arg_info = &fn->op_array.arg_info[i];
+ if (zend_string_equals(arg_info->name, arg_name)) {
+ return i + 1;
}
}
@@ -3714,21 +3775,25 @@ static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) {
return (uint32_t) -1;
}
-static uint32_t zend_compile_args(
- zend_ast *ast, 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) /* {{{ */
{
- zend_ast_list *args = zend_ast_get_list(ast);
+ const zend_ast_list *args = zend_ast_get_list(ast);
uint32_t i;
- bool uses_arg_unpack = 0;
+ 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.
* May not actually use named argument passing. */
- bool uses_named_args = 0;
+ bool uses_named_args = false;
/* Whether there may be any undef arguments due to the use of named arguments. */
- bool may_have_undef = 0;
+ bool may_have_undef = false;
/* Whether there may be any extra named arguments collected into a variadic. */
- *may_have_extra_named_args = 0;
+ *may_have_extra_named_args = false;
for (i = 0; i < args->children; ++i) {
zend_ast *arg = args->child[i];
@@ -3745,13 +3810,18 @@ 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 = 1;
+ may_have_undef = true;
if (!fbc || (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) {
- *may_have_extra_named_args = 1;
+ *may_have_extra_named_args = true;
}
- uses_arg_unpack = 1;
+ uses_arg_unpack = true;
fbc = NULL;
zend_compile_expr(&arg_node, arg->child[0]);
@@ -3763,7 +3833,7 @@ static uint32_t zend_compile_args(
}
if (arg->kind == ZEND_AST_NAMED_ARG) {
- uses_named_args = 1;
+ uses_named_args = true;
arg_name = zval_make_interned_string(zend_ast_get_zval(arg->child[0]));
arg = arg->child[1];
@@ -3775,15 +3845,20 @@ static uint32_t zend_compile_args(
arg_count++;
} else {
// TODO: We could track which arguments were passed, even if out of order.
- may_have_undef = 1;
+ may_have_undef = true;
if (arg_num == (uint32_t) -1 && (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) {
- *may_have_extra_named_args = 1;
+ *may_have_extra_named_args = true;
}
}
} else {
arg_num = (uint32_t) -1;
- may_have_undef = 1;
- *may_have_extra_named_args = 1;
+ 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) {
@@ -3791,18 +3866,72 @@ static uint32_t zend_compile_args(
"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.
* This will error at runtime if the argument is by-ref. */
if (zend_is_call(arg) || is_globals_fetch(arg)) {
- zend_compile_var(&arg_node, arg, BP_VAR_R, 0);
+ uint32_t type = is_globals_fetch(arg) || (fbc && !ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num))
+ ? BP_VAR_R : BP_VAR_FUNC_ARG;
+ zend_compile_var(&arg_node, arg, type, /* by_ref */ false);
if (arg_node.op_type & (IS_CONST|IS_TMP_VAR)) {
/* Function call was converted into builtin instruction */
if (!fbc || ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
@@ -3826,13 +3955,13 @@ static uint32_t zend_compile_args(
opcode = ZEND_SEND_VAR_NO_REF_EX;
}
}
- } else if (zend_is_variable(arg) && !zend_ast_is_short_circuited(arg)) {
+ } else if (zend_is_passable_by_ref(arg) && !zend_ast_is_short_circuited(arg)) {
if (fbc && arg_num != (uint32_t) -1) {
if (ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) {
- zend_compile_var(&arg_node, arg, BP_VAR_W, 1);
+ zend_compile_var(&arg_node, arg, BP_VAR_W, true);
opcode = ZEND_SEND_REF;
} else {
- zend_compile_var(&arg_node, arg, BP_VAR_R, 0);
+ zend_compile_var(&arg_node, arg, BP_VAR_R, false);
opcode = (arg_node.op_type == IS_TMP_VAR) ? ZEND_SEND_VAL : ZEND_SEND_VAR;
}
} else {
@@ -3858,7 +3987,7 @@ static uint32_t zend_compile_args(
} else {
opline->op2.num = arg_num;
}
- zend_compile_var(&arg_node, arg, BP_VAR_FUNC_ARG, 1);
+ zend_compile_var(&arg_node, arg, BP_VAR_FUNC_ARG, true);
opcode = ZEND_SEND_FUNC_ARG;
} while (0);
}
@@ -3909,15 +4038,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;
}
/* }}} */
-ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bool result_used) /* {{{ */
+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;
@@ -3950,7 +4088,39 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bo
}
/* }}} */
-static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *fbc, uint32_t lineno) /* {{{ */
+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;
uint32_t opnum_init = get_next_op_number() - 1;
@@ -3958,16 +4128,50 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) {
opline = &CG(active_op_array)->opcodes[opnum_init];
opline->extended_value = 0;
+ /* opcode array may be reallocated, so don't access opcode field after zend_emit_op_tmp(). */
+ uint8_t opcode = opline->opcode;
- if (opline->opcode == ZEND_NEW) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression");
+ if (opcode == ZEND_NEW) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression");
}
- if (opline->opcode == ZEND_INIT_FCALL) {
- opline->op1.num = zend_vm_calc_used_stack(0, fbc);
+ zend_ast_list *fcc_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args);
+
+ /* 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) {
+
+ 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;
}
- zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL);
+ 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;
}
@@ -3978,8 +4182,9 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
opline = &CG(active_op_array)->opcodes[opnum_init];
opline->extended_value = arg_count;
+ uint8_t init_opcode = opline->opcode;
- if (opline->opcode == ZEND_INIT_FCALL) {
+ if (init_opcode == ZEND_INIT_FCALL) {
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
}
@@ -3993,6 +4198,12 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
false
);
opline = zend_emit_op(result, call_op, NULL, NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ if (init_opcode != ZEND_NEW && opline->result_type == IS_VAR) {
+ opline->result_type = IS_TMP_VAR;
+ result->op_type = IS_TMP_VAR;
+ }
+ }
if (may_have_extra_named_args) {
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
}
@@ -4015,7 +4226,7 @@ static bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /*
}
/* }}} */
-static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast, uint32_t lineno) /* {{{ */
+static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast, uint32_t lineno, uint32_t type) /* {{{ */
{
if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) {
const char *colon;
@@ -4045,24 +4256,24 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast
zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node);
}
- zend_compile_call_common(result, args_ast, NULL, lineno);
+ zend_compile_call_common(result, args_ast, NULL, lineno, type);
}
/* }}} */
-static inline bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */
+static inline bool zend_args_contain_unpack_or_named(const zend_ast_list *args) /* {{{ */
{
uint32_t i;
for (i = 0; i < args->children; ++i) {
- zend_ast *arg = args->child[i];
+ const zend_ast *arg = args->child[i];
if (arg->kind == ZEND_AST_UNPACK || arg->kind == ZEND_AST_NAMED_ARG) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
/* }}} */
-static zend_result zend_compile_func_strlen(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_strlen(znode *result, const zend_ast_list *args) /* {{{ */
{
znode arg_node;
@@ -4082,7 +4293,7 @@ static zend_result zend_compile_func_strlen(znode *result, zend_ast_list *args)
}
/* }}} */
-static zend_result zend_compile_func_typecheck(znode *result, zend_ast_list *args, uint32_t type) /* {{{ */
+static zend_result zend_compile_func_typecheck(znode *result, const zend_ast_list *args, uint32_t type) /* {{{ */
{
znode arg_node;
zend_op *opline;
@@ -4102,7 +4313,7 @@ static zend_result zend_compile_func_typecheck(znode *result, zend_ast_list *arg
}
/* }}} */
-static zend_result zend_compile_func_is_scalar(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_is_scalar(znode *result, const zend_ast_list *args) /* {{{ */
{
znode arg_node;
zend_op *opline;
@@ -4117,7 +4328,7 @@ static zend_result zend_compile_func_is_scalar(znode *result, zend_ast_list *arg
return SUCCESS;
}
-static zend_result zend_compile_func_cast(znode *result, zend_ast_list *args, uint32_t type) /* {{{ */
+static zend_result zend_compile_func_cast(znode *result, const zend_ast_list *args, uint32_t type) /* {{{ */
{
znode arg_node;
zend_op *opline;
@@ -4137,7 +4348,7 @@ static zend_result zend_compile_func_cast(znode *result, zend_ast_list *args, ui
}
/* }}} */
-static zend_result zend_compile_func_defined(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_defined(znode *result, const zend_ast_list *args) /* {{{ */
{
zend_string *name;
zend_op *opline;
@@ -4152,7 +4363,7 @@ static zend_result zend_compile_func_defined(znode *result, zend_ast_list *args)
return FAILURE;
}
- if (zend_try_ct_eval_const(&result->u.constant, name, 0)) {
+ if (zend_try_ct_eval_const(&result->u.constant, name, false)) {
zend_string_release_ex(name, 0);
zval_ptr_dtor(&result->u.constant);
ZVAL_TRUE(&result->u.constant);
@@ -4211,11 +4422,11 @@ static zend_result zend_compile_func_ord(znode *result, const zend_ast_list *arg
/* We can only calculate the stack size for functions that have been fully compiled, otherwise
* additional CV or TMP slots may still be added. This prevents the use of INIT_FCALL for
* directly or indirectly recursive function calls. */
-static bool fbc_is_finalized(zend_function *fbc) {
+static bool fbc_is_finalized(const zend_function *fbc) {
return !ZEND_USER_CODE(fbc->type) || (fbc->common.fn_flags & ZEND_ACC_DONE_PASS_TWO);
}
-static bool zend_compile_ignore_class(zend_class_entry *ce, zend_string *filename)
+static bool zend_compile_ignore_class(const zend_class_entry *ce, const zend_string *filename)
{
if (ce->type == ZEND_INTERNAL_CLASS) {
return CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
@@ -4225,7 +4436,7 @@ static bool zend_compile_ignore_class(zend_class_entry *ce, zend_string *filenam
}
}
-static bool zend_compile_ignore_function(zend_function *fbc, zend_string *filename)
+static bool zend_compile_ignore_function(const zend_function *fbc, const zend_string *filename)
{
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
return CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS;
@@ -4287,7 +4498,7 @@ static void zend_compile_init_user_func(zend_ast *name_ast, uint32_t num_args, z
/* }}} */
/* cufa = call_user_func_array */
-static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcname) /* {{{ */
+static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcname, uint32_t type) /* {{{ */
{
znode arg_node;
zend_op *opline;
@@ -4322,7 +4533,11 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze
zend_compile_expr(&len_node, list->child[2]);
opline = zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, &len_node);
opline->extended_value = Z_LVAL_P(zv);
- zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
+ opline = zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ opline->result_type = IS_TMP_VAR;
+ result->op_type = IS_TMP_VAR;
+ }
zend_string_release_ex(name, 0);
return SUCCESS;
}
@@ -4333,6 +4548,10 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze
zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL);
zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL);
opline = zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ opline->result_type = IS_TMP_VAR;
+ result->op_type = IS_TMP_VAR;
+ }
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
return SUCCESS;
@@ -4340,7 +4559,7 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze
/* }}} */
/* cuf = call_user_func */
-static zend_result zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcname) /* {{{ */
+static zend_result zend_compile_func_cuf(znode *result, const zend_ast_list *args, zend_string *lcname, uint32_t type) /* {{{ */
{
uint32_t i;
@@ -4360,13 +4579,17 @@ static zend_result zend_compile_func_cuf(znode *result, zend_ast_list *args, zen
opline->op2.num = i;
opline->result.var = EX_NUM_TO_VAR(i - 1);
}
- zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
+ zend_op *opline = zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
+ if (type == BP_VAR_R || type == BP_VAR_IS) {
+ opline->result_type = IS_TMP_VAR;
+ result->op_type = IS_TMP_VAR;
+ }
return SUCCESS;
}
/* }}} */
-static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string *name, zend_function *fbc, uint32_t lineno) /* {{{ */
+static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string *name, const zend_function *fbc, uint32_t lineno, uint32_t type) /* {{{ */
{
if (EG(assertions) >= 0) {
znode name_node;
@@ -4401,7 +4624,7 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string
args = (zend_ast_list *)zend_ast_list_add((zend_ast *) args, arg);
}
- zend_compile_call_common(result, (zend_ast*)args, fbc, lineno);
+ zend_compile_call_common(result, (zend_ast*)args, fbc, lineno, type);
opline = &CG(active_op_array)->opcodes[check_op_number];
opline->op2.opline_num = get_next_op_number();
@@ -4418,7 +4641,7 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string
static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args) /* {{{ */
{
- bool strict = 0;
+ bool strict = false;
znode array, needly;
zend_op *opline;
@@ -4453,7 +4676,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args
}
if (zend_hash_num_elements(Z_ARRVAL(array.u.constant)) > 0) {
- bool ok = 1;
+ bool ok = true;
zval *val, tmp;
HashTable *src = Z_ARRVAL(array.u.constant);
HashTable *dst = zend_new_array(zend_hash_num_elements(src));
@@ -4468,7 +4691,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args
zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
} else {
zend_array_destroy(dst);
- ok = 0;
+ ok = false;
break;
}
} ZEND_HASH_FOREACH_END();
@@ -4477,7 +4700,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args
if (Z_TYPE_P(val) != IS_STRING
|| is_numeric_string(Z_STRVAL_P(val), Z_STRLEN_P(val), NULL, NULL, 0)) {
zend_array_destroy(dst);
- ok = 0;
+ ok = false;
break;
}
zend_hash_add(dst, Z_STR_P(val), &tmp);
@@ -4501,7 +4724,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args
}
/* }}} */
-static zend_result zend_compile_func_count(znode *result, zend_ast_list *args, zend_string *lcname) /* {{{ */
+static zend_result zend_compile_func_count(znode *result, const zend_ast_list *args, const zend_string *lcname) /* {{{ */
{
znode arg_node;
zend_op *opline;
@@ -4518,7 +4741,7 @@ static zend_result zend_compile_func_count(znode *result, zend_ast_list *args, z
}
/* }}} */
-static zend_result zend_compile_func_get_class(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_get_class(znode *result, const zend_ast_list *args) /* {{{ */
{
if (args->children == 0) {
zend_emit_op_tmp(result, ZEND_GET_CLASS, NULL, NULL);
@@ -4536,7 +4759,7 @@ static zend_result zend_compile_func_get_class(znode *result, zend_ast_list *arg
}
/* }}} */
-static zend_result zend_compile_func_get_called_class(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_get_called_class(znode *result, const zend_ast_list *args) /* {{{ */
{
if (args->children != 0) {
return FAILURE;
@@ -4547,7 +4770,7 @@ static zend_result zend_compile_func_get_called_class(znode *result, zend_ast_li
}
/* }}} */
-static zend_result zend_compile_func_gettype(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_gettype(znode *result, const zend_ast_list *args) /* {{{ */
{
znode arg_node;
@@ -4561,7 +4784,7 @@ static zend_result zend_compile_func_gettype(znode *result, zend_ast_list *args)
}
/* }}} */
-static zend_result zend_compile_func_num_args(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_num_args(znode *result, const zend_ast_list *args) /* {{{ */
{
if (CG(active_op_array)->function_name && args->children == 0) {
zend_emit_op_tmp(result, ZEND_FUNC_NUM_ARGS, NULL, NULL);
@@ -4572,7 +4795,7 @@ static zend_result zend_compile_func_num_args(znode *result, zend_ast_list *args
}
/* }}} */
-static zend_result zend_compile_func_get_args(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_get_args(znode *result, const zend_ast_list *args) /* {{{ */
{
if (CG(active_op_array)->function_name && args->children == 0) {
zend_emit_op_tmp(result, ZEND_FUNC_GET_ARGS, NULL, NULL);
@@ -4583,7 +4806,7 @@ static zend_result zend_compile_func_get_args(znode *result, zend_ast_list *args
}
/* }}} */
-static zend_result zend_compile_func_array_key_exists(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_array_key_exists(znode *result, const zend_ast_list *args) /* {{{ */
{
znode subject, needle;
@@ -4599,7 +4822,7 @@ static zend_result zend_compile_func_array_key_exists(znode *result, zend_ast_li
}
/* }}} */
-static zend_result zend_compile_func_array_slice(znode *result, zend_ast_list *args) /* {{{ */
+static zend_result zend_compile_func_array_slice(znode *result, const zend_ast_list *args) /* {{{ */
{
if (CG(active_op_array)->function_name
&& args->children == 2
@@ -4612,8 +4835,8 @@ static zend_result zend_compile_func_array_slice(znode *result, zend_ast_list *a
zend_string *orig_name = zend_ast_get_str(args->child[0]->child[0]);
bool is_fully_qualified;
zend_string *name = zend_resolve_function_name(orig_name, args->child[0]->child[0]->attr, &is_fully_qualified);
- zend_ast_list *list = zend_ast_get_list(args->child[0]->child[1]);
- zval *zv = zend_ast_get_zval(args->child[1]);
+ const zend_ast_list *list = zend_ast_get_list(args->child[0]->child[1]);
+ const zval *zv = zend_ast_get_zval(args->child[1]);
znode first;
if (zend_string_equals_literal_ci(name, "func_get_args")
@@ -4632,7 +4855,7 @@ static zend_result zend_compile_func_array_slice(znode *result, zend_ast_list *a
}
/* }}} */
-static uint32_t find_frameless_function_offset(uint32_t arity, void *handler)
+static uint32_t find_frameless_function_offset(uint32_t arity, const void *handler)
{
void **handlers = zend_flf_handlers;
void **current = handlers;
@@ -4646,16 +4869,12 @@ static uint32_t find_frameless_function_offset(uint32_t arity, void *handler)
return (uint32_t)-1;
}
-static const zend_frameless_function_info *find_frameless_function_info(zend_ast_list *args, zend_function *fbc, uint32_t type)
+static const zend_frameless_function_info *find_frameless_function_info(const zend_ast_list *args, const zend_function *fbc, uint32_t type)
{
if (zend_execute_internal) {
return NULL;
}
- if (type != BP_VAR_R) {
- return NULL;
- }
-
if (ZEND_USER_CODE(fbc->type)) {
return NULL;
}
@@ -4687,9 +4906,9 @@ static const zend_frameless_function_info *find_frameless_function_info(zend_ast
return NULL;
}
-static uint32_t zend_compile_frameless_icall_ex(znode *result, zend_ast_list *args, zend_function *fbc, const zend_frameless_function_info *frameless_function_info, uint32_t type)
+static uint32_t zend_compile_frameless_icall_ex(znode *result, const zend_ast_list *args, const zend_function *fbc, const zend_frameless_function_info *frameless_function_info, uint32_t type)
{
- int lineno = CG(zend_lineno);
+ uint32_t lineno = CG(zend_lineno);
uint32_t num_args = frameless_function_info->num_args;
uint32_t offset = find_frameless_function_offset(num_args, frameless_function_info->handler);
znode arg_zvs[3];
@@ -4697,7 +4916,7 @@ static uint32_t zend_compile_frameless_icall_ex(znode *result, zend_ast_list *ar
if (i < args->children) {
zend_compile_expr(&arg_zvs[i], args->child[i]);
} else {
- zend_internal_arg_info *arg_info = (zend_internal_arg_info *)&fbc->common.arg_info[i];
+ const zend_arg_info *arg_info = &fbc->common.arg_info[i];
arg_zvs[i].op_type = IS_CONST;
if (zend_get_default_from_internal_arg_info(&arg_zvs[i].u.constant, arg_info) == FAILURE) {
ZEND_UNREACHABLE();
@@ -4721,7 +4940,7 @@ static uint32_t zend_compile_frameless_icall_ex(znode *result, zend_ast_list *ar
return opnum;
}
-static uint32_t zend_compile_frameless_icall(znode *result, zend_ast_list *args, zend_function *fbc, uint32_t type)
+static uint32_t zend_compile_frameless_icall(znode *result, const zend_ast_list *args, const zend_function *fbc, uint32_t type)
{
const zend_frameless_function_info *frameless_function_info = find_frameless_function_info(args, fbc, type);
if (!frameless_function_info) {
@@ -4731,12 +4950,12 @@ static uint32_t zend_compile_frameless_icall(znode *result, zend_ast_list *args,
return zend_compile_frameless_icall_ex(result, args, fbc, frameless_function_info, type);
}
-static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast, uint32_t lineno, uint32_t type) /* {{{ */
+static void zend_compile_ns_call(znode *result, const znode *name_node, zend_ast *args_ast, uint32_t lineno, uint32_t type) /* {{{ */
{
int name_constants = zend_add_ns_func_name_literal(Z_STR(name_node->u.constant));
/* Find frameless function with same name. */
- zend_function *frameless_function = NULL;
+ const zend_function *frameless_function = NULL;
if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT
&& !zend_args_contain_unpack_or_named(zend_ast_get_list(args_ast))
/* Avoid blowing up op count with nested frameless branches. */
@@ -4766,7 +4985,7 @@ static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args
opline->op2_type = IS_CONST;
opline->op2.constant = name_constants;
opline->result.num = zend_alloc_cache_slot();
- zend_compile_call_common(result, args_ast, NULL, lineno);
+ zend_compile_call_common(result, args_ast, NULL, lineno, type);
/* Compile frameless call. */
if (frameless_function_info) {
@@ -4927,7 +5146,7 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args)
case 'd':
zend_emit_op_tmp(&elements[placeholder_count], ZEND_CAST, &elements[placeholder_count], NULL)->extended_value = IS_LONG;
break;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
if (rope_elements == 0) {
@@ -4961,7 +5180,62 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args)
return SUCCESS;
}
-static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args)
+static zend_result zend_compile_func_printf(znode *result, zend_ast_list *args) /* {{{ */
+{
+ /* Special case: printf with a single constant string argument and no format specifiers.
+ * In this case, just emit ECHO and return the string length if needed. */
+ if (args->children == 1) {
+ zend_eval_const_expr(&args->child[0]);
+ if (args->child[0]->kind != ZEND_AST_ZVAL) {
+ return FAILURE;
+ }
+ zval *format_string = zend_ast_get_zval(args->child[0]);
+ if (Z_TYPE_P(format_string) != IS_STRING) {
+ return FAILURE;
+ }
+ /* Check if there are any format specifiers */
+ if (!memchr(Z_STRVAL_P(format_string), '%', Z_STRLEN_P(format_string))) {
+ /* No format specifiers - just emit ECHO and return string length */
+ znode format_node;
+ zend_compile_expr(&format_node, args->child[0]);
+ zend_emit_op(NULL, ZEND_ECHO, &format_node, NULL);
+
+ /* Return the string length as a constant if the result is used */
+ result->op_type = IS_CONST;
+ ZVAL_LONG(&result->u.constant, Z_STRLEN_P(format_string));
+ return SUCCESS;
+ }
+ }
+
+ /* Fall back to sprintf optimization for format strings with specifiers */
+ znode rope_result;
+ if (zend_compile_func_sprintf(&rope_result, args) != SUCCESS) {
+ return FAILURE;
+ }
+
+ /* printf() returns the amount of bytes written, so just an ECHO of the
+ * resulting sprintf() optimisation might not be enough. At this early
+ * stage we can't detect if the result is actually used, so we just emit
+ * the opcodes and let them be cleaned up by the dead code elimination
+ * pass in the Zend Optimizer if the result of the printf() is in fact
+ * unused */
+ znode copy;
+ if (rope_result.op_type != IS_CONST) {
+ /* Note: ZEND_COPY_TMP is only valid for TMPVAR. */
+ ZEND_ASSERT(rope_result.op_type == IS_TMP_VAR);
+ zend_emit_op_tmp(©, ZEND_COPY_TMP, &rope_result, NULL);
+ zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL);
+ zend_emit_op_tmp(result, ZEND_STRLEN, ©, NULL);
+ } else {
+ zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL);
+ result->op_type = IS_CONST;
+ ZVAL_LONG(&result->u.constant, Z_STRLEN(rope_result.u.constant));
+ }
+
+ return SUCCESS;
+}
+
+static zend_result zend_compile_func_clone(znode *result, const zend_ast_list *args)
{
znode arg_node;
@@ -4975,7 +5249,92 @@ static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args)
return SUCCESS;
}
-static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
+static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *args, zend_string *lcname, uint32_t lineno) /* {{{ */
+{
+ /* Bail out if we do not have exactly two parameters. */
+ if (args->children != 2) {
+ return FAILURE;
+ }
+
+ zend_ast *callback = args->child[0];
+ 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_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;
+
+ znode array;
+ zend_compile_expr(&array, args->child[1]);
+ /* array is an argument to both ZEND_TYPE_ASSERT and to ZEND_FE_RESET_R. */
+ if (array.op_type == IS_CONST) {
+ Z_TRY_ADDREF(array.u.constant);
+ }
+
+ /* Verify that the input array actually is an array. */
+ znode name;
+ name.op_type = IS_CONST;
+ ZVAL_STR_COPY(&name.u.constant, lcname);
+ opline = zend_emit_op(NULL, ZEND_TYPE_ASSERT, &name, &array);
+ opline->lineno = lineno;
+ opline->extended_value = (2 << 16) | IS_ARRAY;
+ const zval *fbc_zv = zend_hash_find(CG(function_table), lcname);
+ const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val);
+ Z_EXTRA_P(CT_CONSTANT(opline->op1)) = fbc_bucket - CG(function_table)->arData;
+
+ /* Initialize the result array. */
+ zend_emit_op_tmp(result, ZEND_INIT_ARRAY, NULL, NULL);
+
+ /* foreach loop starts here. */
+ znode key;
+
+ uint32_t opnum_reset = get_next_op_number();
+ znode reset_node;
+ zend_emit_op(&reset_node, ZEND_FE_RESET_R, &array, NULL);
+ zend_begin_loop(ZEND_FE_FREE, &reset_node, false);
+ uint32_t opnum_fetch = get_next_op_number();
+ zend_emit_op_tmp(&key, ZEND_FE_FETCH_R, &reset_node, &value);
+
+ /* loop body */
+ znode call_result;
+ switch (callback->kind) {
+ case ZEND_AST_CALL:
+ zend_compile_expr(&call_result, zend_ast_create(ZEND_AST_CALL, callback->child[0], call_args));
+ break;
+ case ZEND_AST_STATIC_CALL:
+ zend_compile_expr(&call_result, zend_ast_create(ZEND_AST_STATIC_CALL, callback->child[0], callback->child[1], call_args));
+ break;
+ }
+ opline = zend_emit_op(NULL, ZEND_ADD_ARRAY_ELEMENT, &call_result, &key);
+ SET_NODE(opline->result, result);
+ /* end loop body */
+
+ zend_emit_jump(opnum_fetch);
+
+ uint32_t opnum_loop_end = get_next_op_number();
+ opline = &CG(active_op_array)->opcodes[opnum_reset];
+ opline->op2.opline_num = opnum_loop_end;
+ opline = &CG(active_op_array)->opcodes[opnum_fetch];
+ opline->extended_value = opnum_loop_end;
+
+ zend_end_loop(opnum_fetch, &reset_node);
+ zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
+
+ return SUCCESS;
+}
+
+static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, uint32_t type, uint32_t lineno) /* {{{ */
{
if (zend_string_equals_literal(lcname, "strlen")) {
return zend_compile_func_strlen(result, args);
@@ -5019,9 +5378,9 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
} else if (zend_string_equals_literal(lcname, "ord") && type == BP_VAR_R) {
return zend_compile_func_ord(result, args);
} else if (zend_string_equals_literal(lcname, "call_user_func_array")) {
- return zend_compile_func_cufa(result, args, lcname);
+ return zend_compile_func_cufa(result, args, lcname, type);
} else if (zend_string_equals_literal(lcname, "call_user_func")) {
- return zend_compile_func_cuf(result, args, lcname);
+ return zend_compile_func_cuf(result, args, lcname, type);
} else if (zend_string_equals_literal(lcname, "in_array")) {
return zend_compile_func_in_array(result, args);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_COUNT))
@@ -5043,14 +5402,18 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
return zend_compile_func_array_key_exists(result, args);
} else if (zend_string_equals_literal(lcname, "sprintf")) {
return zend_compile_func_sprintf(result, args);
+ } else if (zend_string_equals_literal(lcname, "printf")) {
+ return zend_compile_func_printf(result, args);
} else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_CLONE))) {
return zend_compile_func_clone(result, args);
+ } else if (zend_string_equals_literal(lcname, "array_map")) {
+ return zend_compile_func_array_map(result, args, lcname, lineno);
} else {
return FAILURE;
}
}
-static zend_result zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
+static zend_result zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, const zend_function *fbc, uint32_t type, uint32_t lineno) /* {{{ */
{
if (CG(compiler_options) & ZEND_COMPILE_NO_BUILTINS) {
return FAILURE;
@@ -5066,7 +5429,7 @@ static zend_result zend_try_compile_special_func(znode *result, zend_string *lcn
return FAILURE;
}
- if (zend_try_compile_special_func_ex(result, lcname, args, fbc, type) == SUCCESS) {
+ if (zend_try_compile_special_func_ex(result, lcname, args, type, lineno) == SUCCESS) {
return SUCCESS;
}
@@ -5079,7 +5442,7 @@ static const char *zend_get_cstring_from_property_hook_kind(zend_property_hook_k
return "get";
case ZEND_PROPERTY_HOOK_SET:
return "set";
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
@@ -5093,24 +5456,14 @@ static zend_string *zend_copy_unmangled_prop_name(zend_string *prop_name)
}
}
-static bool zend_compile_parent_property_hook_call(znode *result, zend_ast *ast, uint32_t type)
+static bool zend_compile_parent_property_hook_call(znode *result, const zend_ast *ast, uint32_t type)
{
ZEND_ASSERT(ast->kind == ZEND_AST_STATIC_CALL);
- zend_ast *class_ast = ast->child[0];
+ const zend_ast *class_ast = ast->child[0];
zend_ast *method_ast = ast->child[1];
- /* Recognize parent::$prop::get() pattern. */
- if (class_ast->kind != ZEND_AST_STATIC_PROP
- || (class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP)
- || class_ast->child[0]->kind != ZEND_AST_ZVAL
- || Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) != IS_STRING
- || zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) != ZEND_FETCH_CLASS_PARENT
- || class_ast->child[1]->kind != ZEND_AST_ZVAL
- || method_ast->kind != ZEND_AST_ZVAL
- || Z_TYPE_P(zend_ast_get_zval(method_ast)) != IS_STRING
- || (!zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get")
- && !zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set"))) {
+ if (!zend_ast_is_parent_hook_call(ast)) {
return false;
}
@@ -5152,13 +5505,12 @@ static bool zend_compile_parent_property_hook_call(znode *result, zend_ast *ast,
opline->op1.constant = zend_add_literal_string(&property_name);
opline->op2.num = hook_kind;
- zend_function *fbc = NULL;
- zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast));
+ zend_compile_call_common(result, args_ast, NULL, zend_ast_get_lineno(method_ast), BP_VAR_R);
return true;
}
-static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
+static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
zend_ast *args_ast = ast->child[1];
@@ -5168,7 +5520,7 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
if (name_ast->kind != ZEND_AST_ZVAL || Z_TYPE_P(zend_ast_get_zval(name_ast)) != IS_STRING) {
zend_compile_expr(&name_node, name_ast);
- zend_compile_dynamic_call(result, &name_node, args_ast, ast->lineno);
+ zend_compile_dynamic_call(result, &name_node, args_ast, ast->lineno, type);
return;
}
@@ -5177,7 +5529,7 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
if (runtime_resolution) {
if (zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "assert")
&& !is_callable_convert) {
- zend_compile_assert(result, zend_ast_get_list(args_ast), Z_STR(name_node.u.constant), NULL, ast->lineno);
+ zend_compile_assert(result, zend_ast_get_list(args_ast), Z_STR(name_node.u.constant), NULL, ast->lineno, type);
} else {
zend_compile_ns_call(result, &name_node, args_ast, ast->lineno, type);
}
@@ -5186,18 +5538,15 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}
{
- zval *name = &name_node.u.constant;
- zend_string *lcname;
- zend_function *fbc;
- zend_op *opline;
-
- lcname = zend_string_tolower(Z_STR_P(name));
+ const zval *name = &name_node.u.constant;
+ zend_string *lcname = zend_string_tolower(Z_STR_P(name));
zval *fbc_zv = zend_hash_find(CG(function_table), lcname);
- fbc = fbc_zv ? Z_PTR_P(fbc_zv) : NULL;
+ const zend_function *fbc = fbc_zv ? Z_PTR_P(fbc_zv) : NULL;
+ zend_op *opline;
/* Special assert() handling should apply independently of compiler flags. */
if (fbc && zend_string_equals_literal(lcname, "assert") && !is_callable_convert) {
- zend_compile_assert(result, zend_ast_get_list(args_ast), lcname, fbc, ast->lineno);
+ zend_compile_assert(result, zend_ast_get_list(args_ast), lcname, fbc, ast->lineno, type);
zend_string_release(lcname);
zval_ptr_dtor(&name_node.u.constant);
return;
@@ -5207,13 +5556,13 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
|| !fbc_is_finalized(fbc)
|| zend_compile_ignore_function(fbc, CG(active_op_array)->filename)) {
zend_string_release_ex(lcname, 0);
- zend_compile_dynamic_call(result, &name_node, args_ast, ast->lineno);
+ zend_compile_dynamic_call(result, &name_node, args_ast, ast->lineno, type);
return;
}
if (!is_callable_convert &&
zend_try_compile_special_func(result, lcname,
- zend_ast_get_list(args_ast), fbc, type) == SUCCESS
+ zend_ast_get_list(args_ast), fbc, type, ast->lineno) == SUCCESS
) {
zend_string_release_ex(lcname, 0);
zval_ptr_dtor(&name_node.u.constant);
@@ -5228,11 +5577,11 @@ static void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{
/* Store offset to function from symbol table in op2.extra. */
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
- Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val));
+ const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val);
Z_EXTRA_P(CT_CONSTANT(opline->op2)) = fbc_bucket - CG(function_table)->arData;
}
- zend_compile_call_common(result, args_ast, fbc, ast->lineno);
+ zend_compile_call_common(result, args_ast, fbc, ast->lineno, type);
}
}
/* }}} */
@@ -5245,7 +5594,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type
znode obj_node, method_node;
zend_op *opline;
- zend_function *fbc = NULL;
+ const zend_function *fbc = NULL;
bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL;
uint32_t short_circuiting_checkpoint = zend_short_circuiting_checkpoint();
@@ -5253,7 +5602,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type
if (this_guaranteed_exists()) {
obj_node.op_type = IS_UNUSED;
} else {
- zend_emit_op(&obj_node, ZEND_FETCH_THIS, NULL, NULL);
+ zend_emit_op_tmp(&obj_node, ZEND_FETCH_THIS, NULL, NULL);
}
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
@@ -5296,7 +5645,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type
}
}
- if (zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast))) {
+ if (zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast), type)) {
if (short_circuiting_checkpoint != zend_short_circuiting_checkpoint()) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot combine nullsafe operator with Closure creation");
@@ -5305,23 +5654,27 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type
}
/* }}} */
-static bool zend_is_constructor(zend_string *name) /* {{{ */
-{
- return zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME);
-}
-/* }}} */
-
-static zend_function *zend_get_compatible_func_or_null(zend_class_entry *ce, zend_string *lcname) /* {{{ */
+static bool is_func_accessible(const zend_function *fbc)
{
- zend_function *fbc = zend_hash_find_ptr(&ce->function_table, lcname);
- if (!fbc || (fbc->common.fn_flags & ZEND_ACC_PUBLIC) || ce == CG(active_class_entry)) {
- return fbc;
+ if ((fbc->common.fn_flags & ZEND_ACC_PUBLIC) || fbc->common.scope == CG(active_class_entry)) {
+ return true;
}
if (!(fbc->common.fn_flags & ZEND_ACC_PRIVATE)
&& (fbc->common.scope->ce_flags & ZEND_ACC_LINKED)
&& (!CG(active_class_entry) || (CG(active_class_entry)->ce_flags & ZEND_ACC_LINKED))
&& zend_check_protected(zend_get_function_root_class(fbc), CG(active_class_entry))) {
+ return true;
+ }
+
+ return false;
+}
+
+static const zend_function *zend_get_compatible_func_or_null(const zend_class_entry *ce, zend_string *lcname) /* {{{ */
+{
+ const zend_function *fbc = zend_hash_find_ptr(&ce->function_table, lcname);
+
+ if (!fbc || is_func_accessible(fbc)) {
return fbc;
}
@@ -5337,7 +5690,7 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
znode class_node, method_node;
zend_op *opline;
- zend_function *fbc = NULL;
+ const zend_function *fbc = NULL;
if (zend_compile_parent_property_hook_call(result, ast, type)) {
return;
@@ -5401,11 +5754,11 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type
}
}
- zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast));
+ zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast), type);
}
/* }}} */
-static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel);
+static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool toplevel);
static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
{
@@ -5417,23 +5770,47 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
if (class_ast->kind == ZEND_AST_CLASS) {
/* anon class declaration */
- zend_compile_class_decl(&class_node, class_ast, 0);
+ zend_compile_class_decl(&class_node, class_ast, false);
} else {
zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
}
- opline = zend_emit_op(result, ZEND_NEW, NULL, NULL);
+ opline = zend_emit_op_tmp(result, ZEND_NEW, NULL, NULL);
- if (class_node.op_type == IS_CONST) {
- opline->op1_type = IS_CONST;
- opline->op1.constant = zend_add_class_name_literal(
- Z_STR(class_node.u.constant));
+ zend_set_class_name_op1(opline, &class_node);
+
+ if (opline->op1_type == IS_CONST) {
opline->op2.num = zend_alloc_cache_slot();
- } else {
- SET_NODE(opline->op1, &class_node);
}
- zend_compile_call_common(&ctor_result, args_ast, NULL, ast->lineno);
+ zend_class_entry *ce = NULL;
+ if (opline->op1_type == IS_CONST) {
+ zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1);
+ ce = zend_hash_find_ptr(CG(class_table), lcname);
+ if (ce) {
+ if (zend_compile_ignore_class(ce, CG(active_op_array)->filename)) {
+ ce = NULL;
+ }
+ } else if (CG(active_class_entry)
+ && zend_string_equals_ci(CG(active_class_entry)->name, lcname)) {
+ ce = CG(active_class_entry);
+ }
+ } else if (opline->op1_type == IS_UNUSED
+ && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
+ && zend_is_scope_known()) {
+ ce = CG(active_class_entry);
+ }
+
+
+ const zend_function *fbc = NULL;
+ if (ce
+ && ce->default_object_handlers->get_constructor == zend_std_get_constructor
+ && ce->constructor
+ && is_func_accessible(ce->constructor)) {
+ fbc = ce->constructor;
+ }
+
+ zend_compile_call_common(&ctor_result, args_ast, fbc, ast->lineno, BP_VAR_R);
zend_do_free(&ctor_result);
}
/* }}} */
@@ -5552,7 +5929,7 @@ static void zend_compile_static_var(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_unset(zend_ast *ast) /* {{{ */
+static void zend_compile_unset(const zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
znode var_node;
@@ -5582,7 +5959,7 @@ static void zend_compile_unset(zend_ast *ast) /* {{{ */
} else if (zend_try_compile_cv(&var_node, var_ast, BP_VAR_UNSET) == SUCCESS) {
opline = zend_emit_op(NULL, ZEND_UNSET_CV, &var_node, NULL);
} else {
- opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET, 0);
+ opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET, false);
opline->opcode = ZEND_UNSET_VAR;
}
return;
@@ -5592,25 +5969,25 @@ static void zend_compile_unset(zend_ast *ast) /* {{{ */
return;
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
- opline = zend_compile_prop(NULL, var_ast, BP_VAR_UNSET, 0);
+ opline = zend_compile_prop(NULL, var_ast, BP_VAR_UNSET, false);
opline->opcode = ZEND_UNSET_OBJ;
return;
case ZEND_AST_STATIC_PROP:
- opline = zend_compile_static_prop(NULL, var_ast, BP_VAR_UNSET, 0, 0);
+ opline = zend_compile_static_prop(NULL, var_ast, BP_VAR_UNSET, false, false);
opline->opcode = ZEND_UNSET_STATIC_PROP;
return;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
/* }}} */
static bool zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value) /* {{{ */
{
- zend_loop_var *base;
+ const zend_loop_var *base;
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
if (!loop_var) {
- return 1;
+ return true;
}
base = zend_stack_base(&CG(loop_var_stack));
for (; loop_var >= base; loop_var--) {
@@ -5661,11 +6038,11 @@ static bool zend_handle_loops_and_finally(znode *return_value) /* {{{ */
static bool zend_has_finally_ex(zend_long depth) /* {{{ */
{
- zend_loop_var *base;
+ const zend_loop_var *base;
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
if (!loop_var) {
- return 0;
+ return false;
}
base = zend_stack_base(&CG(loop_var_stack));
for (; loop_var >= base; loop_var--) {
@@ -5681,7 +6058,7 @@ static bool zend_has_finally_ex(zend_long depth) /* {{{ */
depth--;
}
}
- return 0;
+ return false;
}
/* }}} */
@@ -5691,7 +6068,7 @@ static bool zend_has_finally(void) /* {{{ */
}
/* }}} */
-static void zend_compile_return(zend_ast *ast) /* {{{ */
+static void zend_compile_return(const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
bool is_generator = (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0;
@@ -5702,19 +6079,29 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
if (is_generator) {
/* For generators the by-ref flag refers to yields, not returns */
- by_ref = 0;
+ by_ref = false;
}
if (!expr_ast) {
expr_node.op_type = IS_CONST;
ZVAL_NULL(&expr_node.u.constant);
- } else if (by_ref && zend_is_variable(expr_ast)) {
+ } else if (by_ref && zend_is_variable_or_call(expr_ast)) {
zend_assert_not_short_circuited(expr_ast);
- zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
+ zend_compile_var(&expr_node, expr_ast, BP_VAR_W, true);
} else {
zend_compile_expr(&expr_node, expr_ast);
}
+ if (expr_ast) {
+ if (CG(active_class_entry) != NULL) {
+ if (zend_is_constructor(CG(active_op_array)->function_name)) {
+ zend_error(E_DEPRECATED, "Returning a value from a constructor is deprecated");
+ } else if (zend_string_equals_literal_ci(CG(active_op_array)->function_name, ZEND_DESTRUCTOR_FUNC_NAME)) {
+ zend_error(E_DEPRECATED, "Returning a value from a destructor is deprecated");
+ }
+ }
+ }
+
if ((CG(active_op_array)->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)
&& (expr_node.op_type == IS_CV || (by_ref && expr_node.op_type == IS_VAR))
&& zend_has_finally()) {
@@ -5729,7 +6116,7 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
/* Generator return types are handled separately */
if (!is_generator && (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_emit_return_type_check(
- expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0);
+ expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, false);
}
uint32_t opnum_before_finally = get_next_op_number();
@@ -5743,7 +6130,7 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
&& !is_generator
&& (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_emit_return_type_check(
- expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0);
+ expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, false);
}
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
@@ -5759,7 +6146,7 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_void_cast(znode *result, zend_ast *ast)
+static void zend_compile_void_cast(znode *result, const zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
@@ -5779,7 +6166,7 @@ static void zend_compile_void_cast(znode *result, zend_ast *ast)
}
}
-static void zend_compile_echo(zend_ast *ast) /* {{{ */
+static void zend_compile_echo(const zend_ast *ast) /* {{{ */
{
zend_op *opline;
zend_ast *expr_ast = ast->child[0];
@@ -5792,7 +6179,7 @@ static void zend_compile_echo(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_throw(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
@@ -5809,7 +6196,7 @@ static void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_break_continue(zend_ast *ast) /* {{{ */
+static void zend_compile_break_continue(const zend_ast *ast) /* {{{ */
{
zend_ast *depth_ast = ast->child[0];
@@ -5819,7 +6206,7 @@ static void zend_compile_break_continue(zend_ast *ast) /* {{{ */
ZEND_ASSERT(ast->kind == ZEND_AST_BREAK || ast->kind == ZEND_AST_CONTINUE);
if (depth_ast) {
- zval *depth_zv;
+ const zval *depth_zv;
if (depth_ast->kind != ZEND_AST_ZVAL) {
zend_error_noreturn(E_COMPILE_ERROR, "'%s' operator with non-integer operand "
"is no longer supported", ast->kind == ZEND_AST_BREAK ? "break" : "continue");
@@ -5889,7 +6276,7 @@ static void zend_compile_break_continue(zend_ast *ast) /* {{{ */
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
{
zend_label *dest;
- int current, remove_oplines = opline->op1.num;
+ int remove_oplines = opline->op1.num;
zval *label;
uint32_t opnum = opline - op_array->opcodes;
@@ -5906,7 +6293,7 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
zval_ptr_dtor_str(label);
ZVAL_NULL(label);
- current = opline->extended_value;
+ uint32_t current = opline->extended_value;
for (; current != dest->brk_cont; current = CG(context).brk_cont_array[current].parent) {
if (current == -1) {
CG(in_compilation) = 1;
@@ -5920,7 +6307,7 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
}
for (current = 0; current < op_array->last_try_catch; ++current) {
- zend_try_catch_element *elem = &op_array->try_catch_array[current];
+ const zend_try_catch_element *elem = &op_array->try_catch_array[current];
if (elem->try_op > opnum) {
break;
}
@@ -5947,7 +6334,7 @@ void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
}
/* }}} */
-static void zend_compile_goto(zend_ast *ast) /* {{{ */
+static void zend_compile_goto(const zend_ast *ast) /* {{{ */
{
zend_ast *label_ast = ast->child[0];
znode label_node;
@@ -5964,7 +6351,7 @@ static void zend_compile_goto(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_label(zend_ast *ast) /* {{{ */
+static void zend_compile_label(const zend_ast *ast) /* {{{ */
{
zend_string *label = zend_ast_get_str(ast->child[0]);
zend_label dest;
@@ -5983,7 +6370,7 @@ static void zend_compile_label(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_while(zend_ast *ast) /* {{{ */
+static void zend_compile_while(const zend_ast *ast) /* {{{ */
{
zend_ast *cond_ast = ast->child[0];
zend_ast *stmt_ast = ast->child[1];
@@ -5992,7 +6379,7 @@ static void zend_compile_while(zend_ast *ast) /* {{{ */
opnum_jmp = zend_emit_jump(0);
- zend_begin_loop(ZEND_NOP, NULL, 0);
+ zend_begin_loop(ZEND_NOP, NULL, false);
opnum_start = get_next_op_number();
zend_compile_stmt(stmt_ast);
@@ -6007,7 +6394,7 @@ static void zend_compile_while(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_do_while(zend_ast *ast) /* {{{ */
+static void zend_compile_do_while(const zend_ast *ast) /* {{{ */
{
zend_ast *stmt_ast = ast->child[0];
zend_ast *cond_ast = ast->child[1];
@@ -6015,7 +6402,7 @@ static void zend_compile_do_while(zend_ast *ast) /* {{{ */
znode cond_node;
uint32_t opnum_start, opnum_cond;
- zend_begin_loop(ZEND_NOP, NULL, 0);
+ zend_begin_loop(ZEND_NOP, NULL, false);
opnum_start = get_next_op_number();
zend_compile_stmt(stmt_ast);
@@ -6031,7 +6418,7 @@ static void zend_compile_do_while(zend_ast *ast) /* {{{ */
static void zend_compile_for_expr_list(znode *result, zend_ast *ast) /* {{{ */
{
- zend_ast_list *list;
+ const zend_ast_list *list;
uint32_t i;
result->op_type = IS_CONST;
@@ -6057,7 +6444,7 @@ static void zend_compile_for_expr_list(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_for(zend_ast *ast) /* {{{ */
+static void zend_compile_for(const zend_ast *ast) /* {{{ */
{
zend_ast *init_ast = ast->child[0];
zend_ast *cond_ast = ast->child[1];
@@ -6072,7 +6459,7 @@ static void zend_compile_for(zend_ast *ast) /* {{{ */
opnum_jmp = zend_emit_jump(0);
- zend_begin_loop(ZEND_NOP, NULL, 0);
+ zend_begin_loop(ZEND_NOP, NULL, false);
opnum_start = get_next_op_number();
zend_compile_stmt(stmt_ast);
@@ -6098,7 +6485,8 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
zend_ast *key_ast = ast->child[2];
zend_ast *stmt_ast = ast->child[3];
bool by_ref = value_ast->kind == ZEND_AST_REF;
- bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast);
+ bool is_variable = (zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast))
+ || zend_is_call(expr_ast);
znode expr_node, reset_node, value_node, key_node;
zend_op *opline;
@@ -6118,11 +6506,11 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
}
if (value_ast->kind == ZEND_AST_ARRAY && zend_propagate_list_refs(value_ast)) {
- by_ref = 1;
+ by_ref = true;
}
if (by_ref && is_variable) {
- zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1);
+ zend_compile_var(&expr_node, expr_ast, BP_VAR_W, true);
} else {
zend_compile_expr(&expr_node, expr_ast);
}
@@ -6133,8 +6521,12 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
opnum_reset = get_next_op_number();
opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
+ if (!by_ref) {
+ opline->result_type = IS_TMP_VAR;
+ reset_node.op_type = IS_TMP_VAR;
+ }
- zend_begin_loop(ZEND_FE_FREE, &reset_node, 0);
+ zend_begin_loop(ZEND_FE_FREE, &reset_node, false);
opnum_fetch = get_next_op_number();
opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL);
@@ -6145,11 +6537,11 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
zend_try_compile_cv(&value_node, value_ast, BP_VAR_R) == SUCCESS) {
SET_NODE(opline->op2, &value_node);
} else {
- opline->op2_type = IS_VAR;
+ opline->op2_type = by_ref ? IS_VAR : IS_TMP_VAR;
opline->op2.var = get_temporary_variable();
GET_NODE(&value_node, opline->op2);
if (value_ast->kind == ZEND_AST_ARRAY) {
- zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr);
+ zend_compile_list_assign(NULL, value_ast, &value_node, value_ast->attr, BP_VAR_R);
} else if (by_ref) {
zend_emit_assign_ref_znode(value_ast, &value_node);
} else {
@@ -6185,7 +6577,7 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */
static void zend_compile_if(zend_ast *ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
uint32_t *jmp_opnums = NULL;
@@ -6194,7 +6586,7 @@ static void zend_compile_if(zend_ast *ast) /* {{{ */
}
for (i = 0; i < list->children; ++i) {
- zend_ast *elem_ast = list->child[i];
+ const zend_ast *elem_ast = list->child[i];
zend_ast *cond_ast = elem_ast->child[0];
zend_ast *stmt_ast = elem_ast->child[1];
@@ -6235,13 +6627,13 @@ static void zend_compile_if(zend_ast *ast) /* {{{ */
}
/* }}} */
-static uint8_t determine_switch_jumptable_type(zend_ast_list *cases) {
+static uint8_t determine_switch_jumptable_type(const zend_ast_list *cases) {
uint32_t i;
uint8_t common_type = IS_UNDEF;
for (i = 0; i < cases->children; i++) {
zend_ast *case_ast = cases->child[i];
zend_ast **cond_ast = &case_ast->child[0];
- zval *cond_zv;
+ const zval *cond_zv;
if (!case_ast->child[0]) {
/* Skip default clause */
continue;
@@ -6276,9 +6668,9 @@ static uint8_t determine_switch_jumptable_type(zend_ast_list *cases) {
return common_type;
}
-static bool should_use_jumptable(zend_ast_list *cases, uint8_t jumptable_type) {
+static bool should_use_jumptable(const zend_ast_list *cases, uint8_t jumptable_type) {
if (CG(compiler_options) & ZEND_COMPILE_NO_JUMPTABLES) {
- return 0;
+ return false;
}
/* Thresholds are chosen based on when the average switch time for equidistributed
@@ -6297,7 +6689,7 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_ast_list *cases = zend_ast_get_list(ast->child[1]);
uint32_t i;
- bool has_default_case = 0;
+ bool has_default_case = false;
znode expr_node, case_node;
zend_op *opline;
@@ -6307,7 +6699,7 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
- zend_begin_loop(ZEND_FREE, &expr_node, 1);
+ zend_begin_loop(ZEND_FREE, &expr_node, true);
case_node.op_type = IS_TMP_VAR;
case_node.u.op.var = get_temporary_variable();
@@ -6347,7 +6739,7 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR,
"Switch statements may only contain one default clause");
}
- has_default_case = 1;
+ has_default_case = true;
continue;
}
@@ -6430,26 +6822,26 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */
}
/* }}} */
-static uint32_t count_match_conds(zend_ast_list *arms)
+static uint32_t count_match_conds(const zend_ast_list *arms)
{
uint32_t num_conds = 0;
for (uint32_t i = 0; i < arms->children; i++) {
- zend_ast *arm_ast = arms->child[i];
+ const zend_ast *arm_ast = arms->child[i];
if (arm_ast->child[0] == NULL) {
continue;
}
- zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
+ const zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
num_conds += conds->children;
}
return num_conds;
}
-static bool can_match_use_jumptable(zend_ast_list *arms) {
+static bool can_match_use_jumptable(const zend_ast_list *arms) {
for (uint32_t i = 0; i < arms->children; i++) {
- zend_ast *arm_ast = arms->child[i];
+ const zend_ast *arm_ast = arms->child[i];
if (!arm_ast->child[0]) {
/* Skip default arm */
continue;
@@ -6461,17 +6853,17 @@ static bool can_match_use_jumptable(zend_ast_list *arms) {
zend_eval_const_expr(cond_ast);
if ((*cond_ast)->kind != ZEND_AST_ZVAL) {
- return 0;
+ return false;
}
- zval *cond_zv = zend_ast_get_zval(*cond_ast);
+ const zval *cond_zv = zend_ast_get_zval(*cond_ast);
if (Z_TYPE_P(cond_zv) != IS_LONG && Z_TYPE_P(cond_zv) != IS_STRING) {
- return 0;
+ return false;
}
}
}
- return 1;
+ return true;
}
static bool zend_is_pipe_optimizable_callable_name(zend_ast *ast)
@@ -6481,14 +6873,83 @@ static bool zend_is_pipe_optimizable_callable_name(zend_ast *ast)
* pipe optimization that uses a temporary znode for the reference elimination.
* Therefore, disable the optimization for assert.
* Note that "assert" as a name is always treated as fully qualified. */
- zend_string *str = zend_ast_get_str(ast);
- return !zend_string_equals_literal_ci(str, "assert");
+ return !zend_string_equals_literal_ci(zend_ast_get_str(ast), "assert");
}
return true;
}
-static void zend_compile_pipe(znode *result, zend_ast *ast)
+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];
zend_ast *callable_ast = ast->child[1];
@@ -6512,29 +6973,35 @@ static void zend_compile_pipe(znode *result, zend_ast *ast)
}
/* 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,
@@ -6543,14 +7010,14 @@ static void zend_compile_pipe(znode *result, zend_ast *ast)
zend_do_extended_stmt(&operand_result);
- zend_compile_expr(result, fcall_ast);
+ zend_compile_var(result, fcall_ast, type, /* by_ref */ false);
}
static void zend_compile_match(znode *result, zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
zend_ast_list *arms = zend_ast_get_list(ast->child[1]);
- bool has_default_arm = 0;
+ bool has_default_arm = false;
uint32_t opnum_match = (uint32_t)-1;
znode expr_node;
@@ -6575,7 +7042,7 @@ static void zend_compile_match(znode *result, zend_ast *ast)
zend_error_noreturn(E_COMPILE_ERROR,
"Match expressions may only contain one default arm");
}
- has_default_arm = 1;
+ has_default_arm = true;
}
}
@@ -6628,7 +7095,7 @@ static void zend_compile_match(znode *result, zend_ast *ast)
opnum_default_jmp = zend_emit_jump(0);
}
- bool is_first_case = 1;
+ bool is_first_case = true;
uint32_t cond_count = 0;
uint32_t *jmp_end_opnums = safe_emalloc(sizeof(uint32_t), arms->children, 0);
@@ -6700,7 +7167,7 @@ static void zend_compile_match(znode *result, zend_ast *ast)
if (is_first_case) {
zend_emit_op_tmp(result, ZEND_QM_ASSIGN, &body_node, NULL);
- is_first_case = 0;
+ is_first_case = false;
} else {
zend_op *opline_qm_assign = zend_emit_op(NULL, ZEND_QM_ASSIGN, &body_node, NULL);
SET_NODE(opline_qm_assign->result, result);
@@ -6732,10 +7199,10 @@ static void zend_compile_match(znode *result, zend_ast *ast)
efree(jmp_end_opnums);
}
-static void zend_compile_try(zend_ast *ast) /* {{{ */
+static void zend_compile_try(const zend_ast *ast) /* {{{ */
{
zend_ast *try_ast = ast->child[0];
- zend_ast_list *catches = zend_ast_get_list(ast->child[1]);
+ const zend_ast_list *catches = zend_ast_get_list(ast->child[1]);
zend_ast *finally_ast = ast->child[2];
uint32_t i, j;
@@ -6786,8 +7253,8 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
}
for (i = 0; i < catches->children; ++i) {
- zend_ast *catch_ast = catches->child[i];
- zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
+ const zend_ast *catch_ast = catches->child[i];
+ const zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
zend_ast *var_ast = catch_ast->child[1];
zend_ast *stmt_ast = catch_ast->child[2];
zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL;
@@ -6909,18 +7376,18 @@ static void zend_compile_try(zend_ast *ast) /* {{{ */
/* Encoding declarations must already be handled during parsing */
bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */
{
- zend_ast_list *declares = zend_ast_get_list(ast);
+ const zend_ast_list *declares = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < declares->children; ++i) {
- zend_ast *declare_ast = declares->child[i];
+ const zend_ast *declare_ast = declares->child[i];
zend_ast *name_ast = declare_ast->child[0];
zend_ast *value_ast = declare_ast->child[1];
- zend_string *name = zend_ast_get_str(name_ast);
+ const zend_string *name = zend_ast_get_str(name_ast);
if (zend_string_equals_literal_ci(name, "encoding")) {
if (value_ast->kind != ZEND_AST_ZVAL) {
zend_throw_exception(zend_ce_compile_error, "Encoding must be a literal", 0);
- return 0;
+ return false;
}
if (CG(multibyte)) {
@@ -6954,15 +7421,15 @@ bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */
}
}
- return 1;
+ return true;
}
/* }}} */
/* Check whether this is the first statement, not counting declares. */
-static zend_result zend_is_first_statement(zend_ast *ast, bool allow_nop) /* {{{ */
+static zend_result zend_is_first_statement(const zend_ast *ast, bool allow_nop) /* {{{ */
{
uint32_t i = 0;
- zend_ast_list *file_ast = zend_ast_get_list(CG(ast));
+ const zend_ast_list *file_ast = zend_ast_get_list(CG(ast));
while (i < file_ast->children) {
if (file_ast->child[i] == ast) {
@@ -6980,9 +7447,9 @@ static zend_result zend_is_first_statement(zend_ast *ast, bool allow_nop) /* {{{
}
/* }}} */
-static void zend_compile_declare(zend_ast *ast) /* {{{ */
+static void zend_compile_declare(const zend_ast *ast) /* {{{ */
{
- zend_ast_list *declares = zend_ast_get_list(ast->child[0]);
+ const zend_ast_list *declares = zend_ast_get_list(ast->child[0]);
zend_ast *stmt_ast = ast->child[1];
zend_declarables orig_declarables = FC(declarables);
uint32_t i;
@@ -7004,7 +7471,7 @@ static void zend_compile_declare(zend_ast *ast) /* {{{ */
zval_ptr_dtor_nogc(&value_zv);
} else if (zend_string_equals_literal_ci(name, "encoding")) {
- if (FAILURE == zend_is_first_statement(ast, /* allow_nop */ 0)) {
+ if (FAILURE == zend_is_first_statement(ast, /* allow_nop */ false)) {
zend_error_noreturn(E_COMPILE_ERROR, "Encoding declaration pragma must be "
"the very first statement in the script");
}
@@ -7046,7 +7513,7 @@ static void zend_compile_declare(zend_ast *ast) /* {{{ */
static void zend_compile_stmt_list(zend_ast *ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; ++i) {
zend_compile_stmt(list->child[i]);
@@ -7172,9 +7639,9 @@ static void zend_are_intersection_types_redundant(const zend_type left_type, con
{
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(left_type));
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(right_type));
- zend_type_list *l_type_list = ZEND_TYPE_LIST(left_type);
- zend_type_list *r_type_list = ZEND_TYPE_LIST(right_type);
- zend_type_list *smaller_type_list, *larger_type_list;
+ const zend_type_list *l_type_list = ZEND_TYPE_LIST(left_type);
+ const zend_type_list *r_type_list = ZEND_TYPE_LIST(right_type);
+ const zend_type_list *smaller_type_list, *larger_type_list;
bool flipped = false;
if (r_type_list->num_types < l_type_list->num_types) {
@@ -7188,15 +7655,15 @@ static void zend_are_intersection_types_redundant(const zend_type left_type, con
unsigned int sum = 0;
const zend_type *outer_type;
- ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type)
+ ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type) {
const zend_type *inner_type;
- ZEND_TYPE_LIST_FOREACH(larger_type_list, inner_type)
+ ZEND_TYPE_LIST_FOREACH(larger_type_list, inner_type) {
if (zend_string_equals_ci(ZEND_TYPE_NAME(*inner_type), ZEND_TYPE_NAME(*outer_type))) {
sum++;
break;
}
- ZEND_TYPE_LIST_FOREACH_END();
- ZEND_TYPE_LIST_FOREACH_END();
+ } ZEND_TYPE_LIST_FOREACH_END();
+ } ZEND_TYPE_LIST_FOREACH_END();
if (sum == smaller_type_list->num_types) {
zend_string *smaller_type_str;
@@ -7224,14 +7691,14 @@ static void zend_is_intersection_type_redundant_by_single_type(const zend_type i
ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(single_type));
const zend_type *single_intersection_type = NULL;
- ZEND_TYPE_FOREACH(intersection_type, single_intersection_type)
+ ZEND_TYPE_FOREACH(intersection_type, single_intersection_type) {
if (zend_string_equals_ci(ZEND_TYPE_NAME(*single_intersection_type), ZEND_TYPE_NAME(single_type))) {
zend_string *single_type_str = zend_type_to_string(single_type);
zend_string *complete_type = zend_type_to_string(intersection_type);
zend_error_noreturn(E_COMPILE_ERROR, "Type %s is redundant as it is more restrictive than type %s",
ZSTR_VAL(complete_type), ZSTR_VAL(single_type_str));
}
- ZEND_TYPE_FOREACH_END();
+ } ZEND_TYPE_FOREACH_END();
}
/* Used by both intersection and union types prior to transforming the type list to a full zend_type */
@@ -7264,7 +7731,7 @@ static zend_type zend_compile_typename_ex(
}
if (ast->kind == ZEND_AST_TYPE_UNION) {
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
zend_type_list *type_list;
bool is_composite = false;
bool has_only_iterable_class = true;
@@ -7380,7 +7847,7 @@ static zend_type zend_compile_typename_ex(
ZSTR_VAL(type_str));
}
} else if (ast->kind == ZEND_AST_TYPE_INTERSECTION) {
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
zend_type_list *type_list;
/* Allocate the type list directly on the arena as it must be a type
@@ -7400,14 +7867,12 @@ static zend_type zend_compile_typename_ex(
zend_string *standard_type_str = zend_type_to_string(single_type);
zend_error_noreturn(E_COMPILE_ERROR,
"Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str));
- zend_string_release_ex(standard_type_str, false);
}
/* An intersection of standard types cannot exist so invalidate it */
if (ZEND_TYPE_IS_ONLY_MASK(single_type)) {
zend_string *standard_type_str = zend_type_to_string(single_type);
zend_error_noreturn(E_COMPILE_ERROR,
"Type %s cannot be part of an intersection type", ZSTR_VAL(standard_type_str));
- zend_string_release_ex(standard_type_str, false);
}
/* Check for "self" and "parent" too */
if (
@@ -7494,14 +7959,14 @@ static bool zend_is_valid_default_value(zend_type type, zval *value)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(value))) {
- return 1;
+ return true;
}
if ((ZEND_TYPE_FULL_MASK(type) & MAY_BE_DOUBLE) && Z_TYPE_P(value) == IS_LONG) {
/* Integers are allowed as initializers for floating-point values. */
convert_to_double(value);
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static void zend_compile_attributes(
@@ -7510,20 +7975,20 @@ static void zend_compile_attributes(
zend_attribute *attr;
zend_internal_attribute *config;
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t g, i, j;
ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST);
for (g = 0; g < list->children; g++) {
- zend_ast_list *group = zend_ast_get_list(list->child[g]);
+ const zend_ast_list *group = zend_ast_get_list(list->child[g]);
ZEND_ASSERT(group->kind == ZEND_AST_ATTRIBUTE_GROUP);
for (i = 0; i < group->children; i++) {
ZEND_ASSERT(group->child[i]->kind == ZEND_AST_ATTRIBUTE);
- zend_ast *el = group->child[i];
+ const zend_ast *el = group->child[i];
if (el->child[1] &&
el->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) {
@@ -7556,7 +8021,7 @@ static void zend_compile_attributes(
if (args) {
ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
- bool uses_named_args = 0;
+ bool uses_named_args = false;
for (j = 0; j < args->children; j++) {
zend_ast **arg_ast_ptr = &args->child[j];
zend_ast *arg_ast = *arg_ast_ptr;
@@ -7569,7 +8034,7 @@ static void zend_compile_attributes(
if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0]));
arg_ast_ptr = &arg_ast->child[1];
- uses_named_args = 1;
+ uses_named_args = true;
for (uint32_t k = 0; k < j; k++) {
if (attr->args[k].name &&
@@ -7592,7 +8057,7 @@ static void zend_compile_attributes(
if (*attributes != NULL) {
/* Allow delaying target validation for forward compatibility. */
- zend_attribute *delayed_target_validation = NULL;
+ const zend_attribute *delayed_target_validation = NULL;
if (target == ZEND_ATTRIBUTE_TARGET_PARAMETER) {
ZEND_ASSERT(offset >= 1);
/* zend_get_parameter_attribute_str will add 1 too */
@@ -7640,7 +8105,6 @@ static void zend_compile_attributes(
if (error != NULL) {
if (delayed_target_validation == NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "%s", ZSTR_VAL(error));
- zend_string_efree(error);
} else {
attr->validation_error = error;
}
@@ -7653,10 +8117,10 @@ static void zend_compile_attributes(
static void zend_compile_property_hooks(
zend_property_info *prop_info, zend_string *prop_name,
- zend_ast *prop_type_ast, zend_ast_list *hooks);
+ zend_ast *prop_type_ast, const zend_ast_list *hooks);
typedef struct {
- zend_string *property_name;
+ const zend_string *property_name;
bool uses_property;
} find_property_usage_context;
@@ -7668,14 +8132,14 @@ static void zend_property_hook_find_property_usage(zend_ast **ast_ptr, void *_co
if (ast == NULL) {
return;
} else if (ast->kind == ZEND_AST_PROP || ast->kind == ZEND_AST_NULLSAFE_PROP) {
- zend_ast *object_ast = ast->child[0];
+ const zend_ast *object_ast = ast->child[0];
zend_ast *property_ast = ast->child[1];
if (object_ast->kind == ZEND_AST_VAR
&& object_ast->child[0]->kind == ZEND_AST_ZVAL
&& property_ast->kind == ZEND_AST_ZVAL) {
- zval *object = zend_ast_get_zval(object_ast->child[0]);
- zval *property = zend_ast_get_zval(property_ast);
+ const zval *object = zend_ast_get_zval(object_ast->child[0]);
+ const zval *property = zend_ast_get_zval(property_ast);
if (Z_TYPE_P(object) == IS_STRING
&& Z_TYPE_P(property) == IS_STRING
&& zend_string_equals_literal(Z_STR_P(object), "this")
@@ -7693,7 +8157,7 @@ static void zend_property_hook_find_property_usage(zend_ast **ast_ptr, void *_co
}
}
-static bool zend_property_hook_uses_property(zend_string *property_name, zend_string *hook_name, zend_ast *hook_ast)
+static bool zend_property_hook_uses_property(const zend_string *property_name, const zend_string *hook_name, zend_ast *hook_ast)
{
if (zend_string_equals_literal_ci(hook_name, "set")
&& hook_ast->kind == ZEND_AST_PROPERTY_HOOK_SHORT_BODY) {
@@ -7705,7 +8169,7 @@ static bool zend_property_hook_uses_property(zend_string *property_name, zend_st
return context.uses_property;
}
-static bool zend_property_is_virtual(zend_class_entry *ce, zend_string *property_name, zend_ast *hooks_ast, uint32_t flags)
+static bool zend_property_is_virtual(const zend_class_entry *ce, const zend_string *property_name, zend_ast *hooks_ast)
{
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
return true;
@@ -7716,9 +8180,9 @@ static bool zend_property_is_virtual(zend_class_entry *ce, zend_string *property
bool is_virtual = true;
- zend_ast_list *hooks = zend_ast_get_list(hooks_ast);
+ const zend_ast_list *hooks = zend_ast_get_list(hooks_ast);
for (uint32_t i = 0; i < hooks->children; i++) {
- zend_ast_decl *hook = (zend_ast_decl *) hooks->child[i];
+ const zend_ast_decl *hook = (const zend_ast_decl *) hooks->child[i];
zend_ast *body = hook->child[2];
if (body && zend_property_hook_uses_property(property_name, hook->name, body)) {
is_virtual = false;
@@ -7746,6 +8210,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
} else {
arg_infos->type = (zend_type) ZEND_TYPE_INIT_CODE(fallback_return_type, 0, 0);
}
+ arg_infos->doc_comment = NULL;
arg_infos++;
op_array->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
@@ -7843,6 +8308,8 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
arg_info = &arg_infos[i];
arg_info->name = zend_string_copy(name);
arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0);
+ arg_info->default_value = NULL;
+ arg_info->doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
if (attributes_ast) {
zend_compile_attributes(
@@ -7919,16 +8386,16 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
}
if (is_promoted) {
- zend_op_array *op_array = CG(active_op_array);
- zend_class_entry *scope = op_array->scope;
+ const zend_op_array *active_op_array = CG(active_op_array);
+ zend_class_entry *scope = active_op_array->scope;
bool is_ctor =
- scope && zend_is_constructor(op_array->function_name);
+ scope && zend_is_constructor(active_op_array->function_name);
if (!is_ctor) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot declare promoted property outside a constructor");
}
- if ((op_array->fn_flags & ZEND_ACC_ABSTRACT)
+ if ((active_op_array->fn_flags & ZEND_ACC_ABSTRACT)
|| (scope->ce_flags & ZEND_ACC_INTERFACE)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot declare promoted property in an abstract constructor");
@@ -7980,10 +8447,10 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
zend_property_info *prop = zend_declare_typed_property(
scope, name, &default_value,
- property_flags | (zend_property_is_virtual(scope, name, hooks_ast, property_flags) ? ZEND_ACC_VIRTUAL : 0) | ZEND_ACC_PROMOTED,
+ property_flags | (zend_property_is_virtual(scope, name, hooks_ast) ? ZEND_ACC_VIRTUAL : 0) | ZEND_ACC_PROMOTED,
doc_comment, type);
if (hooks_ast) {
- zend_ast_list *hooks = zend_ast_get_list(hooks_ast);
+ const zend_ast_list *hooks = zend_ast_get_list(hooks_ast);
zend_compile_property_hooks(prop, name, type_ast, hooks);
}
if (attributes_ast) {
@@ -8038,7 +8505,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array, zend_ast *uses_ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(uses_ast);
+ const zend_ast_list *list = zend_ast_get_list(uses_ast);
uint32_t i;
if (!list->children) {
@@ -8086,6 +8553,8 @@ typedef struct {
bool varvars_used;
} closure_info;
+static void find_implicit_binds(closure_info *info, zend_ast *params_ast, zend_ast *stmt_ast);
+
static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) {
if (!ast) {
return;
@@ -8107,21 +8576,21 @@ static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) {
zend_hash_add_empty_element(&info->uses, name);
} else {
- info->varvars_used = 1;
+ info->varvars_used = true;
find_implicit_binds_recursively(info, name_ast);
}
} else if (zend_ast_is_list(ast)) {
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; i++) {
find_implicit_binds_recursively(info, list->child[i]);
}
} else if (ast->kind == ZEND_AST_CLOSURE) {
/* For normal closures add the use() list. */
- zend_ast_decl *closure_ast = (zend_ast_decl *) ast;
+ const zend_ast_decl *closure_ast = (const zend_ast_decl *) ast;
zend_ast *uses_ast = closure_ast->child[1];
if (uses_ast) {
- zend_ast_list *uses_list = zend_ast_get_list(uses_ast);
+ const zend_ast_list *uses_list = zend_ast_get_list(uses_ast);
uint32_t i;
for (i = 0; i < uses_list->children; i++) {
zend_hash_add_empty_element(&info->uses, zend_ast_get_str(uses_list->child[i]));
@@ -8129,8 +8598,16 @@ static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) {
}
} else if (ast->kind == ZEND_AST_ARROW_FUNC) {
/* For arrow functions recursively check the expression. */
- zend_ast_decl *closure_ast = (zend_ast_decl *) ast;
- find_implicit_binds_recursively(info, closure_ast->child[2]);
+ const zend_ast_decl *closure_ast = (const zend_ast_decl *) ast;
+ closure_info inner_info;
+ find_implicit_binds(&inner_info, closure_ast->child[0], closure_ast->child[2]);
+ if (inner_info.varvars_used) {
+ info->varvars_used = true;
+ }
+ if (zend_hash_num_elements(&inner_info.uses)) {
+ zend_hash_copy(&info->uses, &inner_info.uses, NULL);
+ }
+ zend_hash_destroy(&inner_info.uses);
} else if (!zend_ast_is_special(ast)) {
uint32_t i, children = zend_ast_get_num_children(ast);
for (i = 0; i < children; i++) {
@@ -8141,22 +8618,23 @@ static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) {
static void find_implicit_binds(closure_info *info, zend_ast *params_ast, zend_ast *stmt_ast)
{
- zend_ast_list *param_list = zend_ast_get_list(params_ast);
+ const zend_ast_list *param_list = zend_ast_get_list(params_ast);
uint32_t i;
zend_hash_init(&info->uses, param_list->children, NULL, NULL, 0);
+ info->varvars_used = false;
find_implicit_binds_recursively(info, stmt_ast);
/* Remove variables that are parameters */
for (i = 0; i < param_list->children; i++) {
- zend_ast *param_ast = param_list->child[i];
+ const zend_ast *param_ast = param_list->child[i];
zend_hash_del(&info->uses, zend_ast_get_str(param_ast->child[1]));
}
}
static void compile_implicit_lexical_binds(
- closure_info *info, znode *closure, zend_op_array *op_array)
+ const closure_info *info, znode *closure, zend_op_array *op_array)
{
zend_string *var_name;
zend_op *opline;
@@ -8170,7 +8648,7 @@ static void compile_implicit_lexical_binds(
op_array->static_variables = zend_new_array(8);
}
- ZEND_HASH_MAP_FOREACH_STR_KEY(&info->uses, var_name)
+ ZEND_HASH_MAP_FOREACH_STR_KEY(&info->uses, var_name) {
zval *value = zend_hash_add(
op_array->static_variables, var_name, &EG(uninitialized_zval));
uint32_t offset = (uint32_t)((char*)value - (char*)op_array->static_variables->arData);
@@ -8179,13 +8657,13 @@ static void compile_implicit_lexical_binds(
opline->op2_type = IS_CV;
opline->op2.var = lookup_cv(var_name);
opline->extended_value = offset | ZEND_BIND_IMPLICIT;
- ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
}
static void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
{
- zend_op_array *op_array = CG(active_op_array);
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_op_array *op_array = CG(active_op_array);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; ++i) {
@@ -8216,14 +8694,14 @@ static void zend_compile_closure_uses(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_implicit_closure_uses(closure_info *info)
+static void zend_compile_implicit_closure_uses(const closure_info *info)
{
zend_string *var_name;
- ZEND_HASH_MAP_FOREACH_STR_KEY(&info->uses, var_name)
+ ZEND_HASH_MAP_FOREACH_STR_KEY(&info->uses, var_name) {
zval zv;
ZVAL_NULL(&zv);
zend_compile_static_var_common(var_name, &zv, ZEND_BIND_IMPLICIT);
- ZEND_HASH_FOREACH_END();
+ } ZEND_HASH_FOREACH_END();
}
static void add_stringable_interface(zend_class_entry *ce) {
@@ -8317,7 +8795,7 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
}
zend_add_magic_method(ce, (zend_function *) op_array, lcname);
- if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)
+ if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)
&& !(ce->ce_flags & ZEND_ACC_TRAIT)) {
add_stringable_interface(ce);
}
@@ -8341,7 +8819,7 @@ enum func_decl_level {
FUNC_DECL_LEVEL_CONSTEXPR,
};
-static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, enum func_decl_level level) /* {{{ */
+static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array, const zend_ast_decl *decl, enum func_decl_level level) /* {{{ */
{
zend_string *unqualified_name, *name, *lcname;
zend_op *opline;
@@ -8353,7 +8831,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
zend_string *class = zend_empty_string;
zend_string *separator = zend_empty_string;
zend_string *function = filename;
- char *parens = "";
+ const char *parens = "";
if (CG(active_op_array) && CG(active_op_array)->function_name) {
if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) {
@@ -8391,7 +8869,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
lcname = zend_string_tolower(name);
if (FC(imports_function)) {
- zend_string *import_name =
+ const zend_string *import_name =
zend_hash_find_ptr_lc(FC(imports_function), unqualified_name);
if (import_name && !zend_string_equals_ci(lcname, import_name)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare function %s() (previously declared as local import)",
@@ -8454,7 +8932,6 @@ static zend_op_array *zend_compile_func_decl_ex(
zend_op_array *op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
zend_oparray_context orig_oparray_context;
closure_info info;
- memset(&info, 0, sizeof(closure_info));
init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE);
@@ -8506,7 +8983,7 @@ static zend_op_array *zend_compile_func_decl_ex(
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0);
- zend_attribute *override_attribute = zend_get_attribute_str(
+ const zend_attribute *override_attribute = zend_get_attribute_str(
op_array->attributes,
"override",
sizeof("override")-1
@@ -8516,7 +8993,7 @@ static zend_op_array *zend_compile_func_decl_ex(
op_array->fn_flags |= ZEND_ACC_OVERRIDE;
}
- zend_attribute *deprecated_attribute = zend_get_attribute_str(
+ const zend_attribute *deprecated_attribute = zend_get_attribute_str(
op_array->attributes,
"deprecated",
sizeof("deprecated")-1
@@ -8549,8 +9026,16 @@ static zend_op_array *zend_compile_func_decl_ex(
}
zend_compile_params(params_ast, return_type_ast,
- is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0);
+ is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME) ? IS_STRING : 0);
if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) {
+ if (CG(active_class_entry) != NULL) {
+ if (zend_is_constructor(CG(active_op_array)->function_name)) {
+ zend_error(E_DEPRECATED, "Making a constructor a Generator is deprecated");
+ } else if (zend_string_equals_literal_ci(CG(active_op_array)->function_name, ZEND_DESTRUCTOR_FUNC_NAME)) {
+ zend_error(E_DEPRECATED, "Making a destructor a Generator is deprecated");
+ }
+ }
+
zend_mark_function_as_generator();
zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL);
}
@@ -8564,7 +9049,7 @@ static zend_op_array *zend_compile_func_decl_ex(
if (ast->kind == ZEND_AST_ARROW_FUNC && decl->child[2]->kind != ZEND_AST_RETURN) {
bool needs_return = true;
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_arg_info *return_info = CG(active_op_array)->arg_info - 1;
+ const zend_arg_info *return_info = CG(active_op_array)->arg_info - 1;
needs_return = !ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_NEVER);
}
if (needs_return) {
@@ -8582,7 +9067,7 @@ static zend_op_array *zend_compile_func_decl_ex(
ZEND_ASSERT(!is_hook);
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_arg_info *return_info = CG(active_op_array)->arg_info - 1;
+ const zend_arg_info *return_info = CG(active_op_array)->arg_info - 1;
if (ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_VOID)) {
zend_error_noreturn(E_COMPILE_ERROR,
"A void %s does not return a value, but #[\\NoDiscard] requires a return value",
@@ -8615,7 +9100,7 @@ static zend_op_array *zend_compile_func_decl_ex(
CG(zend_lineno) = decl->end_lineno;
zend_do_extended_stmt(NULL);
- zend_emit_final_return(0);
+ zend_emit_final_return(false);
pass_two(CG(active_op_array));
zend_oparray_context_end(&orig_oparray_context);
@@ -8642,7 +9127,7 @@ static zend_op_array *zend_compile_func_decl(znode *result, zend_ast *ast, enum
return zend_compile_func_decl_ex(result, ast, level, /* property_info */ NULL, (zend_property_hook_kind)-1);
}
-zend_property_hook_kind zend_get_property_hook_kind_from_name(zend_string *name) {
+zend_property_hook_kind zend_get_property_hook_kind_from_name(const zend_string *name) {
if (zend_string_equals_literal_ci(name, "get")) {
return ZEND_PROPERTY_HOOK_GET;
} else if (zend_string_equals_literal_ci(name, "set")) {
@@ -8654,7 +9139,7 @@ zend_property_hook_kind zend_get_property_hook_kind_from_name(zend_string *name)
static void zend_compile_property_hooks(
zend_property_info *prop_info, zend_string *prop_name,
- zend_ast *prop_type_ast, zend_ast_list *hooks)
+ zend_ast *prop_type_ast, const zend_ast_list *hooks)
{
zend_class_entry *ce = CG(active_class_entry);
@@ -8738,12 +9223,12 @@ static void zend_compile_property_hooks(
*return_type_ast_ptr = prop_type_ast;
} else if (hook_kind == ZEND_PROPERTY_HOOK_SET) {
if (hook->child[0]) {
- zend_ast_list *param_list = zend_ast_get_list(hook->child[0]);
+ const zend_ast_list *param_list = zend_ast_get_list(hook->child[0]);
if (param_list->children != 1) {
zend_error_noreturn(E_COMPILE_ERROR, "%s hook of property %s::$%s must accept exactly one parameters",
ZSTR_VAL(name), ZSTR_VAL(ce->name), ZSTR_VAL(prop_name));
}
- zend_ast *value_param_ast = param_list->child[0];
+ const zend_ast *value_param_ast = param_list->child[0];
if (value_param_ast->attr & ZEND_PARAM_REF) {
zend_error_noreturn(E_COMPILE_ERROR, "Parameter $%s of %s hook %s::$%s must not be pass-by-reference",
ZSTR_VAL(zend_ast_get_str(value_param_ast->child[1])), ZSTR_VAL(name), ZSTR_VAL(ce->name), ZSTR_VAL(prop_name));
@@ -8768,8 +9253,8 @@ static void zend_compile_property_hooks(
value_type_ast_ptr = ¶m->child[0];
hook->child[0] = zend_ast_create_list(1, ZEND_AST_PARAM_LIST, param);
}
- zend_ast *return_type = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_VOID));
- return_type->attr = ZEND_NAME_NOT_FQ;
+ zend_ast *return_type = zend_ast_create(ZEND_AST_TYPE);
+ return_type->attr = IS_VOID;
hook->child[3] = return_type;
} else {
ZEND_UNREACHABLE();
@@ -8834,7 +9319,7 @@ static void zend_compile_property_hooks(
static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, zend_ast *attr_ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
zend_class_entry *ce = CG(active_class_entry);
uint32_t i, children = list->children;
@@ -8872,7 +9357,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
zend_string *doc_comment = NULL;
zval value_zv;
zend_type type = ZEND_TYPE_INIT_NONE(0);
- flags |= zend_property_is_virtual(ce, name, hooks_ast, flags) ? ZEND_ACC_VIRTUAL : 0;
+ flags |= zend_property_is_virtual(ce, name, hooks_ast) ? ZEND_ACC_VIRTUAL : 0;
zend_string *old_active_property_info_name = CG(context).active_property_info_name;
CG(context).active_property_info_name = name;
@@ -8915,7 +9400,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
if (*value_ast_ptr) {
zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false);
- if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv)
+ if (ZEND_TYPE_IS_SET(type) && Z_TYPE(value_zv) != IS_CONSTANT_AST
&& !zend_is_valid_default_value(type, &value_zv)) {
zend_string *str = zend_type_to_string(type);
if (Z_TYPE(value_zv) == IS_NULL && !ZEND_TYPE_IS_INTERSECTION(type)) {
@@ -8969,7 +9454,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
if (attr_ast) {
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);
- zend_attribute *override_attribute = zend_get_attribute_str(info->attributes, "override", sizeof("override")-1);
+ const zend_attribute *override_attribute = zend_get_attribute_str(info->attributes, "override", sizeof("override")-1);
if (override_attribute) {
info->flags |= ZEND_ACC_OVERRIDE;
}
@@ -8980,7 +9465,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
}
/* }}} */
-static void zend_compile_prop_group(zend_ast *ast) /* {{{ */
+static void zend_compile_prop_group(const zend_ast *ast) /* {{{ */
{
zend_ast *type_ast = ast->child[0];
zend_ast *prop_ast = ast->child[1];
@@ -9002,7 +9487,7 @@ static void zend_check_trait_alias_modifiers(uint32_t attr) /* {{{ */
static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr_ast, zend_ast *type_ast)
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
zend_class_entry *ce = CG(active_class_entry);
uint32_t i, children = list->children;
@@ -9039,7 +9524,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false);
- if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) {
+ if (Z_TYPE(value_zv) != IS_CONSTANT_AST && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) {
zend_string *type_str = zend_type_to_string(type);
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s",
@@ -9051,7 +9536,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
if (attr_ast) {
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
- zend_attribute *deprecated = zend_get_attribute_str(c->attributes, "deprecated", sizeof("deprecated")-1);
+ const zend_attribute *deprecated = zend_get_attribute_str(c->attributes, "deprecated", sizeof("deprecated")-1);
if (deprecated) {
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
@@ -9060,11 +9545,20 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
+
+ const zend_attribute *override = zend_get_attribute_str(c->attributes, "override", sizeof("override") - 1);
+ if (override) {
+ ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_OVERRIDE;
+ /* We need to be able to remove the flag once the override is
+ * resolved. See ZEND_ACC_DEPRECATED above. */
+ ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
+ ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
}
}
}
-static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */
+static void zend_compile_class_const_group(const zend_ast *ast) /* {{{ */
{
zend_ast *const_ast = ast->child[0];
zend_ast *attr_ast = ast->child[1];
@@ -9074,7 +9568,7 @@ static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_method_ref(zend_ast *ast, zend_trait_method_reference *method_ref) /* {{{ */
+static void zend_compile_method_ref(const zend_ast *ast, zend_trait_method_reference *method_ref) /* {{{ */
{
zend_ast *class_ast = ast->child[0];
zend_ast *method_ast = ast->child[1];
@@ -9089,11 +9583,11 @@ static void zend_compile_method_ref(zend_ast *ast, zend_trait_method_reference *
}
/* }}} */
-static void zend_compile_trait_precedence(zend_ast *ast) /* {{{ */
+static void zend_compile_trait_precedence(const zend_ast *ast) /* {{{ */
{
- zend_ast *method_ref_ast = ast->child[0];
+ const zend_ast *method_ref_ast = ast->child[0];
zend_ast *insteadof_ast = ast->child[1];
- zend_ast_list *insteadof_list = zend_ast_get_list(insteadof_ast);
+ const zend_ast_list *insteadof_list = zend_ast_get_list(insteadof_ast);
uint32_t i;
zend_trait_precedence *precedence = emalloc(sizeof(zend_trait_precedence) + (insteadof_list->children - 1) * sizeof(zend_string*));
@@ -9110,9 +9604,9 @@ static void zend_compile_trait_precedence(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_trait_alias(zend_ast *ast) /* {{{ */
+static void zend_compile_trait_alias(const zend_ast *ast) /* {{{ */
{
- zend_ast *method_ref_ast = ast->child[0];
+ const zend_ast *method_ref_ast = ast->child[0];
zend_ast *alias_ast = ast->child[1];
uint32_t modifiers = ast->attr;
@@ -9134,9 +9628,9 @@ static void zend_compile_trait_alias(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_use_trait(zend_ast *ast) /* {{{ */
+static void zend_compile_use_trait(const zend_ast *ast) /* {{{ */
{
- zend_ast_list *traits = zend_ast_get_list(ast->child[0]);
+ const zend_ast_list *traits = zend_ast_get_list(ast->child[0]);
zend_ast_list *adaptations = ast->child[1] ? zend_ast_get_list(ast->child[1]) : NULL;
zend_class_entry *ce = CG(active_class_entry);
uint32_t i;
@@ -9163,7 +9657,7 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */
}
for (i = 0; i < adaptations->children; ++i) {
- zend_ast *adaptation_ast = adaptations->child[i];
+ const zend_ast *adaptation_ast = adaptations->child[i];
switch (adaptation_ast->kind) {
case ZEND_AST_TRAIT_PRECEDENCE:
zend_compile_trait_precedence(adaptation_ast);
@@ -9171,7 +9665,7 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */
case ZEND_AST_TRAIT_ALIAS:
zend_compile_trait_alias(adaptation_ast);
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
}
@@ -9179,7 +9673,7 @@ static void zend_compile_use_trait(zend_ast *ast) /* {{{ */
static void zend_compile_implements(zend_ast *ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
zend_class_entry *ce = CG(active_class_entry);
zend_class_name *interface_names;
uint32_t i;
@@ -9198,7 +9692,7 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */
}
/* }}} */
-static zend_string *zend_generate_anon_class_name(zend_ast_decl *decl)
+static zend_string *zend_generate_anon_class_name(const zend_ast_decl *decl)
{
zend_string *filename = CG(active_op_array)->filename;
uint32_t start_lineno = decl->start_lineno;
@@ -9208,7 +9702,7 @@ static zend_string *zend_generate_anon_class_name(zend_ast_decl *decl)
if (decl->child[0]) {
prefix = zend_resolve_const_class_name_reference(decl->child[0], "class name");
} else if (decl->child[1]) {
- zend_ast_list *list = zend_ast_get_list(decl->child[1]);
+ const zend_ast_list *list = zend_ast_get_list(decl->child[1]);
prefix = zend_resolve_const_class_name_reference(list->child[0], "interface name");
}
@@ -9238,9 +9732,9 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
zend_type_release(type, 0);
}
-static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
+static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool toplevel) /* {{{ */
{
- zend_ast_decl *decl = (zend_ast_decl *) ast;
+ const zend_ast_decl *decl = (const zend_ast_decl *) ast;
zend_ast *extends_ast = decl->child[0];
zend_ast *implements_ast = decl->child[1];
zend_ast *stmt_ast = decl->child[2];
@@ -9296,7 +9790,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
ce->type = ZEND_USER_CLASS;
ce->name = name;
- zend_initialize_class_data(ce, 1);
+ zend_initialize_class_data(ce, true);
if (!(decl->flags & ZEND_ACC_ANON_CLASS)) {
zend_alloc_ce_cache(ce->name);
}
@@ -9471,6 +9965,11 @@ static void zend_compile_enum_case(zend_ast *ast)
ZVAL_STR_COPY(&class_name_zval, enum_class_name);
zend_ast *class_name_ast = zend_ast_create_zval(&class_name_zval);
+ zval case_id_zval;
+ int case_id = zend_enum_next_case_id(enum_class);
+ ZVAL_LONG(&case_id_zval, case_id);
+ zend_ast *case_id_ast = zend_ast_create_zval(&case_id_zval);
+
zval case_name_zval;
ZVAL_STR_COPY(&case_name_zval, enum_case_name);
zend_ast *case_name_ast = zend_ast_create_zval(&case_name_zval);
@@ -9488,7 +9987,8 @@ static void zend_compile_enum_case(zend_ast *ast)
ZSTR_VAL(enum_class_name));
}
- zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT, class_name_ast, case_name_ast, case_value_ast);
+ zend_ast *const_enum_init_ast = zend_ast_create(ZEND_AST_CONST_ENUM_INIT,
+ class_name_ast, case_id_ast, case_name_ast, case_value_ast);
zval value_zv;
zend_const_expr_to_zval(&value_zv, &const_enum_init_ast, /* allow_dynamic */ false);
@@ -9513,6 +10013,16 @@ static void zend_compile_enum_case(zend_ast *ast)
if (deprecated) {
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
}
+
+ const zend_attribute *override = zend_get_attribute_str(c->attributes, "override", sizeof("override") - 1);
+ if (override) {
+ ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_OVERRIDE;
+ /* We need to be able to remove the flag once the override is
+ * resolved. See ZEND_ACC_DEPRECATED handling in
+ * zend_compile_class_const_decl(). */
+ enum_class->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
+ enum_class->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
+ }
}
}
@@ -9537,7 +10047,7 @@ static HashTable *zend_get_import_ht(uint32_t type) /* {{{ */
zend_hash_init(FC(imports_const), 8, NULL, str_dtor, 0);
}
return FC(imports_const);
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
return NULL;
@@ -9553,14 +10063,14 @@ static char *zend_get_use_type_str(uint32_t type) /* {{{ */
return " function";
case ZEND_SYMBOL_CONST:
return " const";
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
return " unknown";
}
/* }}} */
-static void zend_check_already_in_use(uint32_t type, zend_string *old_name, zend_string *new_name, zend_string *check_name) /* {{{ */
+static void zend_check_already_in_use(uint32_t type, const zend_string *old_name, const zend_string *new_name, const zend_string *check_name) /* {{{ */
{
if (zend_string_equals_ci(old_name, check_name)) {
return;
@@ -9573,7 +10083,7 @@ static void zend_check_already_in_use(uint32_t type, zend_string *old_name, zend
static void zend_compile_use(zend_ast *ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
zend_string *current_ns = FC(current_namespace);
uint32_t type = ast->attr;
@@ -9581,7 +10091,7 @@ static void zend_compile_use(zend_ast *ast) /* {{{ */
bool case_sensitive = type == ZEND_SYMBOL_CONST;
for (i = 0; i < list->children; ++i) {
- zend_ast *use_ast = list->child[i];
+ const zend_ast *use_ast = list->child[i];
zend_ast *old_name_ast = use_ast->child[0];
zend_ast *new_name_ast = use_ast->child[1];
zend_string *old_name = zend_ast_get_str(old_name_ast);
@@ -9644,11 +10154,11 @@ static void zend_compile_use(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_group_use(zend_ast *ast) /* {{{ */
+static void zend_compile_group_use(const zend_ast *ast) /* {{{ */
{
uint32_t i;
- zend_string *ns = zend_ast_get_str(ast->child[0]);
- zend_ast_list *list = zend_ast_get_list(ast->child[1]);
+ const zend_string *ns = zend_ast_get_str(ast->child[0]);
+ const zend_ast_list *list = zend_ast_get_list(ast->child[1]);
for (i = 0; i < list->children; i++) {
zend_ast *inline_use, *use = list->child[i];
@@ -9738,7 +10248,7 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
}
/* }}}*/
-static void zend_compile_namespace(zend_ast *ast) /* {{{ */
+static void zend_compile_namespace(const zend_ast *ast) /* {{{ */
{
zend_ast *name_ast = ast->child[0];
zend_ast *stmt_ast = ast->child[1];
@@ -9766,7 +10276,7 @@ static void zend_compile_namespace(zend_ast *ast) /* {{{ */
bool is_first_namespace = (!with_bracket && !FC(current_namespace))
|| (with_bracket && !FC(has_bracketed_namespaces));
- if (is_first_namespace && FAILURE == zend_is_first_statement(ast, /* allow_nop */ 1)) {
+ if (is_first_namespace && FAILURE == zend_is_first_statement(ast, /* allow_nop */ true)) {
zend_error_noreturn(E_COMPILE_ERROR, "Namespace declaration statement has to be "
"the very first statement or after any declare call in the script");
}
@@ -9801,12 +10311,11 @@ static void zend_compile_namespace(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_halt_compiler(zend_ast *ast) /* {{{ */
+static void zend_compile_halt_compiler(const zend_ast *ast) /* {{{ */
{
zend_ast *offset_ast = ast->child[0];
zend_long offset = Z_LVAL_P(zend_ast_get_zval(offset_ast));
- zend_string *filename, *name;
const char const_name[] = "__COMPILER_HALT_OFFSET__";
if (FC(has_bracketed_namespaces) && FC(in_namespace)) {
@@ -9814,9 +10323,9 @@ static void zend_compile_halt_compiler(zend_ast *ast) /* {{{ */
"__HALT_COMPILER() can only be used from the outermost scope");
}
- filename = zend_get_compiled_filename();
- name = zend_mangle_property_name(const_name, sizeof(const_name) - 1,
- ZSTR_VAL(filename), ZSTR_LEN(filename), 0);
+ const zend_string *filename = zend_get_compiled_filename();
+ zend_string *name = zend_mangle_property_name(const_name, sizeof(const_name) - 1,
+ ZSTR_VAL(filename), ZSTR_LEN(filename), false);
/* Avoid repeated declaration of the __COMPILER_HALT_OFFSET__ constant in
* case this file was already included. */
@@ -9827,10 +10336,10 @@ static void zend_compile_halt_compiler(zend_ast *ast) /* {{{ */
}
/* }}} */
-static bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
+static bool zend_try_ct_eval_magic_const(zval *zv, const zend_ast *ast) /* {{{ */
{
- zend_op_array *op_array = CG(active_op_array);
- zend_class_entry *ce = CG(active_class_entry);
+ const zend_op_array *op_array = CG(active_op_array);
+ const zend_class_entry *ce = CG(active_class_entry);
switch (ast->attr) {
case T_LINE:
@@ -9841,7 +10350,7 @@ static bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
break;
case T_DIR:
{
- zend_string *filename = CG(compiled_filename);
+ const zend_string *filename = CG(compiled_filename);
zend_string *dirname = zend_string_init(ZSTR_VAL(filename), ZSTR_LEN(filename), 0);
#ifdef ZEND_WIN32
ZSTR_LEN(dirname) = php_win32_ioutil_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
@@ -9920,10 +10429,10 @@ static bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
ZVAL_EMPTY_STRING(zv);
}
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
- return 1;
+ return true;
}
/* }}} */
@@ -10027,12 +10536,12 @@ ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, const zval *op1, co
static inline bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */
{
if (zend_binary_op_produces_error(opcode, op1, op2)) {
- return 0;
+ return false;
}
- binary_op_type fn = get_binary_op(opcode);
+ const binary_op_type fn = get_binary_op(opcode);
fn(result, op1, op2);
- return 1;
+ return true;
}
/* }}} */
@@ -10057,12 +10566,12 @@ ZEND_API bool zend_unary_op_produces_error(uint32_t opcode, const zval *op)
static inline bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */
{
if (zend_unary_op_produces_error(opcode, op)) {
- return 0;
+ return false;
}
- unary_op_type fn = get_unary_op(opcode);
+ const unary_op_type fn = get_unary_op(opcode);
fn(result, op);
- return 1;
+ return true;
}
/* }}} */
@@ -10076,7 +10585,7 @@ static inline bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, z
static inline void zend_ct_eval_greater(zval *result, zend_ast_kind kind, zval *op1, zval *op2) /* {{{ */
{
- binary_op_type fn = kind == ZEND_AST_GREATER
+ const binary_op_type fn = kind == ZEND_AST_GREATER
? is_smaller_function : is_smaller_or_equal_function;
fn(result, op2, op1);
}
@@ -10084,10 +10593,10 @@ static inline void zend_ct_eval_greater(zval *result, zend_ast_kind kind, zval *
static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
{
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
zend_ast *last_elem_ast = NULL;
uint32_t i;
- bool is_constant = 1;
+ bool is_constant = true;
if (ast->attr == ZEND_ARRAY_SYNTAX_LIST) {
zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression");
@@ -10112,13 +10621,13 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
if (elem_ast->attr /* by_ref */ || elem_ast->child[0]->kind != ZEND_AST_ZVAL
|| (elem_ast->child[1] && elem_ast->child[1]->kind != ZEND_AST_ZVAL)
) {
- is_constant = 0;
+ is_constant = false;
}
} else {
zend_eval_const_expr(&elem_ast->child[0]);
if (elem_ast->child[0]->kind != ZEND_AST_ZVAL) {
- is_constant = 0;
+ is_constant = false;
}
}
@@ -10126,24 +10635,24 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
}
if (!is_constant) {
- return 0;
+ return false;
}
if (!list->children) {
ZVAL_EMPTY_ARRAY(result);
- return 1;
+ return true;
}
array_init_size(result, list->children);
for (i = 0; i < list->children; ++i) {
- zend_ast *elem_ast = list->child[i];
+ const zend_ast *elem_ast = list->child[i];
zend_ast *value_ast = elem_ast->child[0];
zend_ast *key_ast;
zval *value = zend_ast_get_zval(value_ast);
if (elem_ast->kind == ZEND_AST_UNPACK) {
if (Z_TYPE_P(value) == IS_ARRAY) {
- HashTable *ht = Z_ARRVAL_P(value);
+ const HashTable *ht = Z_ARRVAL_P(value);
zval *val;
zend_string *key;
@@ -10167,7 +10676,7 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
key_ast = elem_ast->child[1];
if (key_ast) {
- zval *key = zend_ast_get_zval(key_ast);
+ const zval *key = zend_ast_get_zval(key_ast);
switch (Z_TYPE_P(key)) {
case IS_LONG:
zend_hash_index_update(Z_ARRVAL_P(result), Z_LVAL_P(key), value);
@@ -10195,7 +10704,6 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
goto fail;
default:
zend_error_noreturn(E_COMPILE_ERROR, "Illegal offset type");
- break;
}
} else if (!zend_hash_next_index_insert(Z_ARRVAL_P(result), value)) {
fail:
@@ -10205,7 +10713,7 @@ static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
}
}
- return 1;
+ return true;
}
/* }}} */
@@ -10306,7 +10814,7 @@ static void zend_compile_greater(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_unary_op(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_unary_op(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
uint32_t opcode = ast->attr;
@@ -10402,7 +10910,7 @@ static void zend_compile_short_circuiting(znode *result, zend_ast *ast) /* {{{ *
}
/* }}} */
-static void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_post_incdec(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
ZEND_ASSERT(ast->kind == ZEND_AST_POST_INC || ast->kind == ZEND_AST_POST_DEC);
@@ -10410,16 +10918,16 @@ static void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
if (var_ast->kind == ZEND_AST_PROP || var_ast->kind == ZEND_AST_NULLSAFE_PROP) {
- zend_op *opline = zend_compile_prop(NULL, var_ast, BP_VAR_RW, 0);
+ zend_op *opline = zend_compile_prop(NULL, var_ast, BP_VAR_RW, false);
opline->opcode = ast->kind == ZEND_AST_POST_INC ? ZEND_POST_INC_OBJ : ZEND_POST_DEC_OBJ;
zend_make_tmp_result(result, opline);
} else if (var_ast->kind == ZEND_AST_STATIC_PROP) {
- zend_op *opline = zend_compile_static_prop(NULL, var_ast, BP_VAR_RW, 0, 0);
+ zend_op *opline = zend_compile_static_prop(NULL, var_ast, BP_VAR_RW, false, false);
opline->opcode = ast->kind == ZEND_AST_POST_INC ? ZEND_POST_INC_STATIC_PROP : ZEND_POST_DEC_STATIC_PROP;
zend_make_tmp_result(result, opline);
} else {
znode var_node;
- zend_op *opline = zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
+ zend_op *opline = zend_compile_var(&var_node, var_ast, BP_VAR_RW, false);
if (opline && opline->opcode == ZEND_FETCH_DIM_RW) {
opline->extended_value = ZEND_FETCH_DIM_INCDEC;
}
@@ -10429,7 +10937,7 @@ static void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_pre_incdec(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
ZEND_ASSERT(ast->kind == ZEND_AST_PRE_INC || ast->kind == ZEND_AST_PRE_DEC);
@@ -10437,18 +10945,18 @@ static void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
zend_ensure_writable_variable(var_ast);
if (var_ast->kind == ZEND_AST_PROP || var_ast->kind == ZEND_AST_NULLSAFE_PROP) {
- zend_op *opline = zend_compile_prop(result, var_ast, BP_VAR_RW, 0);
+ zend_op *opline = zend_compile_prop(result, var_ast, BP_VAR_RW, false);
opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_OBJ : ZEND_PRE_DEC_OBJ;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
} else if (var_ast->kind == ZEND_AST_STATIC_PROP) {
- zend_op *opline = zend_compile_static_prop(result, var_ast, BP_VAR_RW, 0, 0);
+ zend_op *opline = zend_compile_static_prop(result, var_ast, BP_VAR_RW, false, false);
opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_STATIC_PROP : ZEND_PRE_DEC_STATIC_PROP;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
} else {
znode var_node;
- zend_op *opline = zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
+ zend_op *opline = zend_compile_var(&var_node, var_ast, BP_VAR_RW, false);
if (opline && opline->opcode == ZEND_FETCH_DIM_RW) {
opline->extended_value = ZEND_FETCH_DIM_INCDEC;
}
@@ -10458,7 +10966,7 @@ static void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_cast(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_cast(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
@@ -10555,7 +11063,7 @@ static void zend_compile_conditional(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(&false_node, false_ast);
- opline_qm_assign2 = zend_emit_op(NULL, ZEND_QM_ASSIGN, &false_node, NULL);
+ opline_qm_assign2 = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &false_node, NULL);
SET_NODE(opline_qm_assign2->result, result);
zend_update_jump_target_to_next(opnum_jmp);
@@ -10571,7 +11079,7 @@ static void zend_compile_coalesce(znode *result, zend_ast *ast) /* {{{ */
zend_op *opline;
uint32_t opnum;
- zend_compile_var(&expr_node, expr_ast, BP_VAR_IS, 0);
+ zend_compile_var(&expr_node, expr_ast, BP_VAR_IS, false);
opnum = get_next_op_number();
zend_emit_op_tmp(result, ZEND_COALESCE, &expr_node, NULL);
@@ -10602,7 +11110,7 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
znode var_node_is, var_node_w, default_node, assign_node, *node;
zend_op *opline;
uint32_t coalesce_opnum;
- bool need_frees = 0;
+ bool need_frees = false;
/* Remember expressions compiled during the initial BP_VAR_IS lookup,
* to avoid double-evaluation when we compile again with BP_VAR_W. */
@@ -10618,7 +11126,7 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
zend_hash_init(CG(memoized_exprs), 0, NULL, znode_dtor, 0);
CG(memoize_mode) = ZEND_MEMOIZE_COMPILE;
- zend_compile_var(&var_node_is, var_ast, BP_VAR_IS, 0);
+ zend_compile_var(&var_node_is, var_ast, BP_VAR_IS, false);
coalesce_opnum = get_next_op_number();
zend_emit_op_tmp(result, ZEND_COALESCE, &var_node_is, NULL);
@@ -10631,7 +11139,7 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
}
CG(memoize_mode) = ZEND_MEMOIZE_FETCH;
- zend_compile_var(&var_node_w, var_ast, BP_VAR_W, 0);
+ zend_compile_var(&var_node_w, var_ast, BP_VAR_W, false);
/* Reproduce some of the zend_compile_assign() opcode fixup logic here. */
opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
@@ -10663,7 +11171,7 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
opline = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &assign_node, NULL);
@@ -10671,7 +11179,7 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
ZEND_HASH_FOREACH_PTR(CG(memoized_exprs), node) {
if (node->op_type == IS_TMP_VAR || node->op_type == IS_VAR) {
- need_frees = 1;
+ need_frees = true;
break;
}
} ZEND_HASH_FOREACH_END();
@@ -10697,7 +11205,7 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_print(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_print(znode *result, const zend_ast *ast) /* {{{ */
{
zend_op *opline;
zend_ast *expr_ast = ast->child[0];
@@ -10731,16 +11239,16 @@ static void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
}
if (value_ast) {
- if (returns_by_ref && zend_is_variable(value_ast)) {
+ if (returns_by_ref && zend_is_variable_or_call(value_ast)) {
zend_assert_not_short_circuited(value_ast);
- zend_compile_var(&value_node, value_ast, BP_VAR_W, 1);
+ zend_compile_var(&value_node, value_ast, BP_VAR_W, true);
} else {
zend_compile_expr(&value_node, value_ast);
}
value_node_ptr = &value_node;
}
- opline = zend_emit_op(result, ZEND_YIELD, value_node_ptr, key_node_ptr);
+ opline = zend_emit_op_tmp(result, ZEND_YIELD, value_node_ptr, key_node_ptr);
if (value_ast && returns_by_ref && zend_is_call(value_ast)) {
opline->extended_value = ZEND_RETURNS_FUNCTION;
@@ -10748,7 +11256,7 @@ static void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_yield_from(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_yield_from(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
@@ -10797,7 +11305,7 @@ static void zend_compile_instanceof(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_include_or_eval(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
@@ -10806,14 +11314,14 @@ static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
zend_do_extended_fcall_begin();
zend_compile_expr(&expr_node, expr_ast);
- opline = zend_emit_op(result, ZEND_INCLUDE_OR_EVAL, &expr_node, NULL);
+ opline = zend_emit_op_tmp(result, ZEND_INCLUDE_OR_EVAL, &expr_node, NULL);
opline->extended_value = ast->attr;
zend_do_extended_fcall_end();
}
/* }}} */
-static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_isset_or_empty(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
@@ -10866,7 +11374,7 @@ static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
} else if (zend_try_compile_cv(&var_node, var_ast, BP_VAR_IS) == SUCCESS) {
opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_CV, &var_node, NULL);
} else {
- opline = zend_compile_simple_var_no_cv(result, var_ast, BP_VAR_IS, 0);
+ opline = zend_compile_simple_var_no_cv(result, var_ast, BP_VAR_IS, false);
opline->opcode = ZEND_ISSET_ISEMPTY_VAR;
}
break;
@@ -10876,14 +11384,14 @@ static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
break;
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
- opline = zend_compile_prop(result, var_ast, BP_VAR_IS, 0);
+ opline = zend_compile_prop(result, var_ast, BP_VAR_IS, false);
opline->opcode = ZEND_ISSET_ISEMPTY_PROP_OBJ;
break;
case ZEND_AST_STATIC_PROP:
- opline = zend_compile_static_prop(result, var_ast, BP_VAR_IS, 0, 0);
+ opline = zend_compile_static_prop(result, var_ast, BP_VAR_IS, false, false);
opline->opcode = ZEND_ISSET_ISEMPTY_STATIC_PROP;
break;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
result->op_type = opline->result_type = IS_TMP_VAR;
@@ -10893,7 +11401,7 @@ static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_silence(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode silence_node;
@@ -10903,7 +11411,7 @@ static void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
if (expr_ast->kind == ZEND_AST_VAR) {
/* For @$var we need to force a FETCH instruction, otherwise the CV access will
* happen outside the silenced section. */
- zend_compile_simple_var_no_cv(result, expr_ast, BP_VAR_R, 0 );
+ zend_compile_simple_var_no_cv(result, expr_ast, BP_VAR_R, false );
} else {
zend_compile_expr(result, expr_ast);
}
@@ -10912,7 +11420,7 @@ static void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_shell_exec(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_shell_exec(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
@@ -10937,7 +11445,7 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
zend_ast_list *list = zend_ast_get_list(ast);
zend_op *opline;
uint32_t i, opnum_init = -1;
- bool packed = 1;
+ bool packed = true;
if (zend_try_ct_eval_array(&result->u.constant, ast)) {
result->op_type = IS_CONST;
@@ -10981,7 +11489,7 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
if (by_ref) {
zend_ensure_writable_variable(value_ast);
- zend_compile_var(&value_node, value_ast, BP_VAR_W, 1);
+ zend_compile_var(&value_node, value_ast, BP_VAR_W, true);
} else {
zend_compile_expr(&value_node, value_ast);
}
@@ -10998,7 +11506,7 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
opline->extended_value |= by_ref;
if (key_ast && key_node.op_type == IS_CONST && Z_TYPE(key_node.u.constant) == IS_STRING) {
- packed = 0;
+ packed = false;
}
}
@@ -11011,11 +11519,24 @@ static void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */
+static void zend_emit_fetch_constant(znode *result, zend_string *resolved_name, bool unqualified_in_namespace)
{
- zend_ast *name_ast = ast->child[0];
+ zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL);
+ opline->op2_type = IS_CONST;
- zend_op *opline;
+ if (unqualified_in_namespace) {
+ opline->op1.num = IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE;
+ opline->op2.constant = zend_add_const_name_literal(resolved_name, true);
+ } else {
+ opline->op1.num = 0;
+ opline->op2.constant = zend_add_const_name_literal(resolved_name, false);
+ }
+ opline->extended_value = zend_alloc_cache_slot();
+}
+
+static void zend_compile_const(znode *result, const zend_ast *ast) /* {{{ */
+{
+ zend_ast *name_ast = ast->child[0];
bool is_fully_qualified;
zend_string *orig_name = zend_ast_get_str(name_ast);
@@ -11025,7 +11546,7 @@ static void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */
zend_ast *last = CG(ast);
while (last && last->kind == ZEND_AST_STMT_LIST) {
- zend_ast_list *list = zend_ast_get_list(last);
+ const zend_ast_list *list = zend_ast_get_list(last);
if (list->children == 0) {
break;
}
@@ -11045,22 +11566,19 @@ static void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */
return;
}
- opline = zend_emit_op_tmp(result, ZEND_FETCH_CONSTANT, NULL, NULL);
- opline->op2_type = IS_CONST;
-
- if (is_fully_qualified || !FC(current_namespace)) {
- opline->op1.num = 0;
- opline->op2.constant = zend_add_const_name_literal(
- resolved_name, 0);
- } else {
- opline->op1.num = IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE;
- opline->op2.constant = zend_add_const_name_literal(
- resolved_name, 1);
- }
- opline->extended_value = zend_alloc_cache_slot();
+ zend_emit_fetch_constant(result, resolved_name,
+ !is_fully_qualified && FC(current_namespace));
}
/* }}} */
+static void zend_compile_constant(znode *result, zend_ast *ast)
+{
+ zend_string *name = zend_ast_get_constant_name(ast);
+
+ zend_emit_fetch_constant(result, zend_string_copy(name),
+ (ast->attr & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) != 0);
+}
+
static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *class_ast;
@@ -11102,7 +11620,7 @@ static void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_class_name(znode *result, const zend_ast *ast) /* {{{ */
{
zend_ast *class_ast = ast->child[0];
@@ -11297,7 +11815,7 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_magic_const(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_magic_const(znode *result, const zend_ast *ast) /* {{{ */
{
zend_op *opline;
@@ -11399,8 +11917,7 @@ static void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */
case ZEND_FETCH_CLASS_STATIC:
zend_error_noreturn(E_COMPILE_ERROR,
"static::class cannot be used for compile-time class name resolution");
- return;
- EMPTY_SWITCH_DEFAULT_CASE()
+ default: ZEND_UNREACHABLE();
}
}
@@ -11472,7 +11989,7 @@ static void zend_compile_const_expr_new(zend_ast **ast_ptr)
zend_ast *class_ast = (*ast_ptr)->child[0];
zend_compile_const_expr_class_reference(class_ast);
- zend_ast *args_ast = (*ast_ptr)->child[1];
+ const zend_ast *args_ast = (*ast_ptr)->child[1];
if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression");
}
@@ -11481,7 +11998,7 @@ static void zend_compile_const_expr_new(zend_ast **ast_ptr)
static void zend_compile_const_expr_closure(zend_ast **ast_ptr)
{
zend_ast_decl *closure_ast = (zend_ast_decl *) *ast_ptr;
- zend_ast *uses_ast = closure_ast->child[1];
+ const zend_ast *uses_ast = closure_ast->child[1];
if (!(closure_ast->flags & ZEND_ACC_STATIC)) {
zend_error_noreturn(E_COMPILE_ERROR,
"Closures in constant expressions must be static");
@@ -11508,12 +12025,17 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
case ZEND_AST_STATIC_CALL:
args_ast = &(*ast_ptr)->child[2];
break;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) {
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
}
- ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr);
+
+ 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");
+ }
switch ((*ast_ptr)->kind) {
case ZEND_AST_CALL: {
@@ -11546,7 +12068,7 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
}
break;
}
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
}
@@ -11555,7 +12077,7 @@ static void zend_compile_const_expr_args(zend_ast **ast_ptr)
zend_ast_list *list = zend_ast_get_list(*ast_ptr);
bool uses_named_args = false;
for (uint32_t i = 0; i < list->children; i++) {
- zend_ast *arg = list->child[i];
+ const zend_ast *arg = list->child[i];
if (arg->kind == ZEND_AST_UNPACK) {
zend_error_noreturn(E_COMPILE_ERROR,
"Argument unpacking in constant expressions is not supported");
@@ -11579,7 +12101,7 @@ typedef struct {
static void zend_compile_const_expr(zend_ast **ast_ptr, void *context) /* {{{ */
{
- const_expr_context *ctx = (const_expr_context *) context;
+ const const_expr_context *ctx = context;
zend_ast *ast = *ast_ptr;
if (ast == NULL || ast->kind == ZEND_AST_ZVAL) {
return;
@@ -11658,7 +12180,7 @@ void zend_compile_top_stmt(zend_ast *ast) /* {{{ */
}
if (ast->kind == ZEND_AST_STMT_LIST) {
- zend_ast_list *list = zend_ast_get_list(ast);
+ const zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
for (i = 0; i < list->children; ++i) {
zend_compile_top_stmt(list->child[i]);
@@ -11672,7 +12194,7 @@ void zend_compile_top_stmt(zend_ast *ast) /* {{{ */
CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
} else if (ast->kind == ZEND_AST_CLASS) {
CG(zend_lineno) = ast->lineno;
- zend_compile_class_decl(NULL, ast, 1);
+ zend_compile_class_decl(NULL, ast, true);
CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
} else {
zend_compile_stmt(ast);
@@ -11765,7 +12287,7 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
zend_compile_use_trait(ast);
break;
case ZEND_AST_CLASS:
- zend_compile_class_decl(NULL, ast, 0);
+ zend_compile_class_decl(NULL, ast, false);
break;
case ZEND_AST_GROUP_USE:
zend_compile_group_use(ast);
@@ -11788,6 +12310,15 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
case ZEND_AST_CAST_VOID:
zend_compile_void_cast(NULL, ast);
break;
+ case ZEND_AST_ASSIGN: {
+ znode result;
+ zend_compile_assign(&result, ast, /* stmt */ true, BP_VAR_R);
+ zend_do_free(&result);
+ return;
+ }
+ case ZEND_AST_ASSIGN_REF:
+ zend_compile_assign_ref(NULL, ast, BP_VAR_R);
+ return;
default:
{
znode result;
@@ -11808,7 +12339,7 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
CG(zend_lineno) = zend_ast_get_lineno(ast);
if (CG(memoize_mode) != ZEND_MEMOIZE_NONE) {
- zend_compile_memoized_expr(result, ast);
+ zend_compile_memoized_expr(result, ast, BP_VAR_R);
return;
}
@@ -11829,14 +12360,14 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_METHOD_CALL:
case ZEND_AST_NULLSAFE_METHOD_CALL:
case ZEND_AST_STATIC_CALL:
- case ZEND_AST_PARENT_PROPERTY_HOOK_CALL:
- zend_compile_var(result, ast, BP_VAR_R, 0);
+ case ZEND_AST_PIPE:
+ zend_compile_var(result, ast, BP_VAR_R, false);
return;
case ZEND_AST_ASSIGN:
- zend_compile_assign(result, ast);
+ zend_compile_assign(result, ast, /* stmt */ false, BP_VAR_R);
return;
case ZEND_AST_ASSIGN_REF:
- zend_compile_assign_ref(result, ast);
+ zend_compile_assign_ref(result, ast, BP_VAR_R);
return;
case ZEND_AST_NEW:
zend_compile_new(result, ast);
@@ -11913,6 +12444,9 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_CONST:
zend_compile_const(result, ast);
return;
+ case ZEND_AST_CONSTANT:
+ zend_compile_constant(result, ast);
+ return;
case ZEND_AST_CLASS_CONST:
zend_compile_class_const(result, ast);
return;
@@ -11935,9 +12469,6 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_MATCH:
zend_compile_match(result, ast);
return;
- case ZEND_AST_PIPE:
- zend_compile_pipe(result, ast);
- return;
default:
ZEND_ASSERT(0 /* not supported */);
}
@@ -11951,6 +12482,12 @@ static void zend_compile_expr(znode *result, zend_ast *ast)
uint32_t checkpoint = zend_short_circuiting_checkpoint();
zend_compile_expr_inner(result, ast);
zend_short_circuiting_commit(checkpoint, result, ast);
+#if ZEND_DEBUG
+ if (result) {
+ /* BP_VAR_R is not allowed to produce IS_VAR. */
+ ZEND_ASSERT(result->op_type != IS_VAR);
+ }
+#endif
}
static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t type, bool by_ref)
@@ -11963,7 +12500,8 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
case ZEND_AST_METHOD_CALL:
case ZEND_AST_NULLSAFE_METHOD_CALL:
case ZEND_AST_STATIC_CALL:
- zend_compile_memoized_expr(result, ast);
+ case ZEND_AST_PIPE:
+ zend_compile_memoized_expr(result, ast, BP_VAR_W);
/* This might not actually produce an opcode, e.g. for expressions evaluated at comptime. */
return NULL;
}
@@ -11971,20 +12509,17 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
switch (ast->kind) {
case ZEND_AST_VAR:
- return zend_compile_simple_var(result, ast, type, 0);
+ return zend_compile_simple_var(result, ast, type, false);
case ZEND_AST_DIM:
return zend_compile_dim(result, ast, type, by_ref);
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
return zend_compile_prop(result, ast, type, by_ref);
case ZEND_AST_STATIC_PROP:
- return zend_compile_static_prop(result, ast, type, by_ref, 0);
+ return zend_compile_static_prop(result, ast, type, by_ref, false);
case ZEND_AST_CALL:
zend_compile_call(result, ast, type);
return NULL;
- case ZEND_AST_PARENT_PROPERTY_HOOK_CALL:
- zend_compile_parent_property_hook_call(result, ast, type);
- return NULL;
case ZEND_AST_METHOD_CALL:
case ZEND_AST_NULLSAFE_METHOD_CALL:
zend_compile_method_call(result, ast, type);
@@ -11992,9 +12527,18 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
case ZEND_AST_STATIC_CALL:
zend_compile_static_call(result, ast, type);
return NULL;
+ case ZEND_AST_PIPE:
+ zend_compile_pipe(result, ast, type);
+ return NULL;
case ZEND_AST_ZNODE:
*result = *zend_ast_get_znode(ast);
return NULL;
+ case ZEND_AST_ASSIGN_REF:
+ zend_compile_assign_ref(result, ast, type);
+ return NULL;
+ case ZEND_AST_ASSIGN:
+ zend_compile_assign(result, ast, false, type);
+ return NULL;
default:
if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) {
zend_error_noreturn(E_COMPILE_ERROR,
@@ -12013,6 +12557,16 @@ static zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bo
uint32_t checkpoint = zend_short_circuiting_checkpoint();
zend_op *opcode = zend_compile_var_inner(result, ast, type, by_ref);
zend_short_circuiting_commit(checkpoint, result, ast);
+#if ZEND_DEBUG
+ if (result
+ && (type == BP_VAR_R || type == BP_VAR_IS)
+ /* Don't check memoized result, as it will force BP_VAR_W even for BP_VAR_IS. */
+ && CG(memoize_mode) == ZEND_MEMOIZE_NONE
+ ) {
+ /* BP_VAR_{R,IS} is not allowed to produce IS_VAR. */
+ ZEND_ASSERT(result->op_type != IS_VAR);
+ }
+#endif
return opcode;
}
@@ -12022,7 +12576,7 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
switch (ast->kind) {
case ZEND_AST_VAR:
- return zend_compile_simple_var(result, ast, type, 1);
+ return zend_compile_simple_var(result, ast, type, true);
case ZEND_AST_DIM:
return zend_delayed_compile_dim(result, ast, type, by_ref);
case ZEND_AST_PROP:
@@ -12035,9 +12589,9 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
return opline;
}
case ZEND_AST_STATIC_PROP:
- return zend_compile_static_prop(result, ast, type, by_ref, 1);
+ return zend_compile_static_prop(result, ast, type, by_ref, true);
default:
- return zend_compile_var(result, ast, type, 0);
+ return zend_compile_var(result, ast, type, false);
}
}
/* }}} */
@@ -12050,7 +12604,7 @@ bool zend_try_ct_eval_cast(zval *result, uint32_t type, zval *op1)
}
switch (type) {
case _IS_BOOL:
- ZVAL_BOOL(result, zval_is_true(op1));
+ ZVAL_BOOL(result, zend_is_true(op1));
return true;
case IS_LONG:
if (Z_TYPE_P(op1) == IS_DOUBLE && !ZEND_DOUBLE_FITS_LONG(Z_DVAL_P((op1)))) {
@@ -12213,7 +12767,7 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
case ZEND_AST_DIM:
{
/* constant expression should be always read context ... */
- zval *container, *dim;
+ const zval *container, *dim;
if (ast->child[1] == NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
@@ -12355,7 +12909,7 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
zend_eval_const_expr(&ast->child[1]);
return;
case ZEND_AST_CONST_ENUM_INIT:
- zend_eval_const_expr(&ast->child[2]);
+ zend_eval_const_expr(&ast->child[3]);
return;
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index c07fa9bfa7d7..69d7aeb2f373 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
@@ -197,8 +196,8 @@ typedef struct _zend_oparray_context {
struct _zend_oparray_context *prev;
zend_op_array *op_array;
uint32_t opcodes_size;
- int vars_size;
- int literals_size;
+ uint32_t vars_size;
+ uint32_t literals_size;
uint32_t fast_call_var;
uint32_t try_catch_offset;
int current_brk_cont;
@@ -341,6 +340,11 @@ typedef struct _zend_oparray_context {
/* Class cannot be serialized or unserialized | | | */
#define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */
/* | | | */
+/* Class Flags 2 (ce_flags2) (unused: 0-31) | | | */
+/* ========================= | | | */
+/* | | | */
+/* #define ZEND_ACC2_EXAMPLE (1 << 0) X | | | */
+/* | | | */
/* Function Flags (unused: 30) | | | */
/* ============== | | | */
/* | | | */
@@ -364,7 +368,7 @@ typedef struct _zend_oparray_context {
#define ZEND_ACC_USES_THIS (1 << 17) /* | X | | */
/* | | | */
/* call through user function trampoline. e.g. | | | */
-/* __call, __callstatic | | | */
+/* __call, __callStatic | | | */
#define ZEND_ACC_CALL_VIA_TRAMPOLINE (1 << 18) /* | X | | */
/* | | | */
/* disable inline caching | | | */
@@ -407,6 +411,12 @@ typedef struct _zend_oparray_context {
/* | | | */
/* op_array uses strict mode types | | | */
#define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */
+/* | | | */
+/* Function Flags 2 (fn_flags2) (unused: 1-31) | | | */
+/* ============================ | | | */
+/* | | | */
+/* Function forbids dynamic calls | | | */
+#define ZEND_ACC2_FORBID_DYN_CALLS (1 << 0) /* | X | | */
#define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
#define ZEND_ACC_PPP_SET_MASK (ZEND_ACC_PUBLIC_SET | ZEND_ACC_PROTECTED_SET | ZEND_ACC_PRIVATE_SET)
@@ -420,7 +430,7 @@ static zend_always_inline uint32_t zend_visibility_to_set_visibility(uint32_t vi
return ZEND_ACC_PROTECTED_SET;
case ZEND_ACC_PRIVATE:
return ZEND_ACC_PRIVATE_SET;
- EMPTY_SWITCH_DEFAULT_CASE();
+ default: ZEND_UNREACHABLE();
}
}
@@ -443,7 +453,7 @@ static zend_always_inline uint32_t zend_visibility_to_set_visibility(uint32_t vi
// Must not clash with ZEND_SHORT_CIRCUITING_CHAIN_MASK
#define ZEND_JMP_NULL_BP_VAR_IS 4
-char *zend_visibility_string(uint32_t fn_flags);
+const char *zend_visibility_string(uint32_t fn_flags);
#define ZEND_PROPERTY_HOOK_COUNT 2
#define ZEND_PROPERTY_HOOK_STRUCT_SIZE (sizeof(zend_function*) * ZEND_PROPERTY_HOOK_COUNT)
@@ -451,7 +461,7 @@ char *zend_visibility_string(uint32_t fn_flags);
/* Stored in zend_property_info.offset, not returned by zend_get_property_offset(). */
#define ZEND_VIRTUAL_PROPERTY_OFFSET ((uint32_t)-1)
-zend_property_hook_kind zend_get_property_hook_kind_from_name(zend_string *name);
+zend_property_hook_kind zend_get_property_hook_kind_from_name(const zend_string *name);
typedef struct _zend_property_info {
uint32_t offset; /* property offset for object properties or
@@ -471,7 +481,7 @@ typedef struct _zend_property_info {
#define OBJ_PROP_NUM(obj, num) \
(&(obj)->properties_table[(num)])
#define OBJ_PROP_TO_OFFSET(num) \
- ((uint32_t)(XtOffsetOf(zend_object, properties_table) + sizeof(zval) * (num)))
+ ((uint32_t)(offsetof(zend_object, properties_table) + sizeof(zval) * (num)))
#define OBJ_PROP_TO_NUM(offset) \
(((offset) - OBJ_PROP_TO_OFFSET(0)) / sizeof(zval))
#define OBJ_PROP_SLOT_TO_OFFSET(obj, slot) \
@@ -487,6 +497,12 @@ typedef struct _zend_class_constant {
#define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value)
+C23_ENUM(zend_function_type, uint8_t) {
+ ZEND_INTERNAL_FUNCTION = 1,
+ ZEND_USER_FUNCTION = 2,
+ ZEND_EVAL_CODE = 4,
+};
+
/* arg_info for internal functions */
typedef struct _zend_internal_arg_info {
const char *name;
@@ -499,6 +515,7 @@ typedef struct _zend_arg_info {
zend_string *name;
zend_type type;
zend_string *default_value;
+ zend_string *doc_comment;
} zend_arg_info;
/* the following structure repeats the layout of zend_internal_arg_info,
@@ -514,7 +531,7 @@ typedef struct _zend_internal_function_info {
struct _zend_op_array {
/* Common elements */
- uint8_t type;
+ zend_function_type type;
uint8_t arg_flags[3]; /* bitset of arg_info.pass_by_reference */
uint32_t fn_flags;
zend_string *function_name;
@@ -527,12 +544,13 @@ struct _zend_op_array {
ZEND_MAP_PTR_DEF(void **, run_time_cache);
zend_string *doc_comment;
uint32_t T; /* number of temporary variables */
+ uint32_t fn_flags2;
const zend_property_info *prop_info; /* The corresponding prop_info if this is a hook. */
/* END of common elements */
- int cache_size; /* number of run_time_cache_slots * sizeof(void*) */
- int last_var; /* number of CV variables */
- uint32_t last; /* number of opcodes */
+ uint32_t cache_size; /* number of run_time_cache_slots * sizeof(void*) */
+ int last_var; /* number of CV variables */
+ uint32_t last; /* number of opcodes */
zend_op *opcodes;
ZEND_MAP_PTR_DEF(HashTable *, static_variables_ptr);
@@ -541,8 +559,8 @@ struct _zend_op_array {
uint32_t *refcount;
- int last_live_range;
- int last_try_catch;
+ uint32_t last_live_range;
+ uint32_t last_try_catch;
zend_live_range *live_range;
zend_try_catch_element *try_catch_array;
@@ -550,7 +568,7 @@ struct _zend_op_array {
uint32_t line_start;
uint32_t line_end;
- int last_literal;
+ uint32_t last_literal;
uint32_t num_dynamic_func_defs;
zval *literals;
@@ -573,7 +591,7 @@ typedef void (ZEND_FASTCALL *zif_handler)(INTERNAL_FUNCTION_PARAMETERS);
typedef struct _zend_internal_function {
/* Common elements */
- uint8_t type;
+ zend_function_type type;
uint8_t arg_flags[3]; /* bitset of arg_info.pass_by_reference */
uint32_t fn_flags;
zend_string* function_name;
@@ -581,11 +599,12 @@ typedef struct _zend_internal_function {
zend_function *prototype;
uint32_t num_args;
uint32_t required_num_args;
- zend_internal_arg_info *arg_info;
+ zend_arg_info *arg_info;
HashTable *attributes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
zend_string *doc_comment;
uint32_t T; /* number of temporary variables */
+ uint32_t fn_flags2;
const zend_property_info *prop_info; /* The corresponding prop_info if this is a hook. */
/* END of common elements */
@@ -598,11 +617,11 @@ typedef struct _zend_internal_function {
#define ZEND_FN_SCOPE_NAME(function) ((function) && (function)->common.scope ? ZSTR_VAL((function)->common.scope->name) : "")
union _zend_function {
- uint8_t type; /* MUST be the first element of this struct! */
+ zend_function_type type; /* MUST be the first element of this struct! */
uint32_t quick_arg_flags;
struct {
- uint8_t type; /* never used */
+ zend_function_type type; /* never used */
uint8_t arg_flags[3]; /* bitset of arg_info.pass_by_reference */
uint32_t fn_flags;
zend_string *function_name;
@@ -615,6 +634,7 @@ union _zend_function {
ZEND_MAP_PTR_DEF(void **, run_time_cache);
zend_string *doc_comment;
uint32_t T; /* number of temporary variables */
+ uint32_t fn_flags2;
const zend_property_info *prop_info; /* The corresponding prop_info if this is a hook. */
} common;
@@ -867,9 +887,9 @@ void shutdown_compiler(void);
void zend_init_compiler_data_structures(void);
void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_array *op_array);
-void zend_oparray_context_end(zend_oparray_context *prev_context);
+void zend_oparray_context_end(const zend_oparray_context *prev_context);
void zend_file_context_begin(zend_file_context *prev_context);
-void zend_file_context_end(zend_file_context *prev_context);
+void zend_file_context_end(const zend_file_context *prev_context);
extern ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type);
extern ZEND_API zend_op_array *(*zend_compile_string)(zend_string *source_string, const char *filename, zend_compile_position position);
@@ -881,7 +901,7 @@ void shutdown_scanner(void);
ZEND_API zend_string *zend_set_compiled_filename(zend_string *new_compiled_filename);
ZEND_API void zend_restore_compiled_filename(zend_string *original_compiled_filename);
ZEND_API zend_string *zend_get_compiled_filename(void);
-ZEND_API int zend_get_compiled_lineno(void);
+ZEND_API uint32_t zend_get_compiled_lineno(void);
ZEND_API size_t zend_get_scanned_file_offset(void);
ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var);
@@ -920,8 +940,8 @@ uint32_t zend_modifier_list_to_flags(zend_modifier_target target, zend_ast *modi
bool zend_handle_encoding_declaration(zend_ast *ast);
ZEND_API zend_class_entry *zend_bind_class_in_slot(
- zval *class_table_slot, zval *lcname, zend_string *lc_parent_name);
-ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname);
+ zval *class_table_slot, const zval *lcname, zend_string *lc_parent_name);
+ZEND_API zend_result do_bind_function(zend_function *func, const zval *lcname);
ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name);
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline);
@@ -938,19 +958,20 @@ 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, ...);
ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handle *file_handle);
ZEND_API zend_result open_file_for_scanning(zend_file_handle *file_handle);
-ZEND_API void init_op_array(zend_op_array *op_array, uint8_t type, int initial_ops_size);
+ZEND_API void init_op_array(zend_op_array *op_array, zend_function_type type, int initial_ops_size);
ZEND_API void destroy_op_array(zend_op_array *op_array);
ZEND_API void zend_destroy_static_vars(zend_op_array *op_array);
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce);
ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
ZEND_API void zend_type_release(zend_type type, bool persistent);
-ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name);
+ZEND_API zend_string *zend_create_member_string(const zend_string *class_name, const zend_string *member_name);
ZEND_API ZEND_COLD void zend_user_exception_handler(void);
@@ -963,7 +984,8 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void);
} \
} while (0)
-void zend_free_internal_arg_info(zend_internal_function *function);
+ZEND_API void zend_free_internal_arg_info(zend_internal_function *function,
+ bool permanent);
ZEND_API void destroy_zend_function(zend_function *function);
ZEND_API void zend_function_dtor(zval *zv);
ZEND_API void destroy_zend_class(zval *zv);
@@ -983,7 +1005,7 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen
#define ZEND_FUNCTION_DTOR zend_function_dtor
#define ZEND_CLASS_DTOR destroy_zend_class
-typedef bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline);
+typedef bool (*zend_needs_live_range_cb)(const zend_op_array *op_array, const zend_op *opline);
ZEND_API void zend_recalc_live_ranges(
zend_op_array *op_array, zend_needs_live_range_cb needs_live_range);
@@ -992,7 +1014,7 @@ ZEND_API bool zend_is_compiling(void);
ZEND_API char *zend_make_compiled_string_description(const char *name);
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers);
uint32_t zend_get_class_fetch_type(const zend_string *name);
-ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bool result_used);
+ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *fbc, bool result_used);
ZEND_API bool zend_is_smart_branch(const zend_op *opline);
typedef bool (*zend_auto_global_callback)(zend_string *name);
@@ -1014,15 +1036,9 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem);
void zend_assert_valid_class_name(const zend_string *const_name, const char *type);
-zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope);
+zend_string *zend_type_to_string_resolved(zend_type type, const zend_class_entry *scope);
ZEND_API zend_string *zend_type_to_string(zend_type type);
-/* BEGIN: OPCODES */
-
-#include "zend_vm_opcodes.h"
-
-/* END: OPCODES */
-
/* class fetches */
#define ZEND_FETCH_CLASS_DEFAULT 0
#define ZEND_FETCH_CLASS_SELF 1
@@ -1063,15 +1079,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define BP_VAR_FUNC_ARG 4
#define BP_VAR_UNSET 5
-#define ZEND_INTERNAL_FUNCTION 1
-#define ZEND_USER_FUNCTION 2
-#define ZEND_EVAL_CODE 4
-
#define ZEND_USER_CODE(type) ((type) != ZEND_INTERNAL_FUNCTION)
-#define ZEND_INTERNAL_CLASS 1
-#define ZEND_USER_CLASS 2
-
#define ZEND_EVAL (1<<0)
#define ZEND_INCLUDE (1<<1)
#define ZEND_INCLUDE_ONCE (1<<2)
@@ -1111,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
@@ -1222,6 +1232,9 @@ static zend_always_inline bool zend_check_arg_send_type(const zend_function *zf,
#define ZEND_IS_BINARY_ASSIGN_OP_OPCODE(opcode) \
(((opcode) >= ZEND_ADD) && ((opcode) <= ZEND_POW))
+/* PFAs/FCCs */
+#define ZEND_PLACEHOLDER_VARIADIC (1<<0)
+
/* Pseudo-opcodes that are used only temporarily during compilation */
#define ZEND_GOTO 253
#define ZEND_BRK 254
@@ -1238,10 +1251,18 @@ END_EXTERN_C()
#define ZEND_UNSET_FUNC_NAME "__unset"
#define ZEND_ISSET_FUNC_NAME "__isset"
#define ZEND_CALL_FUNC_NAME "__call"
-#define ZEND_CALLSTATIC_FUNC_NAME "__callstatic"
-#define ZEND_TOSTRING_FUNC_NAME "__tostring"
+#define ZEND_CALLSTATIC_FUNC_NAME "__callStatic"
+#define ZEND_CALLSTATIC_FUNC_LCNAME "__callstatic"
+#define ZEND_TOSTRING_FUNC_NAME "__toString"
+#define ZEND_TOSTRING_FUNC_LCNAME "__tostring"
#define ZEND_INVOKE_FUNC_NAME "__invoke"
-#define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo"
+#define ZEND_DEBUGINFO_FUNC_NAME "__debugInfo"
+#define ZEND_DEBUGINFO_FUNC_LCNAME "__debuginfo"
+#define ZEND_SLEEP_FUNC_NAME "__sleep"
+#define ZEND_WAKEUP_FUNC_NAME "__wakeup"
+#define ZEND_SERIALIZE_FUNC_NAME "__serialize"
+#define ZEND_UNSERIALIZE_FUNC_NAME "__unserialize"
+#define ZEND_SET_STATE_FUNC_NAME "__set_state"
/* The following constants may be combined in CG(compiler_options)
* to change the default compiler behavior */
@@ -1313,4 +1334,6 @@ ZEND_API bool zend_unary_op_produces_error(uint32_t opcode, const zval *op);
bool zend_try_ct_eval_cast(zval *result, uint32_t type, zval *op1);
+bool zend_op_may_elide_result(uint8_t opcode);
+
#endif /* ZEND_COMPILE_H */
diff --git a/Zend/zend_config.w32.h b/Zend/zend_config.w32.h
index a44a8b9f70d2..629d7b4437f2 100644
--- a/Zend/zend_config.w32.h
+++ b/Zend/zend_config.w32.h
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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: Andi Gutmans |
| Zeev Suraski |
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index a18c393be7f9..18292203bee9 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -2,15 +2,14 @@
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+ | Copyright © Zend Technologies Ltd., a subsidiary company of |
+ | Perforce Software, Inc., and Contributors. |
+----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
+ | 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