diff --git a/.editorconfig b/.editorconfig
index 7911bf8490b63..19be96087d908 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -32,3 +32,7 @@ max_line_length = 80
[*.patch]
trim_trailing_whitespace = false
+
+[*.rst]
+indent_style = space
+max_line_length = 100
diff --git a/.gitattributes b/.gitattributes
index 86797917210fa..c4e1bd57b2a4b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -21,7 +21,7 @@
# Collapse generated files within git and pull request diff.
**/*_arginfo.h linguist-generated -diff
-/main/gdb_inlined_script.c 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
/Zend/zend_vm_opcodes.[ch] linguist-generated -diff
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index acd9c1220c20e..53bd4df5a1ca5 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -22,15 +22,23 @@ body:
```
validations:
required: true
- - type: input
+ - type: textarea
attributes:
label: PHP Version
- description: "The used PHP version. Make sure it is [supported](https://www.php.net/supported-versions.php)."
- placeholder: "PHP 8.0.12"
+ 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 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
+ render: plain
validations:
required: true
- type: input
attributes:
label: Operating System
description: "The used operating system, if relevant."
- placeholder: "Ubuntu 20.04"
+ placeholder: "Ubuntu 24.04"
diff --git a/.github/actions/apt-x32/action.yml b/.github/actions/apt-x32/action.yml
index 0638881d1e4c2..39ef1df6c2c8c 100644
--- a/.github/actions/apt-x32/action.yml
+++ b/.github/actions/apt-x32/action.yml
@@ -33,13 +33,12 @@ runs:
libsodium-dev:i386 \
libsqlite3-dev:i386 \
libssl-dev:i386 \
- libtidy-dev:i386 \
libwebp-dev:i386 \
libxml2-dev:i386 \
libxml2-dev:i386 \
libxpm-dev:i386 \
libxslt1-dev:i386 \
- libzip-dev:i386 \
+ firebird-dev:i386 \
locales \
make \
pkg-config:i386 \
diff --git a/.github/actions/brew/action.yml b/.github/actions/brew/action.yml
index 287062e6f9f9c..07595a5ada931 100644
--- a/.github/actions/brew/action.yml
+++ b/.github/actions/brew/action.yml
@@ -13,7 +13,7 @@ runs:
# Some packages exist on x86 but not arm, or vice versa.
# Install them with reinstall to avoid warnings.
- brew reinstall autoconf webp tidy-html5 libzip libsodium icu4c
+ brew reinstall autoconf webp tidy-html5 libzip libsodium icu4c curl
brew install \
bison \
re2c
diff --git a/.github/actions/configure-x32/action.yml b/.github/actions/configure-x32/action.yml
index c07c49bb2c4f1..a5c5df4f7971d 100644
--- a/.github/actions/configure-x32/action.yml
+++ b/.github/actions/configure-x32/action.yml
@@ -10,6 +10,7 @@ runs:
run: |
set -x
+ export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/lib/i386-linux-gnu/pkgconfig"
./buildconf --force
export CFLAGS="-m32 -msse2"
export CXXFLAGS="-m32 -msse2"
@@ -26,6 +27,7 @@ runs:
--with-pgsql \
--with-pdo-pgsql \
--with-pdo-sqlite \
+ --with-pdo-firebird \
--without-pear \
--enable-gd \
--with-jpeg \
@@ -33,12 +35,10 @@ runs:
--with-freetype \
--with-xpm \
--enable-exif \
- --with-zip \
--with-zlib \
--enable-soap \
--enable-xmlreader \
--with-xsl \
- --with-tidy \
--enable-sysvsem \
--enable-sysvshm \
--enable-shmop \
diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml
index 415790bab5a71..d375a51a6d173 100644
--- a/.github/actions/freebsd/action.yml
+++ b/.github/actions/freebsd/action.yml
@@ -27,7 +27,6 @@ runs:
bzip2 \
t1lib \
gmp \
- tidyp \
libsodium \
libzip \
libxml2 \
diff --git a/.github/actions/test-linux/action.yml b/.github/actions/test-linux/action.yml
index 164606ecddc12..a5bf49bd00157 100644
--- a/.github/actions/test-linux/action.yml
+++ b/.github/actions/test-linux/action.yml
@@ -30,7 +30,9 @@ runs:
export PDO_PGSQL_TEST_DSN="pgsql:host=localhost port=5432 dbname=test user=postgres password=postgres"
fi
export PDO_FIREBIRD_TEST_DATABASE=test.fdb
- export PDO_FIREBIRD_TEST_DSN=firebird:dbname=localhost:test.fdb
+ if [[ -z "$PDO_FIREBIRD_TEST_DSN" ]]; then
+ export PDO_FIREBIRD_TEST_DSN=firebird:dbname=localhost:test.fdb
+ fi
export PDO_FIREBIRD_TEST_PASS=test
export PDO_FIREBIRD_TEST_USER=test
export ODBC_TEST_USER="odbc_test"
diff --git a/.github/lsan-suppressions.txt b/.github/lsan-suppressions.txt
index b8f863ce603b0..b5dbc26afddf8 100644
--- a/.github/lsan-suppressions.txt
+++ b/.github/lsan-suppressions.txt
@@ -1,3 +1,4 @@
leak:acommon::DictInfoList::elements
leak:timer_create
leak:netsnmp_init_mib_internals
+leak:isc_attach_database
diff --git a/.github/scripts/windows/build_task.bat b/.github/scripts/windows/build_task.bat
index 071c0c28f5ac5..b654794518496 100644
--- a/.github/scripts/windows/build_task.bat
+++ b/.github/scripts/windows/build_task.bat
@@ -25,18 +25,17 @@ 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
-rem Some undefined behavior is reported on 32-bit, this should be fixed
-if "%PLATFORM%" == "x86" (
- set CFLAGS=/W1
-) else (
- set CFLAGS=/W1 /WX
-)
+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
cmd /c configure.bat ^
--enable-snapshot-build ^
--disable-debug-pack ^
- --enable-com-dotnet=shared ^
--without-analyzer ^
--enable-object-out-dir=%PHP_BUILD_OBJ_DIR% ^
--with-php-build=%DEPS_DIR% ^
@@ -46,5 +45,7 @@ if %errorlevel% neq 0 exit /b 3
nmake /NOLOGO
if %errorlevel% neq 0 exit /b 3
+nmake /NOLOGO comtest.dll
+if %errorlevel% neq 0 exit /b 3
exit /b 0
diff --git a/.github/scripts/windows/find-target-branch.bat b/.github/scripts/windows/find-target-branch.bat
index 77d1684048142..a0b47f2488946 100644
--- a/.github/scripts/windows/find-target-branch.bat
+++ b/.github/scripts/windows/find-target-branch.bat
@@ -3,6 +3,6 @@
for /f "usebackq tokens=3" %%i in (`findstr PHP_MAJOR_VERSION main\php_version.h`) do set BRANCH=%%i
for /f "usebackq tokens=3" %%i in (`findstr PHP_MINOR_VERSION main\php_version.h`) do set BRANCH=%BRANCH%.%%i
-if /i "%BRANCH%" equ "8.4" (
+if /i "%BRANCH%" equ "8.5" (
set BRANCH=master
)
diff --git a/.github/scripts/windows/test_task.bat b/.github/scripts/windows/test_task.bat
index 223d654300357..43e7763e70294 100644
--- a/.github/scripts/windows/test_task.bat
+++ b/.github/scripts/windows/test_task.bat
@@ -92,8 +92,6 @@ rem set SSLEAY_CONF=
rem prepare for OPcache
if "%OPCACHE%" equ "1" set OPCACHE_OPTS=-d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit_buffer_size=64M -d opcache.jit=tracing
-rem work-around for failing to dl(mysqli) with OPcache (https://github.com/php/php-src/issues/8508)
-if "%OPCACHE%" equ "1" set OPCACHE_OPTS=%OPCACHE_OPTS% -d extension=mysqli
rem prepare for enchant
mkdir %~d0\usr\local\lib\enchant-2
@@ -123,6 +121,9 @@ hMailServer.exe /verysilent
cd %APPVEYOR_BUILD_FOLDER%
%PHP_BUILD_DIR%\php.exe -dextension_dir=%PHP_BUILD_DIR% -dextension=com_dotnet .github\setup_hmailserver.php
+rem prepare for com_dotnet
+nmake register_comtest
+
mkdir %PHP_BUILD_DIR%\test_file_cache
rem generate php.ini
echo extension_dir=%PHP_BUILD_DIR% > %PHP_BUILD_DIR%\php.ini
@@ -137,21 +138,23 @@ for %%i in (ldap) do (
del %PHP_BUILD_DIR%\php_%%i.dll
)
+rem reduce excessive stack reserve for testing
+editbin /stack:8388608 %PHP_BUILD_DIR%\php.exe
+editbin /stack:8388608 %PHP_BUILD_DIR%\php-cgi.exe
+
set TEST_PHPDBG_EXECUTABLE=%PHP_BUILD_DIR%\phpdbg.exe
copy /-y %DEPS_DIR%\bin\*.dll %PHP_BUILD_DIR%\*
+if "%ASAN%" equ "1" set ASAN_OPTS=--asan
+
mkdir c:\tests_tmp
-nmake test TESTS="%OPCACHE_OPTS% -g FAIL,BORK,LEAK,XLEAK --no-progress -q --offline --show-diff --show-slow 1000 --set-timeout 120 --temp-source c:\tests_tmp --temp-target c:\tests_tmp --bless %PARALLEL%"
+nmake test TESTS="%OPCACHE_OPTS% -g FAIL,BORK,LEAK,XLEAK %ASAN_OPTS% --no-progress -q --offline --show-diff --show-slow 1000 --set-timeout 120 --temp-source c:\tests_tmp --temp-target c:\tests_tmp %PARALLEL%"
set EXIT_CODE=%errorlevel%
+nmake unregister_comtest
taskkill /f /im snmpd.exe
-if %EXIT_CODE% GEQ 1 (
- git checkout ext\pgsql\tests\config.inc
- git diff > bless_tests.patch
-)
-
exit /b %EXIT_CODE%
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 2254b8f037e54..6ae972d92e49c 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -19,9 +19,9 @@ jobs:
- name: git checkout
uses: actions/checkout@v4
- name: Install dependencies
- run: pip install sphinx-design sphinxawesome-theme rstfmt
+ run: pip install -r docs/requirements.txt
- name: Check formatting
- run: rstfmt --check -w 100 docs/source
+ run: make -C docs check-formatting
- name: Publish
if: github.event_name == 'push'
uses: sphinx-notes/pages@v3
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index c9e6850604312..1b1532af7f799 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -29,6 +29,9 @@ on:
windows_version:
required: true
type: string
+ skip_laravel:
+ required: true
+ type: boolean
skip_symfony:
required: true
type: boolean
@@ -550,7 +553,7 @@ jobs:
git clone "https://github.com/amphp/$repository.git" "amphp-$repository" --depth 1
cd "amphp-$repository"
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
vendor/bin/phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
X=1;
@@ -559,12 +562,12 @@ jobs:
done
exit $X
- name: Test Laravel
- if: ${{ !cancelled() }}
+ if: ${{ !cancelled() && !inputs.skip_laravel }}
run: |
git clone https://github.com/laravel/framework.git --depth=1
cd framework
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
# Hack to disable a test that hangs
php -r '$c = file_get_contents("tests/Filesystem/FilesystemTest.php"); $c = str_replace("public function testSharedGet()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testSharedGet()", $c); file_put_contents("tests/Filesystem/FilesystemTest.php", $c);'
php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$?
@@ -581,7 +584,7 @@ jobs:
git clone "https://github.com/reactphp/$repository.git" "reactphp-$repository" --depth 1
cd "reactphp-$repository"
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
vendor/bin/phpunit || EXIT_CODE=$?
if [ $[EXIT_CODE:-0} -gt 128 ]; then
X=1;
@@ -595,7 +598,7 @@ jobs:
git clone https://github.com/revoltphp/event-loop.git --depth=1
cd event-loop
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
vendor/bin/phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
@@ -606,7 +609,7 @@ jobs:
git clone https://github.com/symfony/symfony.git --depth=1
cd symfony
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
php ./phpunit install
# Test causes a heap-buffer-overflow but I cannot reproduce it locally...
php -r '$c = file_get_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php"); $c = str_replace("public function testSanitizeDeepNestedString()", "/** @group skip */\n public function testSanitizeDeepNestedString()", $c); file_put_contents("src/Symfony/Component/HtmlSanitizer/Tests/HtmlSanitizerCustomTest.php", $c);'
@@ -627,7 +630,7 @@ jobs:
git clone https://github.com/sebastianbergmann/phpunit.git --branch=main --depth=1
cd phpunit
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
php ./phpunit || EXIT_CODE=$?
if [ ${EXIT_CODE:-0} -gt 128 ]; then
exit 1
@@ -635,7 +638,7 @@ jobs:
- name: 'Symfony Preloading'
if: ${{ !cancelled() && !inputs.skip_symfony }}
run: |
- php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-reqs
+ php /usr/bin/composer create-project symfony/symfony-demo symfony_demo --no-progress --ignore-platform-req=php+
cd symfony_demo
git rev-parse HEAD
sed -i 's/PHP_SAPI/"cli-server"/g' var/cache/dev/App_KernelDevDebugContainer.preload.php
@@ -646,7 +649,7 @@ jobs:
git clone https://github.com/WordPress/wordpress-develop.git wordpress --depth=1
cd wordpress
git rev-parse HEAD
- php /usr/bin/composer install --no-progress --ignore-platform-reqs
+ php /usr/bin/composer install --no-progress --ignore-platform-req=php+
cp wp-tests-config-sample.php wp-tests-config.php
sed -i 's/youremptytestdbnamehere/test/g' wp-tests-config.php
sed -i 's/yourusernamehere/root/g' wp-tests-config.php
diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index b4805d9031d45..2f82179b90ec6 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -78,7 +78,8 @@ jobs:
zts: true
asan: true
name: "LINUX_X64_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || '' }}"
- runs-on: ubuntu-${{ !matrix.asan && '22' || '24' }}.04
+ runs-on: ubuntu-24.04
+ timeout-minutes: 50
steps:
- name: git checkout
uses: actions/checkout@v4
@@ -139,12 +140,14 @@ jobs:
if: github.repository == 'php/php-src' || github.event_name == 'pull_request'
name: LINUX_X32_DEBUG_ZTS
runs-on: ubuntu-latest
+ timeout-minutes: 50
container:
- image: ubuntu:22.04
+ image: ubuntu:24.04
env:
MYSQL_TEST_HOST: mysql
PDO_MYSQL_TEST_DSN: mysql:host=mysql;dbname=test
PDO_MYSQL_TEST_HOST: mysql
+ PDO_FIREBIRD_TEST_DSN: firebird:dbname=firebird:test.fdb
services:
mysql:
image: mysql:8.3
@@ -153,6 +156,15 @@ jobs:
env:
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: root
+ firebird:
+ image: jacobalberty/firebird
+ ports:
+ - 3050:3050
+ env:
+ ISC_PASSWORD: test
+ FIREBIRD_DATABASE: test.fdb
+ FIREBIRD_USER: test
+ FIREBIRD_PASSWORD: test
steps:
- name: git checkout
uses: actions/checkout@v4
@@ -186,12 +198,11 @@ jobs:
fail-fast: false
matrix:
include:
- - os: 13
- arch: X64
- os: 14
arch: ARM64
name: MACOS_${{ matrix.arch }}_DEBUG_NTS
runs-on: macos-${{ matrix.os }}
+ timeout-minutes: 50
steps:
- name: git checkout
uses: actions/checkout@v4
@@ -226,6 +237,7 @@ jobs:
if: github.repository == 'php/php-src' || github.event_name == 'pull_request'
name: WINDOWS_X64_ZTS
runs-on: windows-2022
+ timeout-minutes: 50
env:
PHP_BUILD_CACHE_BASE_DIR: C:\build-cache
PHP_BUILD_OBJ_DIR: C:\obj
@@ -251,7 +263,8 @@ jobs:
BENCHMARKING:
name: BENCHMARKING
if: github.repository == 'php/php-src' || github.event_name == 'pull_request'
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
+ timeout-minutes: 50
steps:
- name: git checkout
uses: actions/checkout@v4
diff --git a/.github/workflows/real-time-benchmark.yml b/.github/workflows/real-time-benchmark.yml
new file mode 100644
index 0000000000000..9e1fa9fdbe6a2
--- /dev/null
+++ b/.github/workflows/real-time-benchmark.yml
@@ -0,0 +1,132 @@
+name: Real-time Benchmark
+on:
+ schedule:
+ - cron: "30 0 * * *"
+permissions:
+ contents: read
+jobs:
+ REAL_TIME_BENCHMARK:
+ name: REAL_TIME_BENCHMARK
+ if: github.repository == 'php/php-src'
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Install dependencies
+ run: |
+ set -ex
+ sudo apt-get update
+ sudo apt-get install gpg
+
+ wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
+ gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
+ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
+ export DEBIAN_FRONTEND=noninteractive
+ sudo apt-get update -y
+ sudo apt-get install -y terraform=1.5.7-*
+ - name: Checkout benchmark suite
+ uses: actions/checkout@v4
+ with:
+ repository: 'kocsismate/php-version-benchmarks'
+ ref: 'main'
+ fetch-depth: 1
+ path: 'php-version-benchmarks'
+ - name: Checkout php-src
+ uses: actions/checkout@v4
+ with:
+ repository: 'php/php-src'
+ ref: '${{ github.sha }}'
+ fetch-depth: 100
+ path: 'php-version-benchmarks/tmp/php_master'
+ - name: Setup benchmark results
+ run: |
+ git config --global user.name "Benchmark"
+ git config --global user.email "benchmark@php.net"
+
+ rm -rf ./php-version-benchmarks/docs/results
+ - name: Checkout benchmark data
+ uses: actions/checkout@v4
+ with:
+ repository: php/real-time-benchmark-data
+ ssh-key: ${{ secrets.PHP_VERSION_BENCHMARK_RESULTS_DEPLOY_KEY }}
+ path: 'php-version-benchmarks/docs/results'
+ - name: Set benchmark config
+ run: |
+ set -e
+
+ # Set infrastructure config
+ 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
+ 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
+
+ YEAR="$(date '+%Y')"
+ DATABASE="./php-version-benchmarks/docs/results/$YEAR/database.tsv"
+ if [ -f "$DATABASE" ]; then
+ LAST_RESULT_SHA="$(tail -n 2 "$DATABASE" | head -n 1 | cut -f 6)"
+ else
+ YESTERDAY="$(date -d "-2 day 23:59:59" '+%Y-%m-%d %H:%M:%S')"
+ LAST_RESULT_SHA="$(cd ./php-version-benchmarks/tmp/php_master/ && git --no-pager log --until="$YESTERDAY" -n 1 --pretty='%H')"
+ fi
+
+ BASELINE_SHA="d5f6e56610c729710073350af318c4ea1b292cfe"
+ BASELINE_SHORT_SHA="$(echo "$BASELINE_SHA" | cut -c1-4)"
+
+ # Set config for the baseline PHP version
+ cp ./php-version-benchmarks/config/php/master.ini.dist ./php-version-benchmarks/config/php/master_baseline.ini
+ sed -i 's/PHP_NAME="PHP - master"/PHP_NAME="PHP - baseline@'"$BASELINE_SHORT_SHA"'"/g' ./php-version-benchmarks/config/php/master_baseline.ini
+ sed -i "s/PHP_ID=php_master/PHP_ID=php_master_baseline/g" ./php-version-benchmarks/config/php/master_baseline.ini
+ sed -i "s/PHP_COMMIT=/PHP_COMMIT=$BASELINE_SHA/g" ./php-version-benchmarks/config/php/master_baseline.ini
+
+ # Set config for the previous PHP version
+ cp ./php-version-benchmarks/config/php/master.ini.dist ./php-version-benchmarks/config/php/master_last.ini
+ sed -i 's/PHP_NAME="PHP - master"/PHP_NAME="PHP - previous master"/g' ./php-version-benchmarks/config/php/master_last.ini
+ sed -i "s/PHP_ID=php_master/PHP_ID=php_master_previous/g" ./php-version-benchmarks/config/php/master_last.ini
+ sed -i "s/PHP_COMMIT=/PHP_COMMIT=$LAST_RESULT_SHA/g" ./php-version-benchmarks/config/php/master_last.ini
+
+ # Set config for the current PHP version
+ cp ./php-version-benchmarks/config/php/master.ini.dist ./php-version-benchmarks/config/php/master_now.ini
+ sed -i "s/PHP_COMMIT=/PHP_COMMIT=${{ github.sha }}/g" ./php-version-benchmarks/config/php/master_now.ini
+
+ # Set config for current PHP version with JIT
+ git clone ./php-version-benchmarks/tmp/php_master/ ./php-version-benchmarks/tmp/php_master_jit
+ cp ./php-version-benchmarks/config/php/master_jit.ini.dist ./php-version-benchmarks/config/php/master_now_jit.ini
+ sed -i "s/PHP_COMMIT=/PHP_COMMIT=${{ github.sha }}/g" ./php-version-benchmarks/config/php/master_now_jit.ini
+
+ # Set test configs
+ cp ./php-version-benchmarks/config/test/1_laravel.ini.dist ./php-version-benchmarks/config/test/1_laravel.ini
+ cp ./php-version-benchmarks/config/test/2_symfony_main.ini.dist ./php-version-benchmarks/config/test/2_symfony_main.ini
+ cp ./php-version-benchmarks/config/test/4_wordpress.ini.dist ./php-version-benchmarks/config/test/4_wordpress.ini
+ cp ./php-version-benchmarks/config/test/5_bench.php.ini.dist ./php-version-benchmarks/config/test/5_bench.php.ini
+ cp ./php-version-benchmarks/config/test/6_micro_bench.php.ini.dist ./php-version-benchmarks/config/test/6_micro_bench.php.ini
+ - name: Run benchmark
+ run: ./php-version-benchmarks/benchmark.sh run aws
+ - name: Store results
+ run: |
+ set -ex
+
+ cd ./php-version-benchmarks/docs/results
+ git pull --autostash
+ if [ -e ".git/MERGE_HEAD" ]; then
+ echo "Merging, can't proceed"
+ exit 1
+ fi
+ git add .
+ if git diff --cached --quiet; then
+ exit 1
+ fi
+ git commit -m "Add result for ${{ github.repository }}@${{ github.sha }}"
+ git push
+ - 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/root.yml b/.github/workflows/root.yml
index 2bb895e96b668..a98bb39ba0d92 100644
--- a/.github/workflows/root.yml
+++ b/.github/workflows/root.yml
@@ -59,6 +59,7 @@ jobs:
(((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04')
|| '22.04' }}
windows_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '2022' || '2019' }}
+ skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }}
skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }}
skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }}
secrets: inherit
diff --git a/.gitignore b/.gitignore
index 91120a5fa6b2f..55c441323cf15 100644
--- a/.gitignore
+++ b/.gitignore
@@ -237,6 +237,7 @@ php
**/tests/**/*.exp
**/tests/**/*.log
**/tests/**/*.sh
+**/tests/**/*.stdin
# Generated by some test cases
**/tests/**/*.db
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2318f896b7a9b..a91545fa9bd79 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -32,6 +32,12 @@ had several contributions accepted, commit privileges are often quickly granted.
PHP welcomes pull requests to [add tests](#writing-tests), fix bugs and to
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.
+
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
[the supported version page](https://www.php.net/supported-versions.php) are
@@ -350,9 +356,10 @@ Currently, we have the following branches in use:
| Branch | |
| --------- | --------- |
-| master | Active development branch for PHP 8.4, which is open for backwards incompatible changes and major internal API changes. |
+| 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 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. |
diff --git a/EXTENSIONS b/EXTENSIONS
index 6c3a83b8d9124..2ceb1afd5050e 100644
--- a/EXTENSIONS
+++ b/EXTENSIONS
@@ -170,56 +170,56 @@ EXTENSION: dom
PRIMARY MAINTAINER: Christian Stocker (2003 - 2011)
Rob Richards (2003 - 2012)
Marcus Börger (2003 - 2006)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
SINCE: 5.0
-------------------------------------------------------------------------------
EXTENSION: simplexml
PRIMARY MAINTAINER: Marcus Börger (2003 - 2008)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
SINCE: 5.0
-------------------------------------------------------------------------------
EXTENSION: soap
PRIMARY MAINTAINER: Dmitry Stogov (2004 - 2018)
- Niels Dossche (2024 - 2024)
+ Niels Dossche (2024 - 2025)
MAINTENANCE: Odd fixes
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: xml
PRIMARY MAINTAINER: Thies C. Arntzen (1999 - 2002)
Rob Richards (2003 - 2013)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: libxml
PRIMARY MAINTAINER: Rob Richards (2003 - 2009)
Christian Stocker (2004 - 2011)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: xmlreader
PRIMARY MAINTAINER: Rob Richards (2004 - 2010)
Christian Stocker (2004 - 2004)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: xmlwriter
PRIMARY MAINTAINER: Rob Richards (2004 - 2010)
Pierre-Alain Joye (2005-2009)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: xsl
PRIMARY MAINTAINER: Christian Stocker (2003 - 2011)
Rob Richards (2003 - 2010)
- Niels Dossche (2023 - 2024)
+ Niels Dossche (2023 - 2025)
MAINTENANCE: Maintained
STATUS: Working
SINCE: 5.0
@@ -412,7 +412,7 @@ STATUS: Working
-------------------------------------------------------------------------------
EXTENSION: random
PRIMARY MAINTAINER Go Kudo (2022 - 2024)
- Tim Düsterhus (2022 - 2024)
+ Tim Düsterhus (2022 - 2025)
MAINTENANCE: Maintained
STATUS: Working
SINCE: 8.2.0
@@ -426,6 +426,7 @@ EXTENSION: reflection
PRIMARY MAINTAINER: Marcus Börger (2003 - 2009)
Johannes Schlüter (2006 - 2014)
Nikita Popov (2019 - 2020)
+ Daniel Scherzer (2025 - 2025)
MAINTENANCE: Maintained
STATUS: Working
-------------------------------------------------------------------------------
diff --git a/NEWS b/NEWS
index efb76ac127a60..706533b9c898a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,1237 +1,242 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
-?? ??? ????, PHP 8.4.7
-
-- Core:
- . Fixed bug GH-17711 and GH-18022 (Infinite recursion on deprecated attribute
- evaluation). (ilutov)
- . Fixed bug GH-18038 (Lazy proxy calls magic methods twice). (Arnaud)
- . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov)
- . Fixed bug GH-18268 (Segfault in array_walk() on object with added property
- hooks). (ilutov)
- . Fixed bug GH-18304 (Changing the properties of a DateInterval through
- dynamic properties triggers a SegFault). (nielsdos)
- . Fix some leaks in php_scandir. (nielsdos)
-
-- DBA:
- . FIxed bug GH-18247 dba_popen() memory leak on invalid path. (David Carlier)
-
-- Filter:
- . Fixed bug GH-18309 (ipv6 filter integer overflow). (nielsdos)
-
-- GD:
- . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage
- in gdImageCrop(). (David Carlier)
- . Fixed GH-18243 imagettftext() overflow/underflow on font size value.
- (David Carlier)
-
-- Intl:
- . Fix reference support for intltz_get_offset(). (nielsdos)
-
-- LDAP:
- . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi)
- . Fix NULL deref on high modification key. (nielsdos)
-
-- libxml:
- . Fixed custom external entity loader returning an invalid resource leading
- to a confusing TypeError message. (Girgias)
-
-- Opcache:
- . Fixed bug GH-18294 (assertion failure zend_jit_ir.c). (nielsdos)
- . Fixed bug GH-18289 (Fix segfault in JIT). (Florian Engelhardt)
- . Fixed bug GH-18136 (tracing JIT floating point register clobbering on
- Windows and ARM64). (nielsdos)
-
-- OpenSSL:
- . Fix memory leak in openssl_sign() when passing invalid algorithm.
- (nielsdos)
- . Fix potential leaks when writing to BIO fails. (nielsdos)
-
-- PDO Firebird:
- . Fixed bug GH-18276 (persistent connection - "zend_mm_heap corrupted"
- with setAttribute()) (SakiTakamachi).
- . Fixed bug GH-17383 (PDOException has wrong code and message since PHP 8.4)
- (SakiTakamachi).
-
-- PDO Sqlite:
- . Fix memory leak on error return of collation callback. (nielsdos)
-
-- PgSql:
- . Fix uouv in pg_put_copy_end(). (nielsdos)
-
-- SPL:
- . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory).
- (nielsdos)
-
-- Standard:
- . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()).
- (Jakub Zelenka)
- . Fix resource leak in iptcembed() on error. (nielsdos)
-
-- Tests:
- . Address deprecated PHP 8.4 session options to prevent test failures.
- (willvar)
-
-- Zip:
- . Fix uouv when handling empty options in ZipArchive::addGlob(). (nielsdos)
- . Fix memory leak when handling a too long path in ZipArchive::addGlob().
- (nielsdos)
-
-10 Apr 2025, PHP 8.4.6
-
-- BCMath:
- . Fixed pointer subtraction for scale. (SakiTakamachi)
-
-- Core:
- . Fixed property hook backing value access in multi-level inheritance.
- (ilutov)
- . Fixed accidentally inherited default value in overridden virtual properties.
- (ilutov)
- . Fixed bug GH-17376 (Broken JIT polymorphism for property hooks added to
- child class). (ilutov)
- . Fixed bug GH-17913 (ReflectionFunction::isDeprecated() returns incorrect
- results for closures created from magic __call()). (timwolla)
- . Fixed bug GH-17941 (Stack-use-after-return with lazy objects and hooks).
- (nielsdos)
- . Fixed bug GH-17988 (Incorrect handling of hooked props without get hook in
- get_object_vars()). (ilutov)
- . Fixed bug GH-17998 (Skipped lazy object initialization on primed
- SIMPLE_WRITE cache). (ilutov)
- . Fixed bug GH-17998 (Assignment to backing value in set hook of lazy proxy
- calls hook again). (ilutov)
- . Fixed bug GH-17961 (use-after-free during dl()'ed module class destruction).
- (Arnaud)
- . Fixed bug GH-15367 (dl() of module with aliased class crashes in shutdown).
- (Arnaud)
- . Fixed OSS-Fuzz #403308724. (nielsdos)
- . Fixed bug GH-13193 again (Significant performance degradation in 'foreach').
- (nielsdos)
-
-- DBA:
- . Fixed assertion violation when opening the same file with dba_open
- multiple times. (chschneider)
-
-- DOM:
- . Fixed bug GH-17991 (Assertion failure dom_attr_value_write). (nielsdos)
- . Fix weird unpack behaviour in DOM. (nielsdos)
- . Fixed bug GH-18090 (DOM: Svg attributes and tag names are being lowercased).
- (nielsdos)
- . Fix xinclude destruction of live attributes. (nielsdos)
-
-- Fuzzer:
- . Fixed bug GH-18081 (Memory leaks in error paths of fuzzer SAPI).
- (Lung-Alexandra)
-
-- GD:
- . Fixed bug GH-17984 (calls with arguments as array with references).
- (David Carlier)
-
-- LDAP:
- . Fixed bug GH-18015 (Error messages for ldap_mod_replace are confusing).
- (nielsdos)
-
-- Mbstring:
- . Fixed bug GH-17989 (mb_output_handler crash with unset
- http_output_conv_mimetypes). (nielsdos)
-
-- Opcache:
- . Fixed bug GH-15834 (Segfault with hook "simple get" cache slot and minimal
- JIT). (nielsdos)
- . Fixed bug GH-17966 (Symfony JIT 1205 assertion failure). (nielsdos)
- . Fixed bug GH-18037 (SEGV Zend/zend_execute.c). (nielsdos)
- . Fixed bug GH-18050 (IN_ARRAY optimization in DFA pass is broken). (ilutov)
- . Fixed bug GH-18113 (stack-buffer-overflow ext/opcache/jit/ir/ir_sccp.c).
- (nielsdos)
- . Fixed bug GH-18112 (NULL access with preloading and INI option). (nielsdos)
- . Fixed bug GH-18107 (Opcache CFG jmp optimization with try-finally breaks
- the exception table). (nielsdos)
-
-- PDO:
- . Fix memory leak when destroying PDORow. (nielsdos)
-
-- PGSQL:
- . Fixed bug GH-18148 (pg_copy_from() regression with explicit \n terminator
- due to wrong offset check). (David Carlier)
-
-- Standard:
- . Fix memory leaks in array_any() / array_all(). (nielsdos)
-
-- SOAP:
- . Fixed bug #66049 (Typemap can break parsing in parse_packet_soap leading to
- a segfault) . (Remi)
-
-- SPL:
- . Fixed bug GH-18018 (RC1 data returned from offsetGet causes UAF in
- ArrayObject). (nielsdos)
-
-- Treewide:
- . Fixed bug GH-17736 (Assertion failure zend_reference_destroy()). (nielsdos)
-
-- Windows:
- . Fixed bug GH-17836 (zend_vm_gen.php shouldn't break on Windows line
- endings). (DanielEScherzer)
-
-27 Feb 2025, PHP 8.4.5
+?? ??? ????, PHP 8.5.0alpha1
- BCMath:
- . Fixed bug GH-17398 (bcmul memory leak). (SakiTakamachi)
-
-- Core:
- . Fixed bug GH-17623 (Broken stack overflow detection for variable
- compilation). (ilutov)
- . Fixed bug GH-17618 (UnhandledMatchError does not take
- zend.exception_ignore_args=1 into account). (timwolla)
- . Fix fallback paths in fast_long_{add,sub}_function. (nielsdos)
- . Fixed bug OSS-Fuzz #391975641 (Crash when accessing property backing value
- by reference). (ilutov)
- . Fixed bug GH-17718 (Calling static methods on an interface that has
- `__callStatic` is allowed). (timwolla)
- . Fixed bug GH-17713 (ReflectionProperty::getRawValue() and related methods
- may call hooks of overridden properties). (Arnaud)
- . Fixed bug GH-17916 (Final abstract properties should error).
- (DanielEScherzer)
- . Fixed bug GH-17866 (zend_mm_heap corrupted error after upgrading from
- 8.4.3 to 8.4.4). (nielsdos)
- . Fixed GHSA-rwp7-7vc6-8477 (Reference counting in php_request_shutdown
- causes Use-After-Free). (CVE-2024-11235) (ilutov)
-
-- DOM:
- . Fixed bug GH-17609 (Typo in error message: Dom\NO_DEFAULT_NS instead of
- Dom\HTML_NO_DEFAULT_NS). (nielsdos)
- . Fixed bug GH-17802 (\Dom\HTMLDocument querySelector attribute name is case
- sensitive in HTML). (nielsdos)
- . Fixed bug GH-17847 (xinclude destroys live node). (nielsdos)
- . Fix using Dom\Node with Dom\XPath callbacks. (nielsdos)
-
-- FFI:
- . Fix FFI Parsing of Pointer Declaration Lists. (davnotdev)
-
-- FPM:
- . Fixed bug GH-17643 (FPM with httpd ProxyPass encoded PATH_INFO env).
- (Jakub Zelenka)
-
-- GD:
- . Fixed bug GH-17703 (imagescale with both width and height negative values
- triggers only an Exception on width). (David Carlier)
- . Fixed bug GH-17772 (imagepalettetotruecolor crash with memory_limit=2M).
- (David Carlier)
-
-- LDAP:
- . Fixed bug GH-17704 (ldap_search fails when $attributes contains a
- non-packed array with numerical keys). (nielsdos, 7u83)
-
-- LibXML:
- . Fixed GHSA-wg4p-4hqh-c3g9 (Reocurrence of #72714). (nielsdos)
- . Fixed GHSA-p3x9-6h7p-cgfc (libxml streams use wrong `content-type` header
- when requesting a redirected resource). (CVE-2025-1219) (timwolla)
-
-- MBString:
- . Fixed bug GH-17503 (Undefined float conversion in mb_convert_variables).
- (cmb)
-
-- Opcache:
- . Fixed bug GH-17654 (Multiple classes using same trait causes function
- JIT crash). (nielsdos)
- . Fixed bug GH-17577 (JIT packed type guard crash). (nielsdos, Dmitry)
- . Fixed bug GH-17747 (Exception on reading property in register-based
- FETCH_OBJ_R breaks JIT). (Dmitry, nielsdos)
- . Fixed bug GH-17715 (Null pointer deref in observer API when calling
- cases() method on preloaded enum). (Bob)
- . Fixed bug GH-17868 (Cannot allocate memory with tracing JIT on 8.4.4).
- (nielsdos)
-
-- PDO_SQLite:
- . Fixed GH-17837 ()::getColumnMeta() on unexecuted statement segfaults).
- (cmb)
- . Fix cycle leak in sqlite3 setAuthorizer(). (nielsdos)
- . Fix memory leaks in pdo_sqlite callback registration. (nielsdos)
-
-- Phar:
- . Fixed bug GH-17808: PharFileInfo refcount bug. (nielsdos)
-
-- PHPDBG:
- . Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos)
- . Fix memory leak in phpdbg calling registered function. (nielsdos)
-
-- Reflection:
- . Fixed bug GH-15902 (Core dumped in ext/reflection/php_reflection.c).
- (DanielEScherzer)
- . Fixed missing final and abstract flags when dumping properties.
- (DanielEScherzer)
-
-- Standard:
- . Fixed bug #72666 (stat cache clearing inconsistent between file:// paths
- and plain paths). (Jakub Zelenka)
-
-- Streams:
- . Fixed bug GH-17650 (realloc with size 0 in user_filters.c). (nielsdos)
- . Fix memory leak on overflow in _php_stream_scandir(). (nielsdos)
- . Fixed GHSA-hgf5-96fm-v528 (Stream HTTP wrapper header check might omit
- basic auth header). (CVE-2025-1736) (Jakub Zelenka)
- . Fixed GHSA-52jp-hrpf-2jff (Stream HTTP wrapper truncate redirect location
- to 1024 bytes). (CVE-2025-1861) (Jakub Zelenka)
- . Fixed GHSA-pcmh-g36c-qc44 (Streams HTTP wrapper does not fail for headers
- without colon). (CVE-2025-1734) (Jakub Zelenka)
- . Fixed GHSA-v8xr-gpvj-cx9g (Header parser of `http` stream wrapper does not
- handle folded headers). (CVE-2025-1217) (Jakub Zelenka)
-
-- Windows:
- . Fixed phpize for Windows 11 (24H2). (bwoebi)
- . Fixed GH-17855 (CURL_STATICLIB flag set even if linked with shared lib).
- (cmb)
-
-- Zlib:
- . Fixed bug GH-17745 (zlib extension incorrectly handles object arguments).
- (nielsdos)
- . Fix memory leak when encoding check fails. (nielsdos)
- . Fix zlib support for large files. (nielsdos)
-
-13 Feb 2025, PHP 8.4.4
-
-- Core:
- . Fixed bug GH-17234 (Numeric parent hook call fails with assertion).
- (nielsdos)
- . Fixed bug GH-16892 (ini_parse_quantity() fails to parse inputs starting
- with 0x0b). (nielsdos)
- . Fixed bug GH-16886 (ini_parse_quantity() fails to emit warning for 0x+0).
- (nielsdos)
- . Fixed bug GH-17222 (__PROPERTY__ magic constant does not work in all
- constant expression contexts). (ilutov)
- . Fixed bug GH-17214 (Relax final+private warning for trait methods with
- inherited final). (ilutov)
- . Fixed NULL arithmetic during system program execution on Windows. (cmb,
- nielsdos)
- . Fixed potential OOB when checking for trailing spaces on Windows. (cmb)
- . Fixed bug GH-17408 (Assertion failure Zend/zend_exceptions.c).
- (nielsdos, ilutov)
- . Fix may_have_extra_named_args flag for ZEND_AST_UNPACK. (nielsdos)
- . Fix NULL arithmetic in System V shared memory emulation for Windows. (cmb)
- . Fixed bug GH-17597 (#[\Deprecated] does not work for __call() and
- __callStatic()). (timwolla)
-
-- DOM:
- . Fixed bug GH-17397 (Assertion failure ext/dom/php_dom.c). (nielsdos)
- . Fixed bug GH-17486 (Incorrect error line numbers reported in
- Dom\HTMLDocument::createFromString). (nielsdos)
- . Fixed bug GH-17481 (UTF-8 corruption in \Dom\HTMLDocument). (nielsdos)
- . Fixed bug GH-17500 (Segfault with requesting nodeName on nameless doctype).
- (nielsdos)
- . Fixed bug GH-17485 (upstream fix, Self-closing tag on void elements
- shouldn't be a parse error/warning in \Dom\HTMLDocument). (lexborisov)
- . Fixed bug GH-17572 (getElementsByTagName returns collections with
- tagName-based indexing). (nielsdos)
-
-- Enchant:
- . Fix crashes in enchant when passing null bytes. (nielsdos)
-
-- FTP:
- . Fixed bug GH-16800 (ftp functions can abort with EINTR). (nielsdos)
-
-- GD:
- . Fixed bug GH-17349 (Tiled truecolor filling looses single color
- transparency). (cmb)
- . Fixed bug GH-17373 (imagefttext() ignores clipping rect for palette
- images). (cmb)
- . Ported fix for libgd 223 (gdImageRotateGeneric() does not properly
- interpolate). (cmb)
- . Added support for reading GIFs without colormap to bundled libgd. (Andrew
- Burley, cmb)
-
-- Gettext:
- . Fixed bug GH-17400 (bindtextdomain SEGV on invalid domain).
- (David Carlier)
-
-- Intl:
- . Fixed bug GH-11874 (intl causing segfault in docker images). (nielsdos)
-
-- Opcache:
- . Fixed bug GH-15981 (Segfault with frameless jumps and minimal JIT).
- (nielsdos)
- . Fixed bug GH-17307 (Internal closure causes JIT failure). (nielsdos)
- . Fixed bug GH-17428 (Assertion failure ext/opcache/jit/zend_jit_ir.c:8940).
- (nielsdos)
- . Fixed bug GH-17564 (Potential UB when reading from / writing to struct
- padding). (ilutov)
-
-- PCNTL:
- . Fixed pcntl_setcpuaffinity exception type from ValueError to TypeError for
- the cpu mask argument with entries type different than int/string.
- (David Carlier)
-
-- PCRE:
- . Fixed bug GH-17122 (memory leak in regex). (nielsdos)
-
-- PDO:
- . Fixed a memory leak when the GC is used to free a PDOStatment. (Girgias)
- . Fixed a crash in the PDO Firebird Statement destructor. (nielsdos)
- . Fixed UAFs when changing default fetch class ctor args. (Girgias, nielsdos)
-
-- PgSql:
- . Fixed build failure when the constant PGRES_TUPLES_CHUNK is not present
- in the system. (chschneider)
-
-- Phar:
- . Fixed bug GH-17518 (offset overflow phar extractTo()). (nielsdos)
-
-- PHPDBG:
- . Fix crashes in function registration + test. (nielsdos, Girgias)
-
-- Session:
- . Fix type confusion with session SID constant. (nielsdos)
- . Fixed bug GH-17541 (ext/session NULL pointer dereferencement during
- ID reset). (Girgias)
-
-- SimpleXML:
- . Fixed bug GH-17409 (Assertion failure Zend/zend_hash.c:1730). (nielsdos)
-
-- SNMP:
- . Fixed bug GH-17330 (SNMP::setSecurity segfault on closed session).
- (David Carlier)
-
-- SPL:
- . Fixed bug GH-15833 (Segmentation fault (access null pointer) in
- ext/spl/spl_array.c). (nielsdos)
- . Fixed bug GH-17516 (SplFileTempObject::getPathInfo() Undefined behavior
- on invalid class). (David Carlier)
-
-- Standard:
- . Fixed bug GH-17447 (Assertion failure when array popping a self addressing
- variable). (nielsdos)
-
-- Windows:
- . Fixed clang compiler detection. (cmb)
-
-- Zip:
- . Fixed bug GH-17139 (Fix zip_entry_name() crash on invalid entry).
- (nielsdos)
-
-16 Jan 2025, PHP 8.4.3
-
-- BcMath:
- . Fixed bug GH-17049 (Correctly compare 0 and -0). (Saki Takamachi)
- . Fixed bug GH-17061 (Now Number::round() does not remove trailing zeros).
- (Saki Takamachi)
- . Fixed bug GH-17064 (Correctly round rounding mode with zero edge case).
- (Saki Takamachi)
- . Fixed bug GH-17275 (Fixed the calculation logic of dividend scale).
- (Saki Takamachi)
-
-- Core:
- . Fixed bug OSS-Fuzz #382922236 (Duplicate dynamic properties in hooked object
- iterator properties table). (ilutov)
- . Fixed unstable get_iterator pointer for hooked classes in shm on Windows.
- (ilutov)
- . Fixed bug GH-17106 (ZEND_MATCH_ERROR misoptimization). (ilutov)
- . Fixed bug GH-17162 (zend_array_try_init() with dtor can cause engine UAF).
- (nielsdos)
- . Fixed bug GH-17101 (AST->string does not reproduce constructor property
- promotion correctly). (nielsdos)
- . Fixed bug GH-17200 (Incorrect dynamic prop offset in hooked prop iterator).
- (ilutov)
- . Fixed bug GH-17216 (Trampoline crash on error). (nielsdos)
-
-- DBA:
- . Skip test if inifile is disabled. (orlitzky)
-
-- DOM:
- . Fixed bug GH-17145 (DOM memory leak). (nielsdos)
- . Fixed bug GH-17201 (Dom\TokenList issues with interned string replace).
- (nielsdos)
- . Fixed bug GH-17224 (UAF in importNode). (nielsdos)
-
-- Embed:
- . Make build command for program using embed portable. (dunglas)
-
-- FFI:
- . Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos)
- . Fix memory leak on ZEND_FFI_TYPE_CHAR conversion failure. (nielsdos)
- . Fixed bug GH-16013 and bug #80857 (Big endian issues). (Dmitry, nielsdos)
-
-- Fileinfo:
- . Fixed bug GH-17039 (PHP 8.4: Incorrect MIME content type). (nielsdos)
-
-- FPM:
- . Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already
- locked)). (Jakub Zelenka)
- . Fixed bug GH-17112 (Macro redefinitions). (cmb, nielsdos)
- . Fixed bug GH-17208 (bug64539-status-json-encoding.phpt fail on 32-bits).
- (nielsdos)
-
-- GD:
- . Fixed bug GH-16255 (Unexpected nan value in ext/gd/libgd/gd_filter.c).
- (nielsdos, cmb)
- . Ported fix for libgd bug 276 (Sometimes pixels are missing when storing
- images as BMPs). (cmb)
-
-- Gettext:
- . Fixed bug GH-17202 (Segmentation fault ext/gettext/gettext.c
- bindtextdomain()). (Michael Orlitzky)
-
-- Iconv:
- . Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos)
-
-- LDAP:
- . Fixed bug GH-17280 (ldap_search() fails when $attributes array has holes).
- (nielsdos)
-
-- LibXML:
- . Fixed bug GH-17223 (Memory leak in libxml encoding handling). (nielsdos)
-
-- MBString:
- . Fixed bug GH-17112 (Macro redefinitions). (nielsdos, cmb)
-
-- Opcache:
- . opcache_get_configuration() properly reports jit_prof_threshold. (cmb)
- . Fixed bug GH-17140 (Assertion failure in JIT trace exit with
- ZEND_FETCH_DIM_FUNC_ARG). (nielsdos, Dmitry)
- . Fixed bug GH-17151 (Incorrect RC inference of op1 of FETCH_OBJ and
- INIT_METHOD_CALL). (Dmitry, ilutov)
- . Fixed bug GH-17246 (GC during SCCP causes segfault). (Dmitry)
- . Fixed bug GH-17257 (UBSAN warning in ext/opcache/jit/zend_jit_vm_helpers.c).
- (nielsdos, Dmitry)
-
-- PCNTL:
- . Fix memory leak in cleanup code of pcntl_exec() when a non stringable
- value is encountered past the first entry. (Girgias)
-
-- PgSql:
- . Fixed bug GH-17158 (pg_fetch_result Shows Incorrect ArgumentCountError
- Message when Called With 1 Argument). (nielsdos)
- . Fixed further ArgumentCountError for calls with flexible
- number of arguments. (David Carlier)
-
-- Phar:
- . Fixed bug GH-17137 (Segmentation fault ext/phar/phar.c). (nielsdos)
-
-- SimpleXML:
- . Fixed bug GH-17040 (SimpleXML's unset can break DOM objects). (nielsdos)
- . Fixed bug GH-17153 (SimpleXML crash when using autovivification on
- document). (nielsdos)
-
-- Sockets:
- . Fixed bug GH-16276 (socket_strerror overflow handling with INT_MIN).
- (David Carlier / cmb)
- . Fixed overflow on SO_LINGER values setting, strengthening values check
- on SO_SNDTIMEO/SO_RCVTIMEO for socket_set_option().
- (David Carlier)
-
-- SPL:
- . Fixed bug GH-17198 (SplFixedArray assertion failure with get_object_vars).
- (nielsdos)
- . Fixed bug GH-17225 (NULL deref in spl_directory.c). (nielsdos)
-
-- Streams:
- . Fixed bug GH-17037 (UAF in user filter when adding existing filter name due
- to incorrect error handling). (nielsdos)
- . Fixed bug GH-16810 (overflow on fopen HTTP wrapper timeout value).
- (David Carlier)
- . Fixed bug GH-17067 (glob:// wrapper doesn't cater to CWD for ZTS builds).
- (cmb)
-
-- Windows:
- . Hardened proc_open() against cmd.exe hijacking. (cmb)
-
-- XML:
- . Fixed bug GH-1718 (unreachable program point in zend_hash). (nielsdos)
+ . 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)
-19 Dec 2024, PHP 8.4.2
+- 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)
-- BcMath:
- . Fixed bug GH-16978 (Avoid unnecessary padding with leading zeros).
- (Saki Takamachi)
+- CURL:
+ . Added CURLFOLLOW_ALL, CURLFOLLOW_OBEYCODE and CURLFOLLOW_FIRSTONLY
+ values for CURLOPT_FOLLOLOCATION curl_easy_setopt option. (David Carlier)
- COM:
- . Fixed bug GH-16991 (Getting typeinfo of non DISPATCH variant segfaults).
- (cmb)
-
-- Core:
- . Fixed bug GH-16344 (setRawValueWithoutLazyInitialization() and
- skipLazyInitialization() may change initialized proxy). (Arnaud)
- . Fix is_zend_ptr() huge block comparison. (nielsdos)
- . Fixed potential OOB read in zend_dirname() on Windows. (cmb)
- . Fixed bug GH-15964 (printf() can strip sign of -INF). (divinity76, cmb)
-
-- Curl:
- . Fix various memory leaks in curl mime handling. (nielsdos)
-
-- DBA:
- . Fixed bug GH-16990 (dba_list() is now zero-indexed instead of using
- resource ids) (kocsismate)
-
-- DOM:
- . Fixed bug GH-16906 (Reloading document can cause UAF in iterator).
- (nielsdos)
-
-- FPM:
- . Fixed bug GH-16932 (wrong FPM status output). (Jakub Zelenka, James Lucas)
-
-- GMP:
- . Fixed bug GH-16890 (array_sum() with GMP can loose precision (LLP64)).
- (cmb)
-
-- Opcache:
- . Fixed bug GH-16851 (JIT_G(enabled) not set correctly on other threads).
- (dktapps)
- . Fixed bug GH-16902 (Set of opcache tests fail zts+aarch64). (nielsdos)
- . Fixed bug GH-16879 (JIT dead code skipping does not update call_level).
- (nielsdos)
-
-- SAPI:
- . Fixed bug GH-16998 (UBSAN warning in rfc1867). (nielsdos)
-
-- PHPDBG:
- . Fixed bug GH-15208 (Segfault with breakpoint map and phpdbg_clear()).
- (nielsdos)
-
-- Standard:
- . Fixed bug GH-16905 (Internal iterator functions can't handle UNDEF
- properties). (nielsdos)
- . Fixed bug GH-16957 (Assertion failure in array_shift with
- self-referencing array). (nielsdos)
-
-- Streams:
- . Fixed network connect poll interuption handling. (Jakub Zelenka)
-
-- Windows:
- . Fixed bug GH-16849 (Error dialog causes process to hang). (cmb)
- . Windows Server 2025 is now properly reported. (cmb)
-
-21 Nov 2024, PHP 8.4.1
-
-- BcMath:
- . [RFC] Add bcfloor, bcceil and bcround to BCMath. (Saki Takamachi)
- . Improve performance. (Saki Takamachi, nielsdos)
- . Adjust bcround()'s $mode parameter to only accept the RoundingMode
- enum. (timwolla, saki)
- . Fixed LONG_MAX in BCMath ext. (Saki Takamachi)
- . Fixed bcdiv() div by one. (Saki Takamachi)
- . [RFC] Support object types in BCMath. (Saki Takamachi)
- . bcpow() performance improvement. (Jorg Sowa)
- . ext/bcmath: Check for scale overflow. (SakiTakamachi)
- . [RFC] ext/bcmath: Added bcdivmod. (SakiTakamachi)
- . Fix GH-15968 (Avoid converting objects to strings in operator calculations).
- (SakiTakamachi)
- . Fixed bug GH-16265 (Added early return case when result is 0)
- (Saki Takamachi).
- . Fixed bug GH-16262 (Fixed a bug where size_t underflows) (Saki Takamachi).
- . Fixed GH-16236 (Fixed a bug in BcMath\Number::pow() and bcpow() when
- raising negative powers of 0) (Saki Takamachi).
+ . Fixed property access of PHP objects wrapped in variant. (cmb)
+ . Fixed method calls for PHP objects wrapped in variant. (cmb)
- Core:
- . Added zend_call_stack_get implementation for NetBSD, DragonFlyBSD,
- Solaris and Haiku. (David Carlier)
- . Enabled ifunc checks on FreeBSD from the 12.x releases. (Freaky)
- . Changed the type of PHP_DEBUG and PHP_ZTS constants to bool. (haszi)
- . Fixed bug GH-13142 (Undefined variable name is shortened when contains \0).
- (nielsdos)
- . Fixed bug GH-13178 (Iterator positions incorrect when converting packed
- array to hashed). (ilutov)
- . Fixed zend fiber build for solaris default mode (32 bits). (David Carlier)
- . Fixed zend call stack size for macOs/arm64. (David Carlier)
- . Added support for Zend Max Execution Timers on FreeBSD. (Kévin Dunglas)
- . Ensure fiber stack is not backed by THP. (crrodriguez)
- . Implement GH-13609 (Dump wrapped object in WeakReference class). (nielsdos)
- . Added sparc64 arch assembly support for zend fiber. (Claudio Jeker)
- . Fixed GH-13581 no space available for TLS on NetBSD. (Paul Ripke)
- . Added fiber Sys-V loongarch64 support. (qiangxuhui)
- . Adjusted closure names to include the parent function's name. (timwolla)
- . Improve randomness of uploaded file names and files created by tempnam().
- (Arnaud)
- . Added gc and shutdown callbacks to zend_mm custom handlers.
- (Florian Engelhardt)
- . Fixed bug GH-14650 (Compute the size of pages before allocating memory).
- (Julien Voisin)
- . Fixed bug GH-11928 (The --enable-re2c-cgoto doesn't add the -g flag).
- (Peter Kokot)
- . Added the #[\Deprecated] attribute. (beberlei, timwolla)
- . Fixed GH-11389 (Allow suspending fibers in destructors). (Arnaud, trowski)
- . Fixed bug GH-14801 (Fix build for armv7). (andypost)
- . Implemented property hooks RFC. (ilutov)
- . Fix GH-14978 (The xmlreader extension phpize build). (Peter Kokot)
- . Throw Error exception when encountering recursion during comparison, rather
- than fatal error. (ilutov)
- . Added missing cstddef include for C++ builds. (cmb)
- . Updated build system scripts config.guess to 2024-07-27 and config.sub to
- 2024-05-27. (Peter Kokot)
- . Fixed bug GH-15240 (Infinite recursion in trait hook). (ilutov)
- . Fixed bug GH-15140 (Missing variance check for abstract set with asymmetric
- type). (ilutov)
- . Fixed bug GH-15181 (Disabled output handler is flushed again). (cmb)
- . Passing E_USER_ERROR to trigger_error() is now deprecated. (Girgias)
- . Fixed bug GH-15292 (Dynamic AVX detection is broken for MSVC). (nielsdos)
- . Using "_" as a class name is now deprecated. (Girgias)
- . Exiting a namespace now clears seen symbols. (ilutov)
- . The exit (and die) language constructs now behave more like a function.
- They can be passed liked callables, are affected by the strict_types
- declare statement, and now perform the usual type coercions instead of
- casting any non-integer value to a string.
- As such, passing invalid types to exit/die may now result in a TypeError
- being thrown. (Girgias)
- . Fixed bug GH-15438 (Hooks on constructor promoted properties without
- visibility are ignored). (ilutov)
- . Fixed bug GH-15419 (Missing readonly+hook incompatibility check for readonly
- classes). (ilutov)
- . Fixed bug GH-15187 (Various hooked object iterator issues). (ilutov)
- . Fixed bug GH-15456 (Crash in get_class_vars() on virtual properties).
- (ilutov)
- . Fixed bug GH-15501 (Windows HAVE__H macros defined to 1 or
- undefined). (Peter Kokot)
- . Implemented asymmetric visibility for properties. (ilutov)
- . Fixed bug GH-15644 (Asymmetric visibility doesn't work with hooks). (ilutov)
- . Implemented lazy objects RFC. (Arnaud)
- . Fixed bug GH-15686 (Building shared iconv with external iconv library).
- (Peter Kokot, zeriyoshi)
- . Fixed missing error when adding asymmetric visibility to unilateral virtual
- property. (ilutov)
- . Fixed bug GH-15693 (Unnecessary include in main.c bloats binary).
- (nielsdos)
- . Fixed bug GH-15731 (AllowDynamicProperties validation should error on
- enums). (DanielEScherzer)
- . Fixed bug GH-16040 (Use-after-free of object released in hook). (ilutov)
- . Fixed bug GH-16026 (Reuse of dtor fiber during shutdown). (Arnaud)
- . Fixed bug GH-15999 (zend_std_write_property() assertion failure with lazy
- objects). (Arnaud)
- . Fixed bug GH-15960 (Foreach edge cases with lazy objects). (Arnaud)
- . Fixed bug GH-16185 (Various hooked object iterator issues). (ilutov)
- . Fixed bug OSS-Fuzz #371445205 (Heap-use-after-free in attr_free).
- (nielsdos)
- . Fixed missing error when adding asymmetric visibility to static properties.
+ . Fixed bug GH-16665 (\array and \callable should not be usable in
+ class_alias). (nielsdos)
+ . Added PHP_BUILD_DATE constant. (cmb)
+ . Added support for Closures and first class callables in constant
+ expressions. (timwolla, Volker Dusch)
+ . Use `clock_gettime_nsec_np()` for high resolution timer on macOS
+ if available. (timwolla)
+ . Implement GH-15680 (Enhance zend_dump_op_array to properly represent
+ non-printable characters in string literals). (nielsdos, WangYihang)
+ . Add support for backtraces for fatal errors. (enorris)
+ . Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos)
+ . Improved error message of UnhandledMatchError for
+ zend.exception_string_param_max_len=0. (timwolla)
+ . Fixed bug GH-17959 (Relax missing trait fatal error to error exception).
(ilutov)
- . Fixed bug OSS-Fuzz #71407 (Null-dereference WRITE in
- zend_lazy_object_clone). (Arnaud)
- . Fixed bug GH-16574 (Incorrect error "undefined method" messages).
- (nielsdos)
- . Fixed bug GH-16577 (EG(strtod_state).freelist leaks with opcache.preload).
- (nielsdos)
- . Fixed bug GH-16615 (Assertion failure in zend_std_read_property). (Arnaud)
- . Fixed bug GH-16342 (Added ReflectionProperty::isLazy()). (Arnaud)
- . Fixed bug GH-16725 (Incorrect access check for non-hooked props in hooked
- object iterator). (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)
+ . Added the #[\NoDiscard] attribute to indicate that a function's return
+ value is important and should be consumed. (timwolla, Volker Dusch)
+ . Added the (void) cast to indicate that not using a value is intentional.
+ (timwolla, Volker Dusch)
+ . Added get_error_handler(), get_exception_handler() functions. (Arnaud)
+ . Fixed bug GH-15753 and GH-16198 (Bind traits before parent class). (ilutov)
+ . Added support for casts in constant expressions. (nielsdos)
- Curl:
- . Deprecated the CURLOPT_BINARYTRANSFER constant. (divinity76)
- . Bumped required libcurl version to 7.61.0. (Ayesh)
- . Added feature_list key to the curl_version() return value. (Ayesh)
- . Added constants CURL_HTTP_VERSION_3 (libcurl 7.66) and CURL_HTTP_VERSION_3ONLY
- (libcurl 7.88) as options for CURLOPT_HTTP_VERSION (Ayesh Karunaratne)
- . Added CURLOPT_TCP_KEEPCNT to set the number of probes to send before
- dropping the connection. (David Carlier)
- . Added CURLOPT_PREREQFUNCTION Curl option to set a custom callback
- after the connection is established, but before the request is
- performed. (Ayesh Karunaratne)
- . Added CURLOPT_SERVER_RESPONSE_TIMEOUT, which was formerly known as
- CURLOPT_FTP_RESPONSE_TIMEOUT. (Ayesh Karunaratne)
- . The CURLOPT_DNS_USE_GLOBAL_CACHE option is now silently ignored. (Ayesh Karunaratne)
- . Added CURLOPT_DEBUGFUNCTION as a Curl option. (Ayesh Karunaratne)
- . Fixed bug GH-16359 (crash with curl_setopt* CURLOPT_WRITEFUNCTION
- without null callback). (David Carlier)
- . Fixed bug GH-16723 (CURLMOPT_PUSHFUNCTION issues). (cmb)
+ . 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)
- Date:
- . Added DateTime[Immutable]::createFromTimestamp. (Marc Bennewitz)
- . Added DateTime[Immutable]::[get|set]Microsecond. (Marc Bennewitz)
- . Constants SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, and SUNFUNCS_RET_DOUBLE
- are now deprecated. (Jorg Sowa)
- . Fixed bug GH-13773 (DatePeriod not taking into account microseconds for end
- date). (Mark Bennewitz, Derick)
+ . Fix undefined behaviour problems regarding integer overflow in extreme edge
+ cases. (nielsdos, cmb, ilutov)
-- DBA:
- . Passing null or false to dba_key_split() is deprecated. (Grigias)
+- DOM:
+ . Added Dom\Element::$outerHTML. (nielsdos)
+ . Added Dom\Element::insertAdjacentHTML(). (nielsdos)
-- Debugging:
- . Fixed bug GH-15923 (GDB: Python Exception :
- exceptions must derive from BaseException). (nielsdos)
+- Enchant:
+ . Added enchant_dict_remove_from_session(). (nielsdos)
+ . Added enchant_dict_remove(). (nielsdos)
-- DOM:
- . Added DOMNode::compareDocumentPosition(). (nielsdos)
- . Implement #53655 (Improve speed of DOMNode::C14N() on large XML documents).
- (nielsdos)
- . Fix cloning attribute with namespace disappearing namespace. (nielsdos)
- . Implement DOM HTML5 parsing and serialization RFC. (nielsdos)
- . Fix DOMElement->prefix with empty string creates bogus prefix. (nielsdos)
- . Handle OOM more consistently. (nielsdos)
- . Implemented "Improve callbacks in ext/dom and ext/xsl" RFC. (nielsdos)
- . Added DOMXPath::quote() static method. (divinity76)
- . Implemented opt-in ext/dom spec compliance RFC. (nielsdos)
- . Fixed bug #79701 (getElementById does not correctly work with duplicate
- definitions). (nielsdos)
- . Implemented "New ext-dom features in PHP 8.4" RFC. (nielsdos)
- . Fixed GH-14698 (segfault on DOM node dereference). (David Carlier)
- . Improve support for template elements. (nielsdos)
- . Fix trampoline leak in xpath callables. (nielsdos)
- . Throw instead of silently failing when creating a too long text node in
- (DOM)ParentNode and (DOM)ChildNode. (nielsdos)
- . Fixed bug GH-15192 (Segmentation fault in dom extension
- (html5_serializer)). (nielsdos)
- . Deprecated DOM_PHP_ERR constant. (nielsdos)
- . Removed DOMImplementation::getFeature(). (nielsdos)
- . Fixed bug GH-15331 (Element::$substitutedNodeValue test failed). (nielsdos)
- . Fixed bug GH-15570 (Segmentation fault (access null pointer) in
- ext/dom/html5_serializer.c). (nielsdos)
- . Fixed bug GH-13988 (Storing DOMElement consume 4 times more memory in
- PHP 8.1 than in PHP 8.0). (nielsdos)
- . Fix XML serializer errata: xmlns="" serialization should be allowed.
- (nielsdos)
- . Fixed bug GH-15910 (Assertion failure in ext/dom/element.c). (nielsdos)
- . Fix unsetting DOM properties. (nielsdos)
- . Fixed bug GH-16190 (Using reflection to call Dom\Node::__construct
- causes assertion failure). (nielsdos)
- . Fix edge-case in DOM parsing decoding. (nielsdos)
- . Fixed bug GH-16465 (Heap buffer overflow in DOMNode->getElementByTagName).
- (nielsdos)
- . Fixed bug GH-16594 (Assertion failure in DOM -> before). (nielsdos)
+- EXIF:
+ . Add OffsetTime* Exif tags. (acc987)
- Fileinfo:
- . Update to libmagic 5.45. (nielsdos)
- . Fixed bug #65106 (PHP fails to compile ext/fileinfo). (Guillaume Outters)
+ . Upgrade to file 5.46. (nielsdos)
+ . Change return type of finfo_close() to true. (timwolla)
- FPM:
- . Implement GH-12385 (flush headers without body when calling flush()).
- (nielsdos)
- . Added DragonFlyBSD system to the list which set FPM_BACKLOG_DEFAULT
- to SOMAXCONN. (David Carlier)
- . /dev/poll events.mechanism for Solaris/Illumos setting had been retired.
- (David Carlier)
- . Added memory peak to the scoreboard / status page. (Flávio Heleno)
-
-- FTP:
- . Removed the deprecated inet_ntoa call support. (David Carlier)
- . Fixed bug #63937 (Upload speed 10 times slower with PHP). (nielsdos)
+ . Fixed GH-17645 (FPM with httpd ProxyPass does not decode script path).
+ (Jakub Zelenka)
- GD:
- . Fix parameter numbers and missing alpha check for imagecolorset().
- (Giovanni Giacobbi)
- . imagepng/imagejpeg/imagewep/imageavif now throw an exception on
- invalid quality parameter. (David Carlier)
- . Check overflow/underflow for imagescale/imagefilter. (David Carlier)
- . Added gdImageClone to bundled libgd. (David Carlier)
-
-- Gettext:
- . bind_textdomain_codeset, textdomain and d(*)gettext functions
- now throw an exception on empty domain. (David Carlier)
-
-- GMP:
- . The GMP class is now final and cannot be extended anymore. (Girgias)
- . RFC: Change GMP bool cast behavior. (Saki Takamachi)
-
-- Hash:
- . Changed return type of hash_update() to true. (nielsdos)
- . Added HashContext::__debugInfo(). (timwolla)
- . Deprecated passing incorrect data types for options to ext/hash functions.
- (nielsdos)
- . Added SSE2 and SHA-NI implementation of SHA-256. (timwolla, Colin Percival,
- Graham Percival)
- . Fix GH-15384 (Build fails on Alpine / Musl for amd64). (timwolla)
- . Fixed bug GH-15742 (php_hash_sha.h incompatible with C++). (cmb)
-
-- IMAP:
- . Moved to PECL. (Derick Rethans)
+ . Fixed bug #68629 (Transparent artifacts when using imagerotate). (pierre,
+ cmb)
+ . Fixed bug #64823 (ZTS GD fails to to find system TrueType font). (cmb)
- Intl:
- . Added IntlDateFormatter::PATTERN constant. (David Carlier)
- . Fixed Numberformatter::__construct when the locale is invalid, now
- throws an exception. (David Carlier)
- . Added NumberFormatter::ROUND_TOWARD_ZERO and ::ROUND_AWAY_FROM_ZERO as
- aliases for ::ROUND_DOWN and ::ROUND_UP. (Jorg Sowa)
- . Added NumberFormatter::ROUND_HALFODD. (Ayesh Karunaratne)
- . Added PROPERTY_IDS_UNARY_OPERATOR, PROPERTY_ID_COMPAT_MATH_START and
- PROPERTY_ID_COMPAT_MATH_CONTINUE constants. (David Carlier)
- . Added IntlDateFormatter::getIanaID/intltz_get_iana_id method/function.
- (David Carlier)
- . Set to C++17 standard for icu 74 and onwards. (David Carlier)
- . resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
- ResourceBundle object now throw:
- - TypeError for invalid offset types
- - ValueError for an empty string
- - ValueError if the integer index does not fit in a signed 32 bit integer
- . ResourceBundle::get() now has a tentative return type of:
- ResourceBundle|array|string|int|null
- . Added the new Grapheme function grapheme_str_split. (youkidearitai)
- . Added IntlDateFormatter::parseToCalendar. (David Carlier)
- . Added SpoofChecker::setAllowedChars to set unicode chars ranges.
+ . 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)
-- LDAP:
- . Added LDAP_OPT_X_TLS_PROTOCOL_MAX/LDAP_OPT_X_TLS_PROTOCOL_TLS1_3
- constants. (StephenWall)
-
-- LibXML:
- . Added LIBXML_RECOVER constant. (nielsdos)
- . libxml_set_streams_context() now throws immediately on an invalid context
- instead of at the use-site. (nielsdos)
- . Added LIBXML_NO_XXE constant. (nielsdos)
-
-- MBString:
- . Added mb_trim, mb_ltrim and mb_rtrim. (Yuya Hamada)
- . Added mb_ucfirst and mb_lcfirst. (Yuya Hamada)
- . Updated Unicode data tables to Unicode 15.1. (Ayesh Karunaratne)
- . Fixed bug GH-15824 (mb_detect_encoding(): Argument $encodings contains
- invalid encoding "UTF8"). (Yuya Hamada)
- . Updated Unicode data tables to Unicode 16.0. (Ayesh Karunaratne)
-
-- Mysqli:
- . The mysqli_ping() function and mysqli::ping() method are now deprecated,
- as the reconnect feature was removed in PHP 8.2. (Kamil Tekiela)
- . The mysqli_kill() function and mysqli::kill() method are now deprecated.
- If this functionality is needed a SQL "KILL" command can be used instead.
- (Kamil Tekiela)
- . The mysqli_refresh() function and mysqli::refresh() method are now deprecated.
- If this functionality is needed a SQL "FLUSH" command can be used instead.
- (Kamil Tekiela)
- . Passing explicitly the $mode parameter to mysqli_store_result() has been
- deprecated. As the MYSQLI_STORE_RESULT_COPY_DATA constant was only used in
- conjunction with this function it has also been deprecated. (Girgias)
-
-- MySQLnd:
- . Fixed bug GH-13440 (PDO quote bottleneck). (nielsdos)
- . Fixed bug GH-10599 (Apache crash on Windows when using a self-referencing
- anonymous function inside a class with an active mysqli connection).
+- MySQLi:
+ . Fixed bugs GH-17900 and GH-8084 (calling mysqli::__construct twice).
(nielsdos)
-- Opcache:
- . Added large shared segments support for FreeBSD. (David Carlier)
- . If JIT is enabled, PHP will now exit with a fatal error on startup in case
- of JIT startup initialization issues. (danog)
- . Increased the maximum value of opcache.interned_strings_buffer to 32767 on
- 64bit archs. (Arnaud)
- . Fixed bug GH-13834 (Applying non-zero offset 36 to null pointer in
- zend_jit.c). (nielsdos)
- . Fixed bug GH-14361 (Deep recursion in zend_cfg.c causes segfault).
- (nielsdos)
- . Fixed bug GH-14873 (PHP 8.4 min function fails on typed integer).
- (nielsdos)
- . Fixed bug GH-15490 (Building of callgraph modifies preloaded symbols).
- (ilutov)
- . Fixed bug GH-15178 (Assertion in tracing JIT on hooks). (ilutov)
- . Fixed bug GH-15657 (Segmentation fault in dasm_x86.h). (nielsdos)
- . Added opcache_jit_blacklist() function. (Bob)
- . Fixed bug GH-16009 (Segmentation fault with frameless functions and
- undefined CVs). (nielsdos)
- . Fixed bug GH-16186 (Assertion failure in Zend/zend_operators.c). (Arnaud)
- . Fixed bug GH-16572 (Incorrect result with reflection in low-trigger JIT).
- (nielsdos)
- . Fixed GH-16839 (Error on building Opcache JIT for Windows ARM64). (cmb)
+- MySQLnd:
+ . Added mysqlnd.collect_memory_statistics to ini quick reference.
+ (hauk92)
-- OpenSSL:
- . Fixed bug #80269 (OpenSSL sets Subject wrong with extraattribs parameter).
- (Jakub Zelenka)
- . Implement request #48520 (openssl_csr_new - allow multiple values in DN).
- (Jakub Zelenka)
- . Introduced new serial_hex parameter to openssl_csr_sign. (Jakub Zelenka,
- Florian Sowade)
- . Added X509_PURPOSE_OCSP_HELPER and X509_PURPOSE_TIMESTAMP_SIGN constants.
- (Vincent Jardin)
- . Bumped minimum required OpenSSL version to 1.1.1. (Ayesh Karunaratne)
- . Added compile-time option --with-openssl-legacy-provider to enable legacy
- provider. (Adam Saponara)
- . Added support for Curve25519 + Curve448 based keys. (Manuel Mausz)
- . Fixed bug GH-13343 (openssl_x509_parse should not allow omitted seconds in
- UTCTimes). (Jakub Zelenka)
- . Bumped minimum required OpenSSL version to 1.1.0. (cmb)
- . Implement GH-13514 PASSWORD_ARGON2 from OpenSSL 3.2. (Remi)
+- OPcache:
+ . Fixed ZTS OPcache build on Cygwin. (cmb)
+ . Added opcache.file_cache_read_only. (Samuel Melrose)
- Output:
- . Clear output handler status flags during handler initialization. (haszi)
- . Fixed bug with url_rewriter.hosts not used by output_add_rewrite_var().
- (haszi)
-
-- PCNTL:
- . Added pcntl_setns for Linux. (David Carlier)
- . Added pcntl_getcpuaffinity/pcntl_setcpuaffinity. (David Carlier)
- . Updated pcntl_get_signal_handler signal id upper limit to be
- more in line with platforms limits. (David Carlier)
- . Added pcntl_getcpu for Linux/FreeBSD/Solaris/Illumos. (David Carlier)
- . Added pcntl_getqos_class/pcntl_setqos_class for macOs. (David Carlier)
- . Added SIGCKPT/SIGCKPTEXIT constants for DragonFlyBSD. (David Carlier)
- . Added FreeBSD's SIGTRAP handling to pcntl_siginfo_to_zval. (David Carlier)
- . Added POSIX pcntl_waitid. (Vladimir Vrzić)
- . Fixed bug GH-16769: (pcntl_sigwaitinfo aborts on signal value
- as reference). (David Carlier)
+ . Fixed calculation of aligned buffer size. (cmb)
- PCRE:
- . Upgrade bundled pcre2lib to version 10.43. (nielsdos)
- . Add "/r" modifier. (Ayesh)
- . Upgrade bundled pcre2lib to version 10.44. (Ayesh)
- . Fixed GH-16189 (underflow on offset argument). (David Carlier)
- . Fix UAF issues with PCRE after request shutdown. (nielsdos)
-
-- PDO:
- . Fixed setAttribute and getAttribute. (SakiTakamachi)
- . Implemented PDO driver-specific subclasses RFC. (danack, kocsismate)
- . Added support for PDO driver-specific SQL parsers. (Matteo Beccati)
- . Fixed bug GH-14792 (Compilation failure on pdo_* extensions).
- (Peter Kokot)
- . mysqlnd: support ER_CLIENT_INTERACTION_TIMEOUT. (Appla)
- . The internal header php_pdo_int.h is no longer installed; it is not
- supposed to be used by PDO drivers. (cmb)
- . Fixed bug GH-16167 (Prevent mixing PDO sub-classes with different DSN).
- (kocsismate)
- . Fixed bug GH-16314 ("Pdo\Mysql object is uninitialized" when opening a
- persistent connection). (kocsismate)
-
-- PDO_DBLIB:
- . Fixed setAttribute and getAttribute. (SakiTakamachi)
- . Added class Pdo\DbLib. (danack, kocsismate)
-
-- PDO_Firebird:
- . Fixed setAttribute and getAttribute. (SakiTakamachi)
- . Feature: Add transaction isolation level and mode settings to pdo_firebird.
- (SakiTakamachi)
- . Added class Pdo\Firebird. (danack, kocsismate)
- . Added Pdo\Firebird::ATTR_API_VERSION. (SakiTakamachi)
- . Added getApiVersion() and removed from getAttribute().
- (SakiTakamachi)
- . Supported Firebird 4.0 datatypes. (sim1984)
- . Support proper formatting of time zone types. (sim1984)
- . Fixed GH-15604 (Always make input parameters nullable). (sim1984)
-
-- PDO_MYSQL:
- . Fixed setAttribute and getAttribute. (SakiTakamachi)
- . Added class Pdo\Mysql. (danack, kocsismate)
- . Added custom SQL parser. (Matteo Beccati)
- . Fixed GH-15949 (PDO_MySQL not properly quoting PDO_PARAM_LOB binary
- data). (mbeccati, lcobucci)
-
-- PDO_ODBC:
- . Added class Pdo\Odbc. (danack, kocsismate)
+ . Upgraded to pre2lib from 10.44 to 10.45. (nielsdos)
+ . Remove PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK from pcre compile options.
+ (mvorisek)
- PDO_PGSQL:
- . Fixed GH-12423, DSN credentials being prioritized over the user/password
- PDO constructor arguments. (SakiTakamachi)
- . Fixed native float support with pdo_pgsql query results. (Yurunsoft)
- . Added class Pdo\Pgsql. (danack, kocsismate)
- . Retrieve the memory usage of the query result resource. (KentarouTakeda)
- . Added Pdo\Pgsql::setNoticeCallBack method to receive DB notices.
- (outtersg)
- . Added custom SQL parser. (Matteo Beccati)
- . Fixed GH-15986 (Double-free due to Pdo\Pgsql::setNoticeCallback()). (cmb,
- nielsdos)
- . Fixed GH-12940 (Using PQclosePrepared when available instead of
- the DEALLOCATE command to free statements resources). (David Carlier)
- . Remove PGSQL_ATTR_RESULT_MEMORY_SIZE constant as it is provided by
- the new PDO Subclass as Pdo\Pgsql::ATTR_RESULT_MEMORY_SIZE. (Girgias)
+ . 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:
- . Added class Pdo\Sqlite. (danack, kocsismate)
- . Fixed bug #81227 (PDO::inTransaction reports false when in transaction).
- (nielsdos)
- . Added custom SQL parser. (Matteo Beccati)
-
-- PHPDBG:
- . array out of bounds, stack overflow handled for segfault handler on windows.
- (David Carlier)
- . Fixed bug GH-16041 (Support stack limit in phpdbg). (Arnaud)
+ . throw on null bytes / resolve GH-13952 (divinity76).
+ . Implement GH-17321: Add setAuthorizer to Pdo\Sqlite. (nielsdos)
- PGSQL:
- . Added the possibility to have no conditions for pg_select. (OmarEmaraDev)
- . Persistent connections support the PGSQL_CONNECT_FORCE_RENEW flag.
+ . 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_result_memory_size to get the query result memory usage.
- (KentarouTakeda)
- . Added pg_change_password to alter an user's password. (David Carlier)
- . Added pg_put_copy_data/pg_put_copy_end to send COPY commands and signal
- the end of the COPY. (David Carlier)
- . Added pg_socket_poll to poll on the connection. (David Carlier)
- . Added pg_jit to get infos on server JIT support. (David Carlier)
- . Added pg_set_chunked_rows_size to fetch results per chunk. (David Carlier)
- . pg_convert/pg_insert/pg_update/pg_delete ; regexes are now cached.
+ . Added pg_service to get the connection current service identifier.
(David Carlier)
-- Phar:
- . Fixed bug GH-12532 (PharData created from zip has incorrect timestamp).
- (nielsdos)
-
- POSIX:
- . Added POSIX_SC_CHILD_MAX and POSIX_SC_CLK_TCK constants. (Jakub Zelenka)
- . Updated posix_isatty to set the error number on file descriptors.
- (David Carlier)
-
-- PSpell:
- . Moved to PECL. (Derick Rethans)
+ . 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)
- Random:
- . Fixed bug GH-15094 (php_random_default_engine() is not C++ conforming).
- (cmb)
- . lcg_value() is now deprecated. (timwolla)
-
-- Readline:
- . Fixed readline_info, rl_line_buffer_length/rl_len globals on update.
- (David Carlier)
- . Fixed bug #51558 (Shared readline build fails). (Peter Kokot)
- . Fixed UAF with readline_info(). (David Carlier)
+ . Moves from /dev/urandom usage to arc4random_buf on Haiku. (David Carlier)
- Reflection:
- . Implement GH-12908 (Show attribute name/class in ReflectionAttribute dump).
- (nielsdos)
- . Make ReflectionGenerator::getFunction() legal after generator termination.
- (timwolla)
- . Added ReflectionGenerator::isClosed(). (timwolla)
- . Fixed bug GH-15718 (Segfault on ReflectionProperty::get{Hook,Hooks}() on
- dynamic properties). (DanielEScherzer)
- . Fixed bug GH-15694 (ReflectionProperty::isInitialized() is incorrect for
- hooked properties). (ilutov)
- . Add missing ReflectionProperty::hasHook[s]() methods. (ilutov)
- . Add missing ReflectionProperty::isFinal() method. (ilutov)
- . Fixed bug GH-16122 (The return value of ReflectionFunction::getNamespaceName()
- and ReflectionFunction::inNamespace() for closures is incorrect). (timwolla)
- . Fixed bug GH-16162 (No ReflectionProperty::IS_VIRTUAL) (DanielEScherzer)
- . Fixed the name of the second parameter of
- ReflectionClass::resetAsLazyGhost(). (Arnaud)
+ . Added ReflectionConstant::getExtension() and ::getExtensionName().
+ (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)
- Session:
- . INI settings session.sid_length and session.sid_bits_per_character are now
- deprecated. (timwolla)
- . Emit warnings for non-positive values of session.gc_divisor and negative values
- of session.gc_probability. (Jorg Sowa)
- . Fixed bug GH-16590 (UAF in session_encode()). (nielsdos)
+ . 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)
- SimpleXML:
- . Fix signature of simplexml_import_dom(). (nielsdos)
+ . Fixed bug GH-12231 (SimpleXML xpath should warn when returning other return
+ types than node lists). (nielsdos)
- SNMP:
- . Removed the deprecated inet_ntoa call support. (David Carlier)
+ . 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:
- . Add support for clark notation for namespaces in class map. (lxShaDoWxl)
- . Mitigate #51561 (SoapServer with a extented class and using sessions,
- lost the setPersistence()). (nielsdos)
- . Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if
- wsdl operation !has output). (nielsdos)
- . Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos)
- . Fixed bug GH-11941 (soap with session persistence will silently fail when
- "session" built as a shared object). (nielsdos)
- . Passing an int to SoapServer::addFunction() is now deprecated.
- If all PHP functions need to be provided flatten the array returned by
- get_defined_functions(). (Girgias)
- . The SOAP_FUNCTIONS_ALL constant is now deprecated. (Girgias)
- . Fixed bug #61525 (SOAP functions require at least one space after HTTP
- header colon). (nielsdos)
- . Implement request #47317 (SoapServer::__getLastResponse()). (nielsdos)
+ . Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action"
+ header is correct). (nielsdos)
- Sockets:
- . Removed the deprecated inet_ntoa call support. (David Carlier)
- . Added the SO_EXECLUSIVEADDRUSE windows constant. (David Carlier)
- . Added the SOCK_CONN_DGRAM/SOCK_DCCP netbsd constants. (David Carlier)
- . Added multicast group support for ipv4 on FreeBSD. (jonathan@tangential.ca)
- . Added the TCP_SYNCNT constant for Linux to set number of attempts to send
- SYN packets from the client. (David Carlier)
- . Added the SO_EXCLBIND constant for exclusive socket binding on illumos/solaris.
+ . Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage.
+ (David Carlier)
+ . Added TCP_FUNCTION_BLK to change the TCP stack algorithm on FreeBSD.
(David Carlier)
- . Updated the socket_create_listen backlog argument default value to SOMAXCONN.
+ . socket_set_option() catches possible overflow with SO_RCVTIMEO/SO_SNDTIMEO
+ with timeout setting on windows. (David Carlier)
+ . Added TCP_FUNCTION_ALIAS, TCP_REUSPORT_LB_NUMA, TCP_REUSPORT_LB_NUMA_NODOM,
+ TCP_REUSPORT_LB_CURDOM, TCP_BBR_ALGORITHM constants.
+ . socket_create_listen() throws an exception on invalid port value.
(David Carlier)
- . Added the SO_NOSIGPIPE constant to control the generation of SIGPIPE for
- macOs and FreeBSD. (David Carlier)
- . Added SO_LINGER_SEC for macOs, true equivalent of SO_LINGER in other platforms.
+ . socket_bind() throws an exception on invalid port value.
(David Carlier)
- . Add close-on-exec on socket created with socket_accept on unixes. (David Carlier)
- . Added IP_PORTRANGE* constants for BSD systems to control ephemeral port
- ranges. (David Carlier)
- . Added SOCK_NONBLOCK/SOCK_CLOEXEC constants for socket_create and
- socket_create_pair to apply O_NONBLOCK/O_CLOEXEC flags to the
- newly created sockets. (David Carlier)
- . Added SO_BINDTOIFINDEX to bind a socket to an interface index.
+ . 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)
+ . Added IP_BINDANY for a socket to bind to any address. (David Carlier)
+ . Added SO_BUSY_POOL to reduce packets poll latency. (David Carlier)
- Sodium:
- . Add support for AEGIS-128L and AEGIS-256. (jedisct1)
- . Enable AES-GCM on aarch64 with the ARM crypto extensions. (jedisct1)
-
-- SPL:
- . Implement SeekableIterator for SplObjectStorage. (nielsdos)
- . The SplFixedArray::__wakeup() method has been deprecated as it implements
- __serialize() and __unserialize() which need to be overwritten instead.
- (TysonAndre)
- . Passing a non-empty string for the $escape parameter of:
- - SplFileObject::setCsvControl()
- - SplFileObject::fputcsv()
- - SplFileObject::fgetcsv()
- is now deprecated. (Girgias)
+ . Fix overall theorical overflows on zend_string buffer allocations.
+ (David Carlier/nielsdos)
- Standard:
- . Implement GH-12188 (Indication for the int size in phpinfo()). (timwolla)
- . Partly fix GH-12143 (Incorrect round() result for 0.49999999999999994).
- (timwolla)
- . Fix GH-12252 (round(): Validate the rounding mode). (timwolla)
- . Increase the default BCrypt cost to 12. (timwolla)
- . Fixed bug GH-12592 (strcspn() odd behaviour with NUL bytes and empty mask).
- (nielsdos)
- . Removed the deprecated inet_ntoa call support. (David Carlier)
- . Cast large floats that are within int range to int in number_format so
- the precision is not lost. (Marc Bennewitz)
- . Add support for 4 new rounding modes to the round() function. (Jorg Sowa)
- . debug_zval_dump() now indicates whether an array is packed. (Max Semenik)
- . Fix GH-12143 (Optimize round). (SakiTakamachi)
- . Changed return type of long2ip to string from string|false. (Jorg Sowa)
- . Fix GH-12143 (Extend the maximum precision round can handle by one digit).
- (SakiTakamachi)
- . Added the http_get_last_response_headers() and
- http_clear_last_response_headers() that allows retrieving the same content
- as the magic $http_response_header variable.
- . Add php_base64_encode_ex() API. (Remi)
- . Implemented "Raising zero to the power of negative number" RFC. (Jorg Sowa)
- . Added array_find(), array_find_key(), array_all(), and array_any(). (josh)
- . Change highlight_string() and print_r() return type to string|true. (Ayesh)
- . Fix references in request_parse_body() options array. (nielsdos)
- . Add RoundingMode enum. (timwolla, saki)
- . Unserializing the uppercase 'S' tag is now deprecated. (timwolla)
- . Enables crc32 auxiliary detection on OpenBSD. (David Carlier)
- . Passing a non-empty string for the $escape parameter of:
- - fputcsv()
- - fgetcsv()
- - str_getcsv()
- is now deprecated. (Girgias)
- . The str_getcsv() function now throws ValueErrors when the $separator and
- $enclosure arguments are not one byte long, or if the $escape is not one
- byte long or the empty string. This aligns the behaviour to be identical
- to that of fputcsv() and fgetcsv(). (Girgias)
- . php_uname() now throws ValueErrors on invalid inputs. (Girgias)
- . The "allowed_classes" option for unserialize() now throws TypeErrors and
- ValueErrors if it is not an array of class names. (Girgias)
- . Implemented GH-15685 (improve proc_open error reporting on Windows). (cmb)
- . Add support for backed enums in http_build_query(). (ilutov)
- . Fixed bug GH-15982 (Assertion failure with array_find when references are
- involved). (nielsdos)
- . Fixed parameter names of fpow() to be identical to pow(). (Girgias)
+ . 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)
- Streams:
- . Implemented GH-15155 (Stream context is lost when custom stream wrapper is
- being filtered). (Quentin Dreyer)
+ . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows).
+ (cmb)
-- Tidy:
- . Failures in the constructor now throw exceptions rather than emitting
- warnings and having a broken object. (nielsdos)
- . Add tidyNode::getNextSibling() and tidyNode::getPreviousSibling().
- (nielsdos)
+- Tests:
+ . Allow to shuffle tests even in non-parallell mode. (dhuang00)
- Windows:
- . Update the icon of the Windows executables, e.g. php.exe. (Ayesh,
- Nurudin Imširović)
- . Fixed bug GH-16199 (GREP_HEADER() is broken). (Peter Kokot)
-
-- XML:
- . Added XML_OPTION_PARSE_HUGE parser option. (nielsdos)
- . Fixed bug #81481 (xml_get_current_byte_index limited to 32-bit numbers on
- 64-bit builds). (nielsdos)
- . The xml_set_object() function has been deprecated. (Girgias)
- . Passing non-callable strings to the xml_set_*_handler() functions is now
- deprecated. (Girgias)
-
-- XMLReader:
- . Declares class constant types. (Ayesh)
- . Add XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString(). (nielsdos)
- . Fixed bug GH-15123 (var_dump doesn't actually work on XMLReader).
- (nielsdos)
+ . Fixed bug GH-10992 (Improper long path support for relative paths). (cmb,
+ nielsdos)
+ . Fixed bug GH-16843 (Windows phpize builds ignore source subfolders). (cmb)
- XMLWriter:
- . Add XMLWriter::toStream(), XMLWriter::toUri(), XMLWriter::toMemory(). (nielsdos)
+ . Improved performance and reduce memory consumption. (nielsdos)
- XSL:
- . Implement request #64137 (XSLTProcessor::setParameter() should allow both
- quotes to be used). (nielsdos)
- . Implemented "Improve callbacks in ext/dom and ext/xsl" RFC. (nielsdos)
- . Added XSLTProcessor::$maxTemplateDepth and XSLTProcessor::$maxTemplateVars.
- (nielsdos)
- . Fix trampoline leak in xpath callables. (nielsdos)
+ . Implement request #30622 (make $namespace parameter functional). (nielsdos)
+
+- 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)
+
-- Zip:
- . Added ZipArchive::ER_TRUNCATED_ZIP added in libzip 1.11. (Remi)
+<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c
index 81136bee4daa9..e99993204b6f9 100644
--- a/TSRM/TSRM.c
+++ b/TSRM/TSRM.c
@@ -778,7 +778,7 @@ TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void)
return 0;
#elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__) && \
!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__) && \
- !defined(__HAIKU__)
+ !defined(__HAIKU__) && !defined(__CYGWIN__)
size_t ret;
asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
@@ -786,7 +786,7 @@ TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void)
return ret;
#elif defined(__i386__) && defined(__GNUC__) && !defined(__FreeBSD__) && \
!defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__MUSL__) && \
- !defined(__HAIKU__)
+ !defined(__HAIKU__) && !defined(__CYGWIN__)
size_t ret;
asm ("leal _tsrm_ls_cache@ntpoff,%0"
diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c
index 1fe2a47a87c24..4c8fc9d19aa9a 100644
--- a/TSRM/tsrm_win32.c
+++ b/TSRM/tsrm_win32.c
@@ -636,7 +636,7 @@ TSRM_API int shmget(key_t key, size_t size, int flags)
{/*{{{*/
shm_pair *shm;
char shm_segment[sizeof(SEGMENT_PREFIX INT_MIN_AS_STRING)];
- HANDLE shm_handle = NULL, info_handle = NULL;
+ HANDLE shm_handle = NULL;
BOOL created = FALSE;
if (key != IPC_PRIVATE) {
diff --git a/UPGRADING b/UPGRADING
index a67fc1b1d88fd..6000c237f85d0 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -1,4 +1,4 @@
-PHP 8.4 UPGRADE NOTES
+PHP 8.5 UPGRADE NOTES
1. Backward Incompatible Changes
2. New Features
@@ -19,1229 +19,508 @@ PHP 8.4 UPGRADE NOTES
1. Backward Incompatible Changes
========================================
+- 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.
+
- Core:
- . The type of PHP_DEBUG and PHP_ZTS constants changed to bool.
- . The name of uploaded files and files created by the tempnam() function are
- now 13 bytes longer. Total length is platform-dependent.
- . Encountering recursion during comparison now results in a Error exception,
- rather than a fatal error.
- . Indirect modification of readonly properties within __clone() is no longer
- allowed, e.g. $ref = &$this->readonly. This was already forbidden for
- readonly initialization, and was an oversight in the "readonly
- reinitialization during cloning" implementation.
- . The exit (and die) language constructs now behave more like a function.
- They can be passed like callables, are affected by the strict_types
- declare statement, and now perform the usual type coercions instead of
- casting any non-integer value to a string.
- As such, passing invalid types to exit/die may now result in a TypeError
- being thrown.
- RFC: https://wiki.php.net/rfc/exit-as-function
- . The E_STRICT constant was deprecated and its corresponding error level was
- removed.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#remove_e_strict_error_level_and_deprecate_e_strict_constant
-
-- Extension Class constants are now typed:
- . Date
- . Intl
- . PDO
- . Reflection
- . SPL
- . Sqlite
- . XMLReader
-
-- Resource to Object conversions:
- Return value checks using is_resource() should be replaced with checks
- for `false`, unless specified otherwise.
- . DBA:
- . dba_open() and dba_popen() will now return Dba\Connection
- . ODBC:
- . odbc_connect() and odbc_pconnect() will now return Odbc\Connection
- . odbc_prepare(), odbc_exec(), and various other functions will now return
- Odbc\Result
- . SOAP:
- . SoapClient::$httpurl is now a Soap\Url object rather than a resource.
- Checks using is_resource() (i.e. is_resource($client->httpurl)) should be
- replaced with checks for null (i.e. $client->httpurl !== null).
- . SoapClient::$sdl is now a Soap\Sdl object rather than a resource.
- Checks using is_resource() (i.e. is_resource($client->sdl)) should be
- replaced with checks for null (i.e. $client->sdl !== null).
-
-- New warnings and exceptions:
- . Curl:
- . curl_multi_select throws a ValueError if the timeout argument if it's negative
- or greater than PHP_INT_MAX.
- . GD:
- . imagejpeg/imagewebp/imagepng/imageavif throws an exception if an invalid
- quality parameter value is passed. In addition, imageavif will throw an exception
- if an invalid speed parameter value is passed.
- . imagescale throws an exception if the width/height argument underflows/overflows or
- if the mode argument is invalid.
- imagefilter with IMG_FILTER_SCATTER throws an exception if the sub/plus arguments
- underflows/overflows.
- . Gettext:
- . bind_textdomain_codeset, textdomain and d(*)gettext functions now throw an exception
- if the domain argument is empty.
- . Intl:
- . resourcebundle_get(), ResourceBundle::get(), and accessing offsets on a
- ResourceBundle object now throw:
- - TypeError for invalid offset types
- - ValueError for an empty string
- - ValueError if the integer index does not fit in a signed 32 bit integer
- . IntlDateFormatter::__construct() throws a ValueError if the locale is invalid.
- . NumberFormatter::__construct() throws a ValueError if the locale is invalid.
- . MBString:
- . mb_encode_numericentity() and mb_decode_numericentity() now check that
- the $map is only composed of integers, if not a ValueError is thrown.
- . mb_http_input() now always throws a ValueError if the $type is invalid.
- . mb_http_output() now checks that the $encoding parameter does not
- contain any null bytes. If it does, a ValueError is now thrown.
- . ODBC:
- . odbc_fetch_row() now emits a warning when a value less than or equal to 0 is
- passed for parameter $row.
- . PCNTL:
- . The functions pcntl_sigprocmask(), pcntl_sigwaitinfo() and
- pcntl_sigtimedwait() now throw:
- - A ValueError if the $signals array is empty (except for
- pcntl_sigprocmask() if the $mode is SIG_SETMASK).
- - A TypeError if a value of the $signals array is not an integer
- - A ValueError if a value of the $signals array is not a valid signal number
- . The function pcntl_sigprocmask() now throw:
- - A ValueError if $mode is not one of SIG_BLOCK, SIG_UNBLOCK, or SIG_SETMASK
- . The function pcntl_sigtimedwait() now throw:
- - A ValueError if $seconds is less than 0
- - A ValueError if $nanoseconds is less than 0 or greater than 1e9
- - A ValueError if both $seconds and $nanoseconds are 0
- . SimpleXML:
- . Calling simplexml_import_dom() with a non-XML object now throws a
- TypeError instead of a ValueError.
- . Standard:
- . round() now validates the value of the $mode parameter and throws a
- ValueError for invalid modes. Previously invalid modes would have been
- interpreted as PHP_ROUND_HALF_UP.
- . The str_getcsv() function now throws ValueErrors when the $separator and
- $enclosure arguments are not one byte long, or if the $escape is not one
- byte long or the empty string. This aligns the behaviour to be identical
- to that of fputcsv() and fgetcsv().
- . php_uname() now throws ValueErrors if the $move parameter is invalid.
- . The "allowed_classes" option for unserialize() now throws TypeErrors and
- ValueErrors if it is not an array of class names.
- . XMLReader:
- . Passing an invalid character encoding to XMLReader::open() or
- XMLReader::XML() now throws a ValueError. Passing an encoding
- containing null bytes previously emitted a warning and now throws
- a ValueError as well.
- . XMLWriter:
- . Passing an encoding containing null bytes previously emitted a
- warning and now throws a ValueError.
- . XSL:
- . XSLTProcessor::setParameter() will now throw a ValueError when its
- arguments contain null bytes. This never actually worked correctly in
- the first place, which is why it throws an exception nowadays.
- . Calling XSLTProcessor::importStyleSheet() with a non-XML object now
- throws a TypeError instead of a ValueError.
- . Failure to call a PHP function callback during evaluation now throws
- instead of emitting a warning.
- RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl
+ . 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 closer match user expectations, demonstrated by GH-15753
+ and GH-16198.
+
+- 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.
-- DOM:
- . Some DOM methods previously returned false or a PHP_ERR DOMException if a new
- node could not be allocated. They consistently throw an INVALID_STATE_ERR
- DOMException now. This situation is extremely unlikely though and probably
- will not affect you. As a result DOMImplementation::createDocument() now has
- a tentative return type of DOMDocument instead of DOMDocument|false.
- . Previously, DOMXPath objects could be cloned, but resulted in an unusable
- object. This is no longer possible, and cloning a DOMXPath object now throws
- an error.
- . Removed DOMImplementation::getFeature().
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#remove_domimplementationgetfeature_feature_version
-
-- GMP:
- . The GMP class is now final and cannot be extended anymore.
- RFC: https://wiki.php.net/rfc/gmp-final
-
-- MBString:
- . On invalid strings (those with encoding errors), mb_substr() now interprets
- character indices in the same manner as most other mbstring functions. This
- means that character indices returned by mb_strpos() can be passed to mb_substr().
- . For SJIS-Mac (MacJapanese) strings, character indices passed to mb_substr() now
- refer to the indices of the Unicode codepoints which are produced when the string
- is converted to Unicode. This is significant because around 40 SJIS-Mac characters
- convert to a sequence of multiple Unicode codepoints.
-
-- Mysqli:
- . The unused and undocumented constant MYSQLI_SET_CHARSET_DIR
- has been removed.
- . The MYSQLI_STMT_ATTR_PREFETCH_ROWS constant has been removed.
- The feature is unavailable with mysqlnd.
- . The MYSQLI_CURSOR_TYPE_FOR_UPDATE and MYSQLI_CURSOR_TYPE_SCROLLABLE
- constants have been removed. This functionality was never implemented,
- neither with mysqlnd nor with libmysql.
- . The unused MYSQLI_TYPE_INTERVAL constant, which is currently a stub
- and an alias for MYSQLI_TYPE_ENUM, has been removed. There are no
- plans to add such data type to MySQL yet, so it's unclear what its value
- would finally be.
-
-- MySQLnd:
- . The error code reported for MySQL server wait timeouts has been changed from 2006
- to 4031 for MySQL server versions 8.0.24 and above.
+- Intl:
+ . The extension now requires at least ICU 57.1.
-- Opcache:
- . The JIT config defaults changed from opcache.jit=tracing and
- opcache.jit_buffer_size=0 to opcache.jit=disable and
- opcache.jit_buffer_size=64M, respectively. This does not affect the default
- behavior, the JIT is still disabled by default. However, it is now disabled
- through the opcache.jit setting, rather than opcache.jit_buffer_size. This
- may affect users who previously enabled JIT through opcache.jit_buffer_size
- exclusively, without also specifying a JIT mode using opcache.jit. To enable
- JIT, set the opcache.jit config value accordingly.
- . The maximum value of the opcache.interned_strings_buffer setting on 64bit
- architectures is now 32767 (it was previously 4095).
- . If JIT is enabled, PHP will now exit with a fatal error on startup in case
- of JIT startup initialization issues.
+- LDAP:
+ . ldap_get_option() and ldap_set_option() now throw a ValueError when
+ passing an invalid option.
+
+- MySQLi:
+ . Calling the mysqli constructor on an already-constructed object
+ is now no longer possible and throws an Error.
- PCNTL:
- . The functions pcntl_sigprocmask(), pcntl_sigwaitinfo() and
- pcntl_sigtimedwait() now always return false on failure.
- In some case previously it could return the value -1.
+ . 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.
- PCRE:
- . The bundled pcre2lib has been updated to version 10.44.
- As a consequence, this means {,3} is now recognized as a quantifier instead
- of as text. Furthermore, the meaning of some character classes in UCP mode
- has changed. Consult https://github.com/PCRE2Project/pcre2/blob/master/NEWS
- for a full changelog.
+ . The extension is compiled without semi-deprecated
+ PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK compile option.
+ https://github.com/PCRE2Project/pcre2/issues/736#issuecomment-2754024651
-- PDO_DBLIB:
- . setAttribute, DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER and DBLIB_ATTR_DATETIME_CONVERT
- have been changed to set value as a bool.
+- 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
+ has 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:
- . Since some Firebird C++ APIs are used now, this extension requires a C++
- compiler to be built. This also implies that the extension has to be built
- against fbclient 3.0 or higher.
- . getAttribute, ATTR_AUTOCOMMIT has been changed to get the value as a bool.
-
-- PDO_MYSQL:
- . getAttribute, ATTR_AUTOCOMMIT, ATTR_EMULATE_PREPARES, MYSQL_ATTR_DIRECT_QUERY have
- been changed to get values as bool.
-
-- PDO_PGSQL:
- . The DSN's credentials, when set, are given priority over their PDO
- constructor counterparts, being closer to the documentation states.
+ . A ValueError is now thrown when trying to set a cursor name that is too
+ long on a PDOStatement resulting from the Firebird driver.
- SimpleXML:
- . Get methods called, or casting to a string on a SimpleXMLElement will no
- longer implicitly reset the iterator data, unless explicitly rewound.
- For example, casting an element to a string within a foreach loop would
- cause an infinite loop because it destroyed the current iterator data.
- This is no longer the case as a consequence of the bugfixes for GH-12192,
- GH-12208, #55098.
-
-- SOAP:
- . SoapClient::$typemap is now an array rather than a resource.
- Checks using is_resource() (i.e. is_resource($client->typemap)) should be
- replaced with checks for null (i.e. $client->typemap !== null).
- . The SOAP extension gained an optional dependency on the session extension.
- If you build PHP without the session extension and with --enable-rtld-now,
- you will experience errors on startup if you also use the SOAP extension.
- To solve this, either don't use rtld-now or load the session extension.
+ . 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.
-- Standard:
- . strcspn() with empty $characters now returns the length of the string instead
- of incorrectly stopping at the first NUL character. See GH-12592.
- . http_build_query() now correctly handles backed enums.
- . stream_bucket_make_writeable() and stream_bucket_new() will now return a
- StreamBucket instance instead of an instance of stdClass.
- RFC: https://wiki.php.net/rfc/dedicated_stream_bucket
-
-- Tidy:
- . Failures in the constructor now throw exceptions rather than emitting
- warnings and having a broken object.
-
-- XML:
- . The xml_set_*_handler() functions now declare and check for an effective
- signature of callable|string|null for the $handler parameters.
- Moreover, values of type string that correspond to method names,
- of object set with xml_set_object() are now checked to see if the method
- exists on the class of the previously passed object.
- This means that xml_set_object() must now always be called prior to setting
- method names as callables.
- Passing an empty string to disable the handler is still allowed,
- but deprecated.
+- 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.
========================================
2. New Features
========================================
- Core:
- . Added request_parse_body() function that allows parsing RFC1867 (multipart)
- requests in non-POST HTTP requests.
- RFC: https://wiki.php.net/rfc/rfc1867-non-post
- . Getting the debug info for WeakReference will now also output the object
- it references, or null if the reference is no longer valid.
- . The output of Closure::__debugInfo() now includes the name, file, and line
- of the Closure.
- . new expressions with constructor arguments are now dereferencable, meaning
- they allow chaining method calls, property accesses, etc. without enclosing
- the expression in parentheses.
- RFC: https://wiki.php.net/rfc/new_without_parentheses
- . Added the #[\Deprecated] attribute.
- RFC: https://wiki.php.net/rfc/deprecated_attribute
- . Implemented property hooks.
- RFC: https://wiki.php.net/rfc/property-hooks
- . Exiting a namespace now clears seen symbols. This allows using a symbol in a
- namespace block, even if a previous namespace block declared a symbol with
- the same name.
- See Zend/tests/use_function/ns_end_resets_seen_symbols_1.phpt.
- . Implemented asymmetric property visibility.
- RFC: https://wiki.php.net/rfc/asymmetric-visibility-v2
- . Implemented lazy objects.
- RFC: https://wiki.php.net/rfc/lazy-objects
+ . 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
- Curl:
- . curl_version() returns an additional feature_list value, which is an
- associative array of all known Curl features, and whether they are
- supported (true) or not (false).
- . Added CURL_HTTP_VERSION_3 and CURL_HTTP_VERSION_3ONLY constants (available
- since libcurl 7.66 and 7.88) as available options for CURLOPT_HTTP_VERSION.
- . Added CURLOPT_PREREQFUNCTION as a Curl option that accepts a callback to
- be called after the connection is made, but before the request is sent.
- The callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
- to allow or abort the request.
- . Added CURLOPT_SERVER_RESPONSE_TIMEOUT, which was formerly known as
- CURLOPT_FTP_RESPONSE_TIMEOUT. Both constants hold the same value.
- . Added CURLOPT_DEBUGFUNCTION support. This Curl option accepts a callable
- that gets called during the request lifetime with the CurlHandle object,
- an integer containing the debug message type, and a string containing the
- debug message. The debug message type is one of CURLINFO_TEXT, CURLINFO_HEADER_IN,
- CURLINFO_HEADER_OUT, CURLINFO_DATA_IN, CURLINFO_DATA_OUT, CURLINFO_SSL_DATA_OUT,
- CURLINFO_SSL_DATA_IN constants. Once this option is set, CURLINFO_HEADER_OUT
- must not be set because it uses the same libcurl functionality.
- . curl_getinfo() function now returns "posttransfer_time_us", containing the
- number of microseconds from the start until the last byte is sent. When a
- redirect is followed, the time from each request is added together. This
- value can also be retrieved by passing CURLINFO_POSTTRANSFER_TIME_T to the
- curl_getinfo() $option parameter. This requires libcurl 8.10.0 or later.
+ . 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 of redirect
+ if they are allowed. CURLFOLLOW_FIRSTONLY to follow only the
+ first redirect thus if there any follow up redirect, it won't go
+ any further. CURLFOLLOW_ALL is equivalent to set CURLOPT_FOLLOWLOCATION
+ to true.
- DOM:
- . Added DOMNode::compareDocumentPosition()
- . Added constant DOMNode::DOCUMENT_POSITION_DISCONNECTED.
- . Added constant DOMNode::DOCUMENT_POSITION_PRECEDING.
- . Added constant DOMNode::DOCUMENT_POSITION_FOLLOWING.
- . Added constant DOMNode::DOCUMENT_POSITION_CONTAINS.
- . Added constant DOMNode::DOCUMENT_POSITION_CONTAINED_BY.
- . Added constant DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC.
- . It is now possible to pass any callable to registerPhpFunctions().
- RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl
+ . Added Dom\Element::$outerHTML.
-- FPM:
- . Flushing headers without a body will now succeed. See GH-12785.
- . Status page has a new field to display a memory peak.
+- EXIF:
+ . Add OffsetTime* Exif tags.
- Intl:
- . NumberFormatter::ROUND_HALFODD added to complement existing
- NumberFormatter::ROUND_HALFEVEN functionality.
-
-- OpenSSL:
- . Added support for Curve25519 + Curve448 based keys. Specifically x25519,
- ed25519, x448 and ed448 fields are supported in openssl_pkey_new and
- openssl_pkey_get_details as well as openssl_sign and openssl_verify were
- extended to support those keys.
- . Implement PASSWORD_ARGON2 password hashing.
- Requires OpenSSL 3.2 and NTS build.
-
-- PCRE:
- . The bundled pcre2lib has been updated to version 10.44.
- As a consequence, LoongArch JIT support has been added, spaces
- are now allowed between braces in Perl-compatible items, and
- variable-length lookbehind assertions are now supported.
- . With pcre2lib version 10.44, the maximum length of named capture groups
- has changed from 32 to 128.
- . Added support for the "r" (PCRE2_EXTRA_CASELESS_RESTRICT) modifier, as well
- as the (?r) mode modifier. When enabled along with the case-insensitive
- modifier ("i"), the expression locks out mixing of ASCII and non-ASCII
- characters.
-
-- PDO:
- . Added support for driver-specific subclasses.
- RFC: https://wiki.php.net/rfc/pdo_driver_specific_subclasses
- This RFC adds subclasses for PDO in order to better support
- database-specific functionalities. The new classes are
- instantiatable either via calling the PDO::connect() method
- or by invoking their constructor directly.
- . Added support for driver specific SQL parsers. The default parser supports:
- - single and double quoted literals, with doubling as escaping mechanism.
- - two-dashes and non-nested C-style comments.
- RFC: https://wiki.php.net/rfc/pdo_driver_specific_parsers
-
-- PDO_MYSQL:
- . Added custom parser supporting:
- - single and double-quoted literals, with doubling and backslash as escaping
- mechanism
- - backtick literal identifiers and with doubling as escaping mechanism
- - two dashes followed by at least 1 whitespace, non-nested C-style comments,
- and hash-comments
- RFC: https://wiki.php.net/rfc/pdo_driver_specific_parsers
-
-- PDO_PGSQL:
- . Added custom parser supporting:
- - single and double quoted literals, with doubling as escaping mechanism
- - C-style "escape" string literals (E'string')
- - dollar-quoted string literals
- - two-dashes and C-style comments (non-nested)
- - support for "??" as escape sequence for the "?" operator
- RFC: https://wiki.php.net/rfc/pdo_driver_specific_parsers
-
-- PDO_SQLITE:
- . Added custom parser supporting:
- - single, double quoted, and backtick literals, with doubling as escaping mechanism
- - square brackets quoting for identifiers
- - two-dashes and C-style comments (non-nested)
- RFC: https://wiki.php.net/rfc/pdo_driver_specific_parsers
-
-- Phar:
- . Added support for the unix timestamp extension for zip archives.
-
-- Readfile:
- . Added ability to change .php_history path through PHP_HISTFILE env variable.
-
-- Reflection:
- . ReflectionAttribute now contains a $name property to improve the debugging
- experience.
- . ReflectionClassConstant::__toString() and ReflectionProperty::__toString()
- now returns the attached doc comments.
- . Multiple methods and constants related to lazy objects were introduced:
- - ReflectionClass::newLazyGhost()
- - ReflectionClass::newLazyProxy()
- - ReflectionClass::resetAsLazyGhost()
- - ReflectionClass::resetAsLazyProxy()
- - ReflectionClass::isUninitializedLazyObject()
- - ReflectionClass::initializeLazyObject()
- - ReflectionClass::markLazyObjectAsInitialized()
- - ReflectionClass::getLazyInitializer()
- - ReflectionProperty::skipLazyInitialization()
- - ReflectionProperty::setRawValueWithoutLazyInitialization()
- - ReflectionClass::SKIP_INITIALIZATION_ON_SERIALIZE
- - ReflectionClass::SKIP_DESTRUCTOR
- RFC: https://wiki.php.net/rfc/lazy-objects
-
-- SOAP:
- . Added support for clark notation for namespaces in class map.
- It is now possible to specify entries in a class map with clark notation
- to resolve a type with a specific namespace to a specific class.
- For example: '{http://example.com}foo' => 'FooClass'.
- . Instances of DateTimeInterface that are passed to xsd:datetime or similar
- elements are now serialized as such instead of being serialized as an
- empty string.
- . Session persistence now works with a shared session module.
-
-- Standard:
- . Added a new RoundingMode enum with clearer naming and improved discoverability
- compared to the PHP_ROUND_* constants.
- RFC: https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum
+ . 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.
- XSL:
- . It is now possible to use parameters that contain both single and double
- quotes.
- . It is now possible to pass any callable to registerPhpFunctions().
- RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl
- . Added XSLTProcessor::$maxTemplateDepth and XSLTProcessor::$maxTemplateVars
- to control the recursion depth of XSL template evaluation.
+ . 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.
-- Zip:
- . Added ZipArchive::ER_TRUNCATED_ZIP added in libzip 1.11
+- Zlib:
+ . flock() is now supported on zlib streams. Previously, this always
+ failed to perform any locking action.
========================================
3. Changes in SAPI modules
========================================
-- apache2handler
- . Support for EOL Apache 2.0 and 2.2 has been removed. Minimum required Apache
- version is now 2.4.
-
- CLI:
- . The builtin server looks for an index file recursively by traversing parent
- directories in case the specified file cannot be located. This process was
- previously skipped if the path looked like it was referring to a file, i.e.
- if the last path component contained a period. In that case, a 404 error was
- returned. The behavior has been changed to look for an index file in all
- cases.
+ . 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:
- . /dev/poll events.mechanism setting for Solaris/Illumos had been retired.
+ . FPM with httpd ProxyPass decodes the full script path. Added
+ fastcgi.script_path_encoded INI setting to prevent this new behavior.
========================================
4. Deprecated Functionality
========================================
-- Core:
- . Implicitly nullable parameter types are now deprecated.
- RFC: https://wiki.php.net/rfc/deprecate-implicitly-nullable-types
- . Passing E_USER_ERROR to trigger_error() is now deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_passing_e_user_error_to_trigger_error
- . Using "_" as a class name is now deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_using_a_single_underscore_as_a_class_name
- . Raising zero to the power of negative number is deprecated.
- RFC: https://wiki.php.net/rfc/raising_zero_to_power_of_negative_number
- . The E_STRICT constant was deprecated and its corresponding error level was
- removed.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#remove_e_strict_error_level_and_deprecate_e_strict_constant
-
-- Curl:
- . The CURLOPT_BINARYTRANSFER constant is deprecated.
-
-- Date:
- . Calling DatePeriod::__construct(string $isostr, int $options = 0) is
- deprecated. Use DatePeriod::createFromISO8601String() instead.
- . Constants SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, and
- SUNFUNCS_RET_DOUBLE are now deprecated, following the deprecation of
- the associated date_sunset() and date_sunrise() functions in PHP 8.1.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#constants_sunfuncs_ret_string_sunfuncs_ret_double_sunfuncs_ret_timestamp
-
-- DBA:
- . Passing null or false to dba_key_split() is deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_passing_null_and_false_to_dba_key_split
-
-- DOM:
- . Deprecated DOM_PHP_ERR constant.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_dom_php_err_constant
- . DOMDocument::$actualEncoding, DOMDocument::config, DOMEntity::$actualEncoding,
- DOMEntity::$encoding, DOMEntity::$version have been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#formally_deprecate_soft-deprecated_domdocument_and_domentity_properties
-
- Hash:
- . Deprecated passing incorrect data types for options to ext/hash functions.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_passing_incorrect_data_types_for_options_to_exthash_functions
-
-- Intl:
- . Calling intlcal_set() as well as calling IntlCalendar::set() with
- more than 2 arguments is deprecated. Use either IntlCalendar::setDate()
- or IntlCalendar::setDateTime() instead.
- . Calling intlgregcal_create_instance() as well as calling
- IntlGregorianCalendar::__construct() with more than 2 arguments is
- deprecated. Use either IntlGregorianCalendar::createFromDate() or
- IntlGregorianCalendar::createFromDateTime() instead.
-
-- LDAP:
- . Calling ldap_connect() with more than 2 arguments is deprecated. Use
- ldap_connect_wallet() instead.
- . Calling ldap_exop() with more than 4 arguments is deprecated. Use
- ldap_exop_sync() instead.
-
-- Mysqli:
- . The mysqli_ping() function and mysqli::ping() method are now deprecated,
- as the reconnect feature was removed in PHP 8.2.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#mysqli_ping_and_mysqliping
- . The mysqli_kill() function and mysqli::kill() method are now deprecated.
- If this functionality is needed a SQL "KILL" command can be used instead.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_mysqli_kill
- . The mysqli_refresh() function and mysqli::refresh() method are now deprecated.
- If this functionality is needed a SQL "FLUSH" command can be used instead.
- All MYSQLI_REFRESH_* constants have been deprecated as well.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_mysqli_refresh
- . Passing explicitly the $mode parameter to mysqli_store_result() has been
- deprecated. As the MYSQLI_STORE_RESULT_COPY_DATA constant was only used in
- conjunction with this function it has also been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_the_second_parameter_to_mysqli_store_result
-
-- PDO_PGSQL:
- . Using escaped question marks (??) inside dollar-quoted strings is deprecated.
- Since PDO_PGSQL has its own SQL parser with dollar-quoted strings support, it
- is no longer necessary to escape question marks inside them.
-
-- PgSQL:
- . Calling pg_fetch_result() with 2 arguments is deprecated. Use the
- 3-parameter signature with a null $row parameter instead.
- . Calling pg_field_prtlen() with 2 arguments is deprecated. Use the
- 3-parameter signature with a null $row parameter instead.
- . Calling pg_field_is_null() with 2 arguments is deprecated. Use the
- 3-parameter signature with a null $row parameter instead.
-
-- Random:
- . lcg_value() is deprecated, as the function is broken in multiple ways.
- Use \Random\Randomizer::getFloat() instead.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_lcg_value
-
-- Reflection:
- . Calling ReflectionMethod::__construct() with 1 argument is deprecated.
- Use ReflectionMethod::createFromMethodName() instead.
-
-- Session:
- . Calling session_set_save_handler() with more than 2 arguments is
- deprecated. Use the 2-parameter signature instead.
- . Changing the INI settings session.sid_length and session.sid_bits_per_character
- is deprecated. Update the session storage backend to accept 32 character
- hexadecimal session IDs and stop changing these two INI settings.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#sessionsid_length_and_sessionsid_bits_per_character
- . Changing the INI settings session.use_only_cookies, session.use_trans_sid,
- session.trans_sid_tags, session.trans_sid_hosts, and session.referer_check
- is deprecated. The SID constant is also deprecated.
- RFC: https://wiki.php.net/rfc/deprecate-get-post-sessions
-
-- SOAP:
- . Passing an int to SoapServer::addFunction() is now deprecated.
- If all PHP functions need to be provided flatten the array returned by
- get_defined_functions().
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_soap_functions_all_constant_and_passing_it_to_soapserveraddfunction
- . The SOAP_FUNCTIONS_ALL constant is now deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_soap_functions_all_constant_and_passing_it_to_soapserveraddfunction
-
-- SPL:
- . The SplFixedArray::__wakeup() method has been deprecated as it implements
- __serialize() and __unserialize() which need to be overwritten instead.
- . Using the default value for $escape parameter of:
- - SplFileObject::setCsvControl()
- - SplFileObject::fputcsv()
- - SplFileObject::fgetcsv()
- is now deprecated. It must be passed explicitly either positionally or via named arguments.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_proprietary_csv_escaping_mechanism
-
-- Standard:
- . Calling stream_context_set_option() with 2 arguments is deprecated.
- Use stream_context_set_options() instead.
- . Unserializing strings using the uppercase 'S' tag is deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#unserialize_s_s_tag
- . Using the default value for $escape parameter of:
- - fputcsv()
- - fgetcsv()
- - str_getcsv()
- is now deprecated. It must be passed explicitly either positionally or via named arguments.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#deprecate_proprietary_csv_escaping_mechanism
-
-- XML:
- . The xml_set_object() function has been deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#xml_set_object_and_xml_set_handler_with_string_method_names
- . Passing non-callable strings to the xml_set_*_handler() functions is now
- deprecated.
- RFC: https://wiki.php.net/rfc/deprecations_php_8_4#xml_set_object_and_xml_set_handler_with_string_method_names
+ . 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
========================================
5. Changed Functions
========================================
-- Core:
- . trigger_error() and user_error() now have a return type of true instead of
- bool.
-
-- DOM:
- . DOMDocument::registerNodeClass() now has a tentative return type of true.
- Previously, the return type was bool but only true could be returned in practice.
-
-- GD:
- . imagescale now throws a ValueError when both width and height arguments are negative.
-
-- Hash:
- . Changed the return type of hash_update() to true. It was already the case that only
- true could be returned, but the stub was not updated yet.
-
- Intl:
- . NumberFormatter::ROUND_TOWARD_ZERO and NumberFormatter::ROUND_AWAY_FROM_ZERO
- have been added as aliases for NumberFormatter::ROUND_DOWN and
- NumberFormatter::ROUND_UP to be consistent with the new PHP_ROUND_* modes.
- RFC: https://wiki.php.net/rfc/new_rounding_modes_to_round_function
- . ResourceBundle::get() now has a tentative return type of:
- ResourceBundle|array|string|int|null
- . The idn_to_ascii() and idn_to_utf8() now always throw ValueErrors if the
- $domain name is empty or too long, and if $variant is not
- INTL_IDNA_VARIANT_UTS46.
-
-- LibXML:
- . libxml_set_streams_context() now immediately throws a TypeError when a
- non-stream-context resource is passed to the function, instead of throwing
- later when the stream context is used.
-
-- MBString:
- . The behavior of mb_strcut is more consistent now on invalid UTF-8 and UTF-16
- strings. (For valid UTF-8 and UTF-16 strings, there is no change.)
-
-- ODBC:
- . Parameter $row of odbc_fetch_object(), odbc_fetch_array(), and
- odbc_fetch_into() now has a default value of null, consistent with
- odbc_fetch_row(). Previously, the default values were -1, -1, and 0,
- respectively.
-
-- OpenSSL:
- . The extra_attributes parameter in openssl_csr_new sets CSR attributes
- instead of subject DN which was incorrectly done previously.
- . The dn parameter in openssl_csr_new allows setting array of values for
- a single entry.
- . New serial_hex parameter added to openssl_csr_sign to allow setting serial
- number in the hexadecimal format.
- . Parsing ASN.1 UTCTime by openssl_x509_parse fails if seconds are omitted
- for OpenSSL version below 3.2 (-1 is returned for such fields). The
- OpenSSL version 3.3+ does not load such certificates already.
-
-- Output:
- . Output handler status flags passed to the flags parameter of ob_start
- are now cleared.
+ . 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.
+
+- libxml:
+ . libxml_set_external_entity_loader() now has a formal return type of true.
-- PDO:
- . getAttribute, enabled to get the value of ATTR_STRINGIFY_FETCHES.
-
-- PDO_FIREBIRD:
- . getAttribute, enabled to get values of FB_ATTR_DATE_FORMAT, FB_ATTR_TIME_FORMAT,
- FB_ATTR_TIMESTAMP_FORMAT.
- . Added new attributes to specify transaction isolation level and access mode.
- Along with these, five constants (Pdo\Firebird::TRANSACTION_ISOLATION_LEVEL,
- Pdo\Firebird::READ_COMMITTED, Pdo\Firebird::REPEATABLE_READ,
- Pdo\Firebird::SERIALIZABLE, Pdo\Firebird::WRITABLE_TRANSACTION) have been added.
- . When using persistent connections, there is now a liveness check in the
- constructor.
- . The content that is built changes depending on the value of FB_API_VER in
- ibase.h, so added static method Pdo\Firebird::getApiVersion() to obtain that
- value. This value can also be referenced from phpinfo.
- . Five new data types are now available: INT128, DEC16, DEC34, TIMESTAMP_TZ, TIME_TZ.
- These are available starting with Firebird 4.0.
-
-- PDO_MYSQL:
- . getAttribute, enabled to get the value of ATTR_FETCH_TABLE_NAMES.
+- PCNTL:
+ . pcntl_exec() now has a formal return type of false.
- PDO_PGSQL:
- . getAttribute() can now retrieve the memory usage of query results.
- PDO::PGSQL_ATTR_RESULT_MEMORY_SIZE was added for this feature.
- . If the column is of FLOAT4OID/FLOAT8OID type, query returns it as a
- float instead of a string.
+ . PDO::pgsqlCopyFromArray also supports inputs as Iterable.
+ . Pdo\Pgsql::setAttribute and Pdo\Pgsql::prepare supports
+ PDO::ATTR_PREFETCH sets to 0 which set to lazy fetch mode.
+ In this mode, statements cannot be run parallely.
-- PGSQL:
- . pg_select, the conditions arguments accepts an empty array and is optional.
+- 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.
-- Phar:
- . Phar::setAlias() and Phar::setDefaultStub() methods now have a tentative
- return type of true instead of bool.
+- PGSQL:
+ . pg_copy_from also supports inputs as Iterable.
+ . 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_isatty now sets the error number when the file descriptor/stream argument
- is invalid.
+ . 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.
- Reflection:
- . ReflectionGenerator::getFunction() may now be called after the generator
- finished executing.
-
-- Sockets:
- . Parameter $backlog of socket_create_listen() now has a default value of SOMAXCONN.
- Previously, it was 128.
+ . 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.
-- SPL:
- . SplPriorityQueue::insert() and SplPriorityQueue::recoverFromCorruption()
- now has a tentative return type of true
- . SplHeap::insert() and SplHeap::recoverFromCorruption()
- now has a tentative return type of true instead of bool
+- Session:
+ . session_start is stricter in regard of the option argument.
+ It throws a ValueError if the whole is not a hashmap or
+ a TypeError if read_on_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.
-- Standard:
- . The internal implementation for rounding to integers has been rewritten
- to be easier to verify for correctness and to be easier to maintain.
- Some rounding bugs have been fixed as a result of the rewrite. For
- example previously rounding 0.49999999999999994 to the nearest integer
- would have resulted in 1.0 instead of the correct result 0.0. Additional
- inputs might also be affected and result in different outputs compared to
- earlier PHP versions.
- . The $mode parameter of the round() function has been widened to RoundingMode|int,
- accepting instances of a new RoundingMode enum.
- RFC: https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum
- . Four new modes have been added to the round() function: RoundingMode::PositiveInfinity,
- RoundingMode::NegativeInfinity, RoundingMode::TowardsZero, RoundingMode::AwayFromZero.
- RFC: https://wiki.php.net/rfc/new_rounding_modes_to_round_function
- . Fixed a bug caused by "pre-rounding" of the round() function. Previously, using
- "pre-rounding" to treat a value like 0.285 (actually 0.28499999999999998) as a
- decimal number and round it to 0.29. However, "pre-rounding" incorrectly rounds
- certain numbers, so this fix removes "pre-rounding" and changes the way numbers
- are compared, so that the values are correctly rounded as decimal numbers.
- . The maximum precision that can be handled by round() has been extended by one
- digit. For example, `round(4503599627370495.5)` returned in `4503599627370495.5`,
- but now returns `4503599627370496`.
- . The default value of the 'cost' option for PASSWORD_BCRYPT for password_hash()
- has been increased from '10' to '12'.
- RFC: https://wiki.php.net/rfc/bcrypt_cost_2023
- . debug_zval_dump() now indicates whether an array is packed.
- . long2ip() now returns string instead of string|false.
- . output_add_rewrite_var() now uses url_rewriter.hosts instead of
- session.trans_sid_hosts for selecting hosts that will be rewritten.
- . highlight_string() now has a return type of string|true instead of string|bool.
- . print_r() now has a return type of string|true instead of string|bool.
+- Sockets:
+ . socket_create_listen, socket_bind and socket_sendto throw a
+ ValueError if the port is lower than 0 or greater than 65535,
+ also if any of the hints array entry is indexes numerically.
+ . socket_addrinfo_lookup throws a TypeError if any of the hints
+ values cannot be cast to a 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.
+
+- Zlib:
+ . The "use_include_path" argument for the
+ gzfile, gzopen and readgzfile functions had been changed
+ from int to boolean.
+ . gzfile, gzopen and readgzfile functions now respect the default
+ stream context.
========================================
6. New Functions
========================================
- Core:
- . request_parse_body()
-
-- BCMath:
- . Added bcfloor(), bcceil(), bcround().
- RFC: https://wiki.php.net/rfc/adding_bcround_bcfloor_bcceil_to_bcmath
- . Added bcdivmod().
- RFC: https://wiki.php.net/rfc/add_bcdivmod_to_bcmath
+ . 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
-- Date:
- . Added static methods
- DateTime[Immutable]::createFromTimestamp(int|float $timestamp): static.
- . Added method DateTime[Immutable]::getMicrosecond(): int.
- . Added method
- DateTime[Immutable]::setMicrosecond(int $microsecond): static.
+- Curl:
+ . curl_multi_get_handles() allows retrieving all CurlHandles current
+ 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 DOMNode::compareDocumentPosition().
- . Added DOMXPath::registerPhpFunctionNS().
- RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl
- . Added DOMXPath::quote() to quote a string for use in an XPath expression.
- Example usage: "//span[contains(text()," . $xpath->quote($string) . ")]"
+ . Added Dom\Element::insertAdjacentHTML().
-- Hash:
- . Added HashContext::__debugInfo().
+- 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 IntlTimeZone::getIanaID()/intltz_get_iana_id() to
- the IANA identifier from a given timezone.
- . Added grapheme_str_split which allow to support emoji and Variation
- Selectors.
- RFC: https://wiki.php.net/rfc/grapheme_str_split
- . Added IntlDateFormatter::parseToCalendar which behaves like
- IntlDateFormatter::parse except the time zone is updated.
- . Added SpoofChecker::setAllowedChars to limit the range of unicode
- chars.
-
-- MBString:
- . Added mb_trim, mb_ltrim and mb_rtrim functions.
- RFC: https://wiki.php.net/rfc/mb_trim
- Note: this was amended by GH-13820 to fix GH-13815.
- . Added mb_ucfirst and mb_lcfirst functions.
- RFC: https://wiki.php.net/rfc/mb_ucfirst
-
-- OPCache:
- . Added opcache_jit_blacklist function. It allows skipping the tracing JIT
- execution of select functions.
+ . 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
-- PCNTL:
- . Added pcntl_setns allowing a process to be reassociated with a namespace in order
- to share resources with other processes within this context.
- . Added pcntl_getcpuaffinity to get the cpu(s) bound to a process and
- pcntl_setcpuaffinity to bind 1 or more cpus to a process.
- . Added pcntl_getcpu to get the cpu id from where the current process runs.
- . Added pcntl_getqos_class to get the QoS level (aka performance and related
- energy consumption) of the current process and pcntl_setqos_class to set it.
- . Added pcntl_waitid to obtain status information pertaining to termination, stop,
- and/or continue events in one of the caller's child processes.
-
-- PDO_PGSQL:
- . Added Pdo\Pgsql::setNoticeCallback() to allow a callback to be triggered on
- every notice sent (e.g. RAISE NOTICE).
+- 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:
- . Added pg_change_password to alter a given user's password. It handles
- transparently the password encryption from the database settings.
- . Added pg_put_copy_data to send COPY commands and pg_put_copy_end to send
- end-of-data to the server.
- . Added pg_socket_poll to check if there is any read and/or write events
- with an optional timeout.
- . Added pg_jit to get informations on the server JIT support.
- . Added pg_set_chunked_rows_size to allow to fetch results in chunk of
- max N rows.
- . Added pg_result_memory_size to get the visibility the memory used by a query result.
+ . 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.
- Reflection:
- . Multiple methods related to lazy objects were introduced:
- - ReflectionClass::newLazyGhost()
- - ReflectionClass::newLazyProxy()
- - ReflectionClass::resetAsLazyGhost()
- - ReflectionClass::resetAsLazyProxy()
- - ReflectionClass::isUninitializedLazyObject()
- - ReflectionClass::initializeLazyObject()
- - ReflectionClass::markLazyObjectAsInitialized()
- - ReflectionClass::getLazyInitializer()
- - ReflectionProperty::skipLazyInitialization()
- - ReflectionProperty::setRawValueWithoutLazyInitialization()
- RFC: https://wiki.php.net/rfc/lazy-objects
- . ReflectionClassConstant::isDeprecated() was introduced.
- . ReflectionGenerator::isClosed() was introduced.
- . ReflectionProperty::isDynamic() was introduced.
-
-- Sodium:
- . Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*()
- functions to support the AEGIS family of authenticated encryption algorithms,
- that was introduced in libsodium 1.0.19.
- . sodium_crypto_aead_aes256gcm_*() functions are now enabled on aarch64 CPUs
- with the ARM cryptographic extensions.
-
-- SPL:
- . Added seek() method to SplObjectStorage, now it implements
- SeekableIterator.
-
-- SOAP:
- . Added SoapServer::__getLastResponse(). This only tracks the last response
- if the trace option is set to true in the SoapServer constructor's $options
- argument.
+ . ReflectionConstant::getFileName() was introduced.
+ . ReflectionConstant::getExtension() and
+ ReflectionConstant::getExtensionName() were introduced.
+ . ReflectionConstant::getAttributes() was introduced.
+ RFC: https://wiki.php.net/rfc/attributes-on-constants
- Standard:
- . Added the http_get_last_response_headers() and
- http_clear_last_response_headers() that allows retrieving the same content
- as the magic $http_response_header variable.
- RFC: https://wiki.php.net/rfc/http-last-response-headers
- . Added function fpow() following rules of IEEE 754.
- RFC: https://wiki.php.net/rfc/raising_zero_to_power_of_negative_number
- . Added functions array_find(), array_find_key(), array_all(), and
- array_any().
- RFC: https://wiki.php.net/rfc/array_find
-
-- Tidy:
- . Added tidyNode::getNextSibling() and tidyNode::getPreviousSibling().
-
-- XMLReader:
- . Added XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString().
- RFC: https://wiki.php.net/rfc/xmlreader_writer_streams
-
-- XMLWriter:
- . Added XMLWriter::toStream(), XMLWriter::toUri(), XMLWriter::toMemory().
- RFC: https://wiki.php.net/rfc/xmlreader_writer_streams
-
-- XSL:
- . Added XSLTProcessor::registerPhpFunctionNS().
- RFC: https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl
+ . Added array_first() and array_last().
+ RFC: https://wiki.php.net/rfc/array_first_last
========================================
7. New Classes and Interfaces
========================================
-- BCMath:
- . Added BcMath\Number class. It is an immutable object, has methods that are
- equivalent to existing BCMath calculation functions, and can also be calculated
- using operators.
- The existing BCMath function returned a string for each calculation, but this
- class returns an object.
- RFC: https://wiki.php.net/rfc/support_object_type_in_bcmath,
- https://wiki.php.net/rfc/fix_up_bcmath_number_class
-
-- Core:
- . RequestParseBodyException.
- RFC: https://wiki.php.net/rfc/rfc1867-non-post
- . #[\Deprecated] attribute.
- RFC: https://wiki.php.net/rfc/deprecated_attribute
-
-- DBA:
- . Dba\Connection opaque object replacing DBA resources
-
-- DOM:
- . Implemented DOM HTML5 parsing and serialization.
- RFC: https://wiki.php.net/rfc/domdocument_html5_parser.
- This RFC adds the new Dom namespace along with new classes and
- constant aliases.
- There are two new classes to handle HTML and XML documents:
- Dom\HTMLDocument and Dom\XMLDocument.
- These classes provide a cleaner API to handle HTML and XML documents.
- Furthermore, the Dom\HTMLDocument class implements spec-compliant HTML5
- parsing and serialization.
- . Implemented opt-in ext/dom spec compliance RFC.
- This adds new classes in the DOM namespace that correspond to modern
- equivalents to the old DOM classes in the global namespaces.
- The new classes follow the DOM living spec.
- RFC: https://wiki.php.net/rfc/opt_in_dom_spec_compliance
- . Implemented "New ext-dom features in PHP 8.4" RFC.
- RFC: https://wiki.php.net/rfc/dom_additions_84
-
-- ODBC:
- . Odbc\Connection
- . Odbc\Result
-
-- PDO_DBLIB:
- . Pdo\DbLib.
-
-- PDO_FIREBIRD:
- . Pdo\Firebird.
-
-- PDO_MYSQL:
- . Pdo\Mysql.
-
-- PDO_ODBC:
- . Pdo\Odbc.
-
-- PDO_PGSQL:
- . Pdo\Pgsql.
-
-- PDO_SQLITE:
- . Pdo\Sqlite.
-
-- Reflection:
- . ReflectionConstant
-
-- SOAP:
- . Soap\Url
- . Soap\Sdl
-
-- Standard:
- . StreamBucket
+- Curl:
+ . CurlSharePersistentHandle representing a share handle that is persisted
+ across multiple PHP requests.
+ RFC: https://wiki.php.net/rfc/curl_share_persistence_improvement
========================================
8. Removed Extensions and SAPIs
========================================
-- IMAP:
- . The IMAP extension has been unbundled and moved to PECL.
- RFC: https://wiki.php.net/rfc/unbundle_imap_pspell_oci8
-
-- OCI8:
- . The OCI8 extension has been unbundled and moved to PECL.
- RFC: https://wiki.php.net/rfc/unbundle_imap_pspell_oci8
-
-- PDO_OCI:
- . The PDO_OCI extension has been unbundled and moved to PECL.
- RFC: https://wiki.php.net/rfc/unbundle_imap_pspell_oci8
-
-- PSpell:
- . The pspell extension has been unbundled and moved to PECL.
- RFC: https://wiki.php.net/rfc/unbundle_imap_pspell_oci8
-
========================================
9. Other Changes to Extensions
========================================
- Curl:
- . The Curl extension now requires at least libcurl 7.61.0.
- . The CURLOPT_DNS_USE_GLOBAL_CACHE Curl option no longer has any
- effect, and is silently ignored. This underlying feature was
- deprecated in libcurl 7.11.1 and removed in 7.62.0.
-
-- GMP:
- . Casting a GMP object to bool is now possible instead of emitting a
- E_RECOVERABLE_ERROR. The casting behaviour is overloaded such that a GMP
- object representing the value 0 is cast to false.
- RFC: https://wiki.php.net/rfc/fix_up_bcmath_number_class
-
-- Intl:
- . The behaviour of Intl class has been normalized to always throw Error
- exceptions when attempting to use a non-initialized object,
- or when cloning fails.
-
-- LibXML:
- . The libxml extension now requires at least libxml2 2.9.4.
+ . curl_easy_setopt with CURLOPT_FOLLOWLOCATION option's value no longer
+ is treated as boolean but integer to handle CURLFOLLOW_OBEYCODE and
+ CURLFOLLOW_FIRSTONLY.
-- MBString:
- . Unicode data tables have been updated to Unicode 16.0.
-
-- Mysqlnd:
- . Support for the new VECTOR data type from MySQL 9.
-
-- OpenSSL:
- . The OpenSSL extension now requires at least OpenSSL 1.1.1.
-
-- PDO_PGSQL:
- . The pdo_pgsql extension now requires at least libpq 10.0.
+- Fileinfo:
+ . Upgraded to file 5.46.
+ . The return type of finfo_close() has been changed to true, rather
+ than bool.
-- PgSQL:
- . The pgsql extension now requires at least libpq 10.0.
-- SPL:
- . Out of bounds accesses in SplFixedArray now throw an exception of type
- OutOfBoundsException instead of RuntimeException. As OutOfBoundsException
- is a child class of RuntimeException, code that uses RuntimeException
- continues to function.
-
-- XSL:
- . The typed properties XSLTProcessor::$cloneDocument and
- XSLTProcessor::$doXInclude are now declared.
+- PCRE:
+ . Upgraded to pcre2lib from 10.44 to 10.45.
-- zlib:
- . The zlib extension now requires at least zlib 1.2.11.
+- Readline:
+ . The return types of readline_add_history(), readline_clear_history(), and
+ readline_callback_handler_install() have been changed to true, rather
+ than bool.
========================================
10. New Global Constants
========================================
- Core:
- . PHP_OUTPUT_HANDLER_PROCESSED.
- . PHP_SBINDIR.
+ . PHP_BUILD_DATE.
- Curl:
- . CURL_HTTP_VERSION_3.
- . CURL_HTTP_VERSION_3ONLY.
- . CURLOPT_TCP_KEEPCNT.
- . CURLOPT_PREREQFUNCTION.
- . CURL_PREREQFUNC_OK.
- . CURL_PREREQFUNC_ABORT.
- . CURLOPT_SERVER_RESPONSE_TIMEOUT.
- . CURLOPT_DEBUGFUNCTION.
- . CURLINFO_TEXT.
- . CURLINFO_HEADER_IN.
- . CURLINFO_DATA_IN.
- . CURLINFO_DATA_OUT.
- . CURLINFO_SSL_DATA_OUT.
- . CURLINFO_SSL_DATA_IN.
- . CURLINFO_POSTTRANSFER_TIME_T.
+ . CURLINFO_USED_PROXY.
+ . CURLINFO_HTTPAUTH_USED.
+ . CURLINFO_PROXYAUTH_USED.
+ . CURLOPT_INFILESIZE_LARGE.
+ . CURLFOLLOW_ALL.
+ . CURLFOLLOW_OBEYCODE.
+ . CURLFOLLOW_FIRSTONLY.
- Intl:
- . The IntlDateFormatter class exposes now the new PATTERN constant
- reflecting udat api's UDAT_PATTERN.
- . The IntlChar class exposes now the new PROPERTY_IDS_UNARY_OPERATOR (new
- IDS binary operator), PROPERTY_ID_COMPAT_MATH_START,
- PROPERTY_ID_COMPAT_MATH_CONTINUE (both for mathematical
- identifier profiling purpose) constants.
-
-- LDAP:
- . LDAP_OPT_X_TLS_PROTOCOL_MAX.
- . LDAP_OPT_X_TLS_PROTOCOL_TLS1_3.
-
-- LibXML:
- . LIBXML_RECOVER.
- . LIBXML_NO_XXE.
- This is used together with LIBXML_NOENT for when you want to perform entity
- substitution, but want to disallow external entity loading.
- This constant is available as of libxml2 2.13.
-
-- Mysqli:
- . MYSQLI_TYPE_VECTOR.
-
-- OpenSSL:
- . X509_PURPOSE_OCSP_HELPER.
- . X509_PURPOSE_TIMESTAMP_SIGN.
-
-- PCNTL:
- . Pcntl\QosClass::Background (macOs only).
- . Pcntl\QosClass::Default (macOs only).
- . Pctnl\QosClass::UserInteractive (macOs only).
- . Pcntl\QosClass::UserInitiated (macOs only).
- . Pcntl\QosClass::Utility (macOs only).
- . SIGCKPT (DragonFlyBSD only).
- . SIGCKPTEXIT (DragonFlyBSD only).
- . WEXITED.
- . WSTOPPED.
- . WNOWAIT.
- . P_ALL.
- . P_PID.
- . P_PGID.
- . P_PIDFD (Linux only).
- . P_UID (NetBSD/FreeBSD only).
- . P_GID (NetBSD/FreeBSD only).
- . P_SID (NetBSD/FreeBSD only).
- . P_JAILID (FreeBSD only).
-
-- PgSQL:
- . PGSQL_TUPLES_CHUNK
+ . DECIMAL_COMPACT_SHORT.
+ . DECIMAL_COMPACT_LONG.
- POSIX:
- . POSIX_SC_CHILD_MAX
- . POSIX_SC_CLK_TCK
+ . POSIX_SC_OPEN_MAX.
- Sockets:
- . SO_EXCLUSIVEADDRUSE (Windows only).
- . SOCK_CONN_DGRAM (NetBSD only).
- . SOCK_DCCP (NetBSD only).
- . TCP_SYNCNT (Linux only).
- . SO_EXCLBIND (Solaris/Illumos only).
- . SO_NOSIGPIPE (macOs and FreeBSD).
- . SO_LINGER_SEC (macOs only).
- . IP_PORTRANGE (FreeBSD/NetBSD/OpenBSD only).
- . IP_PORTRANGE_DEFAULT (FreeBSD/NetBSD/OpenBSD only).
- . IP_PORTRANGE_HIGH (FreeBSD/NetBSD/OpenBSD only).
- . IP_PORTRANGE_LOW (FreeBSD/NetBSD/OpenBSD only).
- . SOCK_NONBLOCK.
- . SOCK_CLOEXEC.
- . SO_BINDTOIFINDEX.
-
-- Sodium:
- . SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES
- . SODIUM_CRYPTO_AEAD_AEGIS128L_NSECBYTES
- . SODIUM_CRYPTO_AEAD_AEGIS128L_NPUBBYTES
- . SODIUM_CRYPTO_AEAD_AEGIS128L_ABYTES
- . SODIUM_CRYPTO_AEAD_AEGIS256_KEYBYTES
- . SODIUM_CRYPTO_AEAD_AEGIS256_NSECBYTES
- . SODIUM_CRYPTO_AEAD_AEGIS256_NPUBBYTES
- . SODIUM_CRYPTO_AEAD_AEGIS256_ABYTES
-
-- XML:
- . Added XML_OPTION_PARSE_HUGE to allow large inputs in xml_parse and
- xml_parse_into_struct.
- RFC: https://wiki.php.net/rfc/xml_option_parse_huge.
+ . 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).
+ . IP_BINDANY (FreeBSD/NetBSD/OpenBSD only).
+ . SO_BUSY_POLL (Linux only).
========================================
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
+
+- 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.
+
========================================
12. Windows Support
========================================
-* Building with Visual Studio now requires at least Visual Studio 2019 (Visual
- Studio 2022 is recommended, though).
+* The configuration variables PHP_VERSION, PHP_MINOR_VERSION, and
+ PHP_RELEASE_VERSION are now always numbers. Previously, they have been
+ strings for buildconf builds.
-* AVX(2) CPU support is now properly detected for MSVC builds.
-
-* Native AVX-512 builds are now supported (--enable-native-intrinsics=avx512).
-
-========================================
-13. Other Changes
-========================================
+* phpize builds now reflect the source tree in the build dir (like that already
+ worked for in-tree builds); some extension builds (especially when using
+ Makefile.frag.w32) may need adjustments.
-* Closure names have been adjusted to include the parent function's name
- and the line of definition to make them easier to distinguish, for example
- within stack traces.
+* --enable-sanitzer is now supported for MSVC builds. This enables ASan and
+ debug assertions, and is supported as of MSVC 16.10 and Windows 10.
-* run-tests.php now skips online tests by default. Set the SKIP_ONLINE_TESTS
- environment variable to 0, or pass the --online flag to run-tests.php to
- execute them.
+* The --with-uncritical-warn-choke configuration option for clang builds is
+ no longer supported. Select warnings to suppress via CFLAGS instead.
-* Fiber switching during destructor execution is now allowed. It was previously
- blocked due to conflicts with garbage collection.
+* 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.
- Destructors may now be executed in a separate Fiber:
+* 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.
- When garbage collection is triggered in a Fiber, destructors called by the GC
- are executed in a separate Fiber: the gc_destructor_fiber. If this Fiber
- suspends, a new one is created to execute the remaining destructors. The
- previous gc_destructor_fiber is not referenced anymore by the GC and may be
- collected if it's not referenced anywhere else. Objects whose destructor is
- suspended will not be collected until the destructor returns or the Fiber is
- collected.
+* 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.
========================================
-14. Performance Improvements
+13. Other Changes
========================================
-- BCMath:
- . Improved performance of number conversions and operations.
-
- Core:
- . Improved the performance of floating point number parsing and formatting in
- ZTS builds under highly concurrent loads. This affects the `printf()` family
- of functions as well as serialization functions such as `json_encode()`,
- `serialize()`.
- . `sprintf()` 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.
-
-- DOM:
- . The performance of DOMNode::C14N() is greatly improved for the case without
- an xpath query. This can give a time improvement of easily two order of
- magnitude for documents with tens of thousands of nodes.
- . Improved performance and reduce memory consumption of XML serialization.
- . Reduced memory usage of node classes.
-
-- FTP:
- . Improved the performance of FTP uploads up to a factor of 10x for large
- uploads.
+ . The high resolution timer (`hrtime()`) on macOS now uses the recommended
+ `clock_gettime_nsec_np(CLOCK_UPTIME_RAW)` API instead of
+ `mach_absolute_time()`.
-- Hash:
- . Added SSE2 and SHA-NI implementations of SHA-256. This improves the performance
- on supported CPUs by ~1.3x (SSE2) and 3x - 5x (SHA-NI).
+- CLI/CGI:
+ . The `-z` or `--zend-extension` option has been removed as it was
+ non-functional. Use `-d zend_extension=` instead.
-- MBString:
- . mb_strcut() is much faster now for UTF-8 and UTF-16 strings.
- . Looking up mbstring encoding names is much faster now.
- . The performance of converting SJIS-win to unicode is greatly improved.
-
-- MySQLnd:
- . Improved the performance of MySQLnd quoting.
+========================================
+14. Performance Improvements
+========================================
-- PCRE:
- . Improved the performance of named capture groups.
+- Core:
+ . Remove OPcodes for identity comparisons against booleans, particularly
+ for the match(true) pattern.
-- Random:
- . Improved the performance of \Random\Randomizer, with a specific focus
- on the getBytes() and getBytesFromString() methods.
+- ReflectionProperty:
+ . Improved performance of the following methods: getValue(), getRawValue(),
+ isInitialized(), setValue(), setRawValue().
-- SimpleXML:
- . Improved performance and reduce memory consumption of XML serialization.
+- SPL:
+ . Improved performance of dimension accessors and methods of SplFixedArray.
- Standard:
- . Improved the performance of strpbrk().
- . The performance of strspn() and strcspn() is greatly improved.
- They now run in linear time instead of being bounded by quadratic time.
- . get_browser() is much faster now, up to 1.5x - 2.5x for some test cases.
+ . Improved performance of array functions with callbacks
+ (array_find, array_filter, array_map, usort, ...).
+ . Improved performance of urlencode() and rawurlencode().
+
+- XMLReader:
+ . Improved property access performance.
+- XMLWriter:
+ . Improved performance and reduce memory consumption.
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index a186dbf77febc..2ed9c2510521c 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -1,4 +1,4 @@
-PHP 8.4 INTERNALS UPGRADE NOTES
+PHP 8.5 INTERNALS UPGRADE NOTES
1. Internal API changes
@@ -14,413 +14,63 @@ PHP 8.4 INTERNALS UPGRADE NOTES
1. Internal API changes
========================
-* zend_register_module_ex() now takes an additional int module_type argument.
- This function will also assign the module number and type, there is no need
- to do this at the call site anymore. Writing the handle should happen after
- successful registration.
-
-* ZPP now accepts a F or Z_PARAM_FUNC_NO_TRAMPOLINE_FREE type check.
- This is identical to the 'f' or Z_PARAM_FUNC type check, except the FCC is
- always initialized because it doesn't free trampolines.
- Trampolines MUST be freed using zend_release_fcall_info_cache() or consumed.
- Z_PARAM_FUNC_EX2 was added as well with the same arguments as Z_PARAM_FUNC_EX
- plus an additional argument free_trampoline.
-
-* The zend_object_iterator_funcs valid member has changed its signature from
- int(*)(zend_object_iterator *) to zend_result(*)(zend_object_iterator *) to
- be more in line with what callbacks are returning.
-
-* The backwards compatibility headers ext/standard/{php_lcg.h,php_mt_rand.h,
- php_rand.h,php_random.h} have been removed. Include ext/random/php_random.h
- directly.
-
-* The zend_*printf family of functions now supports the "%S" modifier to print
- out zend_string*. This won't cut off the string if it embeds a NUL byte.
-
-* The inet_aton() and win32/inet.h have been removed. Use platform-agnostic
- inet_pton() from arpa/inet.h or ws2tcpip.h on Windows.
-
-* zend_mm_set_custom_debug_handlers() has been removed from ZendMM, use
- zend_mm_set_custom_handlers() instead which now supports DEBUG builds
-
-* zend_mm_set_custom_handlers() has changed its signature from
- void()(zend_mm_heap *heap,
- void* (*_malloc)(size_t),
- void (*_free)(void*),
- void* (*_realloc)(void*, size_t))
- to
- void()(zend_mm_heap *heap,
- void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
- void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
- void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC))
-
-* zend_mm_get_custom_handlers() has changed its signature from
- void()(zend_mm_heap *heap,
- void* (**_malloc)(size_t),
- void (**_free)(void*),
- void* (**_realloc)(void*, size_t))
- to
- void()(zend_mm_heap *heap,
- void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
- void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
- void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC))
-
-* Added gc and shutdown custom handlers, settable via
- zend_mm_set_custom_handlers_ex()
-
-* __zend_malloc() has changed their signature from
- void(*)(size_t) to
- void(*)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
-
-* __zend_calloc() has changed their signature from
- void(*)(size_t, size_t) to
- void(*)(size_t, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
-
-* __zend_realloc() has changed their signature from
- void(*)(void *, size_t) to
- void(*)(void *, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
-
-* zend_observer_remove_begin_handler() and zend_observer_remove_end_handler()
- got each a new parameter returning an observer which must be called, if the
- removal happened during observer execution.
-
-* zend_get_internal_function_extension_handle[s]() must now be used over
- zend_get_op_array_extension_handle[s]() when registering run_time_cache slots
- for internal functions. If you need a cache slot for both internal and user
- functions, you may obtain a slot for each through the corresponding function.
-
-* zend_is_true now returns bool rather than int. Note that on PHP 8 this has
- always returned 0 or 1, so conversion should be trivial.
-
-* Added zend_hash_get_current_pos_ex() variant of zend_hash_get_current_pos().
-
-* Renamed rebuild_object_properties() to rebuild_object_properties_internal().
- This function should not be used outside of zend_std_get_properties_ex() and
- zend_std_get_properties(). Use zend_std_get_properties_ex() or
- zend_std_get_properties() instead.
-
-* zend_object.properties must not be accessed directly. Use
- zend_std_get_properties_ex() instead.
-
-* Removed IS_STATIC_VAR_UNINITIALIZED constant. Check for IS_NULL in the
- static_variables array instead.
-
-* Removed ZEND_DIM_ALTERNATIVE_SYNTAX constant. This syntax no longer has a
- specialized error message.
+- Zend
+ . 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.
========================
2. Build system changes
========================
- a. Abstract
- - The configure option --with-imap has been removed.
- - The configure option --with-mhash emits deprecation warning.
- - The configure option --with-pdo-oci has been removed.
- - The configure option --with-pspell has been removed.
- - Symbol SIZEOF_SHORT removed (size of 2 on 32-bit and 64-bit platforms).
- - Symbol DBA_CDB_MAKE removed in ext/dba.
- - Symbol HAVE_LIBM has been removed.
- - Symbol HAVE_INET_ATON has been removed.
- - Symbol HAVE_SIGSETJMP has been removed.
- - The Zend/zend_istdiostream.h header has been removed.
-
- b. Unix build system changes
- - The configure option --with-imap-ssl has been removed.
- - The configure option --with-oci8 has been removed.
- - The configure option --with-zlib-dir has been removed.
- - The configure option --with-kerberos has been removed.
- - The configure option --with-openssl-dir has been removed. SSL support in
- ext/ftp and ext/mysqlnd is enabled implicitly, when building with
- ext/openssl (--with-openssl), or explicitly by using new configure options
- --with-ftp-ssl and --with-mysqlnd-ssl.
- - New configure option --with-openssl-legacy-provider to enable OpenSSL
- legacy provider.
- - New configure option --with-openssl-argon2 to enable PASSWORD_ARGON2
- from OpenSSL 3.2
- - COOKIE_IO_FUNCTIONS_T symbol has been removed (use cookie_io_functions_t).
- - HAVE_SOCKADDR_UN_SUN_LEN symbol renamed to HAVE_STRUCT_SOCKADDR_UN_SUN_LEN.
- - HAVE_UTSNAME_DOMAINNAME symbol renamed to HAVE_STRUCT_UTSNAME_DOMAINNAME.
- - PHP_CHECK_IN_ADDR_T Autoconf macro and 'in_addr_t' fallback definition to
- 'u_int' removed (use AC_CHECK_TYPES Autoconf macro instead).
- - HAVE_ODBC2 symbol has been removed in ext/odbc.
- - Removed linking with obsolete dnet_stub library in ext/pdo_dblib.
- - Removed checking and linking with obsolete libbind for some functions.
- - Symbol HAVE_JSON has been removed (ext/json is always available since PHP
- 8.0).
- - Symbol DARWIN has been removed (use __APPLE__ to target Darwin systems).
- - Symbol MISSING_FCLOSE_DECL and Autoconf macro PHP_MISSING_FCLOSE_DECL were
- removed.
- - Symbol HAVE_BSD_ICONV has been removed.
- - Symbol ZEND_FIBER_ASM has been removed.
- - Symbols HAVE_DLOPEN and HAVE_DLSYM have been removed.
- - Symbol HAVE_MYSQL has been removed.
- - Symbol HAVE_PDO_SQLITELIB has been removed.
- - Symbol HAVE_WAITPID has been removed.
- - Symbol HAVE_LIBPQ has been removed.
- - Symbols HAVE_LIBRT and HAVE_TIMER_CREATE removed.
- - Symbols PHP_FPM_SYSTEMD, PHP_FPM_USER, and PHP_FPM_GROUP removed.
- - Symbol PTHREADS has been removed.
- - Symbol HAVE_STRPTIME_DECL_FAILS has been removed (use HAVE_DECL_STRPTIME).
- - Symbol HAVE_PHPDBG has been removed.
- - Symbols PHP_HAVE_AVX512_SUPPORTS and PHP_HAVE_AVX512_VBMI_SUPPORTS are now
- either defined to 1 or undefined.
- - Symbol HAVE_LIBCRYPT has been removed.
- - Autoconf macro PHP_DEFINE (atomic includes) removed (use AC_DEFINE and
- config.h).
- - Autoconf macro PHP_WITH_SHARED has been removed (use PHP_ARG_WITH).
- - Autoconf macro PHP_STRUCT_FLOCK has been removed (use AC_CHECK_TYPES).
- - Autoconf macro PHP_SOCKADDR_CHECKS has been removed (use AC_CHECK_TYPES and
- AC_CHECK_MEMBERS).
- - Autoconf macro PHP_CHECK_GCC_ARG has been removed since PHP 8.0 (use
- AX_CHECK_COMPILE_FLAG).
- - Autoconf macro PHP_PROG_RE2C got a new 2nd argument to define common
- default re2c command-line options substituted to the Makefile RE2C_FLAGS
- variable.
- - Autoconf macros PHP_CHECK_BUILTIN_* have been removed in favor of
- PHP_CHECK_BUILTIN and all PHP_HAVE_BUILTIN_* symbols changed to be either
- undefined or defined to 1 whether compiler supports the builtin.
- - Added php-config --lib-dir and --lib-embed options for PHP embed SAPI.
- - PDO extensions in php-src don't have the include flag -I$pdo_cv_inc_path
- directory anymore.
- - Autoconf macro PHP_SETUP_OPENSSL doesn't accept the 3rd argument anymore.
- - Autoconf macro PHP_EVAL_LIBLINE got a new 3rd argument to override the
- ext_shared checks.
- - Autoconf macro PHP_SETUP_LIBXML doesn't define the redundant HAVE_LIBXML
- symbol anymore and requires at least libxml2 2.9.4.
- - Autoconf macro PHP_SETUP_ICONV doesn't define the HAVE_ICONV symbol
- anymore.
- - Autoconf macro PHP_AP_EXTRACT_VERSION is obsolete (use the
- 'apxs -q HTTPD_VERSION').
- - Autoconf macro PHP_OUTPUT is obsolete (use AC_CONFIG_FILES).
- - Autoconf macro PHP_TEST_BUILD is obsolete (use AC_* macros).
- - Autoconf macro PHP_BUILD_THREAD_SAFE is obsolete (set enable_zts manually).
- - Autoconf macro PHP_DEF_HAVE is obsolete (use AC_DEFINE).
- - Autoconf macro PHP_PROG_SETUP now accepts an argument to set the minimum
- required PHP version during the build.
- - Autoconf macro PHP_INSTALL_HEADERS arguments can now be also
- blank-or-newline-separated lists instead of only separated with whitespace
- or backslash-then-newline.
- - Autoconf macro PHP_ADD_BUILD_DIR now also accepts 1st argument as a
- blank-or-newline-separated separated list.
- - Autoconf macros PHP_NEW_EXTENSION, PHP_ADD_SOURCES, PHP_ADD_SOURCES_X,
- PHP_SELECT_SAPI now have the source files and flags arguments normalized so
- the list of items can be passed as a blank-or-newline-separated list.
- - Autoconf macro PHP_ADD_INCLUDE now takes also a blank-or-newline-separated
- list of include directories instead of a single directory. The "prepend"
- argument is validated at Autoconf compile time.
- - TSRM/tsrm.m4 file and its TSRM_CHECK_PTHREADS macro have been removed.
- - Added pkg-config support to find libpq for the pdo_pgsql and pgsql
- extensions. The libpq paths can be customized with the PGSQL_CFLAGS and
- PGSQL_LIBS environment variables. When a directory argument is provided to
- configure options (--with-pgsql=DIR or --with-pdo-pgsql=DIR), it will be
- used instead of the pkg-config search.
- - Added pkg-config support to find unixODBC and iODBC for the pdo_odbc
- extension.
- - Added pkg-config support to find GNU MP library. As a fallback default
- system paths are searched. When a directory argument is provided
- (--with-gmp=DIR), it will be used instead of the pkg-config.
- - Added optional pkg-config support to find NET-SNMP library. As a fallback
- net-snmp-config utility is used like before.
- - Removed BC enable_pear variable check due to --enable-pear configure option
- once used (use with_pear variable name).
- - Cache variables synced to php_cv_* naming scheme. If you use them for
- advanced cross-compilation, these were renamed:
- - ac_cv_copy_file_range -> php_cv_func_copy_file_range
- - ac_cv_flush_io -> php_cv_have_flush_io
- - ac_cv_func_getaddrinfo -> php_cv_func_getaddrinfo
- - ac_cv_have_broken_gcc_strlen_opt -> php_cv_have_broken_gcc_strlen_opt
- - ac_cv_have_pcre2_jit -> php_cv_have_pcre2_jit
- - ac_cv_pread -> php_cv_func_pread
- - ac_cv_pwrite -> php_cv_func_pwrite
- - ac_cv_syscall_shadow_stack_exists -> php_cv_have_shadow_stack_syscall
- - ac_cv_time_r_type -> php_cv_time_r_type
- - ac_cv_write_stdout -> php_cv_have_write_stdout
- and all other checks wrapped with their belonging cache variables (see *.m4
- source files for details).
-
- c. Windows build system changes
- - The configure options --with-oci8-11g, --with-oci8-12c, --with-oci8-19,
- --enable-apache2-2handler have been removed.
- - The configure option --enable-apache2-4handler is now an alias for the
- preferred --enable-apache2handler.
- - Added Bison flag '-Wall' when generating lexer files as done in *nix build
- system.
- - HAVE_WIN32_NATIVE_THREAD, USE_WIN32_NATIVE_THREAD, ENABLE_THREADS symbols
- in ext/mbstring/libmbfl removed.
- - FIBER_ASSEMBLER and FIBER_ASM_ARCH Makefile variables removed in favor of
- PHP_ASSEMBLER and FIBER_ASM_ABI.
- - HAVE_PHP_SOAP symbol renamed to HAVE_SOAP.
- - Unused symbols CONFIGURATION_FILE_PATH, DISCARD_PATH, HAVE_ERRMSG_H,
- HAVE_REGCOMP, HAVE_RINT, NEED_ISBLANK, PHP_URL_FOPEN, REGEX, HSREGEX,
- USE_CONFIG_FILE have been removed.
- - The HAVE_OPENSSL symbol has been removed.
- - The HAVE_OPENSSL_EXT symbol is now consistently defined to value 1 whether
- the openssl extension is available either as shared or built statically.
- - Added configure option --enable-phpdbg-debug to build phpdbg in debug mode.
- - The win32/build/libs_version.txt file has been removed.
- - MSVC builds now use the new preprocessor (/Zc:preprocessor).
- - The CHECK_HEADER_ADD_INCLUDE function now consistently defines preprocessor
- macros HAVE__H either to value 1 or leaves them undefined to match
- the Autotools headers checks.
+- Windows build system changes
+ . SAPI() and ADD_SOURCES() now suport 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.
========================
3. Module changes
========================
- a. ext/dom
- - dom_read_t and dom_write_t now expect the function to return zend_result
- instead of int.
- - The macros DOM_NO_ARGS() and DOM_NOT_IMPLEMENTED() have been removed.
- - New public APIs are available to handle callbacks from XPath, see
- xpath_callbacks.h.
- - Added public APIs to manipulate namespace data, see namespace_compat.h.
- - php_dom_create_object() now no longer accepts a NULL obj argument.
- - Removed the `ret` argument from the DOM_RET_OBJ macro, use the return
- value instead.
- - Removed DOM_XMLNS_NAMESPACE from xml_common.h. Use DOM_XMLNS_NS_URI
- from namespace_compat.h instead.
- - Added php_dom_get_ns_mapper(), php_dom_next_in_tree_order(),
- php_dom_follow_spec_doc_ref(), and php_dom_follow_spec_doc_ref().
-
- b. ext/random
- - The macro RAND_RANGE_BADSCALING() has been removed. The implementation
- should either be inlined and undefined behavior fixed or it should be
- replaced by a non-biased scaler.
- - The php_srand() and php_rand() functions have been removed. These were
- slim wrappers around the corresponding php_mt_srand() and php_mt_rand()
- function since PHP 7.1, but using zend_long instead of uint32_t as their
- input/output types. This made their behavior incompatible between 32-bit
- and 64-bit builds of PHP. Users of these functions are encouraged to
- migrate to one of the more modern engines provided since PHP 8.2. If that
- is not possible, due to backwards compatibility requirements, then the
- php_mt_srand() and php_mt_rand() functions should be called directly and
- the values appropriately casted.
- - The PHP_RAND_MAX and RAND_MAX constants corresponding to the removed
- php_rand() have also been removed.
- - The generate member of a php_random_algo is now expected to return
- the new php_random_result struct, replacing the last_generated_size
- member of the php_random_status struct and the generate_size member of
- the php_random_algo struct.
- - The php_random_status struct has been removed, since the previous change
- reduced it to a single void* member containing the actual state, resulting
- in needless indirection. Functions taking a php_random_algo struct pointer
- and a php_random_status struct pointer as separate parameters now take a
- single php_random_algo_with_state struct by value, making it easier to
- pass around the state with its associated algorithm and thus reducing
- the chance for mistakes.
- - The seed member of a php_random_algo has been removed. As a replacement
- engine-specific seeding functions are now exposed. This change allows
- users to better take engine-specific behavior into account. As an example
- Mt19937 ignored the upper half of the seed parameter of the generic
- seeding function.
- - The CSPRNG API (php_random_(bytes|int)_*) is now provided by the new
- and much smaller php_random_csprng.h header. The new header is included
- in php_random.h for compatibility with existing users.
- - A new php_random_generate_fallback_seed() function has been added as a
- replacement for the generically named GENERATE_SEED(). The internal
- implementation has been improved to generate better seeds, however any
- users should use the opportunity to verify that seeding is first
- attempted using the CSPRNG for better output size flexibility.
- - The standalone combined_lcg engine has been removed, as the lcg_value()
- userland function is deprecated and as the engine is unable to return
- unbiased integer values. The internal php_combined_lcg() function remains
- available for now.
+- ext/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.
- c. ext/xsl
- - The function php_xsl_create_object() was removed as it was not used
- nor exported.
+- ext/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.
- d. ext/libxml
- - Added php_libxml_pretend_ctx_error_ex() to emit errors as if they had come
- from libxml.
- - Added php_libxml_error_handler_va() to pass libxml errors, and
- corresponding php_libxml_error_level enum.
- - Removed the "properties" HashTable field from php_libxml_node_object.
- - Added a way to attached private data to a php_libxml_ref_obj.
- - Added a way to fix a class type onto php_libxml_ref_obj.
- - Added a way to record quirks mode in php_libxml_ref_obj.
- - Added php_libxml_uses_internal_errors().
- - Added a way to override document handlers (e.g. serialization) with
- php_libxml_document_handlers.
- - Changed the refcount fields from int to unsigned int.
+- ext/libxml
+ . The refcount APIs now return an `unsigned int` instead of an `int`.
- e. ext/date
- - Added the php_format_date_ex() API to format instances of php_date_obj.
- - Added the php_date_initialize_from_ts_long() and
- php_date_initialize_from_ts_double() to initialize a php_date_obj with
- the given unix timestamp using GMT +00:00.
+- ext/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.
- f. ext/pcre
- - php_pcre_match_impl() now no longer has a use_flags argument.
- When flags should be ignored, pass 0 to the flags argument.
- - php_pcre_match_impl() and pcre_get_compiled_regex_cache_ex() now use
- proper boolean argument types instead of integer types.
- - pcre_get_compiled_regex_cache_ex() now provides an option to collect extra
- options (from modifiers used in the expression, for example), and calls
- pcre2_set_compile_extra_options() with those options.
- - Removed per-request cache, the cache is now always per process or
- per thread depending on whether you use NTS or ZTS.
- This was removed due to fundamental ordering issues between destructors.
-
- g. ext/standard
- - Added the php_base64_encode_ex() API with flag parameters, value can be
- PHP_BASE64_NO_PADDING to encode without the padding character '='.
- - The php_escape_shell_cmd() now takes a zend_string* instead of a char*
- Moreover, providing it with a binary safe string is the responsibility of
- the caller now.
- - The php_escape_shell_arg() now takes a zend_string* instead of a char*
- Moreover, providing it with a binary safe string is the responsibility of
- the caller now.
- - The php_info_html_esc() function has been removed, use
- php_escape_html_entities() with ENT_QUOTES directly instead.
- - The deprecated php_uint32 and php_int32 typedefs have been removed from
- ext/standard/basic_functions.h. Use the standard uint32_t and int32_t
- types instead.
- - The php_mkdir() and php_mkdir_ex() APIs have been removed, use
- php_stream_mkdir() instead.
- - The php_strtoupper(), php_string_toupper(), php_strtolower(), and
- php_string_tolower() functions has been removed, use zend_str_toupper(),
- zend_string_toupper(), zend_str_tolower(), and zend_string_tolower()
- respectively instead.
- - The php_replace_controlchars_ex() function is no longer exposed.
-
- h. ext/session
- - Added the php_get_session_status() API to get the session status, which is
- equivalent to reading PS(session_status) but works with shared objects too.
- - Added the php_get_session_var_str() API to set a session variable without
- needing to create a zend_string.
- - The ext/session/php_session.h doesn't transitively include the
- ext/hash/php_hash.h header anymore.
- - It is no longer allowed to return out of the PS_ENCODE_LOOP macro.
- Instead, you should break out of the loop instead.
-
- i. ext/xml
- - Made the expat compatibility wrapper XML_GetCurrentByteIndex return a long
- instead of an int. This corresponds to the XML_Index type when
- XML_LARGE_SIZE is not used in expat.
+- ext/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.
========================
4. OpCode changes
========================
-* DO_ICALL, DO_FCALL, and DO_FCALL_BY_NAME now call zend_interrupt_function
- while the internal frame is still on the stack. This means interrupt handlers
- will now see the internal call. If your interrupt handler does something like
- switching EG(current_execute_data), it should not do so if an internal func
- is on top.
-* New FRAMELESS_ICALL_[0,3] opcodes for faster internal function calls have been
- added. These opcodes don't create a stack frame, but pass arguments via opcode
- operands. They only work for functions that are known at compile-time, and
- that provide a frameless handler (search for usages of the
- ZEND_FRAMELESS_FUNCTION macro).
-
-* CREATE_GENERATOR now initializes the generator with opline pointing to the
- CREATE_GENERATOR op (before, opline was set to the next op).
-
-* YIELD and YIELD_FROM do not increment the opline anymore.
-
-* The EXIT opcode has been removed as exit is now implemented as a function.
-
========================
5. SAPI changes
========================
diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c
index 6fcbd04f12af5..96a0e81f03825 100644
--- a/Zend/Optimizer/block_pass.c
+++ b/Zend/Optimizer/block_pass.c
@@ -274,7 +274,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
* If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
* hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
* A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
- if (opline->op1_type == IS_TMP_VAR) {
+ if (opline->extended_value == ZEND_FREE_VOID_CAST) {
+ /* Keep the ZEND_FREE opcode alive. */
+ } else if (opline->op1_type == IS_TMP_VAR) {
src = VAR_SOURCE(opline->op1);
if (src) {
switch (src->opcode) {
@@ -468,7 +470,67 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
goto optimize_bool;
}
break;
+ case ZEND_IS_IDENTICAL:
+ if (opline->op1_type == IS_CONST &&
+ opline->op2_type == IS_CONST) {
+ goto optimize_constant_binary_op;
+ }
+
+ if (opline->op1_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP1_LITERAL(opline)) <= IS_TRUE && Z_TYPE(ZEND_OP1_LITERAL(opline)) >= IS_NULL)) {
+ /* IS_IDENTICAL(TRUE, T) => TYPE_CHECK(T, TRUE)
+ * IS_IDENTICAL(FALSE, T) => TYPE_CHECK(T, FALSE)
+ * IS_IDENTICAL(NULL, T) => TYPE_CHECK(T, NULL)
+ */
+ opline->opcode = ZEND_TYPE_CHECK;
+ opline->extended_value = (1 << Z_TYPE(ZEND_OP1_LITERAL(opline)));
+ COPY_NODE(opline->op1, opline->op2);
+ SET_UNUSED(opline->op2);
+ ++(*opt_count);
+ goto optimize_type_check;
+ } else if (opline->op2_type == IS_CONST &&
+ (Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_TRUE && Z_TYPE(ZEND_OP2_LITERAL(opline)) >= IS_NULL)) {
+ /* IS_IDENTICAL(T, TRUE) => TYPE_CHECK(T, TRUE)
+ * IS_IDENTICAL(T, FALSE) => TYPE_CHECK(T, FALSE)
+ * IS_IDENTICAL(T, NULL) => TYPE_CHECK(T, NULL)
+ */
+ opline->opcode = ZEND_TYPE_CHECK;
+ opline->extended_value = (1 << Z_TYPE(ZEND_OP2_LITERAL(opline)));
+ SET_UNUSED(opline->op2);
+ ++(*opt_count);
+ goto optimize_type_check;
+ }
+ break;
+ case ZEND_TYPE_CHECK:
+optimize_type_check:
+ if (opline->extended_value == (1 << IS_TRUE) || opline->extended_value == (1 << IS_FALSE)) {
+ if (opline->op1_type == IS_TMP_VAR &&
+ !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
+ src = VAR_SOURCE(opline->op1);
+ if (src) {
+ switch (src->opcode) {
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ /* T = BOOL(X) + TYPE_CHECK(T, TRUE) -> BOOL(X), NOP
+ * T = BOOL(X) + TYPE_CHECK(T, FALSE) -> BOOL_NOT(X), NOP
+ * T = BOOL_NOT(X) + TYPE_CHECK(T, TRUE) -> BOOL_NOT(X), NOP
+ * T = BOOL_NOT(X) + TYPE_CHECK(T, FALSE) -> BOOL(X), NOP
+ */
+ src->opcode =
+ ((src->opcode == ZEND_BOOL) == (opline->extended_value == (1 << IS_TRUE))) ?
+ ZEND_BOOL : ZEND_BOOL_NOT;
+ COPY_NODE(src->result, opline->result);
+ SET_VAR_SOURCE(src);
+ MAKE_NOP(opline);
+ ++(*opt_count);
+ break;
+ }
+ }
+ }
+ }
+ break;
+
case ZEND_BOOL:
case ZEND_BOOL_NOT:
optimize_bool:
@@ -801,7 +863,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_SR:
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
- case ZEND_IS_IDENTICAL:
case ZEND_IS_NOT_IDENTICAL:
case ZEND_BOOL_XOR:
case ZEND_BW_OR:
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c
index 4b27aebc9d39a..d0aaccec7ce2c 100644
--- a/Zend/Optimizer/compact_literals.c
+++ b/Zend/Optimizer/compact_literals.c
@@ -43,50 +43,6 @@ typedef struct _literal_info {
info[n].num_related = (related); \
} while (0)
-static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
-{
- zend_arg_info *arg_info;
- if (arg_num > 0) {
- if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
- return 0;
- }
- if (EXPECTED(arg_num <= op_array->num_args)) {
- arg_info = &op_array->arg_info[arg_num-1];
- } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
- arg_info = &op_array->arg_info[op_array->num_args];
- } else {
- return 0;
- }
- } else {
- arg_info = op_array->arg_info - 1;
- }
-
- if (ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
- if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
- /* Intersection types cannot have nested list types */
- if (ZEND_TYPE_IS_INTERSECTION(arg_info->type)) {
- return ZEND_TYPE_LIST(arg_info->type)->num_types;
- }
- ZEND_ASSERT(ZEND_TYPE_IS_UNION(arg_info->type));
- size_t count = 0;
- zend_type *list_type;
-
- ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(arg_info->type), list_type) {
- if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
- count += ZEND_TYPE_LIST(*list_type)->num_types;
- } else {
- ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
- count += 1;
- }
- } ZEND_TYPE_LIST_FOREACH_END();
- return count;
- }
- return 1;
- }
-
- return 0;
-}
-
static uint32_t add_static_slot(HashTable *hash,
zend_op_array *op_array,
uint32_t op1,
@@ -165,7 +121,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
HashTable hash;
zend_string *key = NULL;
void *checkpoint = zend_arena_checkpoint(ctx->arena);
- int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
+ int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot;
if (op_array->last_literal) {
info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
@@ -175,6 +131,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
end = opline + op_array->last;
while (opline < end) {
switch (opline->opcode) {
+ case ZEND_JMP_FRAMELESS:
+ LITERAL_INFO(opline->op1.constant, 1);
+ break;
case ZEND_INIT_FCALL_BY_NAME:
LITERAL_INFO(opline->op2.constant, 2);
break;
@@ -480,13 +439,14 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
zend_hash_clean(&hash);
op_array->last_literal = j;
- const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
- memset(const_slot, -1, j * 6 * sizeof(int));
+ const_slot = zend_arena_alloc(&ctx->arena, j * 7 * sizeof(int));
+ memset(const_slot, -1, j * 7 * sizeof(int));
class_slot = const_slot + j;
func_slot = class_slot + j;
bind_var_slot = func_slot + j;
property_slot = bind_var_slot + j;
method_slot = property_slot + j;
+ jmp_slot = method_slot + j;
/* Update opcodes to use new literals table */
cache_size = zend_op_array_extension_handles * sizeof(void*);
@@ -500,26 +460,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
opline->op2.constant = map[opline->op2.constant];
}
switch (opline->opcode) {
- case ZEND_RECV_INIT:
- case ZEND_RECV:
- case ZEND_RECV_VARIADIC:
- {
- size_t num_classes = type_num_classes(op_array, opline->op1.num);
- if (num_classes) {
- opline->extended_value = cache_size;
- cache_size += num_classes * sizeof(void *);
- }
- break;
- }
- case ZEND_VERIFY_RETURN_TYPE:
- {
- size_t num_classes = type_num_classes(op_array, 0);
- if (num_classes) {
- opline->op2.num = cache_size;
- cache_size += num_classes * sizeof(void *);
- }
- break;
- }
case ZEND_ASSIGN_STATIC_PROP_OP:
if (opline->op1_type == IS_CONST) {
// op1 static property
@@ -773,10 +713,19 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_CLASS_DELAYED:
- case ZEND_JMP_FRAMELESS:
opline->extended_value = cache_size;
cache_size += sizeof(void *);
break;
+ case ZEND_JMP_FRAMELESS:
+ // op1 func
+ if (jmp_slot[opline->op1.constant] >= 0) {
+ opline->extended_value = jmp_slot[opline->op1.constant];
+ } else {
+ opline->extended_value = cache_size;
+ cache_size += sizeof(void *);
+ jmp_slot[opline->op1.constant] = opline->extended_value;
+ }
+ break;
case ZEND_SEND_VAL:
case ZEND_SEND_VAL_EX:
case ZEND_SEND_VAR:
diff --git a/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c
index 414abe01f96ac..a00fd8bc6ad30 100644
--- a/Zend/Optimizer/dce.c
+++ b/Zend/Optimizer/dce.c
@@ -80,7 +80,6 @@ static inline bool may_have_side_effects(
case ZEND_IS_IDENTICAL:
case ZEND_IS_NOT_IDENTICAL:
case ZEND_QM_ASSIGN:
- case ZEND_FREE:
case ZEND_FE_FREE:
case ZEND_TYPE_CHECK:
case ZEND_DEFINED:
@@ -127,6 +126,8 @@ static inline bool may_have_side_effects(
case ZEND_ARRAY_KEY_EXISTS:
/* No side effects */
return 0;
+ 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))) {
diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c
index 2c3aaae065997..bf85764c93b49 100644
--- a/Zend/Optimizer/dfa_pass.c
+++ b/Zend/Optimizer/dfa_pass.c
@@ -254,7 +254,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
free_alloca(shiftlist, use_heap);
}
-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;
}
@@ -267,9 +267,9 @@ static bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) {
static inline bool can_elide_list_type(
const zend_script *script, const zend_op_array *op_array,
- const zend_ssa_var_info *use_info, zend_type type)
+ const zend_ssa_var_info *use_info, const zend_type type)
{
- zend_type *single_type;
+ const zend_type *single_type;
/* For intersection: result==false is failure, default is success.
* For union: result==true is success, default is failure. */
bool is_intersection = ZEND_TYPE_IS_INTERSECTION(type);
@@ -280,7 +280,7 @@ static inline bool can_elide_list_type(
}
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type));
- zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname);
+ const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname);
zend_string_release(lcname);
bool result = ce && safe_instanceof(use_info->ce, ce);
if (result == !is_intersection) {
diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c
index ce6c43afaedbe..8b29f47c94976 100644
--- a/Zend/Optimizer/optimize_func_calls.c
+++ b/Zend/Optimizer/optimize_func_calls.c
@@ -78,8 +78,10 @@ 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)
{
+ const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
+
if (func->type == ZEND_USER_FUNCTION
- && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_DEPRECATED))
+ && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_DEPRECATED|no_discard))
/* TODO: function copied from trait may be inconsistent ??? */
&& !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE))
&& fcall->extended_value >= func->op_array.required_num_args
@@ -202,18 +204,12 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
literal_dtor(&ZEND_OP2_LITERAL(fcall));
fcall->op2.constant = fcall->op2.constant + 1;
- if (opline->opcode != ZEND_CALLABLE_CONVERT) {
- opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
- }
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
fcall->opcode = ZEND_INIT_FCALL;
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
literal_dtor(&op_array->literals[fcall->op2.constant]);
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
fcall->op2.constant = fcall->op2.constant + 1;
- if (opline->opcode != ZEND_CALLABLE_CONVERT) {
- opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
- }
} else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|| fcall->opcode == ZEND_INIT_METHOD_CALL
|| fcall->opcode == ZEND_INIT_PARENT_PROPERTY_HOOK_CALL
@@ -223,6 +219,16 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
ZEND_UNREACHABLE();
}
+ /* If the INIT opcode changed the DO opcode can also change to
+ * a more optimized one.
+ *
+ * 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) {
+ 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) {
diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c
index b788b652979de..4e46b38a8eb5e 100644
--- a/Zend/Optimizer/zend_dump.c
+++ b/Zend/Optimizer/zend_dump.c
@@ -23,7 +23,7 @@
#include "zend_func_info.h"
#include "zend_call_graph.h"
#include "zend_dump.h"
-#include "ext/standard/php_string.h"
+#include "zend_smart_str.h"
void zend_dump_ht(HashTable *ht)
{
@@ -66,13 +66,27 @@ void zend_dump_const(const zval *zv)
case IS_DOUBLE:
fprintf(stderr, " float(%g)", Z_DVAL_P(zv));
break;
- case IS_STRING:;
- zend_string *escaped_string = php_addcslashes(Z_STR_P(zv), "\"\\", 2);
+ case IS_STRING: {
+ smart_str escaped_string = {0};
+ smart_str_append_escaped(&escaped_string, Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ smart_str_0(&escaped_string);
- fprintf(stderr, " string(\"%s\")", ZSTR_VAL(escaped_string));
+ fprintf(stderr, " string(\"");
- zend_string_release(escaped_string);
+ /* Also escape '"' */
+ for (size_t i = 0; i < ZSTR_LEN(escaped_string.s); i++) {
+ if (ZSTR_VAL(escaped_string.s)[i] == '"') {
+ fprintf(stderr, "\\\"");
+ } else {
+ putc(ZSTR_VAL(escaped_string.s)[i], stderr);
+ }
+ }
+
+ fprintf(stderr, "\")");
+
+ smart_str_free_ex(&escaped_string, false);
break;
+ }
case IS_ARRAY:
fprintf(stderr, " array(...)");
break;
diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h
index 8751ff30c6950..0fc33ae2f6e1c 100644
--- a/Zend/Optimizer/zend_func_infos.h
+++ b/Zend/Optimizer/zend_func_infos.h
@@ -46,6 +46,7 @@ static const func_info_t func_infos[] = {
F1("curl_multi_strerror", MAY_BE_STRING|MAY_BE_NULL),
F1("curl_share_init", MAY_BE_OBJECT),
F1("curl_share_strerror", MAY_BE_STRING|MAY_BE_NULL),
+ F1("curl_share_init_persistent", MAY_BE_OBJECT),
F1("curl_strerror", MAY_BE_STRING|MAY_BE_NULL),
F1("curl_version", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE),
F1("date", MAY_BE_STRING),
@@ -428,13 +429,12 @@ static const func_info_t func_infos[] = {
#endif
F1("get_current_user", MAY_BE_STRING),
FN("get_cfg_var", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE),
- F1("error_get_last", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_NULL),
+ F1("error_get_last", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_NULL),
F1("highlight_file", MAY_BE_STRING|MAY_BE_BOOL),
F1("php_strip_whitespace", MAY_BE_STRING),
F1("highlight_string", MAY_BE_STRING|MAY_BE_TRUE),
F1("ini_get_all", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_NULL|MAY_BE_FALSE),
F1("set_include_path", MAY_BE_STRING|MAY_BE_FALSE),
- F1("get_include_path", MAY_BE_STRING|MAY_BE_FALSE),
F1("print_r", MAY_BE_STRING|MAY_BE_TRUE),
#if defined(HAVE_GETSERVBYPORT)
F1("getservbyport", MAY_BE_STRING|MAY_BE_FALSE),
diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c
index 9f2e3115d3666..fd8735354c6da 100644
--- a/Zend/Optimizer/zend_optimizer.c
+++ b/Zend/Optimizer/zend_optimizer.c
@@ -82,31 +82,8 @@ 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) /* {{{ */
{
- switch (type) {
- case IS_NULL:
- ZVAL_NULL(result);
- return SUCCESS;
- case _IS_BOOL:
- ZVAL_BOOL(result, zval_is_true(op1));
- return SUCCESS;
- case IS_LONG:
- ZVAL_LONG(result, zval_get_long(op1));
- return SUCCESS;
- case IS_DOUBLE:
- ZVAL_DOUBLE(result, zval_get_double(op1));
- return SUCCESS;
- case IS_STRING:
- /* Conversion from double to string takes into account run-time
- 'precision' setting and cannot be evaluated at compile-time */
- if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
- ZVAL_STR(result, zval_get_string(op1));
- return SUCCESS;
- }
- break;
- case IS_ARRAY:
- ZVAL_COPY(result, op1);
- convert_to_array(result);
- return SUCCESS;
+ if (zend_try_ct_eval_cast(result, type, op1)) {
+ return SUCCESS;
}
return FAILURE;
}
diff --git a/Zend/Zend.m4 b/Zend/Zend.m4
index 47ea9c831d524..45791a34c5f80 100644
--- a/Zend/Zend.m4
+++ b/Zend/Zend.m4
@@ -151,6 +151,11 @@ AC_CHECK_FUNCS(m4_normalize([
pthread_stackseg_np
]))
+AC_CHECK_DECL([clock_gettime_nsec_np],
+ [AC_DEFINE([HAVE_CLOCK_GETTIME_NSEC_NP], [1],
+ [Define to 1 if you have the declaration of 'clock_gettime_nsec_np'.])],,
+ [#include ])
+
dnl
dnl Check for sigsetjmp. If sigsetjmp is defined as a macro, use AC_CHECK_DECL
dnl as a fallback since AC_CHECK_FUNC cannot detect macros.
diff --git a/Zend/tests/ArrayAccess_indirect_append.phpt b/Zend/tests/ArrayAccess/ArrayAccess_indirect_append.phpt
similarity index 100%
rename from Zend/tests/ArrayAccess_indirect_append.phpt
rename to Zend/tests/ArrayAccess/ArrayAccess_indirect_append.phpt
diff --git a/Zend/tests/bug30346.phpt b/Zend/tests/ArrayAccess/bug30346.phpt
similarity index 100%
rename from Zend/tests/bug30346.phpt
rename to Zend/tests/ArrayAccess/bug30346.phpt
diff --git a/Zend/tests/bug33710.phpt b/Zend/tests/ArrayAccess/bug33710.phpt
similarity index 100%
rename from Zend/tests/bug33710.phpt
rename to Zend/tests/ArrayAccess/bug33710.phpt
diff --git a/Zend/tests/bug39297.phpt b/Zend/tests/ArrayAccess/bug39297.phpt
similarity index 100%
rename from Zend/tests/bug39297.phpt
rename to Zend/tests/ArrayAccess/bug39297.phpt
diff --git a/Zend/tests/bug41209.phpt b/Zend/tests/ArrayAccess/bug41209.phpt
similarity index 100%
rename from Zend/tests/bug41209.phpt
rename to Zend/tests/ArrayAccess/bug41209.phpt
diff --git a/Zend/tests/bug63217.phpt b/Zend/tests/ArrayAccess/bug63217.phpt
similarity index 100%
rename from Zend/tests/bug63217.phpt
rename to Zend/tests/ArrayAccess/bug63217.phpt
diff --git a/Zend/tests/bug64417.phpt b/Zend/tests/ArrayAccess/bug64417.phpt
similarity index 100%
rename from Zend/tests/bug64417.phpt
rename to Zend/tests/ArrayAccess/bug64417.phpt
diff --git a/Zend/tests/bug68896.phpt b/Zend/tests/ArrayAccess/bug68896.phpt
similarity index 100%
rename from Zend/tests/bug68896.phpt
rename to Zend/tests/ArrayAccess/bug68896.phpt
diff --git a/Zend/tests/bug69955.phpt b/Zend/tests/ArrayAccess/bug69955.phpt
similarity index 100%
rename from Zend/tests/bug69955.phpt
rename to Zend/tests/ArrayAccess/bug69955.phpt
diff --git a/Zend/tests/bug71731.phpt b/Zend/tests/ArrayAccess/bug71731.phpt
similarity index 100%
rename from Zend/tests/bug71731.phpt
rename to Zend/tests/ArrayAccess/bug71731.phpt
diff --git a/Zend/tests/bug78356.phpt b/Zend/tests/ArrayAccess/bug78356.phpt
similarity index 100%
rename from Zend/tests/bug78356.phpt
rename to Zend/tests/ArrayAccess/bug78356.phpt
diff --git a/Zend/tests/abstract_implicit.phpt b/Zend/tests/abstract_implicit.phpt
new file mode 100644
index 0000000000000..1ccd4d3653fcb
--- /dev/null
+++ b/Zend/tests/abstract_implicit.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Abstract methods not allowed in classes that are not abstract (GH-16067)
+--FILE--
+
+--EXPECTF--
+Fatal error: Class NotAbstract declares abstract method bar() and must therefore be declared abstract in %s on line %d
diff --git a/Zend/tests/access_modifiers_001.phpt b/Zend/tests/access_modifiers/access_modifiers_001.phpt
similarity index 100%
rename from Zend/tests/access_modifiers_001.phpt
rename to Zend/tests/access_modifiers/access_modifiers_001.phpt
diff --git a/Zend/tests/access_modifiers_002.phpt b/Zend/tests/access_modifiers/access_modifiers_002.phpt
similarity index 82%
rename from Zend/tests/access_modifiers_002.phpt
rename to Zend/tests/access_modifiers/access_modifiers_002.phpt
index 9830258c619b4..835d7445aac08 100644
--- a/Zend/tests/access_modifiers_002.phpt
+++ b/Zend/tests/access_modifiers/access_modifiers_002.phpt
@@ -1,5 +1,5 @@
--TEST--
-using multiple access modifiers (attributes)
+using multiple access modifiers (properties)
--FILE--
+--EXPECTF--
+Fatal error: Class ParentClass@anonymous must implement 1 abstract method (ParentClass::f) in %sgh15994.php on line 7
diff --git a/Zend/tests/anon/gh16067.phpt b/Zend/tests/anon/gh16067.phpt
new file mode 100644
index 0000000000000..dc09d14964a5a
--- /dev/null
+++ b/Zend/tests/anon/gh16067.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Compiler prevents explicit `abstract` methods on anonymous classes
+--FILE--
+
+--EXPECTF--
+Fatal error: Anonymous class method f() must not be abstract in %s on line 4
diff --git a/Zend/tests/gh7900.phpt b/Zend/tests/arrow_functions/gh7900.phpt
similarity index 100%
rename from Zend/tests/gh7900.phpt
rename to Zend/tests/arrow_functions/gh7900.phpt
diff --git a/Zend/tests/bug70241.phpt b/Zend/tests/assert/bug70241.phpt
similarity index 100%
rename from Zend/tests/bug70241.phpt
rename to Zend/tests/assert/bug70241.phpt
diff --git a/Zend/tests/bug70293.phpt b/Zend/tests/assert/bug70293.phpt
similarity index 100%
rename from Zend/tests/bug70293.phpt
rename to Zend/tests/assert/bug70293.phpt
diff --git a/Zend/tests/bug71922.phpt b/Zend/tests/assert/bug71922.phpt
similarity index 100%
rename from Zend/tests/bug71922.phpt
rename to Zend/tests/assert/bug71922.phpt
diff --git a/Zend/tests/gh11580.phpt b/Zend/tests/assert/gh11580.phpt
similarity index 100%
rename from Zend/tests/gh11580.phpt
rename to Zend/tests/assert/gh11580.phpt
diff --git a/Zend/tests/gh16293_001.phpt b/Zend/tests/assert/gh16293_001.phpt
similarity index 100%
rename from Zend/tests/gh16293_001.phpt
rename to Zend/tests/assert/gh16293_001.phpt
diff --git a/Zend/tests/gh16293_002.phpt b/Zend/tests/assert/gh16293_002.phpt
similarity index 100%
rename from Zend/tests/gh16293_002.phpt
rename to Zend/tests/assert/gh16293_002.phpt
diff --git a/Zend/tests/asymmetric_visibility/static_props.phpt b/Zend/tests/asymmetric_visibility/static_props.phpt
index 65fd3aa1923d2..f47dcf70b6f74 100644
--- a/Zend/tests/asymmetric_visibility/static_props.phpt
+++ b/Zend/tests/asymmetric_visibility/static_props.phpt
@@ -5,8 +5,145 @@ Asymmetric visibility on static props
class C {
public private(set) static int $prop;
+ public private(set) static array $prop2;
+ public private(set) static stdClass $prop3;
+ public private(set) static object $unset;
+
+ public static function reset() {
+ self::$prop = 1;
+ self::$prop2 = [];
+ self::$prop3 = new stdClass();
+ }
+
+ public static function setProp($prop) {
+ self::$prop = $prop;
+ }
+
+ public static function addProp2($prop2) {
+ self::$prop2[] = $prop2;
+ }
+}
+
+function test() {
+ C::reset();
+
+ try {
+ C::$prop = 2;
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop);
+
+ C::setProp(3);
+ var_dump(C::$prop);
+
+ try {
+ ++C::$prop;
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop);
+
+ try {
+ C::$prop++;
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop);
+
+ try {
+ C::$prop += str_repeat('a', 10);
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop);
+
+ try {
+ $ref = &C::$prop;
+ $ref++;
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop);
+
+ try {
+ $ref = 4;
+ C::$prop = &$ref;
+ $ref++;
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop);
+
+ try {
+ C::$prop2[] = 'foo';
+ } catch (Error $e) {
+ echo $e->getMessage(), "\n";
+ }
+ var_dump(C::$prop2);
+
+ C::addProp2('bar');
+ var_dump(C::$prop2);
+
+ C::$prop3->foo = 'foo';
+ var_dump(C::$prop3);
+
+ unset(C::$unset->foo);
}
+test();
+echo "\nRepeat:\n";
+test();
+
?>
--EXPECTF--
-Fatal error: Static property may not have asymmetric visibility in %s on line %d
+Cannot modify private(set) property C::$prop from global scope
+int(1)
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop2 from global scope
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(3) "bar"
+}
+object(stdClass)#%d (1) {
+ ["foo"]=>
+ string(3) "foo"
+}
+
+Repeat:
+Cannot modify private(set) property C::$prop from global scope
+int(1)
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop from global scope
+int(3)
+Cannot indirectly modify private(set) property C::$prop2 from global scope
+array(0) {
+}
+array(1) {
+ [0]=>
+ string(3) "bar"
+}
+object(stdClass)#%d (1) {
+ ["foo"]=>
+ string(3) "foo"
+}
diff --git a/Zend/tests/attributes/001_placement.phpt b/Zend/tests/attributes/001_placement.phpt
index 518c890ef2c4a..3162aca61a726 100644
--- a/Zend/tests/attributes/001_placement.phpt
+++ b/Zend/tests/attributes/001_placement.phpt
@@ -25,6 +25,9 @@ $f2 = #[A1(9)] function () { };
$f3 = #[A1(10)] fn () => 1;
+#[A1(11)]
+const CT_CONSTANT = 'Demo';
+
$ref = new \ReflectionClass(Foo::class);
$sources = [
@@ -37,7 +40,8 @@ $sources = [
new \ReflectionObject($object),
new \ReflectionFunction('f1'),
new \ReflectionFunction($f2),
- new \ReflectionFunction($f3)
+ new \ReflectionFunction($f3),
+ new \ReflectionConstant('CT_CONSTANT'),
];
foreach ($sources as $r) {
@@ -132,3 +136,11 @@ array(1) {
[0]=>
int(10)
}
+
+string(18) "ReflectionConstant"
+int(1)
+string(2) "A1"
+array(1) {
+ [0]=>
+ int(11)
+}
diff --git a/Zend/tests/attributes/029_reflect_internal_symbols.phpt b/Zend/tests/attributes/029_reflect_internal_symbols.phpt
index d4dc29a0bb997..fdc06cd49db9a 100644
--- a/Zend/tests/attributes/029_reflect_internal_symbols.phpt
+++ b/Zend/tests/attributes/029_reflect_internal_symbols.phpt
@@ -18,6 +18,9 @@ var_dump($rcc->getAttributes());
$rp = new ReflectionProperty('Exception', 'message');
var_dump($rp->getAttributes());
+$rct = new ReflectionConstant('PHP_VERSION');
+var_dump($rct->getAttributes());
+
?>
--EXPECT--
array(0) {
@@ -30,3 +33,5 @@ array(0) {
}
array(0) {
}
+array(0) {
+}
diff --git a/Zend/tests/attributes/034_target_values.phpt b/Zend/tests/attributes/034_target_values.phpt
new file mode 100644
index 0000000000000..e56c0c285fbd6
--- /dev/null
+++ b/Zend/tests/attributes/034_target_values.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Attribute flags are all different, TARGET_ALL includes all targets
+--FILE--
+
+--EXPECT--
+Attribute::TARGET_CLASS = 1 (127 & 1 === 1)
+Attribute::TARGET_FUNCTION = 2 (127 & 2 === 2)
+Attribute::TARGET_METHOD = 4 (127 & 4 === 4)
+Attribute::TARGET_PROPERTY = 8 (127 & 8 === 8)
+Attribute::TARGET_CLASS_CONSTANT = 16 (127 & 16 === 16)
+Attribute::TARGET_PARAMETER = 32 (127 & 32 === 32)
+Attribute::TARGET_CONSTANT = 64 (127 & 64 === 64)
+Attribute::IS_REPEATABLE = 128 (127 & 128 === 0)
+int(127)
+int(127)
+bool(true)
diff --git a/Zend/tests/allow_dynamic_properties_on_enum.phpt b/Zend/tests/attributes/allow_dynamic_properties_on_enum.phpt
similarity index 100%
rename from Zend/tests/allow_dynamic_properties_on_enum.phpt
rename to Zend/tests/attributes/allow_dynamic_properties_on_enum.phpt
diff --git a/Zend/tests/allow_dynamic_properties_on_interface.phpt b/Zend/tests/attributes/allow_dynamic_properties_on_interface.phpt
similarity index 100%
rename from Zend/tests/allow_dynamic_properties_on_interface.phpt
rename to Zend/tests/attributes/allow_dynamic_properties_on_interface.phpt
diff --git a/Zend/tests/allow_dynamic_properties_on_trait.phpt b/Zend/tests/attributes/allow_dynamic_properties_on_trait.phpt
similarity index 100%
rename from Zend/tests/allow_dynamic_properties_on_trait.phpt
rename to Zend/tests/attributes/allow_dynamic_properties_on_trait.phpt
diff --git a/Zend/tests/attributes/constants/allow_named_parameters.phpt b/Zend/tests/attributes/constants/allow_named_parameters.phpt
new file mode 100644
index 0000000000000..7b11bc3c68165
--- /dev/null
+++ b/Zend/tests/attributes/constants/allow_named_parameters.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Verify that named parameters can be passed to attributes on constants
+--FILE--
+getAttributes();
+var_dump($attribs);
+var_dump($attribs[0]->getArguments());
+$attribs[0]->newInstance();
+
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
+array(2) {
+ ["second"]=>
+ string(3) "bar"
+ ["first"]=>
+ string(3) "foo"
+}
+first: foo
+second: bar
diff --git a/Zend/tests/attributes/constants/ast_export.phpt b/Zend/tests/attributes/constants/ast_export.phpt
new file mode 100644
index 0000000000000..655fe1a9ddecb
--- /dev/null
+++ b/Zend/tests/attributes/constants/ast_export.phpt
@@ -0,0 +1,34 @@
+--TEST--
+AST can be recreated when constants have attributes
+--EXTENSIONS--
+zend_test
+--FILE--
+
+--EXPECT--
+#[MyAttrib]
+const WITH_ATTRIBUTE = true;
+#[First]
+#[Second]
+const WITH_UNGROUPED = true;
+#[First, Second]
+const WITH_GROUPED = true;
+#[MyAttrib(5, param: 'example')]
+const WITH_PARAMETERS = true;
+echo zend_test_compile_to_ast(file_get_contents(__FILE__));
diff --git a/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt b/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt
new file mode 100644
index 0000000000000..b0b88c2f6edab
--- /dev/null
+++ b/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Constants listed in valid targets when used wrong (internal attribute)
+--FILE--
+
+--EXPECTF--
+Fatal error: Attribute "Deprecated" cannot target class (allowed targets: function, method, class constant, constant) in %s on line %d
diff --git a/Zend/tests/attributes/constants/constant_listed_as_target-userland.phpt b/Zend/tests/attributes/constants/constant_listed_as_target-userland.phpt
new file mode 100644
index 0000000000000..0fcdf7795bda3
--- /dev/null
+++ b/Zend/tests/attributes/constants/constant_listed_as_target-userland.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Constants listed in valid targets when used wrong (userland attribute)
+--FILE--
+getAttributes();
+var_dump($attribs);
+$attribs[0]->newInstance();
+
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(19) "MyConstantAttribute"
+ }
+}
+
+Fatal error: Uncaught Error: Attribute "MyConstantAttribute" cannot target class (allowed targets: constant) in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionAttribute->newInstance()
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/constants/constant_redefined_addition.phpt b/Zend/tests/attributes/constants/constant_redefined_addition.phpt
new file mode 100644
index 0000000000000..08f9670989627
--- /dev/null
+++ b/Zend/tests/attributes/constants/constant_redefined_addition.phpt
@@ -0,0 +1,21 @@
+--TEST--
+If a constant is redefined, attributes remain unchanged (no attributes)
+--FILE--
+getAttributes())
+
+?>
+--EXPECTF--
+Warning: Constant MY_CONST already defined in %s on line %d
+No attributes
+array(0) {
+}
diff --git a/Zend/tests/attributes/constants/constant_redefined_change.phpt b/Zend/tests/attributes/constants/constant_redefined_change.phpt
new file mode 100644
index 0000000000000..158753ee07d84
--- /dev/null
+++ b/Zend/tests/attributes/constants/constant_redefined_change.phpt
@@ -0,0 +1,27 @@
+--TEST--
+If a constant is redefined, attributes remain unchanged (different attributes)
+--FILE--
+getAttributes())
+
+?>
+--EXPECTF--
+Warning: Constant MY_CONST already defined in %s on line %d
+Has attributes (1)
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
diff --git a/Zend/tests/attributes/constants/constant_redefined_removal.phpt b/Zend/tests/attributes/constants/constant_redefined_removal.phpt
new file mode 100644
index 0000000000000..0b679a55985e3
--- /dev/null
+++ b/Zend/tests/attributes/constants/constant_redefined_removal.phpt
@@ -0,0 +1,26 @@
+--TEST--
+If a constant is redefined, attributes remain unchanged (had attributes)
+--FILE--
+getAttributes())
+
+?>
+--EXPECTF--
+Warning: Constant MY_CONST already defined in %s on line %d
+Has attributes
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
diff --git a/Zend/tests/attributes/constants/multiple_attributes_grouped.phpt b/Zend/tests/attributes/constants/multiple_attributes_grouped.phpt
new file mode 100644
index 0000000000000..71736b68008d8
--- /dev/null
+++ b/Zend/tests/attributes/constants/multiple_attributes_grouped.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Multiple attributes in a group are allowed
+--FILE--
+getAttributes());
+
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(3) "Foo"
+ }
+ [1]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(3) "Bar"
+ }
+}
diff --git a/Zend/tests/attributes/constants/multiple_attributes_ungrouped.phpt b/Zend/tests/attributes/constants/multiple_attributes_ungrouped.phpt
new file mode 100644
index 0000000000000..2f177a9efd3f6
--- /dev/null
+++ b/Zend/tests/attributes/constants/multiple_attributes_ungrouped.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Multiple attributes in separate groups are allowed
+--FILE--
+getAttributes());
+
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(3) "Foo"
+ }
+ [1]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(3) "Bar"
+ }
+}
diff --git a/Zend/tests/attributes/constants/multiple_constants_error.phpt b/Zend/tests/attributes/constants/multiple_constants_error.phpt
new file mode 100644
index 0000000000000..d4258c9f4d080
--- /dev/null
+++ b/Zend/tests/attributes/constants/multiple_constants_error.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Error trying to add attributes to multiple constants at once
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot apply attributes to multiple constants at once in %s on line %d
diff --git a/Zend/tests/attributes/constants/must_target_const-internal.phpt b/Zend/tests/attributes/constants/must_target_const-internal.phpt
new file mode 100644
index 0000000000000..cedb6c5bd161a
--- /dev/null
+++ b/Zend/tests/attributes/constants/must_target_const-internal.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Error when attribute does not target constants (internal attribute)
+--FILE--
+
+--EXPECTF--
+Fatal error: Attribute "Attribute" cannot target constant (allowed targets: class) in %s on line %d
diff --git a/Zend/tests/attributes/constants/must_target_const-userland.phpt b/Zend/tests/attributes/constants/must_target_const-userland.phpt
new file mode 100644
index 0000000000000..3799cf2142006
--- /dev/null
+++ b/Zend/tests/attributes/constants/must_target_const-userland.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Error when attribute does not target constants (useland attribute)
+--FILE--
+getAttributes();
+var_dump($attribs);
+$attribs[0]->newInstance();
+
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(19) "MyFunctionAttribute"
+ }
+}
+
+Fatal error: Uncaught Error: Attribute "MyFunctionAttribute" cannot target constant (allowed targets: function) in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionAttribute->newInstance()
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/constants/not_repeatable-internal.phpt b/Zend/tests/attributes/constants/not_repeatable-internal.phpt
new file mode 100644
index 0000000000000..b3602e186f046
--- /dev/null
+++ b/Zend/tests/attributes/constants/not_repeatable-internal.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Validation of attribute repetition (not allowed; internal attribute)
+--FILE--
+
+--EXPECTF--
+Fatal error: Attribute "Deprecated" must not be repeated in %s on line %d
diff --git a/Zend/tests/attributes/constants/not_repeatable-userland.phpt b/Zend/tests/attributes/constants/not_repeatable-userland.phpt
new file mode 100644
index 0000000000000..85c0f2dff1e8b
--- /dev/null
+++ b/Zend/tests/attributes/constants/not_repeatable-userland.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Validation of attribute repetition (not allowed; userland attribute)
+--FILE--
+getAttributes();
+var_dump($attributes);
+$attributes[0]->newInstance();
+
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+ [1]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
+
+Fatal error: Uncaught Error: Attribute "MyAttribute" must not be repeated in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionAttribute->newInstance()
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/constants/repeatable-internal.phpt b/Zend/tests/attributes/constants/repeatable-internal.phpt
new file mode 100644
index 0000000000000..57cb9eaf274db
--- /dev/null
+++ b/Zend/tests/attributes/constants/repeatable-internal.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Validation of attribute repetition (is allowed; internal attribute)
+--EXTENSIONS--
+zend_test
+--FILE--
+
+--EXPECT--
+Done
diff --git a/Zend/tests/attributes/constants/repeatable-userland.phpt b/Zend/tests/attributes/constants/repeatable-userland.phpt
new file mode 100644
index 0000000000000..ce31a903dbc57
--- /dev/null
+++ b/Zend/tests/attributes/constants/repeatable-userland.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Validation of attribute repetition (is allowed; userland attribute)
+--FILE--
+getAttributes();
+var_dump($attributes);
+$attributes[0]->newInstance();
+
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+ [1]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
diff --git a/Zend/tests/attributes/constants/target_all_targets_const-default.phpt b/Zend/tests/attributes/constants/target_all_targets_const-default.phpt
new file mode 100644
index 0000000000000..7f79a59cd65bd
--- /dev/null
+++ b/Zend/tests/attributes/constants/target_all_targets_const-default.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Attributes with TARGET_ALL (from the default) can target constants
+--FILE--
+getAttributes();
+var_dump($attribs);
+$attribs[0]->newInstance();
+
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
diff --git a/Zend/tests/attributes/constants/target_all_targets_const-explicit.phpt b/Zend/tests/attributes/constants/target_all_targets_const-explicit.phpt
new file mode 100644
index 0000000000000..6daa11b9a537d
--- /dev/null
+++ b/Zend/tests/attributes/constants/target_all_targets_const-explicit.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Attributes with TARGET_ALL (from an explicit parameter) can target constants
+--FILE--
+getAttributes();
+var_dump($attribs);
+$attribs[0]->newInstance();
+
+?>
+--EXPECTF--
+array(1) {
+ [0]=>
+ object(ReflectionAttribute)#%d (1) {
+ ["name"]=>
+ string(11) "MyAttribute"
+ }
+}
diff --git a/Zend/tests/attributes/deprecated/constants/const_messages.phpt b/Zend/tests/attributes/deprecated/constants/const_messages.phpt
new file mode 100644
index 0000000000000..1cf8b11af1565
--- /dev/null
+++ b/Zend/tests/attributes/deprecated/constants/const_messages.phpt
@@ -0,0 +1,49 @@
+--TEST--
+#[\Deprecated]: Messages on compile time constants.
+--FILE--
+
+--EXPECTF--
+Deprecated: Constant DeprecatedConst1 is deprecated in %s on line %d
+1
+
+Deprecated: Constant DeprecatedConst2 is deprecated, use DEPRECATED_CONST_2 in %s on line %d
+2
+
+Deprecated: Constant DeprecatedConst3 is deprecated, use DEPRECATED_CONST_3 in %s on line %d
+3
+
+Deprecated: Constant DeprecatedConst4 is deprecated since 1.0, use DEPRECATED_CONST_4 in %s on line %d
+4
+
+Deprecated: Constant DeprecatedConst5 is deprecated since 1.0, use DEPRECATED_CONST_5 in %s on line %d
+5
+
+Deprecated: Constant DeprecatedConst6 is deprecated since 1.0 in %s on line %d
+6
+
diff --git a/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_001.phpt b/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_001.phpt
new file mode 100644
index 0000000000000..955174b293e20
--- /dev/null
+++ b/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_001.phpt
@@ -0,0 +1,24 @@
+--TEST--
+#[\Deprecated]: Using the value of a deprecated constant as the deprecation message.
+--FILE--
+
+--EXPECTF--
+Deprecated: Constant TEST is deprecated, from itself in %s on line %d
+
+Deprecated: Constant TEST2 is deprecated in %s on line %d
+
+Deprecated: Constant TEST3 is deprecated, from another in %s on line %d
diff --git a/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_002.phpt b/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_002.phpt
new file mode 100644
index 0000000000000..f749293d8facc
--- /dev/null
+++ b/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_002.phpt
@@ -0,0 +1,34 @@
+--TEST--
+#[\Deprecated]: Using the value of a deprecated constant as the deprecation message with a throwing error handler.
+--FILE--
+getMessage(), PHP_EOL;
+}
+
+try {
+ TEST3;
+} catch (ErrorException $e) {
+ echo "Caught: ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Caught: Constant TEST is deprecated, from itself
+Caught: Constant TEST2 is deprecated
diff --git a/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_003.phpt b/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_003.phpt
new file mode 100644
index 0000000000000..c37b57dc42176
--- /dev/null
+++ b/Zend/tests/attributes/deprecated/constants/deprecated_constant_as_message_003.phpt
@@ -0,0 +1,21 @@
+--TEST--
+#[\Deprecated]: Using the value of a deprecated constant in a constant expression.
+--FILE--
+
+--EXPECTF--
+Deprecated: Constant PREFIX is deprecated, prefix in %s on line %d
+
+Deprecated: Constant SUFFIX is deprecated, suffix in %s on line %d
+string(12) "prefixsuffix"
diff --git a/Zend/tests/attributes/deprecated/constants/error_code.phpt b/Zend/tests/attributes/deprecated/constants/error_code.phpt
new file mode 100644
index 0000000000000..0a8efd7f970b4
--- /dev/null
+++ b/Zend/tests/attributes/deprecated/constants/error_code.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Deprecated]: Code is E_USER_DEPRECATED for constants.
+--FILE--
+
+--EXPECT--
+int(16384)
+int(16384)
+bool(true)
diff --git a/Zend/tests/attributes/deprecated/constants/value_unknown_at_compile_time_001.phpt b/Zend/tests/attributes/deprecated/constants/value_unknown_at_compile_time_001.phpt
new file mode 100644
index 0000000000000..a968e26673e39
--- /dev/null
+++ b/Zend/tests/attributes/deprecated/constants/value_unknown_at_compile_time_001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+#[\Deprecated]: Constant with value unknown at compile time.
+--FILE--
+
+--EXPECTF--
+Deprecated: Constant CONSTANT is deprecated in %s on line %d
+string(8) "Prefix-%c"
+bool(true)
diff --git a/Zend/tests/attributes/nodiscard/001.phpt b/Zend/tests/attributes/nodiscard/001.phpt
new file mode 100644
index 0000000000000..8bec9972aed67
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/001.phpt
@@ -0,0 +1,86 @@
+--TEST--
+#[\NoDiscard]: Basic test.
+--FILE--
+test();
+$cls->test2();
+Clazz::test3();
+
+call_user_func([$cls, "test"]);
+
+$closure();
+
+$closure2();
+
+?>
+--EXPECTF--
+Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function test2() should either be used or intentionally ignored by casting it as (void), this is important in %s on line %d
+
+Warning: The return value of function test3() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of method Clazz::test2() should either be used or intentionally ignored by casting it as (void), this is important in %s on line %d
+
+Warning: The return value of method Clazz::test3() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function {closure:%s:%d}() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function {closure:%s:%d}() should either be used or intentionally ignored by casting it as (void) in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/002.phpt b/Zend/tests/attributes/nodiscard/002.phpt
new file mode 100644
index 0000000000000..618aa2f6286d6
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/002.phpt
@@ -0,0 +1,44 @@
+--TEST--
+#[\NoDiscard]: __call(), __callStatic(), and __invoke().
+--FILE--
+test();
+Clazz::test();
+$cls('foo');
+
+?>
+--EXPECTF--
+Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+__call(test)
+
+Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+__callStatic(test)
+
+Warning: The return value of method Clazz::__invoke() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+__invoke(foo)
+
diff --git a/Zend/tests/attributes/nodiscard/003.phpt b/Zend/tests/attributes/nodiscard/003.phpt
new file mode 100644
index 0000000000000..24865f350472c
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/003.phpt
@@ -0,0 +1,22 @@
+--TEST--
+#[\NoDiscard]: Taken from trait.
+--FILE--
+test();
+
+?>
+--EXPECTF--
+Warning: The return value of method Clazz::test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/005.phpt b/Zend/tests/attributes/nodiscard/005.phpt
new file mode 100644
index 0000000000000..9ef1566372892
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/005.phpt
@@ -0,0 +1,11 @@
+--TEST--
+#[\NoDiscard]: Native method.
+--FILE--
+setTimestamp(0);
+
+?>
+--EXPECTF--
+Warning: The return value of method DateTimeImmutable::setTimestamp() should either be used or intentionally ignored by casting it as (void), as DateTimeImmutable::setTimestamp() does not modify the object itself in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/006.phpt b/Zend/tests/attributes/nodiscard/006.phpt
new file mode 100644
index 0000000000000..959b942522204
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/006.phpt
@@ -0,0 +1,20 @@
+--TEST--
+#[\NoDiscard]: execute_ex overwritten
+--EXTENSIONS--
+zend_test
+--INI--
+zend_test.replace_zend_execute_ex=1
+opcache.jit=disable
+--FILE--
+
+--EXPECTF--
+Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/007.phpt b/Zend/tests/attributes/nodiscard/007.phpt
new file mode 100644
index 0000000000000..1b72de8c22a06
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/007.phpt
@@ -0,0 +1,17 @@
+--TEST--
+#[\NoDiscard]: execute_internal overwritten
+--EXTENSIONS--
+zend_test
+--INI--
+zend_test.observer.execute_internal=1
+--FILE--
+
+--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/nodiscard/008.phpt b/Zend/tests/attributes/nodiscard/008.phpt
new file mode 100644
index 0000000000000..6b4635e08d562
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/008.phpt
@@ -0,0 +1,18 @@
+--TEST--
+#[\NoDiscard]: Combining with #[\Deprecated].
+--FILE--
+
+--EXPECTF--
+Deprecated: Function test() is deprecated in %s on line %d
+
+Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void) in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/009.phpt b/Zend/tests/attributes/nodiscard/009.phpt
new file mode 100644
index 0000000000000..ab887db7a14b0
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/009.phpt
@@ -0,0 +1,14 @@
+--TEST--
+#[\NoDiscard]: Combining with #[\Deprecated] (Internal).
+--EXTENSIONS--
+zend_test
+--FILE--
+
+--EXPECTF--
+Deprecated: Function zend_test_deprecated_nodiscard() is deprecated, custom message in %s on line %d
+
+Warning: The return value of function zend_test_deprecated_nodiscard() should either be used or intentionally ignored by casting it as (void), custom message 2 in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/010.phpt b/Zend/tests/attributes/nodiscard/010.phpt
new file mode 100644
index 0000000000000..5534fc3404da9
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/010.phpt
@@ -0,0 +1,12 @@
+--TEST--
+#[\NoDiscard]: Native function.
+--EXTENSIONS--
+zend_test
+--FILE--
+
+--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/nodiscard/error_code_001.phpt b/Zend/tests/attributes/nodiscard/error_code_001.phpt
new file mode 100644
index 0000000000000..2952587db7b87
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/error_code_001.phpt
@@ -0,0 +1,21 @@
+--TEST--
+#[\NoDiscard]: Code is E_USER_WARNING.
+--FILE--
+
+--EXPECT--
+int(512)
+int(512)
+bool(true)
diff --git a/Zend/tests/attributes/nodiscard/property_readonly_001.phpt b/Zend/tests/attributes/nodiscard/property_readonly_001.phpt
new file mode 100644
index 0000000000000..6f9779023126e
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/property_readonly_001.phpt
@@ -0,0 +1,14 @@
+--TEST--
+#[\NoDiscard]: NoDiscard::$message is readonly.
+--FILE--
+message = 'bar';
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot modify readonly property NoDiscard::$message in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/property_readonly_002.phpt b/Zend/tests/attributes/nodiscard/property_readonly_002.phpt
new file mode 100644
index 0000000000000..f01594ca4b26c
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/property_readonly_002.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\NoDiscard]: __construct() respects that properties are readonly.
+--FILE--
+__construct("bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot modify readonly property NoDiscard::$message in %s:%d
+Stack trace:
+#0 %s(%d): NoDiscard->__construct('bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/suppress_assign.phpt b/Zend/tests/attributes/nodiscard/suppress_assign.phpt
new file mode 100644
index 0000000000000..b09f1a39718a8
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/suppress_assign.phpt
@@ -0,0 +1,66 @@
+--TEST--
+#[\NoDiscard]: Assigning to variable suppresses.
+--FILE--
+test();
+$_ = $cls->test2();
+$_ = call_user_func([$cls, "test"]);
+$_ = Clazz::test3();
+
+$_ = $closure();
+
+$_ = $closure2();
+
+?>
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/attributes/nodiscard/suppress_cast.phpt b/Zend/tests/attributes/nodiscard/suppress_cast.phpt
new file mode 100644
index 0000000000000..2f639371cb3dd
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/suppress_cast.phpt
@@ -0,0 +1,66 @@
+--TEST--
+#[\NoDiscard]: Casting to (void) suppresses.
+--FILE--
+test();
+(void)$cls->test2();
+(void)call_user_func([$cls, "test"]);
+(void)Clazz::test3();
+
+(void)$closure();
+
+(void)$closure2();
+
+?>
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/attributes/nodiscard/throwing_error_handler_001.phpt b/Zend/tests/attributes/nodiscard/throwing_error_handler_001.phpt
new file mode 100644
index 0000000000000..51179b26ab194
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/throwing_error_handler_001.phpt
@@ -0,0 +1,35 @@
+--TEST--
+#[\NoDiscard]: Throwing error handler.
+--FILE--
+getMessage(), PHP_EOL;
+}
+
+#[\NoDiscard]
+function test2(): stdClass {
+ return new stdClass();
+}
+
+try {
+ test2();
+} catch (ErrorException $e) {
+ echo "Caught: ", $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+Caught: The return value of function test() should either be used or intentionally ignored by casting it as (void)
+Caught: The return value of function test2() should either be used or intentionally ignored by casting it as (void)
diff --git a/Zend/tests/attributes/nodiscard/type_validation_001.phpt b/Zend/tests/attributes/nodiscard/type_validation_001.phpt
new file mode 100644
index 0000000000000..b7a375ada5c07
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/type_validation_001.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\NoDiscard]: Type validation of $message parameter with int.
+--FILE--
+
+--EXPECTF--
+Warning: The return value of function test() should either be used or intentionally ignored by casting it as (void), 1234 in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/type_validation_002.phpt b/Zend/tests/attributes/nodiscard/type_validation_002.phpt
new file mode 100644
index 0000000000000..254fae15ac47a
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/type_validation_002.phpt
@@ -0,0 +1,20 @@
+--TEST--
+#[\NoDiscard]: Type validation of $message parameter with int and strict types.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, int given in %s:%d
+Stack trace:
+#0 %s(%d): NoDiscard->__construct(1234)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/type_validation_003.phpt b/Zend/tests/attributes/nodiscard/type_validation_003.phpt
new file mode 100644
index 0000000000000..0b66addaea187
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/type_validation_003.phpt
@@ -0,0 +1,18 @@
+--TEST--
+#[\NoDiscard]: Type validation of $message parameter with array.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, array given in %s:%d
+Stack trace:
+#0 %s(%d): NoDiscard->__construct(Array)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/type_validation_004.phpt b/Zend/tests/attributes/nodiscard/type_validation_004.phpt
new file mode 100644
index 0000000000000..44d7c19b54863
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/type_validation_004.phpt
@@ -0,0 +1,18 @@
+--TEST--
+#[\NoDiscard]: Type validation of $message parameter with native enum case.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, Random\IntervalBoundary given in %s:%d
+Stack trace:
+#0 %s(%d): NoDiscard->__construct(Random\IntervalBoundary::ClosedOpen)
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/unsupported_clone.phpt b/Zend/tests/attributes/nodiscard/unsupported_clone.phpt
new file mode 100644
index 0000000000000..7fadfea40fa6a
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/unsupported_clone.phpt
@@ -0,0 +1,16 @@
+--TEST--
+#[\NoDiscard]: Not allowed on '__clone'.
+--FILE--
+
+--EXPECTF--
+Fatal error: Method Clazz::__clone cannot be #[\NoDiscard] in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/unsupported_constructor.phpt b/Zend/tests/attributes/nodiscard/unsupported_constructor.phpt
new file mode 100644
index 0000000000000..a11c6379938d3
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/unsupported_constructor.phpt
@@ -0,0 +1,16 @@
+--TEST--
+#[\NoDiscard]: Not allowed on '__construct'.
+--FILE--
+
+--EXPECTF--
+Fatal error: Method Clazz::__construct cannot be #[\NoDiscard] in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/unsupported_never_function.phpt b/Zend/tests/attributes/nodiscard/unsupported_never_function.phpt
new file mode 100644
index 0000000000000..ee7a81acbcde9
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/unsupported_never_function.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\NoDiscard]: Not allowed on never function.
+--FILE--
+
+--EXPECTF--
+Fatal error: A never returning function does not return a value, but #[\NoDiscard] requires a return value in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/unsupported_property_hook_get.phpt b/Zend/tests/attributes/nodiscard/unsupported_property_hook_get.phpt
new file mode 100644
index 0000000000000..ccedf3ede315a
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/unsupported_property_hook_get.phpt
@@ -0,0 +1,20 @@
+--TEST--
+#[\NoDiscard]: Not allowed on 'get' property hook.
+--FILE--
+test;
+
+?>
+--EXPECTF--
+Fatal error: #[\NoDiscard] is not supported for property hooks in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/unsupported_property_hook_set.phpt b/Zend/tests/attributes/nodiscard/unsupported_property_hook_set.phpt
new file mode 100644
index 0000000000000..351593f5250bb
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/unsupported_property_hook_set.phpt
@@ -0,0 +1,20 @@
+--TEST--
+#[\NoDiscard]: Not allowed on 'set' property hook.
+--FILE--
+test = $value;
+ }
+ }
+}
+
+$cls = new Foo();
+$cls->test = 'foo';
+
+?>
+--EXPECTF--
+Fatal error: #[\NoDiscard] is not supported for property hooks in %s on line %d
diff --git a/Zend/tests/attributes/nodiscard/unsupported_void_function.phpt b/Zend/tests/attributes/nodiscard/unsupported_void_function.phpt
new file mode 100644
index 0000000000000..3c45f255473bc
--- /dev/null
+++ b/Zend/tests/attributes/nodiscard/unsupported_void_function.phpt
@@ -0,0 +1,15 @@
+--TEST--
+#[\NoDiscard]: Not allowed on void function.
+--FILE--
+
+--EXPECTF--
+Fatal error: A void function does not return a value, but #[\NoDiscard] requires a return value in %s on line %d
diff --git a/Zend/tests/bug26697.phpt b/Zend/tests/autoload/bug26697.phpt
similarity index 100%
rename from Zend/tests/bug26697.phpt
rename to Zend/tests/autoload/bug26697.phpt
diff --git a/Zend/tests/bug33116.phpt b/Zend/tests/autoload/bug33116.phpt
similarity index 100%
rename from Zend/tests/bug33116.phpt
rename to Zend/tests/autoload/bug33116.phpt
diff --git a/Zend/tests/bug37138.phpt b/Zend/tests/autoload/bug37138.phpt
similarity index 100%
rename from Zend/tests/bug37138.phpt
rename to Zend/tests/autoload/bug37138.phpt
diff --git a/Zend/tests/bug39003.phpt b/Zend/tests/autoload/bug39003.phpt
similarity index 100%
rename from Zend/tests/bug39003.phpt
rename to Zend/tests/autoload/bug39003.phpt
diff --git a/Zend/tests/bug42798.phpt b/Zend/tests/autoload/bug42798.phpt
similarity index 100%
rename from Zend/tests/bug42798.phpt
rename to Zend/tests/autoload/bug42798.phpt
diff --git a/Zend/tests/bug46665.phpt b/Zend/tests/autoload/bug46665.phpt
similarity index 100%
rename from Zend/tests/bug46665.phpt
rename to Zend/tests/autoload/bug46665.phpt
diff --git a/Zend/tests/bug46665_autoload.inc b/Zend/tests/autoload/bug46665_autoload.inc
similarity index 100%
rename from Zend/tests/bug46665_autoload.inc
rename to Zend/tests/autoload/bug46665_autoload.inc
diff --git a/Zend/tests/bug49908.phpt b/Zend/tests/autoload/bug49908.phpt
similarity index 100%
rename from Zend/tests/bug49908.phpt
rename to Zend/tests/autoload/bug49908.phpt
diff --git a/Zend/tests/bug61011.phpt b/Zend/tests/autoload/bug61011.phpt
similarity index 100%
rename from Zend/tests/bug61011.phpt
rename to Zend/tests/autoload/bug61011.phpt
diff --git a/Zend/tests/bug63741.phpt b/Zend/tests/autoload/bug63741.phpt
similarity index 100%
rename from Zend/tests/bug63741.phpt
rename to Zend/tests/autoload/bug63741.phpt
diff --git a/Zend/tests/bug65322.phpt b/Zend/tests/autoload/bug65322.phpt
similarity index 100%
rename from Zend/tests/bug65322.phpt
rename to Zend/tests/autoload/bug65322.phpt
diff --git a/Zend/tests/bug78868.phpt b/Zend/tests/autoload/bug78868.phpt
similarity index 100%
rename from Zend/tests/bug78868.phpt
rename to Zend/tests/autoload/bug78868.phpt
diff --git a/Zend/tests/bug78921.phpt b/Zend/tests/autoload/bug78921.phpt
similarity index 100%
rename from Zend/tests/bug78921.phpt
rename to Zend/tests/autoload/bug78921.phpt
diff --git a/Zend/tests/bug28377.phpt b/Zend/tests/backtrace/bug28377.phpt
similarity index 100%
rename from Zend/tests/bug28377.phpt
rename to Zend/tests/backtrace/bug28377.phpt
diff --git a/Zend/tests/bug30828.phpt b/Zend/tests/backtrace/bug30828.phpt
similarity index 100%
rename from Zend/tests/bug30828.phpt
rename to Zend/tests/backtrace/bug30828.phpt
diff --git a/Zend/tests/bug39445.phpt b/Zend/tests/backtrace/bug39445.phpt
similarity index 100%
rename from Zend/tests/bug39445.phpt
rename to Zend/tests/backtrace/bug39445.phpt
diff --git a/Zend/tests/bug64239_2.phpt b/Zend/tests/backtrace/bug64239_2.phpt
similarity index 100%
rename from Zend/tests/bug64239_2.phpt
rename to Zend/tests/backtrace/bug64239_2.phpt
diff --git a/Zend/tests/bug64239_3.phpt b/Zend/tests/backtrace/bug64239_3.phpt
similarity index 100%
rename from Zend/tests/bug64239_3.phpt
rename to Zend/tests/backtrace/bug64239_3.phpt
diff --git a/Zend/tests/bug64239_4.phpt b/Zend/tests/backtrace/bug64239_4.phpt
similarity index 100%
rename from Zend/tests/bug64239_4.phpt
rename to Zend/tests/backtrace/bug64239_4.phpt
diff --git a/Zend/tests/bug69180-backtrace.phpt b/Zend/tests/backtrace/bug69180-backtrace.phpt
similarity index 100%
rename from Zend/tests/bug69180-backtrace.phpt
rename to Zend/tests/backtrace/bug69180-backtrace.phpt
diff --git a/Zend/tests/bug70547.phpt b/Zend/tests/backtrace/bug70547.phpt
similarity index 100%
rename from Zend/tests/bug70547.phpt
rename to Zend/tests/backtrace/bug70547.phpt
diff --git a/Zend/tests/bug76047.phpt b/Zend/tests/backtrace/bug76047.phpt
similarity index 100%
rename from Zend/tests/bug76047.phpt
rename to Zend/tests/backtrace/bug76047.phpt
diff --git a/Zend/tests/bug79108.phpt b/Zend/tests/backtrace/bug79108.phpt
similarity index 100%
rename from Zend/tests/bug79108.phpt
rename to Zend/tests/backtrace/bug79108.phpt
diff --git a/Zend/tests/bug_debug_backtrace.phpt b/Zend/tests/backtrace/bug_debug_backtrace.phpt
similarity index 100%
rename from Zend/tests/bug_debug_backtrace.phpt
rename to Zend/tests/backtrace/bug_debug_backtrace.phpt
diff --git a/Zend/tests/bug_debug_backtrace_replace_zend_execute_ex.phpt b/Zend/tests/backtrace/bug_debug_backtrace_replace_zend_execute_ex.phpt
similarity index 100%
rename from Zend/tests/bug_debug_backtrace_replace_zend_execute_ex.phpt
rename to Zend/tests/backtrace/bug_debug_backtrace_replace_zend_execute_ex.phpt
diff --git a/Zend/tests/debug_backtrace_limit.phpt b/Zend/tests/backtrace/debug_backtrace_limit.phpt
similarity index 100%
rename from Zend/tests/debug_backtrace_limit.phpt
rename to Zend/tests/backtrace/debug_backtrace_limit.phpt
diff --git a/Zend/tests/debug_backtrace_options.phpt b/Zend/tests/backtrace/debug_backtrace_options.phpt
similarity index 100%
rename from Zend/tests/debug_backtrace_options.phpt
rename to Zend/tests/backtrace/debug_backtrace_options.phpt
diff --git a/Zend/tests/debug_backtrace_with_include_and_this.phpt b/Zend/tests/backtrace/debug_backtrace_with_include_and_this.phpt
similarity index 100%
rename from Zend/tests/debug_backtrace_with_include_and_this.phpt
rename to Zend/tests/backtrace/debug_backtrace_with_include_and_this.phpt
diff --git a/Zend/tests/debug_print_backtrace_from_main.phpt b/Zend/tests/backtrace/debug_print_backtrace_from_main.phpt
similarity index 100%
rename from Zend/tests/debug_print_backtrace_from_main.phpt
rename to Zend/tests/backtrace/debug_print_backtrace_from_main.phpt
diff --git a/Zend/tests/debug_print_backtrace_limit.phpt b/Zend/tests/backtrace/debug_print_backtrace_limit.phpt
similarity index 100%
rename from Zend/tests/debug_print_backtrace_limit.phpt
rename to Zend/tests/backtrace/debug_print_backtrace_limit.phpt
diff --git a/Zend/tests/backtrace/fatal_error_backtraces_001.phpt b/Zend/tests/backtrace/fatal_error_backtraces_001.phpt
new file mode 100644
index 0000000000000..0a7d1d8f1c683
--- /dev/null
+++ b/Zend/tests/backtrace/fatal_error_backtraces_001.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Fatal error backtrace
+--INI--
+fatal_error_backtraces=On
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot redeclare class Foo (%s) in %s : eval()'d code on line %d
+Stack trace:
+#0 %sfatal_error_backtraces_001.php(%d): eval()
+#1 {main}
diff --git a/Zend/tests/backtrace/fatal_error_backtraces_002.phpt b/Zend/tests/backtrace/fatal_error_backtraces_002.phpt
new file mode 100644
index 0000000000000..c72505cb18132
--- /dev/null
+++ b/Zend/tests/backtrace/fatal_error_backtraces_002.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Fatal error backtrace w/ sensitive parameters
+--INI--
+fatal_error_backtraces=On
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot redeclare class Foo (%s) in %s : eval()'d code on line %d
+Stack trace:
+#0 %sfatal_error_backtraces_002.php(%d): eval()
+#1 %sfatal_error_backtraces_002.php(%d): trigger_fatal(Object(SensitiveParameterValue))
+#2 {main}
diff --git a/Zend/tests/backtrace/fatal_error_backtraces_003.phpt b/Zend/tests/backtrace/fatal_error_backtraces_003.phpt
new file mode 100644
index 0000000000000..ae6cf8ce37f9c
--- /dev/null
+++ b/Zend/tests/backtrace/fatal_error_backtraces_003.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Fatal error backtrace w/ zend.exception_ignore_args
+--INI--
+fatal_error_backtraces=On
+zend.exception_ignore_args=On
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot redeclare class Foo (%s) in %s : eval()'d code on line %d
+Stack trace:
+#0 %sfatal_error_backtraces_003.php(%d): eval()
+#1 %sfatal_error_backtraces_003.php(%d): trigger_fatal()
+#2 {main}
diff --git a/Zend/tests/bug81626.phpt b/Zend/tests/bug81626.phpt
index b05cb23097bbb..26f549795a66e 100644
--- a/Zend/tests/bug81626.phpt
+++ b/Zend/tests/bug81626.phpt
@@ -1,5 +1,5 @@
--TEST--
-Bug #81626: Error on use static:: in __сallStatic() wrapped to Closure::fromCallable()
+Bug #81626: Error on use static:: in __callStatic() wrapped to Closure::fromCallable()
--FILE--
+--EXPECTF--
+Fatal error: Cannot use "array" as a class alias as it is reserved in %s on line %d
diff --git a/Zend/tests/class_alias/gh16665_2.phpt b/Zend/tests/class_alias/gh16665_2.phpt
new file mode 100644
index 0000000000000..3b8ffd4c1e6b0
--- /dev/null
+++ b/Zend/tests/class_alias/gh16665_2.phpt
@@ -0,0 +1,8 @@
+--TEST--
+GH-16665 (\callable should not be usable)
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use "callable" as a class alias as it is reserved in %s on line %d
diff --git a/Zend/tests/bug66811.phpt b/Zend/tests/class_name/bug66811.phpt
similarity index 100%
rename from Zend/tests/bug66811.phpt
rename to Zend/tests/class_name/bug66811.phpt
diff --git a/Zend/tests/bug69754.phpt b/Zend/tests/class_name/bug69754.phpt
similarity index 100%
rename from Zend/tests/bug69754.phpt
rename to Zend/tests/class_name/bug69754.phpt
diff --git a/Zend/tests/bug77530.phpt b/Zend/tests/class_name/bug77530.phpt
similarity index 100%
rename from Zend/tests/bug77530.phpt
rename to Zend/tests/class_name/bug77530.phpt
diff --git a/Zend/tests/class_name_as_scalar.phpt b/Zend/tests/class_name/class_name_as_scalar.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar.phpt
rename to Zend/tests/class_name/class_name_as_scalar.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_001.phpt b/Zend/tests/class_name/class_name_as_scalar_error_001.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_001.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_001.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_002.phpt b/Zend/tests/class_name/class_name_as_scalar_error_002.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_002.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_002.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_003.phpt b/Zend/tests/class_name/class_name_as_scalar_error_003.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_003.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_003.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_004.phpt b/Zend/tests/class_name/class_name_as_scalar_error_004.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_004.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_004.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_005.phpt b/Zend/tests/class_name/class_name_as_scalar_error_005.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_005.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_005.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_006.phpt b/Zend/tests/class_name/class_name_as_scalar_error_006.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_006.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_006.phpt
diff --git a/Zend/tests/class_name_as_scalar_error_007.phpt b/Zend/tests/class_name/class_name_as_scalar_error_007.phpt
similarity index 100%
rename from Zend/tests/class_name_as_scalar_error_007.phpt
rename to Zend/tests/class_name/class_name_as_scalar_error_007.phpt
diff --git a/Zend/tests/class_on_constant_evaluated_expression.phpt b/Zend/tests/class_name/class_on_constant_evaluated_expression.phpt
similarity index 100%
rename from Zend/tests/class_on_constant_evaluated_expression.phpt
rename to Zend/tests/class_name/class_on_constant_evaluated_expression.phpt
diff --git a/Zend/tests/class_on_object.phpt b/Zend/tests/class_name/class_on_object.phpt
similarity index 100%
rename from Zend/tests/class_on_object.phpt
rename to Zend/tests/class_name/class_on_object.phpt
diff --git a/Zend/tests/parent_class_name_without_parent.phpt b/Zend/tests/class_name/parent_class_name_without_parent.phpt
similarity index 100%
rename from Zend/tests/parent_class_name_without_parent.phpt
rename to Zend/tests/class_name/parent_class_name_without_parent.phpt
diff --git a/Zend/tests/self_class_const_in_unknown_scope.phpt b/Zend/tests/class_name/self_class_const_in_unknown_scope.phpt
similarity index 100%
rename from Zend/tests/self_class_const_in_unknown_scope.phpt
rename to Zend/tests/class_name/self_class_const_in_unknown_scope.phpt
diff --git a/Zend/tests/bug24884.phpt b/Zend/tests/clone/bug24884.phpt
similarity index 100%
rename from Zend/tests/bug24884.phpt
rename to Zend/tests/clone/bug24884.phpt
diff --git a/Zend/tests/bug27268.phpt b/Zend/tests/clone/bug27268.phpt
similarity index 100%
rename from Zend/tests/bug27268.phpt
rename to Zend/tests/clone/bug27268.phpt
diff --git a/Zend/tests/bug36071.phpt b/Zend/tests/clone/bug36071.phpt
similarity index 100%
rename from Zend/tests/bug36071.phpt
rename to Zend/tests/clone/bug36071.phpt
diff --git a/Zend/tests/bug42817.phpt b/Zend/tests/clone/bug42817.phpt
similarity index 100%
rename from Zend/tests/bug42817.phpt
rename to Zend/tests/clone/bug42817.phpt
diff --git a/Zend/tests/bug42818.phpt b/Zend/tests/clone/bug42818.phpt
similarity index 100%
rename from Zend/tests/bug42818.phpt
rename to Zend/tests/clone/bug42818.phpt
diff --git a/Zend/tests/bug68262.phpt b/Zend/tests/clone/bug68262.phpt
similarity index 100%
rename from Zend/tests/bug68262.phpt
rename to Zend/tests/clone/bug68262.phpt
diff --git a/Zend/tests/bug69996.phpt b/Zend/tests/clone/bug69996.phpt
similarity index 100%
rename from Zend/tests/bug69996.phpt
rename to Zend/tests/clone/bug69996.phpt
diff --git a/Zend/tests/clone_001.phpt b/Zend/tests/clone/clone_001.phpt
similarity index 100%
rename from Zend/tests/clone_001.phpt
rename to Zend/tests/clone/clone_001.phpt
diff --git a/Zend/tests/clone_002.phpt b/Zend/tests/clone/clone_002.phpt
similarity index 100%
rename from Zend/tests/clone_002.phpt
rename to Zend/tests/clone/clone_002.phpt
diff --git a/Zend/tests/clone_003.phpt b/Zend/tests/clone/clone_003.phpt
similarity index 100%
rename from Zend/tests/clone_003.phpt
rename to Zend/tests/clone/clone_003.phpt
diff --git a/Zend/tests/clone_004.phpt b/Zend/tests/clone/clone_004.phpt
similarity index 100%
rename from Zend/tests/clone_004.phpt
rename to Zend/tests/clone/clone_004.phpt
diff --git a/Zend/tests/bug50146.phpt b/Zend/tests/closures/bug50146.phpt
similarity index 100%
rename from Zend/tests/bug50146.phpt
rename to Zend/tests/closures/bug50146.phpt
diff --git a/Zend/tests/bug52060.phpt b/Zend/tests/closures/bug52060.phpt
similarity index 100%
rename from Zend/tests/bug52060.phpt
rename to Zend/tests/closures/bug52060.phpt
diff --git a/Zend/tests/bug52193.phpt b/Zend/tests/closures/bug52193.phpt
similarity index 100%
rename from Zend/tests/bug52193.phpt
rename to Zend/tests/closures/bug52193.phpt
diff --git a/Zend/tests/bug53958.phpt b/Zend/tests/closures/bug53958.phpt
similarity index 100%
rename from Zend/tests/bug53958.phpt
rename to Zend/tests/closures/bug53958.phpt
diff --git a/Zend/tests/bug54358.phpt b/Zend/tests/closures/bug54358.phpt
similarity index 100%
rename from Zend/tests/bug54358.phpt
rename to Zend/tests/closures/bug54358.phpt
diff --git a/Zend/tests/bug54367.phpt b/Zend/tests/closures/bug54367.phpt
similarity index 100%
rename from Zend/tests/bug54367.phpt
rename to Zend/tests/closures/bug54367.phpt
diff --git a/Zend/tests/bug69802_2.phpt b/Zend/tests/closures/bug69802_2.phpt
similarity index 100%
rename from Zend/tests/bug69802_2.phpt
rename to Zend/tests/closures/bug69802_2.phpt
diff --git a/Zend/tests/bug70397.phpt b/Zend/tests/closures/bug70397.phpt
similarity index 100%
rename from Zend/tests/bug70397.phpt
rename to Zend/tests/closures/bug70397.phpt
diff --git a/Zend/tests/bug70630.phpt b/Zend/tests/closures/bug70630.phpt
similarity index 100%
rename from Zend/tests/bug70630.phpt
rename to Zend/tests/closures/bug70630.phpt
diff --git a/Zend/tests/bug70685.phpt b/Zend/tests/closures/bug70685.phpt
similarity index 100%
rename from Zend/tests/bug70685.phpt
rename to Zend/tests/closures/bug70685.phpt
diff --git a/Zend/tests/bug70987.phpt b/Zend/tests/closures/bug70987.phpt
similarity index 100%
rename from Zend/tests/bug70987.phpt
rename to Zend/tests/closures/bug70987.phpt
diff --git a/Zend/tests/bug75290.phpt b/Zend/tests/closures/bug75290.phpt
similarity index 100%
rename from Zend/tests/bug75290.phpt
rename to Zend/tests/closures/bug75290.phpt
diff --git a/Zend/tests/bug77627.phpt b/Zend/tests/closures/bug77627.phpt
similarity index 100%
rename from Zend/tests/bug77627.phpt
rename to Zend/tests/closures/bug77627.phpt
diff --git a/Zend/tests/bug78658.phpt b/Zend/tests/closures/bug78658.phpt
similarity index 100%
rename from Zend/tests/bug78658.phpt
rename to Zend/tests/closures/bug78658.phpt
diff --git a/Zend/tests/bug78689.phpt b/Zend/tests/closures/bug78689.phpt
similarity index 100%
rename from Zend/tests/bug78689.phpt
rename to Zend/tests/closures/bug78689.phpt
diff --git a/Zend/tests/bug79778.phpt b/Zend/tests/closures/bug79778.phpt
similarity index 100%
rename from Zend/tests/bug79778.phpt
rename to Zend/tests/closures/bug79778.phpt
diff --git a/Zend/tests/closure_001.phpt b/Zend/tests/closures/closure_001.phpt
similarity index 100%
rename from Zend/tests/closure_001.phpt
rename to Zend/tests/closures/closure_001.phpt
diff --git a/Zend/tests/closure_002.phpt b/Zend/tests/closures/closure_002.phpt
similarity index 100%
rename from Zend/tests/closure_002.phpt
rename to Zend/tests/closures/closure_002.phpt
diff --git a/Zend/tests/closure_003.phpt b/Zend/tests/closures/closure_003.phpt
similarity index 100%
rename from Zend/tests/closure_003.phpt
rename to Zend/tests/closures/closure_003.phpt
diff --git a/Zend/tests/closure_004.phpt b/Zend/tests/closures/closure_004.phpt
similarity index 100%
rename from Zend/tests/closure_004.phpt
rename to Zend/tests/closures/closure_004.phpt
diff --git a/Zend/tests/closure_005.phpt b/Zend/tests/closures/closure_005.phpt
similarity index 100%
rename from Zend/tests/closure_005.phpt
rename to Zend/tests/closures/closure_005.phpt
diff --git a/Zend/tests/closure_006.phpt b/Zend/tests/closures/closure_006.phpt
similarity index 100%
rename from Zend/tests/closure_006.phpt
rename to Zend/tests/closures/closure_006.phpt
diff --git a/Zend/tests/closure_007.phpt b/Zend/tests/closures/closure_007.phpt
similarity index 100%
rename from Zend/tests/closure_007.phpt
rename to Zend/tests/closures/closure_007.phpt
diff --git a/Zend/tests/closure_008.phpt b/Zend/tests/closures/closure_008.phpt
similarity index 100%
rename from Zend/tests/closure_008.phpt
rename to Zend/tests/closures/closure_008.phpt
diff --git a/Zend/tests/closure_009.phpt b/Zend/tests/closures/closure_009.phpt
similarity index 100%
rename from Zend/tests/closure_009.phpt
rename to Zend/tests/closures/closure_009.phpt
diff --git a/Zend/tests/closure_010.phpt b/Zend/tests/closures/closure_010.phpt
similarity index 100%
rename from Zend/tests/closure_010.phpt
rename to Zend/tests/closures/closure_010.phpt
diff --git a/Zend/tests/closure_011.phpt b/Zend/tests/closures/closure_011.phpt
similarity index 100%
rename from Zend/tests/closure_011.phpt
rename to Zend/tests/closures/closure_011.phpt
diff --git a/Zend/tests/closure_012.phpt b/Zend/tests/closures/closure_012.phpt
similarity index 100%
rename from Zend/tests/closure_012.phpt
rename to Zend/tests/closures/closure_012.phpt
diff --git a/Zend/tests/closure_013.phpt b/Zend/tests/closures/closure_013.phpt
similarity index 100%
rename from Zend/tests/closure_013.phpt
rename to Zend/tests/closures/closure_013.phpt
diff --git a/Zend/tests/closure_014.phpt b/Zend/tests/closures/closure_014.phpt
similarity index 100%
rename from Zend/tests/closure_014.phpt
rename to Zend/tests/closures/closure_014.phpt
diff --git a/Zend/tests/closure_015.phpt b/Zend/tests/closures/closure_015.phpt
similarity index 100%
rename from Zend/tests/closure_015.phpt
rename to Zend/tests/closures/closure_015.phpt
diff --git a/Zend/tests/closure_016.phpt b/Zend/tests/closures/closure_016.phpt
similarity index 100%
rename from Zend/tests/closure_016.phpt
rename to Zend/tests/closures/closure_016.phpt
diff --git a/Zend/tests/closure_017.phpt b/Zend/tests/closures/closure_017.phpt
similarity index 100%
rename from Zend/tests/closure_017.phpt
rename to Zend/tests/closures/closure_017.phpt
diff --git a/Zend/tests/closure_018.phpt b/Zend/tests/closures/closure_018.phpt
similarity index 100%
rename from Zend/tests/closure_018.phpt
rename to Zend/tests/closures/closure_018.phpt
diff --git a/Zend/tests/closure_019.phpt b/Zend/tests/closures/closure_019.phpt
similarity index 100%
rename from Zend/tests/closure_019.phpt
rename to Zend/tests/closures/closure_019.phpt
diff --git a/Zend/tests/closure_020.phpt b/Zend/tests/closures/closure_020.phpt
similarity index 100%
rename from Zend/tests/closure_020.phpt
rename to Zend/tests/closures/closure_020.phpt
diff --git a/Zend/tests/closure_021.phpt b/Zend/tests/closures/closure_021.phpt
similarity index 100%
rename from Zend/tests/closure_021.phpt
rename to Zend/tests/closures/closure_021.phpt
diff --git a/Zend/tests/closure_022.phpt b/Zend/tests/closures/closure_022.phpt
similarity index 100%
rename from Zend/tests/closure_022.phpt
rename to Zend/tests/closures/closure_022.phpt
diff --git a/Zend/tests/closure_023.phpt b/Zend/tests/closures/closure_023.phpt
similarity index 100%
rename from Zend/tests/closure_023.phpt
rename to Zend/tests/closures/closure_023.phpt
diff --git a/Zend/tests/closure_024.phpt b/Zend/tests/closures/closure_024.phpt
similarity index 100%
rename from Zend/tests/closure_024.phpt
rename to Zend/tests/closures/closure_024.phpt
diff --git a/Zend/tests/closure_026.phpt b/Zend/tests/closures/closure_026.phpt
similarity index 100%
rename from Zend/tests/closure_026.phpt
rename to Zend/tests/closures/closure_026.phpt
diff --git a/Zend/tests/closure_027.phpt b/Zend/tests/closures/closure_027.phpt
similarity index 100%
rename from Zend/tests/closure_027.phpt
rename to Zend/tests/closures/closure_027.phpt
diff --git a/Zend/tests/closure_028.phpt b/Zend/tests/closures/closure_028.phpt
similarity index 100%
rename from Zend/tests/closure_028.phpt
rename to Zend/tests/closures/closure_028.phpt
diff --git a/Zend/tests/closure_029.phpt b/Zend/tests/closures/closure_029.phpt
similarity index 100%
rename from Zend/tests/closure_029.phpt
rename to Zend/tests/closures/closure_029.phpt
diff --git a/Zend/tests/closure_030.phpt b/Zend/tests/closures/closure_030.phpt
similarity index 100%
rename from Zend/tests/closure_030.phpt
rename to Zend/tests/closures/closure_030.phpt
diff --git a/Zend/tests/closure_031.phpt b/Zend/tests/closures/closure_031.phpt
similarity index 100%
rename from Zend/tests/closure_031.phpt
rename to Zend/tests/closures/closure_031.phpt
diff --git a/Zend/tests/closure_032.phpt b/Zend/tests/closures/closure_032.phpt
similarity index 100%
rename from Zend/tests/closure_032.phpt
rename to Zend/tests/closures/closure_032.phpt
diff --git a/Zend/tests/closure_033.phpt b/Zend/tests/closures/closure_033.phpt
similarity index 100%
rename from Zend/tests/closure_033.phpt
rename to Zend/tests/closures/closure_033.phpt
diff --git a/Zend/tests/closure_034.phpt b/Zend/tests/closures/closure_034.phpt
similarity index 100%
rename from Zend/tests/closure_034.phpt
rename to Zend/tests/closures/closure_034.phpt
diff --git a/Zend/tests/closure_035.phpt b/Zend/tests/closures/closure_035.phpt
similarity index 100%
rename from Zend/tests/closure_035.phpt
rename to Zend/tests/closures/closure_035.phpt
diff --git a/Zend/tests/closure_036.phpt b/Zend/tests/closures/closure_036.phpt
similarity index 100%
rename from Zend/tests/closure_036.phpt
rename to Zend/tests/closures/closure_036.phpt
diff --git a/Zend/tests/closure_037.phpt b/Zend/tests/closures/closure_037.phpt
similarity index 100%
rename from Zend/tests/closure_037.phpt
rename to Zend/tests/closures/closure_037.phpt
diff --git a/Zend/tests/closure_038.phpt b/Zend/tests/closures/closure_038.phpt
similarity index 100%
rename from Zend/tests/closure_038.phpt
rename to Zend/tests/closures/closure_038.phpt
diff --git a/Zend/tests/closure_039.phpt b/Zend/tests/closures/closure_039.phpt
similarity index 100%
rename from Zend/tests/closure_039.phpt
rename to Zend/tests/closures/closure_039.phpt
diff --git a/Zend/tests/closure_040.phpt b/Zend/tests/closures/closure_040.phpt
similarity index 100%
rename from Zend/tests/closure_040.phpt
rename to Zend/tests/closures/closure_040.phpt
diff --git a/Zend/tests/closure_041.phpt b/Zend/tests/closures/closure_041.phpt
similarity index 100%
rename from Zend/tests/closure_041.phpt
rename to Zend/tests/closures/closure_041.phpt
diff --git a/Zend/tests/closure_042.phpt b/Zend/tests/closures/closure_042.phpt
similarity index 100%
rename from Zend/tests/closure_042.phpt
rename to Zend/tests/closures/closure_042.phpt
diff --git a/Zend/tests/closure_043.phpt b/Zend/tests/closures/closure_043.phpt
similarity index 100%
rename from Zend/tests/closure_043.phpt
rename to Zend/tests/closures/closure_043.phpt
diff --git a/Zend/tests/closure_044.phpt b/Zend/tests/closures/closure_044.phpt
similarity index 100%
rename from Zend/tests/closure_044.phpt
rename to Zend/tests/closures/closure_044.phpt
diff --git a/Zend/tests/closure_045.phpt b/Zend/tests/closures/closure_045.phpt
similarity index 100%
rename from Zend/tests/closure_045.phpt
rename to Zend/tests/closures/closure_045.phpt
diff --git a/Zend/tests/closure_046.phpt b/Zend/tests/closures/closure_046.phpt
similarity index 100%
rename from Zend/tests/closure_046.phpt
rename to Zend/tests/closures/closure_046.phpt
diff --git a/Zend/tests/closure_047.phpt b/Zend/tests/closures/closure_047.phpt
similarity index 100%
rename from Zend/tests/closure_047.phpt
rename to Zend/tests/closures/closure_047.phpt
diff --git a/Zend/tests/closure_048.phpt b/Zend/tests/closures/closure_048.phpt
similarity index 100%
rename from Zend/tests/closure_048.phpt
rename to Zend/tests/closures/closure_048.phpt
diff --git a/Zend/tests/closure_049.phpt b/Zend/tests/closures/closure_049.phpt
similarity index 100%
rename from Zend/tests/closure_049.phpt
rename to Zend/tests/closures/closure_049.phpt
diff --git a/Zend/tests/closure_050.phpt b/Zend/tests/closures/closure_050.phpt
similarity index 100%
rename from Zend/tests/closure_050.phpt
rename to Zend/tests/closures/closure_050.phpt
diff --git a/Zend/tests/closure_051.phpt b/Zend/tests/closures/closure_051.phpt
similarity index 100%
rename from Zend/tests/closure_051.phpt
rename to Zend/tests/closures/closure_051.phpt
diff --git a/Zend/tests/closure_052.phpt b/Zend/tests/closures/closure_052.phpt
similarity index 100%
rename from Zend/tests/closure_052.phpt
rename to Zend/tests/closures/closure_052.phpt
diff --git a/Zend/tests/closure_053.phpt b/Zend/tests/closures/closure_053.phpt
similarity index 100%
rename from Zend/tests/closure_053.phpt
rename to Zend/tests/closures/closure_053.phpt
diff --git a/Zend/tests/closure_054.phpt b/Zend/tests/closures/closure_054.phpt
similarity index 100%
rename from Zend/tests/closure_054.phpt
rename to Zend/tests/closures/closure_054.phpt
diff --git a/Zend/tests/closure_055.phpt b/Zend/tests/closures/closure_055.phpt
similarity index 100%
rename from Zend/tests/closure_055.phpt
rename to Zend/tests/closures/closure_055.phpt
diff --git a/Zend/tests/closure_056.phpt b/Zend/tests/closures/closure_056.phpt
similarity index 100%
rename from Zend/tests/closure_056.phpt
rename to Zend/tests/closures/closure_056.phpt
diff --git a/Zend/tests/closure_057.phpt b/Zend/tests/closures/closure_057.phpt
similarity index 100%
rename from Zend/tests/closure_057.phpt
rename to Zend/tests/closures/closure_057.phpt
diff --git a/Zend/tests/closure_058.phpt b/Zend/tests/closures/closure_058.phpt
similarity index 100%
rename from Zend/tests/closure_058.phpt
rename to Zend/tests/closures/closure_058.phpt
diff --git a/Zend/tests/closure_059.phpt b/Zend/tests/closures/closure_059.phpt
similarity index 100%
rename from Zend/tests/closure_059.phpt
rename to Zend/tests/closures/closure_059.phpt
diff --git a/Zend/tests/closure_060.phpt b/Zend/tests/closures/closure_060.phpt
similarity index 100%
rename from Zend/tests/closure_060.phpt
rename to Zend/tests/closures/closure_060.phpt
diff --git a/Zend/tests/closure_061.phpt b/Zend/tests/closures/closure_061.phpt
similarity index 100%
rename from Zend/tests/closure_061.phpt
rename to Zend/tests/closures/closure_061.phpt
diff --git a/Zend/tests/closure_062.phpt b/Zend/tests/closures/closure_062.phpt
similarity index 100%
rename from Zend/tests/closure_062.phpt
rename to Zend/tests/closures/closure_062.phpt
diff --git a/Zend/tests/closure_063.phpt b/Zend/tests/closures/closure_063.phpt
similarity index 100%
rename from Zend/tests/closure_063.phpt
rename to Zend/tests/closures/closure_063.phpt
diff --git a/Zend/tests/closure_064.phpt b/Zend/tests/closures/closure_064.phpt
similarity index 100%
rename from Zend/tests/closure_064.phpt
rename to Zend/tests/closures/closure_064.phpt
diff --git a/Zend/tests/closure_065.phpt b/Zend/tests/closures/closure_065.phpt
similarity index 100%
rename from Zend/tests/closure_065.phpt
rename to Zend/tests/closures/closure_065.phpt
diff --git a/Zend/tests/closure_066.phpt b/Zend/tests/closures/closure_066.phpt
similarity index 100%
rename from Zend/tests/closure_066.phpt
rename to Zend/tests/closures/closure_066.phpt
diff --git a/Zend/tests/closure_067.phpt b/Zend/tests/closures/closure_067.phpt
similarity index 100%
rename from Zend/tests/closure_067.phpt
rename to Zend/tests/closures/closure_067.phpt
diff --git a/Zend/tests/closure_068.phpt b/Zend/tests/closures/closure_068.phpt
similarity index 100%
rename from Zend/tests/closure_068.phpt
rename to Zend/tests/closures/closure_068.phpt
diff --git a/Zend/tests/closure_bindTo_preserves_used_variables.phpt b/Zend/tests/closures/closure_bindTo_preserves_used_variables.phpt
similarity index 100%
rename from Zend/tests/closure_bindTo_preserves_used_variables.phpt
rename to Zend/tests/closures/closure_bindTo_preserves_used_variables.phpt
diff --git a/Zend/tests/closure_bug66622.phpt b/Zend/tests/closures/closure_bug66622.phpt
similarity index 100%
rename from Zend/tests/closure_bug66622.phpt
rename to Zend/tests/closures/closure_bug66622.phpt
diff --git a/Zend/tests/closure_call.phpt b/Zend/tests/closures/closure_call.phpt
similarity index 100%
rename from Zend/tests/closure_call.phpt
rename to Zend/tests/closures/closure_call.phpt
diff --git a/Zend/tests/closure_call_bind.phpt b/Zend/tests/closures/closure_call_bind.phpt
similarity index 100%
rename from Zend/tests/closure_call_bind.phpt
rename to Zend/tests/closures/closure_call_bind.phpt
diff --git a/Zend/tests/closure_call_internal.phpt b/Zend/tests/closures/closure_call_internal.phpt
similarity index 100%
rename from Zend/tests/closure_call_internal.phpt
rename to Zend/tests/closures/closure_call_internal.phpt
diff --git a/Zend/tests/closure_call_leak_with_exception.phpt b/Zend/tests/closures/closure_call_leak_with_exception.phpt
similarity index 100%
rename from Zend/tests/closure_call_leak_with_exception.phpt
rename to Zend/tests/closures/closure_call_leak_with_exception.phpt
diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closures/closure_compare.phpt
similarity index 100%
rename from Zend/tests/closure_compare.phpt
rename to Zend/tests/closures/closure_compare.phpt
diff --git a/Zend/tests/closures/closure_const_expr/attributes.phpt b/Zend/tests/closures/closure_const_expr/attributes.phpt
new file mode 100644
index 0000000000000..e331bc2edd568
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Allow defining closures in attributes
+--EXTENSIONS--
+reflection
+--FILE--
+getAttributes() as $reflectionAttribute) {
+ var_dump($reflectionAttribute->newInstance());
+}
+
+?>
+--EXPECTF--
+object(Attr)#%d (1) {
+ ["value"]=>
+ object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(%d)
+ }
+}
+array(1) {
+ [0]=>
+ string(3) "foo"
+}
+object(Attr)#%d (1) {
+ ["value"]=>
+ object(Closure)#%d (4) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(%d)
+ ["parameter"]=>
+ array(1) {
+ ["$args"]=>
+ string(10) ""
+ }
+ }
+}
diff --git a/Zend/tests/closures/closure_const_expr/attributes_ast_printing.phpt b/Zend/tests/closures/closure_const_expr/attributes_ast_printing.phpt
new file mode 100644
index 0000000000000..e87abf8c90611
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes_ast_printing.phpt
@@ -0,0 +1,41 @@
+--TEST--
+AST printing for closures in attributes
+--FILE--
+getMessage(), "\n";
+}
+
+try {
+ \assert(
+ !
+ new #[Attr(static function ($foo) {
+ echo $foo;
+ })]
+ class {}
+ );
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+assert(!#[Attr(static function ($foo) {
+ echo $foo;
+})] function () {
+})
+assert(!new #[Attr(static function ($foo) {
+ echo $foo;
+})] class {
+})
diff --git a/Zend/tests/closures/closure_const_expr/attributes_ast_printing_runtime.phpt b/Zend/tests/closures/closure_const_expr/attributes_ast_printing_runtime.phpt
new file mode 100644
index 0000000000000..dfdb32e492f0c
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes_ast_printing_runtime.phpt
@@ -0,0 +1,22 @@
+--TEST--
+AST printing for closures in attributes at runtime
+--FILE--
+getAttributes() as $attribute) {
+ echo $attribute;
+}
+
+?>
+--EXPECT--
+Attribute [ Attr ] {
+ - Arguments [1] {
+ Argument #0 [ Closure({closure:foo():3}) ]
+ }
+}
diff --git a/Zend/tests/closures/closure_const_expr/attributes_autoload.inc b/Zend/tests/closures/closure_const_expr/attributes_autoload.inc
new file mode 100644
index 0000000000000..48f9b1987cf5d
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes_autoload.inc
@@ -0,0 +1,9 @@
+
diff --git a/Zend/tests/closures/closure_const_expr/attributes_autoload.phpt b/Zend/tests/closures/closure_const_expr/attributes_autoload.phpt
new file mode 100644
index 0000000000000..fa99e8b5abaa3
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes_autoload.phpt
@@ -0,0 +1,57 @@
+--TEST--
+GH-17851: Use-after-free when instantiating autoloaded class with attribute having a Closure parameter.
+--EXTENSIONS--
+reflection
+--FILE--
+getAttributes() as $reflectionAttribute) {
+ var_dump($reflectionAttribute->newInstance());
+}
+
+?>
+--EXPECTF--
+object(Attr)#%d (1) {
+ ["value"]=>
+ object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(%d)
+ }
+}
+array(1) {
+ [0]=>
+ string(3) "foo"
+}
+object(Attr)#%d (1) {
+ ["value"]=>
+ object(Closure)#%d (4) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(%d)
+ ["parameter"]=>
+ array(1) {
+ ["$args"]=>
+ string(10) ""
+ }
+ }
+}
diff --git a/Zend/tests/closures/closure_const_expr/attributes_scope_001.phpt b/Zend/tests/closures/closure_const_expr/attributes_scope_001.phpt
new file mode 100644
index 0000000000000..6d89f410ca02b
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes_scope_001.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Closure in attribute may access private variables
+--EXTENSIONS--
+reflection
+--FILE--
+secret, PHP_EOL;
+})]
+class C {
+ public function __construct(
+ private string $secret,
+ ) {}
+}
+
+foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribute) {
+ ($reflectionAttribute->newInstance()->value)(new C('secret'));
+}
+
+?>
+--EXPECT--
+secret
diff --git a/Zend/tests/closures/closure_const_expr/attributes_scope_002.phpt b/Zend/tests/closures/closure_const_expr/attributes_scope_002.phpt
new file mode 100644
index 0000000000000..ac168fd02de46
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/attributes_scope_002.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Closure in attribute may not access unrelated private variables
+--EXTENSIONS--
+reflection
+--FILE--
+secret, PHP_EOL;
+})]
+class C {
+}
+
+class E {
+ public function __construct(
+ private string $secret,
+ ) {}
+}
+
+foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribute) {
+ ($reflectionAttribute->newInstance()->value)(new E('secret'));
+}
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot access private property E::$secret in %s:%d
+Stack trace:
+#0 %s(%d): C::{closure:%s:%d}(Object(E))
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/closures/closure_const_expr/basic.phpt b/Zend/tests/closures/closure_const_expr/basic.phpt
new file mode 100644
index 0000000000000..d1242c989b2d8
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/basic.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Allow defining Closures in const expressions.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(3)
+}
+called
diff --git a/Zend/tests/closures/closure_const_expr/class_const.phpt b/Zend/tests/closures/closure_const_expr/class_const.phpt
new file mode 100644
index 0000000000000..d392d0d7dfdc6
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/class_const.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Allow defining Closures in class constants.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(4)
+}
+called
diff --git a/Zend/tests/closures/closure_const_expr/complex_array.phpt b/Zend/tests/closures/closure_const_expr/complex_array.phpt
new file mode 100644
index 0000000000000..87044cb3659f5
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/complex_array.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Allow defining Closures wrapped in an array in const expressions.
+--FILE--
+
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(3)
+ }
+ [1]=>
+ object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(5)
+ }
+}
+called
+also called
diff --git a/Zend/tests/closures/closure_const_expr/complex_new.phpt b/Zend/tests/closures/closure_const_expr/complex_new.phpt
new file mode 100644
index 0000000000000..9437bbdc259f4
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/complex_new.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Allow defining Closures passed as constructor arguments in const expressions.
+--FILE--
+c)();
+
+?>
+--EXPECTF--
+object(Dummy)#%d (1) {
+ ["c"]=>
+ object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(9)
+ }
+}
+called
diff --git a/Zend/tests/closures/closure_const_expr/default_args.phpt b/Zend/tests/closures/closure_const_expr/default_args.phpt
new file mode 100644
index 0000000000000..f1a9ee48f1146
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/default_args.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Closures in default argument
+--FILE--
+
+--EXPECT--
+default
+explicit
diff --git a/Zend/tests/closures/closure_const_expr/disallows_non_static.phpt b/Zend/tests/closures/closure_const_expr/disallows_non_static.phpt
new file mode 100644
index 0000000000000..3e67a0a4829f8
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/disallows_non_static.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Disallows using non-static closures.
+--FILE--
+d);
+($foo->d)();
+
+?>
+--EXPECTF--
+Fatal error: Closures in constant expressions must be static in %s on line %d
diff --git a/Zend/tests/closures/closure_const_expr/disallows_using_variables.phpt b/Zend/tests/closures/closure_const_expr/disallows_using_variables.phpt
new file mode 100644
index 0000000000000..a7a212decf332
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/disallows_using_variables.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Disallows using variables.
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use(...) variables in constant expression in %s on line %d
diff --git a/Zend/tests/closures/closure_const_expr/property_initializer.phpt b/Zend/tests/closures/closure_const_expr/property_initializer.phpt
new file mode 100644
index 0000000000000..077a4e4f79a11
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/property_initializer.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Closure in property initializer
+--FILE--
+d);
+($c->d)();
+
+
+?>
+--EXPECTF--
+object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(4)
+}
+called
diff --git a/Zend/tests/closures/closure_const_expr/property_initializer_scope_001.phpt b/Zend/tests/closures/closure_const_expr/property_initializer_scope_001.phpt
new file mode 100644
index 0000000000000..bbd5b5b0ad84c
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/property_initializer_scope_001.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Closure in property initializer may access private variables
+--FILE--
+secret, PHP_EOL;
+ };
+
+ public function __construct(
+ private string $secret,
+ ) {}
+}
+
+$c = new C('secret');
+($c->d)($c);
+
+
+?>
+--EXPECTF--
+secret
diff --git a/Zend/tests/closures/closure_const_expr/property_initializer_scope_002.phpt b/Zend/tests/closures/closure_const_expr/property_initializer_scope_002.phpt
new file mode 100644
index 0000000000000..a1c6942da1765
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/property_initializer_scope_002.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Closure in property initializer may not access unrelated private variables
+--FILE--
+secret, PHP_EOL;
+ };
+
+
+}
+
+class E {
+ public function __construct(
+ private string $secret,
+ ) {}
+}
+
+$c = new C();
+($c->d)(new E('secret'));
+
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot access private property E::$secret in %s:%d
+Stack trace:
+#0 %s(%d): C::{closure:%s:%d}(Object(E))
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/closures/closure_const_expr/static_initalizer.phpt b/Zend/tests/closures/closure_const_expr/static_initalizer.phpt
new file mode 100644
index 0000000000000..65bfb37393d42
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/static_initalizer.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Closure in static initializer
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (3) {
+ ["name"]=>
+ string(17) "{closure:foo():4}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(4)
+}
+called
diff --git a/Zend/tests/closures/closure_const_expr/static_property_initializer.phpt b/Zend/tests/closures/closure_const_expr/static_property_initializer.phpt
new file mode 100644
index 0000000000000..0b7249d78bd6f
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/static_property_initializer.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Closure in static property initializer
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (3) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(4)
+}
+called
diff --git a/Zend/tests/closures/closure_const_expr/static_variable.phpt b/Zend/tests/closures/closure_const_expr/static_variable.phpt
new file mode 100644
index 0000000000000..5aea594d8620b
--- /dev/null
+++ b/Zend/tests/closures/closure_const_expr/static_variable.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Closures in const expressions support static variables.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (4) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(3)
+ ["static"]=>
+ array(2) {
+ ["x"]=>
+ array(0) {
+ }
+ ["i"]=>
+ int(1)
+ }
+}
+array(1) {
+ [0]=>
+ int(2)
+}
+array(2) {
+ [0]=>
+ int(2)
+ [1]=>
+ int(4)
+}
+array(3) {
+ [0]=>
+ int(2)
+ [1]=>
+ int(4)
+ [2]=>
+ int(8)
+}
+object(Closure)#%d (4) {
+ ["name"]=>
+ string(%d) "{closure:%s:%d}"
+ ["file"]=>
+ string(%d) "%s"
+ ["line"]=>
+ int(3)
+ ["static"]=>
+ array(2) {
+ ["x"]=>
+ array(3) {
+ [0]=>
+ int(2)
+ [1]=>
+ int(4)
+ [2]=>
+ int(8)
+ }
+ ["i"]=>
+ int(8)
+ }
+}
diff --git a/Zend/tests/closure_extra_args.phpt b/Zend/tests/closures/closure_extra_args.phpt
similarity index 100%
rename from Zend/tests/closure_extra_args.phpt
rename to Zend/tests/closures/closure_extra_args.phpt
diff --git a/Zend/tests/closure_invoke_case_insensitive.phpt b/Zend/tests/closures/closure_invoke_case_insensitive.phpt
similarity index 100%
rename from Zend/tests/closure_invoke_case_insensitive.phpt
rename to Zend/tests/closures/closure_invoke_case_insensitive.phpt
diff --git a/Zend/tests/closure_invoke_ref_warning.phpt b/Zend/tests/closures/closure_invoke_ref_warning.phpt
similarity index 100%
rename from Zend/tests/closure_invoke_ref_warning.phpt
rename to Zend/tests/closures/closure_invoke_ref_warning.phpt
diff --git a/Zend/tests/closure_use_auto_global.phpt b/Zend/tests/closures/closure_use_auto_global.phpt
similarity index 100%
rename from Zend/tests/closure_use_auto_global.phpt
rename to Zend/tests/closures/closure_use_auto_global.phpt
diff --git a/Zend/tests/closure_use_parameter_name.phpt b/Zend/tests/closures/closure_use_parameter_name.phpt
similarity index 100%
rename from Zend/tests/closure_use_parameter_name.phpt
rename to Zend/tests/closures/closure_use_parameter_name.phpt
diff --git a/Zend/tests/closure_use_trailing_comma.phpt b/Zend/tests/closures/closure_use_trailing_comma.phpt
similarity index 100%
rename from Zend/tests/closure_use_trailing_comma.phpt
rename to Zend/tests/closures/closure_use_trailing_comma.phpt
diff --git a/Zend/tests/closure_use_variable_twice.phpt b/Zend/tests/closures/closure_use_variable_twice.phpt
similarity index 100%
rename from Zend/tests/closure_use_variable_twice.phpt
rename to Zend/tests/closures/closure_use_variable_twice.phpt
diff --git a/Zend/tests/closure_write_prop.phpt b/Zend/tests/closures/closure_write_prop.phpt
similarity index 100%
rename from Zend/tests/closure_write_prop.phpt
rename to Zend/tests/closures/closure_write_prop.phpt
diff --git a/Zend/tests/fake_closure_in_internal_func_leaks.phpt b/Zend/tests/closures/fake_closure_in_internal_func_leaks.phpt
similarity index 100%
rename from Zend/tests/fake_closure_in_internal_func_leaks.phpt
rename to Zend/tests/closures/fake_closure_in_internal_func_leaks.phpt
diff --git a/Zend/tests/gh12073.phpt b/Zend/tests/closures/gh12073.phpt
similarity index 100%
rename from Zend/tests/gh12073.phpt
rename to Zend/tests/closures/gh12073.phpt
diff --git a/Zend/tests/gh8083.phpt b/Zend/tests/closures/gh8083.phpt
similarity index 100%
rename from Zend/tests/gh8083.phpt
rename to Zend/tests/closures/gh8083.phpt
diff --git a/Zend/tests/assign_coalesce_001.phpt b/Zend/tests/coalesce/assign_coalesce_001.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_001.phpt
rename to Zend/tests/coalesce/assign_coalesce_001.phpt
diff --git a/Zend/tests/assign_coalesce_002.phpt b/Zend/tests/coalesce/assign_coalesce_002.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_002.phpt
rename to Zend/tests/coalesce/assign_coalesce_002.phpt
diff --git a/Zend/tests/assign_coalesce_003.phpt b/Zend/tests/coalesce/assign_coalesce_003.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_003.phpt
rename to Zend/tests/coalesce/assign_coalesce_003.phpt
diff --git a/Zend/tests/assign_coalesce_004.phpt b/Zend/tests/coalesce/assign_coalesce_004.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_004.phpt
rename to Zend/tests/coalesce/assign_coalesce_004.phpt
diff --git a/Zend/tests/assign_coalesce_005.phpt b/Zend/tests/coalesce/assign_coalesce_005.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_005.phpt
rename to Zend/tests/coalesce/assign_coalesce_005.phpt
diff --git a/Zend/tests/assign_coalesce_006.phpt b/Zend/tests/coalesce/assign_coalesce_006.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_006.phpt
rename to Zend/tests/coalesce/assign_coalesce_006.phpt
diff --git a/Zend/tests/assign_coalesce_007.phpt b/Zend/tests/coalesce/assign_coalesce_007.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_007.phpt
rename to Zend/tests/coalesce/assign_coalesce_007.phpt
diff --git a/Zend/tests/assign_coalesce_008.phpt b/Zend/tests/coalesce/assign_coalesce_008.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_008.phpt
rename to Zend/tests/coalesce/assign_coalesce_008.phpt
diff --git a/Zend/tests/assign_coalesce_009.phpt b/Zend/tests/coalesce/assign_coalesce_009.phpt
similarity index 100%
rename from Zend/tests/assign_coalesce_009.phpt
rename to Zend/tests/coalesce/assign_coalesce_009.phpt
diff --git a/Zend/tests/bug69889.phpt b/Zend/tests/coalesce/bug69889.phpt
similarity index 100%
rename from Zend/tests/bug69889.phpt
rename to Zend/tests/coalesce/bug69889.phpt
diff --git a/Zend/tests/bug81684.phpt b/Zend/tests/coalesce/bug81684.phpt
similarity index 100%
rename from Zend/tests/bug81684.phpt
rename to Zend/tests/coalesce/bug81684.phpt
diff --git a/Zend/tests/coalesce_assign_optimization.phpt b/Zend/tests/coalesce/coalesce_assign_optimization.phpt
similarity index 100%
rename from Zend/tests/coalesce_assign_optimization.phpt
rename to Zend/tests/coalesce/coalesce_assign_optimization.phpt
diff --git a/Zend/tests/gh11108.phpt b/Zend/tests/coalesce/gh11108.phpt
similarity index 100%
rename from Zend/tests/gh11108.phpt
rename to Zend/tests/coalesce/gh11108.phpt
diff --git a/Zend/tests/gh11108_shutdown.inc b/Zend/tests/coalesce/gh11108_shutdown.inc
similarity index 100%
rename from Zend/tests/gh11108_shutdown.inc
rename to Zend/tests/coalesce/gh11108_shutdown.inc
diff --git a/Zend/tests/gh11108_test.inc b/Zend/tests/coalesce/gh11108_test.inc
similarity index 100%
rename from Zend/tests/gh11108_test.inc
rename to Zend/tests/coalesce/gh11108_test.inc
diff --git a/Zend/tests/compare_001.phpt b/Zend/tests/comparison/compare_001.phpt
similarity index 100%
rename from Zend/tests/compare_001.phpt
rename to Zend/tests/comparison/compare_001.phpt
diff --git a/Zend/tests/compare_001_64bit.phpt b/Zend/tests/comparison/compare_001_64bit.phpt
similarity index 100%
rename from Zend/tests/compare_001_64bit.phpt
rename to Zend/tests/comparison/compare_001_64bit.phpt
diff --git a/Zend/tests/compare_002.phpt b/Zend/tests/comparison/compare_002.phpt
similarity index 100%
rename from Zend/tests/compare_002.phpt
rename to Zend/tests/comparison/compare_002.phpt
diff --git a/Zend/tests/compare_002_64bit.phpt b/Zend/tests/comparison/compare_002_64bit.phpt
similarity index 100%
rename from Zend/tests/compare_002_64bit.phpt
rename to Zend/tests/comparison/compare_002_64bit.phpt
diff --git a/Zend/tests/compare_003.phpt b/Zend/tests/comparison/compare_003.phpt
similarity index 100%
rename from Zend/tests/compare_003.phpt
rename to Zend/tests/comparison/compare_003.phpt
diff --git a/Zend/tests/compare_003_64bit.phpt b/Zend/tests/comparison/compare_003_64bit.phpt
similarity index 100%
rename from Zend/tests/compare_003_64bit.phpt
rename to Zend/tests/comparison/compare_003_64bit.phpt
diff --git a/Zend/tests/compare_004.phpt b/Zend/tests/comparison/compare_004.phpt
similarity index 100%
rename from Zend/tests/compare_004.phpt
rename to Zend/tests/comparison/compare_004.phpt
diff --git a/Zend/tests/compare_004_64bit.phpt b/Zend/tests/comparison/compare_004_64bit.phpt
similarity index 100%
rename from Zend/tests/compare_004_64bit.phpt
rename to Zend/tests/comparison/compare_004_64bit.phpt
diff --git a/Zend/tests/compare_005.phpt b/Zend/tests/comparison/compare_005.phpt
similarity index 100%
rename from Zend/tests/compare_005.phpt
rename to Zend/tests/comparison/compare_005.phpt
diff --git a/Zend/tests/compare_005_64bit.phpt b/Zend/tests/comparison/compare_005_64bit.phpt
similarity index 100%
rename from Zend/tests/compare_005_64bit.phpt
rename to Zend/tests/comparison/compare_005_64bit.phpt
diff --git a/Zend/tests/compare_006.phpt b/Zend/tests/comparison/compare_006.phpt
similarity index 100%
rename from Zend/tests/compare_006.phpt
rename to Zend/tests/comparison/compare_006.phpt
diff --git a/Zend/tests/compare_006_64bit.phpt b/Zend/tests/comparison/compare_006_64bit.phpt
similarity index 100%
rename from Zend/tests/compare_006_64bit.phpt
rename to Zend/tests/comparison/compare_006_64bit.phpt
diff --git a/Zend/tests/assign_concat_array_empty_string.phpt b/Zend/tests/concat/assign_concat_array_empty_string.phpt
similarity index 100%
rename from Zend/tests/assign_concat_array_empty_string.phpt
rename to Zend/tests/concat/assign_concat_array_empty_string.phpt
diff --git a/Zend/tests/bug32833.phpt b/Zend/tests/concat/bug32833.phpt
similarity index 100%
rename from Zend/tests/bug32833.phpt
rename to Zend/tests/concat/bug32833.phpt
diff --git a/Zend/tests/bug40809.phpt b/Zend/tests/concat/bug40809.phpt
similarity index 100%
rename from Zend/tests/bug40809.phpt
rename to Zend/tests/concat/bug40809.phpt
diff --git a/Zend/tests/bug44069.phpt b/Zend/tests/concat/bug44069.phpt
similarity index 100%
rename from Zend/tests/bug44069.phpt
rename to Zend/tests/concat/bug44069.phpt
diff --git a/Zend/tests/bug68118.phpt b/Zend/tests/concat/bug68118.phpt
similarity index 100%
rename from Zend/tests/bug68118.phpt
rename to Zend/tests/concat/bug68118.phpt
diff --git a/Zend/tests/bug79836.phpt b/Zend/tests/concat/bug79836.phpt
similarity index 100%
rename from Zend/tests/bug79836.phpt
rename to Zend/tests/concat/bug79836.phpt
diff --git a/Zend/tests/bug79836_1.phpt b/Zend/tests/concat/bug79836_1.phpt
similarity index 100%
rename from Zend/tests/bug79836_1.phpt
rename to Zend/tests/concat/bug79836_1.phpt
diff --git a/Zend/tests/bug79836_2.phpt b/Zend/tests/concat/bug79836_2.phpt
similarity index 100%
rename from Zend/tests/bug79836_2.phpt
rename to Zend/tests/concat/bug79836_2.phpt
diff --git a/Zend/tests/bug79836_3.phpt b/Zend/tests/concat/bug79836_3.phpt
similarity index 100%
rename from Zend/tests/bug79836_3.phpt
rename to Zend/tests/concat/bug79836_3.phpt
diff --git a/Zend/tests/bug79836_4.phpt b/Zend/tests/concat/bug79836_4.phpt
similarity index 100%
rename from Zend/tests/bug79836_4.phpt
rename to Zend/tests/concat/bug79836_4.phpt
diff --git a/Zend/tests/bug81705.phpt b/Zend/tests/concat/bug81705.phpt
similarity index 100%
rename from Zend/tests/bug81705.phpt
rename to Zend/tests/concat/bug81705.phpt
diff --git a/Zend/tests/concat_001.phpt b/Zend/tests/concat/concat_001.phpt
similarity index 100%
rename from Zend/tests/concat_001.phpt
rename to Zend/tests/concat/concat_001.phpt
diff --git a/Zend/tests/concat_002.phpt b/Zend/tests/concat/concat_002.phpt
similarity index 100%
rename from Zend/tests/concat_002.phpt
rename to Zend/tests/concat/concat_002.phpt
diff --git a/Zend/tests/concat_003.phpt b/Zend/tests/concat/concat_003.phpt
similarity index 100%
rename from Zend/tests/concat_003.phpt
rename to Zend/tests/concat/concat_003.phpt
diff --git a/Zend/tests/008.phpt b/Zend/tests/constants/008.phpt
similarity index 100%
rename from Zend/tests/008.phpt
rename to Zend/tests/constants/008.phpt
diff --git a/Zend/tests/018.phpt b/Zend/tests/constants/018.phpt
similarity index 100%
rename from Zend/tests/018.phpt
rename to Zend/tests/constants/018.phpt
diff --git a/Zend/tests/bug24699.phpt b/Zend/tests/constants/bug24699.phpt
similarity index 100%
rename from Zend/tests/bug24699.phpt
rename to Zend/tests/constants/bug24699.phpt
diff --git a/Zend/tests/bug30702.phpt b/Zend/tests/constants/bug30702.phpt
similarity index 100%
rename from Zend/tests/bug30702.phpt
rename to Zend/tests/constants/bug30702.phpt
diff --git a/Zend/tests/bug33732.phpt b/Zend/tests/constants/bug33732.phpt
similarity index 100%
rename from Zend/tests/bug33732.phpt
rename to Zend/tests/constants/bug33732.phpt
diff --git a/Zend/tests/bug41633_1.phpt b/Zend/tests/constants/bug41633_1.phpt
similarity index 100%
rename from Zend/tests/bug41633_1.phpt
rename to Zend/tests/constants/bug41633_1.phpt
diff --git a/Zend/tests/bug41633_2.phpt b/Zend/tests/constants/bug41633_2.phpt
similarity index 100%
rename from Zend/tests/bug41633_2.phpt
rename to Zend/tests/constants/bug41633_2.phpt
diff --git a/Zend/tests/bug41633_3.phpt b/Zend/tests/constants/bug41633_3.phpt
similarity index 100%
rename from Zend/tests/bug41633_3.phpt
rename to Zend/tests/constants/bug41633_3.phpt
diff --git a/Zend/tests/bug41633_4.phpt b/Zend/tests/constants/bug41633_4.phpt
similarity index 100%
rename from Zend/tests/bug41633_4.phpt
rename to Zend/tests/constants/bug41633_4.phpt
diff --git a/Zend/tests/bug41640.phpt b/Zend/tests/constants/bug41640.phpt
similarity index 100%
rename from Zend/tests/bug41640.phpt
rename to Zend/tests/constants/bug41640.phpt
diff --git a/Zend/tests/bug42820.phpt b/Zend/tests/constants/bug42820.phpt
similarity index 100%
rename from Zend/tests/bug42820.phpt
rename to Zend/tests/constants/bug42820.phpt
diff --git a/Zend/tests/bug44827.phpt b/Zend/tests/constants/bug44827.phpt
similarity index 100%
rename from Zend/tests/bug44827.phpt
rename to Zend/tests/constants/bug44827.phpt
diff --git a/Zend/tests/bug45742.phpt b/Zend/tests/constants/bug45742.phpt
similarity index 100%
rename from Zend/tests/bug45742.phpt
rename to Zend/tests/constants/bug45742.phpt
diff --git a/Zend/tests/bug45910.phpt b/Zend/tests/constants/bug45910.phpt
similarity index 100%
rename from Zend/tests/bug45910.phpt
rename to Zend/tests/constants/bug45910.phpt
diff --git a/Zend/tests/bug45910_2.phpt b/Zend/tests/constants/bug45910_2.phpt
similarity index 100%
rename from Zend/tests/bug45910_2.phpt
rename to Zend/tests/constants/bug45910_2.phpt
diff --git a/Zend/tests/bug46304.phpt b/Zend/tests/constants/bug46304.phpt
similarity index 100%
rename from Zend/tests/bug46304.phpt
rename to Zend/tests/constants/bug46304.phpt
diff --git a/Zend/tests/bug49472.phpt b/Zend/tests/constants/bug49472.phpt
similarity index 100%
rename from Zend/tests/bug49472.phpt
rename to Zend/tests/constants/bug49472.phpt
diff --git a/Zend/tests/bug50816.phpt b/Zend/tests/constants/bug50816.phpt
similarity index 100%
rename from Zend/tests/bug50816.phpt
rename to Zend/tests/constants/bug50816.phpt
diff --git a/Zend/tests/bug51791.phpt b/Zend/tests/constants/bug51791.phpt
similarity index 100%
rename from Zend/tests/bug51791.phpt
rename to Zend/tests/constants/bug51791.phpt
diff --git a/Zend/tests/bug63976.phpt b/Zend/tests/constants/bug63976.phpt
similarity index 100%
rename from Zend/tests/bug63976.phpt
rename to Zend/tests/constants/bug63976.phpt
diff --git a/Zend/tests/bug65291.phpt b/Zend/tests/constants/bug65291.phpt
similarity index 100%
rename from Zend/tests/bug65291.phpt
rename to Zend/tests/constants/bug65291.phpt
diff --git a/Zend/tests/bug69676.phpt b/Zend/tests/constants/bug69676.phpt
similarity index 100%
rename from Zend/tests/bug69676.phpt
rename to Zend/tests/constants/bug69676.phpt
diff --git a/Zend/tests/bug69676_2.phpt b/Zend/tests/constants/bug69676_2.phpt
similarity index 100%
rename from Zend/tests/bug69676_2.phpt
rename to Zend/tests/constants/bug69676_2.phpt
diff --git a/Zend/tests/bug69676_3.phpt b/Zend/tests/constants/bug69676_3.phpt
similarity index 100%
rename from Zend/tests/bug69676_3.phpt
rename to Zend/tests/constants/bug69676_3.phpt
diff --git a/Zend/tests/bug69832.phpt b/Zend/tests/constants/bug69832.phpt
similarity index 100%
rename from Zend/tests/bug69832.phpt
rename to Zend/tests/constants/bug69832.phpt
diff --git a/Zend/tests/bug74657.phpt b/Zend/tests/constants/bug74657.phpt
similarity index 100%
rename from Zend/tests/bug74657.phpt
rename to Zend/tests/constants/bug74657.phpt
diff --git a/Zend/tests/bug76430.phpt b/Zend/tests/constants/bug76430.phpt
similarity index 100%
rename from Zend/tests/bug76430.phpt
rename to Zend/tests/constants/bug76430.phpt
diff --git a/Zend/tests/bug76754.phpt b/Zend/tests/constants/bug76754.phpt
similarity index 100%
rename from Zend/tests/bug76754.phpt
rename to Zend/tests/constants/bug76754.phpt
diff --git a/Zend/tests/class_constant_inheritance_mutable_data.phpt b/Zend/tests/constants/class_constant_inheritance_mutable_data.phpt
similarity index 100%
rename from Zend/tests/class_constant_inheritance_mutable_data.phpt
rename to Zend/tests/constants/class_constant_inheritance_mutable_data.phpt
diff --git a/Zend/tests/class_constant_to_reference_cached.phpt b/Zend/tests/constants/class_constant_to_reference_cached.phpt
similarity index 100%
rename from Zend/tests/class_constant_to_reference_cached.phpt
rename to Zend/tests/constants/class_constant_to_reference_cached.phpt
diff --git a/Zend/tests/class_constants_001.phpt b/Zend/tests/constants/class_constants_001.phpt
similarity index 100%
rename from Zend/tests/class_constants_001.phpt
rename to Zend/tests/constants/class_constants_001.phpt
diff --git a/Zend/tests/class_constants_002.phpt b/Zend/tests/constants/class_constants_002.phpt
similarity index 100%
rename from Zend/tests/class_constants_002.phpt
rename to Zend/tests/constants/class_constants_002.phpt
diff --git a/Zend/tests/class_constants_003.phpt b/Zend/tests/constants/class_constants_003.phpt
similarity index 100%
rename from Zend/tests/class_constants_003.phpt
rename to Zend/tests/constants/class_constants_003.phpt
diff --git a/Zend/tests/class_constants_004.phpt b/Zend/tests/constants/class_constants_004.phpt
similarity index 100%
rename from Zend/tests/class_constants_004.phpt
rename to Zend/tests/constants/class_constants_004.phpt
diff --git a/Zend/tests/class_constants_005.phpt b/Zend/tests/constants/class_constants_005.phpt
similarity index 100%
rename from Zend/tests/class_constants_005.phpt
rename to Zend/tests/constants/class_constants_005.phpt
diff --git a/Zend/tests/class_constants_006.phpt b/Zend/tests/constants/class_constants_006.phpt
similarity index 100%
rename from Zend/tests/class_constants_006.phpt
rename to Zend/tests/constants/class_constants_006.phpt
diff --git a/Zend/tests/class_constants_007.phpt b/Zend/tests/constants/class_constants_007.phpt
similarity index 100%
rename from Zend/tests/class_constants_007.phpt
rename to Zend/tests/constants/class_constants_007.phpt
diff --git a/Zend/tests/const_deprecation.phpt b/Zend/tests/constants/const_deprecation.phpt
similarity index 100%
rename from Zend/tests/const_deprecation.phpt
rename to Zend/tests/constants/const_deprecation.phpt
diff --git a/Zend/tests/constants_001.phpt b/Zend/tests/constants/constants_001.phpt
similarity index 100%
rename from Zend/tests/constants_001.phpt
rename to Zend/tests/constants/constants_001.phpt
diff --git a/Zend/tests/constants_002.phpt b/Zend/tests/constants/constants_002.phpt
similarity index 100%
rename from Zend/tests/constants_002.phpt
rename to Zend/tests/constants/constants_002.phpt
diff --git a/Zend/tests/constants_003.phpt b/Zend/tests/constants/constants_003.phpt
similarity index 100%
rename from Zend/tests/constants_003.phpt
rename to Zend/tests/constants/constants_003.phpt
diff --git a/Zend/tests/constants_004.phpt b/Zend/tests/constants/constants_004.phpt
similarity index 100%
rename from Zend/tests/constants_004.phpt
rename to Zend/tests/constants/constants_004.phpt
diff --git a/Zend/tests/constants_005.phpt b/Zend/tests/constants/constants_005.phpt
similarity index 100%
rename from Zend/tests/constants_005.phpt
rename to Zend/tests/constants/constants_005.phpt
diff --git a/Zend/tests/constants_006.phpt b/Zend/tests/constants/constants_006.phpt
similarity index 100%
rename from Zend/tests/constants_006.phpt
rename to Zend/tests/constants/constants_006.phpt
diff --git a/Zend/tests/constants_007.phpt b/Zend/tests/constants/constants_007.phpt
similarity index 100%
rename from Zend/tests/constants_007.phpt
rename to Zend/tests/constants/constants_007.phpt
diff --git a/Zend/tests/constants_008.phpt b/Zend/tests/constants/constants_008.phpt
similarity index 100%
rename from Zend/tests/constants_008.phpt
rename to Zend/tests/constants/constants_008.phpt
diff --git a/Zend/tests/constants_009.phpt b/Zend/tests/constants/constants_009.phpt
similarity index 100%
rename from Zend/tests/constants_009.phpt
rename to Zend/tests/constants/constants_009.phpt
diff --git a/Zend/tests/defined_fn_no_ns_fallback.phpt b/Zend/tests/constants/defined_fn_no_ns_fallback.phpt
similarity index 100%
rename from Zend/tests/defined_fn_no_ns_fallback.phpt
rename to Zend/tests/constants/defined_fn_no_ns_fallback.phpt
diff --git a/Zend/tests/dynamic_class_const_fetch.phpt b/Zend/tests/constants/dynamic_class_const_fetch.phpt
similarity index 100%
rename from Zend/tests/dynamic_class_const_fetch.phpt
rename to Zend/tests/constants/dynamic_class_const_fetch.phpt
diff --git a/Zend/tests/dynamic_class_const_fetch_cache_slot.phpt b/Zend/tests/constants/dynamic_class_const_fetch_cache_slot.phpt
similarity index 100%
rename from Zend/tests/dynamic_class_const_fetch_cache_slot.phpt
rename to Zend/tests/constants/dynamic_class_const_fetch_cache_slot.phpt
diff --git a/Zend/tests/dynamic_class_const_fetch_const_expr.phpt b/Zend/tests/constants/dynamic_class_const_fetch_const_expr.phpt
similarity index 100%
rename from Zend/tests/dynamic_class_const_fetch_const_expr.phpt
rename to Zend/tests/constants/dynamic_class_const_fetch_const_expr.phpt
diff --git a/Zend/tests/dynamic_class_const_fetch_order.phpt b/Zend/tests/constants/dynamic_class_const_fetch_order.phpt
similarity index 100%
rename from Zend/tests/dynamic_class_const_fetch_order.phpt
rename to Zend/tests/constants/dynamic_class_const_fetch_order.phpt
diff --git a/Zend/tests/gh10709.phpt b/Zend/tests/constants/gh10709.phpt
similarity index 100%
rename from Zend/tests/gh10709.phpt
rename to Zend/tests/constants/gh10709.phpt
diff --git a/Zend/tests/gh10709_2.phpt b/Zend/tests/constants/gh10709_2.phpt
similarity index 100%
rename from Zend/tests/gh10709_2.phpt
rename to Zend/tests/constants/gh10709_2.phpt
diff --git a/Zend/tests/gh10709_3.phpt b/Zend/tests/constants/gh10709_3.phpt
similarity index 100%
rename from Zend/tests/gh10709_3.phpt
rename to Zend/tests/constants/gh10709_3.phpt
diff --git a/Zend/tests/gh17222.phpt b/Zend/tests/constants/gh17222.phpt
similarity index 100%
rename from Zend/tests/gh17222.phpt
rename to Zend/tests/constants/gh17222.phpt
diff --git a/Zend/tests/bug48930.phpt b/Zend/tests/constants/halt_compiler/bug48930.phpt
similarity index 100%
rename from Zend/tests/bug48930.phpt
rename to Zend/tests/constants/halt_compiler/bug48930.phpt
diff --git a/Zend/tests/bug53305.phpt b/Zend/tests/constants/halt_compiler/bug53305.phpt
similarity index 100%
rename from Zend/tests/bug53305.phpt
rename to Zend/tests/constants/halt_compiler/bug53305.phpt
diff --git a/Zend/tests/bug54804.inc b/Zend/tests/constants/halt_compiler/bug54804.inc
similarity index 100%
rename from Zend/tests/bug54804.inc
rename to Zend/tests/constants/halt_compiler/bug54804.inc
diff --git a/Zend/tests/bug54804.phpt b/Zend/tests/constants/halt_compiler/bug54804.phpt
similarity index 100%
rename from Zend/tests/bug54804.phpt
rename to Zend/tests/constants/halt_compiler/bug54804.phpt
diff --git a/Zend/tests/bug70164.phpt b/Zend/tests/constants/halt_compiler/bug70164.phpt
similarity index 100%
rename from Zend/tests/bug70164.phpt
rename to Zend/tests/constants/halt_compiler/bug70164.phpt
diff --git a/Zend/tests/bug77738.phpt b/Zend/tests/constants/halt_compiler/bug77738.phpt
similarity index 100%
rename from Zend/tests/bug77738.phpt
rename to Zend/tests/constants/halt_compiler/bug77738.phpt
diff --git a/Zend/tests/halt01.phpt b/Zend/tests/constants/halt_compiler/halt01.phpt
similarity index 100%
rename from Zend/tests/halt01.phpt
rename to Zend/tests/constants/halt_compiler/halt01.phpt
diff --git a/Zend/tests/halt02.phpt b/Zend/tests/constants/halt_compiler/halt02.phpt
similarity index 100%
rename from Zend/tests/halt02.phpt
rename to Zend/tests/constants/halt_compiler/halt02.phpt
diff --git a/Zend/tests/halt03.phpt b/Zend/tests/constants/halt_compiler/halt03.phpt
similarity index 100%
rename from Zend/tests/halt03.phpt
rename to Zend/tests/constants/halt_compiler/halt03.phpt
diff --git a/Zend/tests/halt_compiler1.phpt b/Zend/tests/constants/halt_compiler/halt_compiler1.phpt
similarity index 100%
rename from Zend/tests/halt_compiler1.phpt
rename to Zend/tests/constants/halt_compiler/halt_compiler1.phpt
diff --git a/Zend/tests/halt_compiler2.phpt b/Zend/tests/constants/halt_compiler/halt_compiler2.phpt
similarity index 100%
rename from Zend/tests/halt_compiler2.phpt
rename to Zend/tests/constants/halt_compiler/halt_compiler2.phpt
diff --git a/Zend/tests/halt_compiler3.phpt b/Zend/tests/constants/halt_compiler/halt_compiler3.phpt
similarity index 100%
rename from Zend/tests/halt_compiler3.phpt
rename to Zend/tests/constants/halt_compiler/halt_compiler3.phpt
diff --git a/Zend/tests/halt_compiler4.phpt b/Zend/tests/constants/halt_compiler/halt_compiler4.phpt
similarity index 100%
rename from Zend/tests/halt_compiler4.phpt
rename to Zend/tests/constants/halt_compiler/halt_compiler4.phpt
diff --git a/Zend/tests/halt_compiler5.phpt b/Zend/tests/constants/halt_compiler/halt_compiler5.phpt
similarity index 100%
rename from Zend/tests/halt_compiler5.phpt
rename to Zend/tests/constants/halt_compiler/halt_compiler5.phpt
diff --git a/Zend/tests/iface_constant_visibility_variance.phpt b/Zend/tests/constants/iface_constant_visibility_variance.phpt
similarity index 100%
rename from Zend/tests/iface_constant_visibility_variance.phpt
rename to Zend/tests/constants/iface_constant_visibility_variance.phpt
diff --git a/Zend/tests/line_const_in_array.phpt b/Zend/tests/constants/line_const_in_array.phpt
similarity index 100%
rename from Zend/tests/line_const_in_array.phpt
rename to Zend/tests/constants/line_const_in_array.phpt
diff --git a/Zend/tests/magic_const_in_global_scope.phpt b/Zend/tests/constants/magic_const_in_global_scope.phpt
similarity index 100%
rename from Zend/tests/magic_const_in_global_scope.phpt
rename to Zend/tests/constants/magic_const_in_global_scope.phpt
diff --git a/Zend/tests/oss_fuzz_57821.phpt b/Zend/tests/constants/oss_fuzz_57821.phpt
similarity index 100%
rename from Zend/tests/oss_fuzz_57821.phpt
rename to Zend/tests/constants/oss_fuzz_57821.phpt
diff --git a/Zend/tests/selfParent_001.phpt b/Zend/tests/constants/selfParent_001.phpt
similarity index 100%
rename from Zend/tests/selfParent_001.phpt
rename to Zend/tests/constants/selfParent_001.phpt
diff --git a/Zend/tests/selfParent_002.phpt b/Zend/tests/constants/selfParent_002.phpt
similarity index 100%
rename from Zend/tests/selfParent_002.phpt
rename to Zend/tests/constants/selfParent_002.phpt
diff --git a/Zend/tests/class_on_expression_in_constant_expression.phpt b/Zend/tests/constexpr/class_on_expression_in_constant_expression.phpt
similarity index 100%
rename from Zend/tests/class_on_expression_in_constant_expression.phpt
rename to Zend/tests/constexpr/class_on_expression_in_constant_expression.phpt
diff --git a/Zend/tests/const_expr_dim_on_null_warning.phpt b/Zend/tests/constexpr/const_expr_dim_on_null_warning.phpt
similarity index 100%
rename from Zend/tests/const_expr_dim_on_null_warning.phpt
rename to Zend/tests/constexpr/const_expr_dim_on_null_warning.phpt
diff --git a/Zend/tests/constant_expressions.phpt b/Zend/tests/constexpr/constant_expressions.phpt
similarity index 100%
rename from Zend/tests/constant_expressions.phpt
rename to Zend/tests/constexpr/constant_expressions.phpt
diff --git a/Zend/tests/constant_expressions_arrays.phpt b/Zend/tests/constexpr/constant_expressions_arrays.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_arrays.phpt
rename to Zend/tests/constexpr/constant_expressions_arrays.phpt
diff --git a/Zend/tests/constexpr/constant_expressions_cast.phpt b/Zend/tests/constexpr/constant_expressions_cast.phpt
new file mode 100644
index 0000000000000..0fd7c48260c18
--- /dev/null
+++ b/Zend/tests/constexpr/constant_expressions_cast.phpt
@@ -0,0 +1,129 @@
+--TEST--
+Constant expressions with cast
+--FILE--
+ 1];
+const T5 = (float) 5;
+const T6 = (array) "";
+const T7 = (array) var_dump(...);
+const T8 = (array) new X;
+const T9 = (array) new DateTime;
+const T10 = (int) new DateTime;
+
+var_dump(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
+
+const C_FLOAT = 0.3;
+const C_EMPTY_ARRAY = [];
+const C_ARRAY = ["a" => 1];
+const C_INT = 5;
+const C_EMPTY_STRING = "";
+const C_CALLABLE = var_dump(...);
+const C_USER_OBJECT = new X;
+const C_DATE_TIME = new DateTime;
+
+const T11 = (int) C_FLOAT;
+const T12 = (bool) C_FLOAT;
+const T13 = (string) C_EMPTY_ARRAY;
+const T14 = (object) C_ARRAY;
+const T15 = (float) C_INT;
+const T16 = (array) C_EMPTY_STRING;
+const T17 = (array) C_CALLABLE;
+const T18 = (array) C_USER_OBJECT;
+const T19 = (array) C_DATE_TIME;
+const T20 = (int) C_DATE_TIME;
+
+var_dump(T11, T12, T13, T14, T15, T16, T17, T18, T19, T20);
+?>
+--EXPECTF--
+Warning: Array to string conversion in %s on line %d
+
+Warning: Object of class DateTime could not be converted to int in %s on line %d
+int(0)
+bool(true)
+string(5) "Array"
+object(stdClass)#%d (1) {
+ ["a"]=>
+ int(1)
+}
+float(5)
+array(1) {
+ [0]=>
+ string(0) ""
+}
+array(1) {
+ [0]=>
+ object(Closure)#%d (2) {
+ ["function"]=>
+ string(8) "var_dump"
+ ["parameter"]=>
+ array(2) {
+ ["$value"]=>
+ string(10) ""
+ ["$values"]=>
+ string(10) ""
+ }
+ }
+}
+array(1) {
+ ["foo"]=>
+ int(3)
+}
+array(3) {
+ ["date"]=>
+ string(%d) "%s"
+ ["timezone_type"]=>
+ int(%d)
+ ["timezone"]=>
+ string(%d) "%s"
+}
+int(1)
+
+Warning: Array to string conversion in %s on line %d
+
+Warning: Object of class DateTime could not be converted to int in %s on line %d
+int(0)
+bool(true)
+string(5) "Array"
+object(stdClass)#%d (1) {
+ ["a"]=>
+ int(1)
+}
+float(5)
+array(1) {
+ [0]=>
+ string(0) ""
+}
+array(1) {
+ [0]=>
+ object(Closure)#%d (2) {
+ ["function"]=>
+ string(8) "var_dump"
+ ["parameter"]=>
+ array(2) {
+ ["$value"]=>
+ string(10) ""
+ ["$values"]=>
+ string(10) ""
+ }
+ }
+}
+array(1) {
+ ["foo"]=>
+ int(3)
+}
+array(3) {
+ ["date"]=>
+ string(%d) "%s"
+ ["timezone_type"]=>
+ int(%d)
+ ["timezone"]=>
+ string(%d) "%s"
+}
+int(1)
diff --git a/Zend/tests/constexpr/constant_expressions_cast_object_property.phpt b/Zend/tests/constexpr/constant_expressions_cast_object_property.phpt
new file mode 100644
index 0000000000000..63616f0f8bf3b
--- /dev/null
+++ b/Zend/tests/constexpr/constant_expressions_cast_object_property.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Constant expressions with object cast in property
+--FILE--
+
+--EXPECTF--
+Fatal error: Object casts are not supported in this context in %s on line %d
diff --git a/Zend/tests/constant_expressions_classes.phpt b/Zend/tests/constexpr/constant_expressions_classes.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_classes.phpt
rename to Zend/tests/constexpr/constant_expressions_classes.phpt
diff --git a/Zend/tests/constant_expressions_coalesce.phpt b/Zend/tests/constexpr/constant_expressions_coalesce.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_coalesce.phpt
rename to Zend/tests/constexpr/constant_expressions_coalesce.phpt
diff --git a/Zend/tests/constant_expressions_coalesce_empty_dim.phpt b/Zend/tests/constexpr/constant_expressions_coalesce_empty_dim.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_coalesce_empty_dim.phpt
rename to Zend/tests/constexpr/constant_expressions_coalesce_empty_dim.phpt
diff --git a/Zend/tests/constant_expressions_dynamic.phpt b/Zend/tests/constexpr/constant_expressions_dynamic.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_dynamic.phpt
rename to Zend/tests/constexpr/constant_expressions_dynamic.phpt
diff --git a/Zend/tests/constant_expressions_dynamic_class_name_error.phpt b/Zend/tests/constexpr/constant_expressions_dynamic_class_name_error.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_dynamic_class_name_error.phpt
rename to Zend/tests/constexpr/constant_expressions_dynamic_class_name_error.phpt
diff --git a/Zend/tests/constant_expressions_exceptions.inc b/Zend/tests/constexpr/constant_expressions_exceptions.inc
similarity index 100%
rename from Zend/tests/constant_expressions_exceptions.inc
rename to Zend/tests/constexpr/constant_expressions_exceptions.inc
diff --git a/Zend/tests/constant_expressions_exceptions_001.phpt b/Zend/tests/constexpr/constant_expressions_exceptions_001.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_exceptions_001.phpt
rename to Zend/tests/constexpr/constant_expressions_exceptions_001.phpt
diff --git a/Zend/tests/constant_expressions_exceptions_002.phpt b/Zend/tests/constexpr/constant_expressions_exceptions_002.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_exceptions_002.phpt
rename to Zend/tests/constexpr/constant_expressions_exceptions_002.phpt
diff --git a/Zend/tests/constant_expressions_invalid_offset_type_error.phpt b/Zend/tests/constexpr/constant_expressions_invalid_offset_type_error.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_invalid_offset_type_error.phpt
rename to Zend/tests/constexpr/constant_expressions_invalid_offset_type_error.phpt
diff --git a/Zend/tests/constant_expressions_self_referencing_array.phpt b/Zend/tests/constexpr/constant_expressions_self_referencing_array.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_self_referencing_array.phpt
rename to Zend/tests/constexpr/constant_expressions_self_referencing_array.phpt
diff --git a/Zend/tests/constant_expressions_static_class_name_error.phpt b/Zend/tests/constexpr/constant_expressions_static_class_name_error.phpt
similarity index 100%
rename from Zend/tests/constant_expressions_static_class_name_error.phpt
rename to Zend/tests/constexpr/constant_expressions_static_class_name_error.phpt
diff --git a/Zend/tests/gh10014.phpt b/Zend/tests/constexpr/gh10014.phpt
similarity index 100%
rename from Zend/tests/gh10014.phpt
rename to Zend/tests/constexpr/gh10014.phpt
diff --git a/Zend/tests/gh10356.phpt b/Zend/tests/constexpr/gh10356.phpt
similarity index 100%
rename from Zend/tests/gh10356.phpt
rename to Zend/tests/constexpr/gh10356.phpt
diff --git a/Zend/tests/gh7771_1.phpt b/Zend/tests/constexpr/gh7771_1.phpt
similarity index 100%
rename from Zend/tests/gh7771_1.phpt
rename to Zend/tests/constexpr/gh7771_1.phpt
diff --git a/Zend/tests/gh7771_1_definition.inc b/Zend/tests/constexpr/gh7771_1_definition.inc
similarity index 100%
rename from Zend/tests/gh7771_1_definition.inc
rename to Zend/tests/constexpr/gh7771_1_definition.inc
diff --git a/Zend/tests/gh7771_2.phpt b/Zend/tests/constexpr/gh7771_2.phpt
similarity index 100%
rename from Zend/tests/gh7771_2.phpt
rename to Zend/tests/constexpr/gh7771_2.phpt
diff --git a/Zend/tests/gh7771_2_definition.inc b/Zend/tests/constexpr/gh7771_2_definition.inc
similarity index 100%
rename from Zend/tests/gh7771_2_definition.inc
rename to Zend/tests/constexpr/gh7771_2_definition.inc
diff --git a/Zend/tests/gh7771_3.phpt b/Zend/tests/constexpr/gh7771_3.phpt
similarity index 100%
rename from Zend/tests/gh7771_3.phpt
rename to Zend/tests/constexpr/gh7771_3.phpt
diff --git a/Zend/tests/gh9136.phpt b/Zend/tests/constexpr/gh9136.phpt
similarity index 100%
rename from Zend/tests/gh9136.phpt
rename to Zend/tests/constexpr/gh9136.phpt
diff --git a/Zend/tests/gh9136_2.phpt b/Zend/tests/constexpr/gh9136_2.phpt
similarity index 100%
rename from Zend/tests/gh9136_2.phpt
rename to Zend/tests/constexpr/gh9136_2.phpt
diff --git a/Zend/tests/gh9138.phpt b/Zend/tests/constexpr/gh9138.phpt
similarity index 100%
rename from Zend/tests/gh9138.phpt
rename to Zend/tests/constexpr/gh9138.phpt
diff --git a/Zend/tests/gh9138_2.phpt b/Zend/tests/constexpr/gh9138_2.phpt
similarity index 100%
rename from Zend/tests/gh9138_2.phpt
rename to Zend/tests/constexpr/gh9138_2.phpt
diff --git a/Zend/tests/constexpr/new_dynamic_class_name.phpt b/Zend/tests/constexpr/new_dynamic_class_name.phpt
index 60ae3ea4195d7..bd7aa07bd1e75 100644
--- a/Zend/tests/constexpr/new_dynamic_class_name.phpt
+++ b/Zend/tests/constexpr/new_dynamic_class_name.phpt
@@ -1,5 +1,5 @@
--TEST--
-Dynamic class name in new is not supported
+Dynamic class name in new is supported
--FILE--
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s%eZend%etests%edebug_info-error-0.0.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-0.phpt b/Zend/tests/debug_info/debug_info-error-0.phpt
similarity index 73%
rename from Zend/tests/debug_info-error-0.phpt
rename to Zend/tests/debug_info/debug_info-error-0.phpt
index 7dbac320a1754..37289c6468fff 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-0.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-1.0.phpt b/Zend/tests/debug_info/debug_info-error-1.0.phpt
similarity index 73%
rename from Zend/tests/debug_info-error-1.0.phpt
rename to Zend/tests/debug_info/debug_info-error-1.0.phpt
index 0213e9daad250..9b168621b5efe 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-1.0.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-1.phpt b/Zend/tests/debug_info/debug_info-error-1.phpt
similarity index 73%
rename from Zend/tests/debug_info-error-1.phpt
rename to Zend/tests/debug_info/debug_info-error-1.phpt
index a8e4644b49083..ae01a6055c004 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-1.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-empty_str.phpt b/Zend/tests/debug_info/debug_info-error-empty_str.phpt
similarity index 72%
rename from Zend/tests/debug_info-error-empty_str.phpt
rename to Zend/tests/debug_info/debug_info-error-empty_str.phpt
index 39f227cb96750..bbab78cd82021 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-empty_str.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-false.phpt b/Zend/tests/debug_info/debug_info-error-false.phpt
similarity index 72%
rename from Zend/tests/debug_info-error-false.phpt
rename to Zend/tests/debug_info/debug_info-error-false.phpt
index bf18ed4d038d8..3e48372c420c2 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-false.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-object.phpt b/Zend/tests/debug_info/debug_info-error-object.phpt
similarity index 73%
rename from Zend/tests/debug_info-error-object.phpt
rename to Zend/tests/debug_info/debug_info-error-object.phpt
index e94c2dfb36ccb..42e073999908c 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-object.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-resource.phpt b/Zend/tests/debug_info/debug_info-error-resource.phpt
similarity index 75%
rename from Zend/tests/debug_info-error-resource.phpt
rename to Zend/tests/debug_info/debug_info-error-resource.phpt
index 285ed72e33072..ccacce7a74b45 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-resource.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-str.phpt b/Zend/tests/debug_info/debug_info-error-str.phpt
similarity index 73%
rename from Zend/tests/debug_info-error-str.phpt
rename to Zend/tests/debug_info/debug_info-error-str.phpt
index daf0ad3b588ea..85d3f63b97e05 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-str.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info-error-true.phpt b/Zend/tests/debug_info/debug_info-error-true.phpt
similarity index 72%
rename from Zend/tests/debug_info-error-true.phpt
rename to Zend/tests/debug_info/debug_info-error-true.phpt
index 41a68c5c3965f..3843c6a7a14a5 100644
--- a/Zend/tests/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%eZend%etests%edebug_info-error-true.php on line %d
+Fatal error: __debuginfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info.phpt b/Zend/tests/debug_info/debug_info.phpt
similarity index 100%
rename from Zend/tests/debug_info.phpt
rename to Zend/tests/debug_info/debug_info.phpt
diff --git a/Zend/tests/recursive_debug_info.phpt b/Zend/tests/debug_info/recursive_debug_info.phpt
similarity index 100%
rename from Zend/tests/recursive_debug_info.phpt
rename to Zend/tests/debug_info/recursive_debug_info.phpt
diff --git a/Zend/tests/bug43027.phpt b/Zend/tests/declare/bug43027.phpt
similarity index 100%
rename from Zend/tests/bug43027.phpt
rename to Zend/tests/declare/bug43027.phpt
diff --git a/Zend/tests/bug69092.2.phpt b/Zend/tests/declare/bug69092.2.phpt
similarity index 100%
rename from Zend/tests/bug69092.2.phpt
rename to Zend/tests/declare/bug69092.2.phpt
diff --git a/Zend/tests/bug69092.phpt b/Zend/tests/declare/bug69092.phpt
similarity index 100%
rename from Zend/tests/bug69092.phpt
rename to Zend/tests/declare/bug69092.phpt
diff --git a/Zend/tests/declare_001.phpt b/Zend/tests/declare/declare_001.phpt
similarity index 100%
rename from Zend/tests/declare_001.phpt
rename to Zend/tests/declare/declare_001.phpt
diff --git a/Zend/tests/declare_002.phpt b/Zend/tests/declare/declare_002.phpt
similarity index 100%
rename from Zend/tests/declare_002.phpt
rename to Zend/tests/declare/declare_002.phpt
diff --git a/Zend/tests/declare_003.phpt b/Zend/tests/declare/declare_003.phpt
similarity index 100%
rename from Zend/tests/declare_003.phpt
rename to Zend/tests/declare/declare_003.phpt
diff --git a/Zend/tests/declare_004.phpt b/Zend/tests/declare/declare_004.phpt
similarity index 100%
rename from Zend/tests/declare_004.phpt
rename to Zend/tests/declare/declare_004.phpt
diff --git a/Zend/tests/declare_005.phpt b/Zend/tests/declare/declare_005.phpt
similarity index 100%
rename from Zend/tests/declare_005.phpt
rename to Zend/tests/declare/declare_005.phpt
diff --git a/Zend/tests/declare_006.phpt b/Zend/tests/declare/declare_006.phpt
similarity index 100%
rename from Zend/tests/declare_006.phpt
rename to Zend/tests/declare/declare_006.phpt
diff --git a/Zend/tests/declare_007.phpt b/Zend/tests/declare/declare_007.phpt
similarity index 100%
rename from Zend/tests/declare_007.phpt
rename to Zend/tests/declare/declare_007.phpt
diff --git a/Zend/tests/declare/gh18033_1.phpt b/Zend/tests/declare/gh18033_1.phpt
new file mode 100644
index 0000000000000..ccce9360b4651
--- /dev/null
+++ b/Zend/tests/declare/gh18033_1.phpt
@@ -0,0 +1,24 @@
+--TEST--
+GH-18033 (NULL-ptr dereference when using register_tick_function in destructor)
+--DESCRIPTION--
+Needs --repeat 2 or something similar to reproduce
+--CREDITS--
+clesmian
+--FILE--
+
+--EXPECT--
+Done
+In destructor
diff --git a/Zend/tests/declare/gh18033_2.phpt b/Zend/tests/declare/gh18033_2.phpt
new file mode 100644
index 0000000000000..8fdcff1b51e6c
--- /dev/null
+++ b/Zend/tests/declare/gh18033_2.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-18033 (NULL-ptr dereference when using register_tick_function in ob_start)
+--DESCRIPTION--
+Needs --repeat 2 or something similar to reproduce
+--CREDITS--
+clesmian
+--FILE--
+
+--EXPECT--
diff --git a/Zend/tests/dereference_001.phpt b/Zend/tests/dereference/dereference_001.phpt
similarity index 100%
rename from Zend/tests/dereference_001.phpt
rename to Zend/tests/dereference/dereference_001.phpt
diff --git a/Zend/tests/dereference_002.phpt b/Zend/tests/dereference/dereference_002.phpt
similarity index 100%
rename from Zend/tests/dereference_002.phpt
rename to Zend/tests/dereference/dereference_002.phpt
diff --git a/Zend/tests/dereference_003.phpt b/Zend/tests/dereference/dereference_003.phpt
similarity index 100%
rename from Zend/tests/dereference_003.phpt
rename to Zend/tests/dereference/dereference_003.phpt
diff --git a/Zend/tests/dereference_004.phpt b/Zend/tests/dereference/dereference_004.phpt
similarity index 100%
rename from Zend/tests/dereference_004.phpt
rename to Zend/tests/dereference/dereference_004.phpt
diff --git a/Zend/tests/dereference_005.phpt b/Zend/tests/dereference/dereference_005.phpt
similarity index 100%
rename from Zend/tests/dereference_005.phpt
rename to Zend/tests/dereference/dereference_005.phpt
diff --git a/Zend/tests/dereference_006.phpt b/Zend/tests/dereference/dereference_006.phpt
similarity index 100%
rename from Zend/tests/dereference_006.phpt
rename to Zend/tests/dereference/dereference_006.phpt
diff --git a/Zend/tests/dereference_007.phpt b/Zend/tests/dereference/dereference_007.phpt
similarity index 100%
rename from Zend/tests/dereference_007.phpt
rename to Zend/tests/dereference/dereference_007.phpt
diff --git a/Zend/tests/dereference_008.phpt b/Zend/tests/dereference/dereference_008.phpt
similarity index 100%
rename from Zend/tests/dereference_008.phpt
rename to Zend/tests/dereference/dereference_008.phpt
diff --git a/Zend/tests/dereference_009.phpt b/Zend/tests/dereference/dereference_009.phpt
similarity index 100%
rename from Zend/tests/dereference_009.phpt
rename to Zend/tests/dereference/dereference_009.phpt
diff --git a/Zend/tests/dereference_010.phpt b/Zend/tests/dereference/dereference_010.phpt
similarity index 100%
rename from Zend/tests/dereference_010.phpt
rename to Zend/tests/dereference/dereference_010.phpt
diff --git a/Zend/tests/dereference_011.phpt b/Zend/tests/dereference/dereference_011.phpt
similarity index 100%
rename from Zend/tests/dereference_011.phpt
rename to Zend/tests/dereference/dereference_011.phpt
diff --git a/Zend/tests/dereference_012.phpt b/Zend/tests/dereference/dereference_012.phpt
similarity index 100%
rename from Zend/tests/dereference_012.phpt
rename to Zend/tests/dereference/dereference_012.phpt
diff --git a/Zend/tests/dereference_013.phpt b/Zend/tests/dereference/dereference_013.phpt
similarity index 100%
rename from Zend/tests/dereference_013.phpt
rename to Zend/tests/dereference/dereference_013.phpt
diff --git a/Zend/tests/dereference_014.phpt b/Zend/tests/dereference/dereference_014.phpt
similarity index 100%
rename from Zend/tests/dereference_014.phpt
rename to Zend/tests/dereference/dereference_014.phpt
diff --git a/Zend/tests/bug46246.phpt b/Zend/tests/dynamic_call/bug46246.phpt
similarity index 100%
rename from Zend/tests/bug46246.phpt
rename to Zend/tests/dynamic_call/bug46246.phpt
diff --git a/Zend/tests/bug47880.phpt b/Zend/tests/dynamic_call/bug47880.phpt
similarity index 100%
rename from Zend/tests/bug47880.phpt
rename to Zend/tests/dynamic_call/bug47880.phpt
diff --git a/Zend/tests/bug48770.phpt b/Zend/tests/dynamic_call/bug48770.phpt
similarity index 100%
rename from Zend/tests/bug48770.phpt
rename to Zend/tests/dynamic_call/bug48770.phpt
diff --git a/Zend/tests/bug48770_2.phpt b/Zend/tests/dynamic_call/bug48770_2.phpt
similarity index 100%
rename from Zend/tests/bug48770_2.phpt
rename to Zend/tests/dynamic_call/bug48770_2.phpt
diff --git a/Zend/tests/bug48770_3.phpt b/Zend/tests/dynamic_call/bug48770_3.phpt
similarity index 100%
rename from Zend/tests/bug48770_3.phpt
rename to Zend/tests/dynamic_call/bug48770_3.phpt
diff --git a/Zend/tests/bug52940.phpt b/Zend/tests/dynamic_call/bug52940.phpt
similarity index 100%
rename from Zend/tests/bug52940.phpt
rename to Zend/tests/dynamic_call/bug52940.phpt
diff --git a/Zend/tests/bug54910.phpt b/Zend/tests/dynamic_call/bug54910.phpt
similarity index 100%
rename from Zend/tests/bug54910.phpt
rename to Zend/tests/dynamic_call/bug54910.phpt
diff --git a/Zend/tests/bug61273.phpt b/Zend/tests/dynamic_call/bug61273.phpt
similarity index 100%
rename from Zend/tests/bug61273.phpt
rename to Zend/tests/dynamic_call/bug61273.phpt
diff --git a/Zend/tests/bug63173.phpt b/Zend/tests/dynamic_call/bug63173.phpt
similarity index 100%
rename from Zend/tests/bug63173.phpt
rename to Zend/tests/dynamic_call/bug63173.phpt
diff --git a/Zend/tests/bug68475.phpt b/Zend/tests/dynamic_call/bug68475.phpt
similarity index 100%
rename from Zend/tests/bug68475.phpt
rename to Zend/tests/dynamic_call/bug68475.phpt
diff --git a/Zend/tests/bug69124.phpt b/Zend/tests/dynamic_call/bug69124.phpt
similarity index 100%
rename from Zend/tests/bug69124.phpt
rename to Zend/tests/dynamic_call/bug69124.phpt
diff --git a/Zend/tests/bug69167.phpt b/Zend/tests/dynamic_call/bug69167.phpt
similarity index 100%
rename from Zend/tests/bug69167.phpt
rename to Zend/tests/dynamic_call/bug69167.phpt
diff --git a/Zend/tests/bug77877.phpt b/Zend/tests/dynamic_call/bug77877.phpt
similarity index 100%
rename from Zend/tests/bug77877.phpt
rename to Zend/tests/dynamic_call/bug77877.phpt
diff --git a/Zend/tests/bug78898.phpt b/Zend/tests/dynamic_call/bug78898.phpt
similarity index 100%
rename from Zend/tests/bug78898.phpt
rename to Zend/tests/dynamic_call/bug78898.phpt
diff --git a/Zend/tests/dynamic_call_002.phpt b/Zend/tests/dynamic_call/dynamic_call_002.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_002.phpt
rename to Zend/tests/dynamic_call/dynamic_call_002.phpt
diff --git a/Zend/tests/dynamic_call_003.phpt b/Zend/tests/dynamic_call/dynamic_call_003.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_003.phpt
rename to Zend/tests/dynamic_call/dynamic_call_003.phpt
diff --git a/Zend/tests/dynamic_call_004.phpt b/Zend/tests/dynamic_call/dynamic_call_004.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_004.phpt
rename to Zend/tests/dynamic_call/dynamic_call_004.phpt
diff --git a/Zend/tests/dynamic_call_005.phpt b/Zend/tests/dynamic_call/dynamic_call_005.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_005.phpt
rename to Zend/tests/dynamic_call/dynamic_call_005.phpt
diff --git a/Zend/tests/dynamic_call_006.phpt b/Zend/tests/dynamic_call/dynamic_call_006.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_006.phpt
rename to Zend/tests/dynamic_call/dynamic_call_006.phpt
diff --git a/Zend/tests/dynamic_call_007.phpt b/Zend/tests/dynamic_call/dynamic_call_007.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_007.phpt
rename to Zend/tests/dynamic_call/dynamic_call_007.phpt
diff --git a/Zend/tests/dynamic_call_008.phpt b/Zend/tests/dynamic_call/dynamic_call_008.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_008.phpt
rename to Zend/tests/dynamic_call/dynamic_call_008.phpt
diff --git a/Zend/tests/dynamic_call_freeing.phpt b/Zend/tests/dynamic_call/dynamic_call_freeing.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_freeing.phpt
rename to Zend/tests/dynamic_call/dynamic_call_freeing.phpt
diff --git a/Zend/tests/dynamic_call_non_static.phpt b/Zend/tests/dynamic_call/dynamic_call_non_static.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_non_static.phpt
rename to Zend/tests/dynamic_call/dynamic_call_non_static.phpt
diff --git a/Zend/tests/dynamic_call_to_ref_returning_function.phpt b/Zend/tests/dynamic_call/dynamic_call_to_ref_returning_function.phpt
similarity index 100%
rename from Zend/tests/dynamic_call_to_ref_returning_function.phpt
rename to Zend/tests/dynamic_call/dynamic_call_to_ref_returning_function.phpt
diff --git a/Zend/tests/dynamic_fully_qualified_call.phpt b/Zend/tests/dynamic_call/dynamic_fully_qualified_call.phpt
similarity index 100%
rename from Zend/tests/dynamic_fully_qualified_call.phpt
rename to Zend/tests/dynamic_call/dynamic_fully_qualified_call.phpt
diff --git a/Zend/tests/enum/comparison.phpt b/Zend/tests/enum/comparison.phpt
index 5df2f282ec064..778e9a48ef5e6 100644
--- a/Zend/tests/enum/comparison.phpt
+++ b/Zend/tests/enum/comparison.phpt
@@ -53,5 +53,5 @@ bool(false)
bool(false)
bool(false)
bool(false)
-bool(false)
-bool(false)
+bool(true)
+bool(true)
diff --git a/Zend/tests/enum_in_stack_trace.phpt b/Zend/tests/enum/enum_in_stack_trace.phpt
similarity index 100%
rename from Zend/tests/enum_in_stack_trace.phpt
rename to Zend/tests/enum/enum_in_stack_trace.phpt
diff --git a/Zend/tests/enum/extending-builtin-error.phpt b/Zend/tests/enum/extending-builtin-error.phpt
new file mode 100644
index 0000000000000..a796c65a617fe
--- /dev/null
+++ b/Zend/tests/enum/extending-builtin-error.phpt
@@ -0,0 +1,10 @@
+--TEST--
+GH-16315: Extending built-in enum
+--FILE--
+
+--EXPECTF--
+Fatal error: Class Demo cannot extend enum RoundingMode in %s on line %d
diff --git a/Zend/tests/enum/extending-user-error.phpt b/Zend/tests/enum/extending-user-error.phpt
new file mode 100644
index 0000000000000..c68db491f3515
--- /dev/null
+++ b/Zend/tests/enum/extending-user-error.phpt
@@ -0,0 +1,12 @@
+--TEST--
+GH-16315: Extending userland enum
+--FILE--
+
+--EXPECTF--
+Fatal error: Class Demo cannot extend enum MyEnum in %s on line 5
diff --git a/Zend/tests/enum/final.phpt b/Zend/tests/enum/final.phpt
index 353e1868d2fa8..a373b28ddece0 100644
--- a/Zend/tests/enum/final.phpt
+++ b/Zend/tests/enum/final.phpt
@@ -5,8 +5,9 @@ Enum is final
enum Foo {}
-class Bar extends Foo {}
+$final = new ReflectionClass(Foo::class)->isFinal();
+var_dump($final);
?>
---EXPECTF--
-Fatal error: Class Bar cannot extend final class Foo in %s on line %d
+--EXPECT--
+bool(true)
diff --git a/Zend/tests/gh10346.phpt b/Zend/tests/enum/gh10346.phpt
similarity index 100%
rename from Zend/tests/gh10346.phpt
rename to Zend/tests/enum/gh10346.phpt
diff --git a/Zend/tests/enum/gh16954.phpt b/Zend/tests/enum/gh16954.phpt
new file mode 100644
index 0000000000000..18a650dd73dcc
--- /dev/null
+++ b/Zend/tests/enum/gh16954.phpt
@@ -0,0 +1,50 @@
+--TEST--
+GH-16954: Enum to bool comparison is inconsistent
+--FILE--
+
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/Zend/tests/gh7792_1.phpt b/Zend/tests/enum/gh7792_1.phpt
similarity index 65%
rename from Zend/tests/gh7792_1.phpt
rename to Zend/tests/enum/gh7792_1.phpt
index a342f169d937c..e8c90e20d65ce 100644
--- a/Zend/tests/gh7792_1.phpt
+++ b/Zend/tests/enum/gh7792_1.phpt
@@ -11,4 +11,4 @@ enum B implements A {}
?>
--EXPECTF--
-Fatal error: Enum B must implement 1 abstract private method (A::a) in %s on line %d
+Fatal error: Enum B must implement 1 abstract method (A::a) in %s on line %d
diff --git a/Zend/tests/gh7792_2.phpt b/Zend/tests/enum/gh7792_2.phpt
similarity index 100%
rename from Zend/tests/gh7792_2.phpt
rename to Zend/tests/enum/gh7792_2.phpt
diff --git a/Zend/tests/gh7792_3.phpt b/Zend/tests/enum/gh7792_3.phpt
similarity index 100%
rename from Zend/tests/gh7792_3.phpt
rename to Zend/tests/enum/gh7792_3.phpt
diff --git a/Zend/tests/gh7792_4.phpt b/Zend/tests/enum/gh7792_4.phpt
similarity index 100%
rename from Zend/tests/gh7792_4.phpt
rename to Zend/tests/enum/gh7792_4.phpt
diff --git a/Zend/tests/gh7792_5.phpt b/Zend/tests/enum/gh7792_5.phpt
similarity index 100%
rename from Zend/tests/gh7792_5.phpt
rename to Zend/tests/enum/gh7792_5.phpt
diff --git a/Zend/tests/gh9775_1.phpt b/Zend/tests/enum/gh9775_1.phpt
similarity index 100%
rename from Zend/tests/gh9775_1.phpt
rename to Zend/tests/enum/gh9775_1.phpt
diff --git a/Zend/tests/gh9775_2.phpt b/Zend/tests/enum/gh9775_2.phpt
similarity index 100%
rename from Zend/tests/gh9775_2.phpt
rename to Zend/tests/enum/gh9775_2.phpt
diff --git a/Zend/tests/enum/no-abstract.phpt b/Zend/tests/enum/no-abstract.phpt
new file mode 100644
index 0000000000000..2593d67e44de5
--- /dev/null
+++ b/Zend/tests/enum/no-abstract.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Compiler prevents `abstract` methods on enums classes (GH-16067)
+--FILE--
+
+--EXPECTF--
+Fatal error: Enum method Example::foo() must not be abstract in %s on line 4
diff --git a/Zend/tests/bug30519.phpt b/Zend/tests/errmsg/bug30519.phpt
similarity index 100%
rename from Zend/tests/bug30519.phpt
rename to Zend/tests/errmsg/bug30519.phpt
diff --git a/Zend/tests/bug43344_1.phpt b/Zend/tests/errmsg/bug43344_1.phpt
similarity index 100%
rename from Zend/tests/bug43344_1.phpt
rename to Zend/tests/errmsg/bug43344_1.phpt
diff --git a/Zend/tests/bug43344_10.phpt b/Zend/tests/errmsg/bug43344_10.phpt
similarity index 100%
rename from Zend/tests/bug43344_10.phpt
rename to Zend/tests/errmsg/bug43344_10.phpt
diff --git a/Zend/tests/bug43344_11.phpt b/Zend/tests/errmsg/bug43344_11.phpt
similarity index 100%
rename from Zend/tests/bug43344_11.phpt
rename to Zend/tests/errmsg/bug43344_11.phpt
diff --git a/Zend/tests/bug43344_12.phpt b/Zend/tests/errmsg/bug43344_12.phpt
similarity index 100%
rename from Zend/tests/bug43344_12.phpt
rename to Zend/tests/errmsg/bug43344_12.phpt
diff --git a/Zend/tests/bug43344_13.phpt b/Zend/tests/errmsg/bug43344_13.phpt
similarity index 100%
rename from Zend/tests/bug43344_13.phpt
rename to Zend/tests/errmsg/bug43344_13.phpt
diff --git a/Zend/tests/bug43344_2.phpt b/Zend/tests/errmsg/bug43344_2.phpt
similarity index 100%
rename from Zend/tests/bug43344_2.phpt
rename to Zend/tests/errmsg/bug43344_2.phpt
diff --git a/Zend/tests/bug43344_3.phpt b/Zend/tests/errmsg/bug43344_3.phpt
similarity index 100%
rename from Zend/tests/bug43344_3.phpt
rename to Zend/tests/errmsg/bug43344_3.phpt
diff --git a/Zend/tests/bug43344_4.phpt b/Zend/tests/errmsg/bug43344_4.phpt
similarity index 100%
rename from Zend/tests/bug43344_4.phpt
rename to Zend/tests/errmsg/bug43344_4.phpt
diff --git a/Zend/tests/bug43344_5.phpt b/Zend/tests/errmsg/bug43344_5.phpt
similarity index 100%
rename from Zend/tests/bug43344_5.phpt
rename to Zend/tests/errmsg/bug43344_5.phpt
diff --git a/Zend/tests/bug43344_6.phpt b/Zend/tests/errmsg/bug43344_6.phpt
similarity index 100%
rename from Zend/tests/bug43344_6.phpt
rename to Zend/tests/errmsg/bug43344_6.phpt
diff --git a/Zend/tests/bug43344_7.phpt b/Zend/tests/errmsg/bug43344_7.phpt
similarity index 100%
rename from Zend/tests/bug43344_7.phpt
rename to Zend/tests/errmsg/bug43344_7.phpt
diff --git a/Zend/tests/bug43344_8.phpt b/Zend/tests/errmsg/bug43344_8.phpt
similarity index 100%
rename from Zend/tests/bug43344_8.phpt
rename to Zend/tests/errmsg/bug43344_8.phpt
diff --git a/Zend/tests/bug43344_9.phpt b/Zend/tests/errmsg/bug43344_9.phpt
similarity index 100%
rename from Zend/tests/bug43344_9.phpt
rename to Zend/tests/errmsg/bug43344_9.phpt
diff --git a/Zend/tests/errmsg_001.phpt b/Zend/tests/errmsg/errmsg_001.phpt
similarity index 100%
rename from Zend/tests/errmsg_001.phpt
rename to Zend/tests/errmsg/errmsg_001.phpt
diff --git a/Zend/tests/errmsg_002.phpt b/Zend/tests/errmsg/errmsg_002.phpt
similarity index 100%
rename from Zend/tests/errmsg_002.phpt
rename to Zend/tests/errmsg/errmsg_002.phpt
diff --git a/Zend/tests/errmsg_003.phpt b/Zend/tests/errmsg/errmsg_003.phpt
similarity index 100%
rename from Zend/tests/errmsg_003.phpt
rename to Zend/tests/errmsg/errmsg_003.phpt
diff --git a/Zend/tests/errmsg_004.phpt b/Zend/tests/errmsg/errmsg_004.phpt
similarity index 100%
rename from Zend/tests/errmsg_004.phpt
rename to Zend/tests/errmsg/errmsg_004.phpt
diff --git a/Zend/tests/errmsg_005.phpt b/Zend/tests/errmsg/errmsg_005.phpt
similarity index 100%
rename from Zend/tests/errmsg_005.phpt
rename to Zend/tests/errmsg/errmsg_005.phpt
diff --git a/Zend/tests/errmsg_006.phpt b/Zend/tests/errmsg/errmsg_006.phpt
similarity index 100%
rename from Zend/tests/errmsg_006.phpt
rename to Zend/tests/errmsg/errmsg_006.phpt
diff --git a/Zend/tests/errmsg_007.phpt b/Zend/tests/errmsg/errmsg_007.phpt
similarity index 100%
rename from Zend/tests/errmsg_007.phpt
rename to Zend/tests/errmsg/errmsg_007.phpt
diff --git a/Zend/tests/errmsg_008.phpt b/Zend/tests/errmsg/errmsg_008.phpt
similarity index 100%
rename from Zend/tests/errmsg_008.phpt
rename to Zend/tests/errmsg/errmsg_008.phpt
diff --git a/Zend/tests/errmsg_009.phpt b/Zend/tests/errmsg/errmsg_009.phpt
similarity index 100%
rename from Zend/tests/errmsg_009.phpt
rename to Zend/tests/errmsg/errmsg_009.phpt
diff --git a/Zend/tests/errmsg_010.phpt b/Zend/tests/errmsg/errmsg_010.phpt
similarity index 100%
rename from Zend/tests/errmsg_010.phpt
rename to Zend/tests/errmsg/errmsg_010.phpt
diff --git a/Zend/tests/errmsg_011.phpt b/Zend/tests/errmsg/errmsg_011.phpt
similarity index 100%
rename from Zend/tests/errmsg_011.phpt
rename to Zend/tests/errmsg/errmsg_011.phpt
diff --git a/Zend/tests/errmsg_013.phpt b/Zend/tests/errmsg/errmsg_013.phpt
similarity index 100%
rename from Zend/tests/errmsg_013.phpt
rename to Zend/tests/errmsg/errmsg_013.phpt
diff --git a/Zend/tests/errmsg_015.phpt b/Zend/tests/errmsg/errmsg_015.phpt
similarity index 100%
rename from Zend/tests/errmsg_015.phpt
rename to Zend/tests/errmsg/errmsg_015.phpt
diff --git a/Zend/tests/errmsg_016.phpt b/Zend/tests/errmsg/errmsg_016.phpt
similarity index 100%
rename from Zend/tests/errmsg_016.phpt
rename to Zend/tests/errmsg/errmsg_016.phpt
diff --git a/Zend/tests/errmsg_017.phpt b/Zend/tests/errmsg/errmsg_017.phpt
similarity index 100%
rename from Zend/tests/errmsg_017.phpt
rename to Zend/tests/errmsg/errmsg_017.phpt
diff --git a/Zend/tests/errmsg/errmsg_018.phpt b/Zend/tests/errmsg/errmsg_018.phpt
new file mode 100644
index 0000000000000..e94f99446ce10
--- /dev/null
+++ b/Zend/tests/errmsg/errmsg_018.phpt
@@ -0,0 +1,13 @@
+--TEST--
+errmsg: static abstract function
+--FILE--
+
+--EXPECTF--
+Fatal error: Class test declares abstract method foo() and must therefore be declared abstract in %s on line %d
diff --git a/Zend/tests/errmsg_019.phpt b/Zend/tests/errmsg/errmsg_019.phpt
similarity index 100%
rename from Zend/tests/errmsg_019.phpt
rename to Zend/tests/errmsg/errmsg_019.phpt
diff --git a/Zend/tests/errmsg_020.phpt b/Zend/tests/errmsg/errmsg_020.phpt
similarity index 100%
rename from Zend/tests/errmsg_020.phpt
rename to Zend/tests/errmsg/errmsg_020.phpt
diff --git a/Zend/tests/errmsg_021.phpt b/Zend/tests/errmsg/errmsg_021.phpt
similarity index 100%
rename from Zend/tests/errmsg_021.phpt
rename to Zend/tests/errmsg/errmsg_021.phpt
diff --git a/Zend/tests/errmsg_022.phpt b/Zend/tests/errmsg/errmsg_022.phpt
similarity index 100%
rename from Zend/tests/errmsg_022.phpt
rename to Zend/tests/errmsg/errmsg_022.phpt
diff --git a/Zend/tests/errmsg_023.phpt b/Zend/tests/errmsg/errmsg_023.phpt
similarity index 100%
rename from Zend/tests/errmsg_023.phpt
rename to Zend/tests/errmsg/errmsg_023.phpt
diff --git a/Zend/tests/errmsg_024.phpt b/Zend/tests/errmsg/errmsg_024.phpt
similarity index 100%
rename from Zend/tests/errmsg_024.phpt
rename to Zend/tests/errmsg/errmsg_024.phpt
diff --git a/Zend/tests/errmsg_025.phpt b/Zend/tests/errmsg/errmsg_025.phpt
similarity index 100%
rename from Zend/tests/errmsg_025.phpt
rename to Zend/tests/errmsg/errmsg_025.phpt
diff --git a/Zend/tests/errmsg_026.phpt b/Zend/tests/errmsg/errmsg_026.phpt
similarity index 100%
rename from Zend/tests/errmsg_026.phpt
rename to Zend/tests/errmsg/errmsg_026.phpt
diff --git a/Zend/tests/errmsg_027.phpt b/Zend/tests/errmsg/errmsg_027.phpt
similarity index 100%
rename from Zend/tests/errmsg_027.phpt
rename to Zend/tests/errmsg/errmsg_027.phpt
diff --git a/Zend/tests/errmsg_028.phpt b/Zend/tests/errmsg/errmsg_028.phpt
similarity index 100%
rename from Zend/tests/errmsg_028.phpt
rename to Zend/tests/errmsg/errmsg_028.phpt
diff --git a/Zend/tests/errmsg_029.phpt b/Zend/tests/errmsg/errmsg_029.phpt
similarity index 100%
rename from Zend/tests/errmsg_029.phpt
rename to Zend/tests/errmsg/errmsg_029.phpt
diff --git a/Zend/tests/errmsg_030.phpt b/Zend/tests/errmsg/errmsg_030.phpt
similarity index 100%
rename from Zend/tests/errmsg_030.phpt
rename to Zend/tests/errmsg/errmsg_030.phpt
diff --git a/Zend/tests/errmsg_031.phpt b/Zend/tests/errmsg/errmsg_031.phpt
similarity index 100%
rename from Zend/tests/errmsg_031.phpt
rename to Zend/tests/errmsg/errmsg_031.phpt
diff --git a/Zend/tests/errmsg_032.phpt b/Zend/tests/errmsg/errmsg_032.phpt
similarity index 100%
rename from Zend/tests/errmsg_032.phpt
rename to Zend/tests/errmsg/errmsg_032.phpt
diff --git a/Zend/tests/errmsg_033.phpt b/Zend/tests/errmsg/errmsg_033.phpt
similarity index 100%
rename from Zend/tests/errmsg_033.phpt
rename to Zend/tests/errmsg/errmsg_033.phpt
diff --git a/Zend/tests/errmsg_034.phpt b/Zend/tests/errmsg/errmsg_034.phpt
similarity index 100%
rename from Zend/tests/errmsg_034.phpt
rename to Zend/tests/errmsg/errmsg_034.phpt
diff --git a/Zend/tests/errmsg_035.phpt b/Zend/tests/errmsg/errmsg_035.phpt
similarity index 100%
rename from Zend/tests/errmsg_035.phpt
rename to Zend/tests/errmsg/errmsg_035.phpt
diff --git a/Zend/tests/errmsg_036.phpt b/Zend/tests/errmsg/errmsg_036.phpt
similarity index 100%
rename from Zend/tests/errmsg_036.phpt
rename to Zend/tests/errmsg/errmsg_036.phpt
diff --git a/Zend/tests/errmsg_037.phpt b/Zend/tests/errmsg/errmsg_037.phpt
similarity index 100%
rename from Zend/tests/errmsg_037.phpt
rename to Zend/tests/errmsg/errmsg_037.phpt
diff --git a/Zend/tests/errmsg_039.phpt b/Zend/tests/errmsg/errmsg_039.phpt
similarity index 100%
rename from Zend/tests/errmsg_039.phpt
rename to Zend/tests/errmsg/errmsg_039.phpt
diff --git a/Zend/tests/errmsg_040.phpt b/Zend/tests/errmsg/errmsg_040.phpt
similarity index 100%
rename from Zend/tests/errmsg_040.phpt
rename to Zend/tests/errmsg/errmsg_040.phpt
diff --git a/Zend/tests/errmsg_042.phpt b/Zend/tests/errmsg/errmsg_042.phpt
similarity index 100%
rename from Zend/tests/errmsg_042.phpt
rename to Zend/tests/errmsg/errmsg_042.phpt
diff --git a/Zend/tests/errmsg_044.phpt b/Zend/tests/errmsg/errmsg_044.phpt
similarity index 100%
rename from Zend/tests/errmsg_044.phpt
rename to Zend/tests/errmsg/errmsg_044.phpt
diff --git a/Zend/tests/errmsg_045.phpt b/Zend/tests/errmsg/errmsg_045.phpt
similarity index 100%
rename from Zend/tests/errmsg_045.phpt
rename to Zend/tests/errmsg/errmsg_045.phpt
diff --git a/Zend/tests/errmsg_018.phpt b/Zend/tests/errmsg_018.phpt
deleted file mode 100644
index 13a0cf451114a..0000000000000
--- a/Zend/tests/errmsg_018.phpt
+++ /dev/null
@@ -1,13 +0,0 @@
---TEST--
-errmsg: static abstract function
---FILE--
-
---EXPECTF--
-Fatal error: Class test contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (test::foo) in %s on line %d
diff --git a/Zend/tests/bug27731.phpt b/Zend/tests/error_reporting/bug27731.phpt
similarity index 100%
rename from Zend/tests/bug27731.phpt
rename to Zend/tests/error_reporting/bug27731.phpt
diff --git a/Zend/tests/bug33771.phpt b/Zend/tests/error_reporting/bug33771.phpt
similarity index 100%
rename from Zend/tests/bug33771.phpt
rename to Zend/tests/error_reporting/bug33771.phpt
diff --git a/Zend/tests/bug72162.phpt b/Zend/tests/error_reporting/bug72162.phpt
similarity index 100%
rename from Zend/tests/bug72162.phpt
rename to Zend/tests/error_reporting/bug72162.phpt
diff --git a/Zend/tests/bug81652.phpt b/Zend/tests/error_reporting/bug81652.phpt
similarity index 100%
rename from Zend/tests/bug81652.phpt
rename to Zend/tests/error_reporting/bug81652.phpt
diff --git a/Zend/tests/error_reporting01.phpt b/Zend/tests/error_reporting/error_reporting01.phpt
similarity index 100%
rename from Zend/tests/error_reporting01.phpt
rename to Zend/tests/error_reporting/error_reporting01.phpt
diff --git a/Zend/tests/error_reporting02.phpt b/Zend/tests/error_reporting/error_reporting02.phpt
similarity index 100%
rename from Zend/tests/error_reporting02.phpt
rename to Zend/tests/error_reporting/error_reporting02.phpt
diff --git a/Zend/tests/error_reporting03.phpt b/Zend/tests/error_reporting/error_reporting03.phpt
similarity index 100%
rename from Zend/tests/error_reporting03.phpt
rename to Zend/tests/error_reporting/error_reporting03.phpt
diff --git a/Zend/tests/error_reporting04.phpt b/Zend/tests/error_reporting/error_reporting04.phpt
similarity index 100%
rename from Zend/tests/error_reporting04.phpt
rename to Zend/tests/error_reporting/error_reporting04.phpt
diff --git a/Zend/tests/error_reporting05.phpt b/Zend/tests/error_reporting/error_reporting05.phpt
similarity index 100%
rename from Zend/tests/error_reporting05.phpt
rename to Zend/tests/error_reporting/error_reporting05.phpt
diff --git a/Zend/tests/error_reporting06.phpt b/Zend/tests/error_reporting/error_reporting06.phpt
similarity index 100%
rename from Zend/tests/error_reporting06.phpt
rename to Zend/tests/error_reporting/error_reporting06.phpt
diff --git a/Zend/tests/error_reporting07.phpt b/Zend/tests/error_reporting/error_reporting07.phpt
similarity index 100%
rename from Zend/tests/error_reporting07.phpt
rename to Zend/tests/error_reporting/error_reporting07.phpt
diff --git a/Zend/tests/error_reporting08.phpt b/Zend/tests/error_reporting/error_reporting08.phpt
similarity index 100%
rename from Zend/tests/error_reporting08.phpt
rename to Zend/tests/error_reporting/error_reporting08.phpt
diff --git a/Zend/tests/error_reporting09.phpt b/Zend/tests/error_reporting/error_reporting09.phpt
similarity index 100%
rename from Zend/tests/error_reporting09.phpt
rename to Zend/tests/error_reporting/error_reporting09.phpt
diff --git a/Zend/tests/error_reporting10.phpt b/Zend/tests/error_reporting/error_reporting10.phpt
similarity index 100%
rename from Zend/tests/error_reporting10.phpt
rename to Zend/tests/error_reporting/error_reporting10.phpt
diff --git a/Zend/tests/bug26698.phpt b/Zend/tests/exceptions/bug26698.phpt
similarity index 100%
rename from Zend/tests/bug26698.phpt
rename to Zend/tests/exceptions/bug26698.phpt
diff --git a/Zend/tests/bug30161.phpt b/Zend/tests/exceptions/bug30161.phpt
similarity index 100%
rename from Zend/tests/bug30161.phpt
rename to Zend/tests/exceptions/bug30161.phpt
diff --git a/Zend/tests/bug30162.phpt b/Zend/tests/exceptions/bug30162.phpt
similarity index 100%
rename from Zend/tests/bug30162.phpt
rename to Zend/tests/exceptions/bug30162.phpt
diff --git a/Zend/tests/bug30707.phpt b/Zend/tests/exceptions/bug30707.phpt
similarity index 100%
rename from Zend/tests/bug30707.phpt
rename to Zend/tests/exceptions/bug30707.phpt
diff --git a/Zend/tests/bug30725.phpt b/Zend/tests/exceptions/bug30725.phpt
similarity index 100%
rename from Zend/tests/bug30725.phpt
rename to Zend/tests/exceptions/bug30725.phpt
diff --git a/Zend/tests/bug31102.phpt b/Zend/tests/exceptions/bug31102.phpt
similarity index 100%
rename from Zend/tests/bug31102.phpt
rename to Zend/tests/exceptions/bug31102.phpt
diff --git a/Zend/tests/bug32226.phpt b/Zend/tests/exceptions/bug32226.phpt
similarity index 100%
rename from Zend/tests/bug32226.phpt
rename to Zend/tests/exceptions/bug32226.phpt
diff --git a/Zend/tests/bug32252.phpt b/Zend/tests/exceptions/bug32252.phpt
similarity index 100%
rename from Zend/tests/bug32252.phpt
rename to Zend/tests/exceptions/bug32252.phpt
diff --git a/Zend/tests/bug33802.phpt b/Zend/tests/exceptions/bug33802.phpt
similarity index 100%
rename from Zend/tests/bug33802.phpt
rename to Zend/tests/exceptions/bug33802.phpt
diff --git a/Zend/tests/bug45805.phpt b/Zend/tests/exceptions/bug45805.phpt
similarity index 100%
rename from Zend/tests/bug45805.phpt
rename to Zend/tests/exceptions/bug45805.phpt
diff --git a/Zend/tests/bug47771.phpt b/Zend/tests/exceptions/bug47771.phpt
similarity index 100%
rename from Zend/tests/bug47771.phpt
rename to Zend/tests/exceptions/bug47771.phpt
diff --git a/Zend/tests/bug48228.phpt b/Zend/tests/exceptions/bug48228.phpt
similarity index 100%
rename from Zend/tests/bug48228.phpt
rename to Zend/tests/exceptions/bug48228.phpt
diff --git a/Zend/tests/bug48408.phpt b/Zend/tests/exceptions/bug48408.phpt
similarity index 100%
rename from Zend/tests/bug48408.phpt
rename to Zend/tests/exceptions/bug48408.phpt
diff --git a/Zend/tests/bug48409.phpt b/Zend/tests/exceptions/bug48409.phpt
similarity index 100%
rename from Zend/tests/bug48409.phpt
rename to Zend/tests/exceptions/bug48409.phpt
diff --git a/Zend/tests/bug48428.phpt b/Zend/tests/exceptions/bug48428.phpt
similarity index 100%
rename from Zend/tests/bug48428.phpt
rename to Zend/tests/exceptions/bug48428.phpt
diff --git a/Zend/tests/bug50005.phpt b/Zend/tests/exceptions/bug50005.phpt
similarity index 100%
rename from Zend/tests/bug50005.phpt
rename to Zend/tests/exceptions/bug50005.phpt
diff --git a/Zend/tests/bug50383.phpt b/Zend/tests/exceptions/bug50383.phpt
similarity index 100%
rename from Zend/tests/bug50383.phpt
rename to Zend/tests/exceptions/bug50383.phpt
diff --git a/Zend/tests/bug51394.phpt b/Zend/tests/exceptions/bug51394.phpt
similarity index 100%
rename from Zend/tests/bug51394.phpt
rename to Zend/tests/exceptions/bug51394.phpt
diff --git a/Zend/tests/bug53511.phpt b/Zend/tests/exceptions/bug53511.phpt
similarity index 100%
rename from Zend/tests/bug53511.phpt
rename to Zend/tests/exceptions/bug53511.phpt
diff --git a/Zend/tests/bug54043.phpt b/Zend/tests/exceptions/bug54043.phpt
similarity index 100%
rename from Zend/tests/bug54043.phpt
rename to Zend/tests/exceptions/bug54043.phpt
diff --git a/Zend/tests/bug60569.phpt b/Zend/tests/exceptions/bug60569.phpt
similarity index 100%
rename from Zend/tests/bug60569.phpt
rename to Zend/tests/exceptions/bug60569.phpt
diff --git a/Zend/tests/bug61362.phpt b/Zend/tests/exceptions/bug61362.phpt
similarity index 100%
rename from Zend/tests/bug61362.phpt
rename to Zend/tests/exceptions/bug61362.phpt
diff --git a/Zend/tests/bug63762.phpt b/Zend/tests/exceptions/bug63762.phpt
similarity index 100%
rename from Zend/tests/bug63762.phpt
rename to Zend/tests/exceptions/bug63762.phpt
diff --git a/Zend/tests/bug64135.phpt b/Zend/tests/exceptions/bug64135.phpt
similarity index 100%
rename from Zend/tests/bug64135.phpt
rename to Zend/tests/exceptions/bug64135.phpt
diff --git a/Zend/tests/bug64821.1.phpt b/Zend/tests/exceptions/bug64821.1.phpt
similarity index 100%
rename from Zend/tests/bug64821.1.phpt
rename to Zend/tests/exceptions/bug64821.1.phpt
diff --git a/Zend/tests/bug64821.2.phpt b/Zend/tests/exceptions/bug64821.2.phpt
similarity index 100%
rename from Zend/tests/bug64821.2.phpt
rename to Zend/tests/exceptions/bug64821.2.phpt
diff --git a/Zend/tests/bug64821.3.phpt b/Zend/tests/exceptions/bug64821.3.phpt
similarity index 100%
rename from Zend/tests/bug64821.3.phpt
rename to Zend/tests/exceptions/bug64821.3.phpt
diff --git a/Zend/tests/bug65254.phpt b/Zend/tests/exceptions/bug65254.phpt
similarity index 100%
rename from Zend/tests/bug65254.phpt
rename to Zend/tests/exceptions/bug65254.phpt
diff --git a/Zend/tests/bug70012.phpt b/Zend/tests/exceptions/bug70012.phpt
similarity index 100%
rename from Zend/tests/bug70012.phpt
rename to Zend/tests/exceptions/bug70012.phpt
diff --git a/Zend/tests/bug70944.phpt b/Zend/tests/exceptions/bug70944.phpt
similarity index 100%
rename from Zend/tests/bug70944.phpt
rename to Zend/tests/exceptions/bug70944.phpt
diff --git a/Zend/tests/bug72581.phpt b/Zend/tests/exceptions/bug72581.phpt
similarity index 100%
rename from Zend/tests/bug72581.phpt
rename to Zend/tests/exceptions/bug72581.phpt
diff --git a/Zend/tests/bug73338.phpt b/Zend/tests/exceptions/bug73338.phpt
similarity index 100%
rename from Zend/tests/bug73338.phpt
rename to Zend/tests/exceptions/bug73338.phpt
diff --git a/Zend/tests/bug73350.phpt b/Zend/tests/exceptions/bug73350.phpt
similarity index 100%
rename from Zend/tests/bug73350.phpt
rename to Zend/tests/exceptions/bug73350.phpt
diff --git a/Zend/tests/bug76025.phpt b/Zend/tests/exceptions/bug76025.phpt
similarity index 100%
rename from Zend/tests/bug76025.phpt
rename to Zend/tests/exceptions/bug76025.phpt
diff --git a/Zend/tests/bug78239.phpt b/Zend/tests/exceptions/bug78239.phpt
similarity index 100%
rename from Zend/tests/bug78239.phpt
rename to Zend/tests/exceptions/bug78239.phpt
diff --git a/Zend/tests/exception_001.phpt b/Zend/tests/exceptions/exception_001.phpt
similarity index 100%
rename from Zend/tests/exception_001.phpt
rename to Zend/tests/exceptions/exception_001.phpt
diff --git a/Zend/tests/exception_002.phpt b/Zend/tests/exceptions/exception_002.phpt
similarity index 100%
rename from Zend/tests/exception_002.phpt
rename to Zend/tests/exceptions/exception_002.phpt
diff --git a/Zend/tests/exception_003.phpt b/Zend/tests/exceptions/exception_003.phpt
similarity index 100%
rename from Zend/tests/exception_003.phpt
rename to Zend/tests/exceptions/exception_003.phpt
diff --git a/Zend/tests/exception_004.phpt b/Zend/tests/exceptions/exception_004.phpt
similarity index 100%
rename from Zend/tests/exception_004.phpt
rename to Zend/tests/exceptions/exception_004.phpt
diff --git a/Zend/tests/exception_005.phpt b/Zend/tests/exceptions/exception_005.phpt
similarity index 100%
rename from Zend/tests/exception_005.phpt
rename to Zend/tests/exceptions/exception_005.phpt
diff --git a/Zend/tests/exception_006.phpt b/Zend/tests/exceptions/exception_006.phpt
similarity index 100%
rename from Zend/tests/exception_006.phpt
rename to Zend/tests/exceptions/exception_006.phpt
diff --git a/Zend/tests/exception_007.phpt b/Zend/tests/exceptions/exception_007.phpt
similarity index 100%
rename from Zend/tests/exception_007.phpt
rename to Zend/tests/exceptions/exception_007.phpt
diff --git a/Zend/tests/exception_008.phpt b/Zend/tests/exceptions/exception_008.phpt
similarity index 100%
rename from Zend/tests/exception_008.phpt
rename to Zend/tests/exceptions/exception_008.phpt
diff --git a/Zend/tests/exception_009.phpt b/Zend/tests/exceptions/exception_009.phpt
similarity index 100%
rename from Zend/tests/exception_009.phpt
rename to Zend/tests/exceptions/exception_009.phpt
diff --git a/Zend/tests/exception_011.phpt b/Zend/tests/exceptions/exception_011.phpt
similarity index 100%
rename from Zend/tests/exception_011.phpt
rename to Zend/tests/exceptions/exception_011.phpt
diff --git a/Zend/tests/exception_013.phpt b/Zend/tests/exceptions/exception_013.phpt
similarity index 100%
rename from Zend/tests/exception_013.phpt
rename to Zend/tests/exceptions/exception_013.phpt
diff --git a/Zend/tests/exception_014.phpt b/Zend/tests/exceptions/exception_014.phpt
similarity index 100%
rename from Zend/tests/exception_014.phpt
rename to Zend/tests/exceptions/exception_014.phpt
diff --git a/Zend/tests/exception_015.phpt b/Zend/tests/exceptions/exception_015.phpt
similarity index 100%
rename from Zend/tests/exception_015.phpt
rename to Zend/tests/exceptions/exception_015.phpt
diff --git a/Zend/tests/exception_016.phpt b/Zend/tests/exceptions/exception_016.phpt
similarity index 100%
rename from Zend/tests/exception_016.phpt
rename to Zend/tests/exceptions/exception_016.phpt
diff --git a/Zend/tests/exception_017.phpt b/Zend/tests/exceptions/exception_017.phpt
similarity index 100%
rename from Zend/tests/exception_017.phpt
rename to Zend/tests/exceptions/exception_017.phpt
diff --git a/Zend/tests/exception_018.phpt b/Zend/tests/exceptions/exception_018.phpt
similarity index 100%
rename from Zend/tests/exception_018.phpt
rename to Zend/tests/exceptions/exception_018.phpt
diff --git a/Zend/tests/exception_019.phpt b/Zend/tests/exceptions/exception_019.phpt
similarity index 100%
rename from Zend/tests/exception_019.phpt
rename to Zend/tests/exceptions/exception_019.phpt
diff --git a/Zend/tests/exception_020.phpt b/Zend/tests/exceptions/exception_020.phpt
similarity index 100%
rename from Zend/tests/exception_020.phpt
rename to Zend/tests/exceptions/exception_020.phpt
diff --git a/Zend/tests/exception_021.phpt b/Zend/tests/exceptions/exception_021.phpt
similarity index 100%
rename from Zend/tests/exception_021.phpt
rename to Zend/tests/exceptions/exception_021.phpt
diff --git a/Zend/tests/exception_022.phpt b/Zend/tests/exceptions/exception_022.phpt
similarity index 100%
rename from Zend/tests/exception_022.phpt
rename to Zend/tests/exceptions/exception_022.phpt
diff --git a/Zend/tests/exception_023.phpt b/Zend/tests/exceptions/exception_023.phpt
similarity index 100%
rename from Zend/tests/exception_023.phpt
rename to Zend/tests/exceptions/exception_023.phpt
diff --git a/Zend/tests/exception_024.phpt b/Zend/tests/exceptions/exception_024.phpt
similarity index 100%
rename from Zend/tests/exception_024.phpt
rename to Zend/tests/exceptions/exception_024.phpt
diff --git a/Zend/tests/exception_025.phpt b/Zend/tests/exceptions/exception_025.phpt
similarity index 100%
rename from Zend/tests/exception_025.phpt
rename to Zend/tests/exceptions/exception_025.phpt
diff --git a/Zend/tests/exception_026.phpt b/Zend/tests/exceptions/exception_026.phpt
similarity index 100%
rename from Zend/tests/exception_026.phpt
rename to Zend/tests/exceptions/exception_026.phpt
diff --git a/Zend/tests/exception_before_fatal.phpt b/Zend/tests/exceptions/exception_before_fatal.phpt
similarity index 100%
rename from Zend/tests/exception_before_fatal.phpt
rename to Zend/tests/exceptions/exception_before_fatal.phpt
diff --git a/Zend/tests/exception_delayed_message.phpt b/Zend/tests/exceptions/exception_delayed_message.phpt
similarity index 100%
rename from Zend/tests/exception_delayed_message.phpt
rename to Zend/tests/exceptions/exception_delayed_message.phpt
diff --git a/Zend/tests/exception_during_by_reference_magic_get.phpt b/Zend/tests/exceptions/exception_during_by_reference_magic_get.phpt
similarity index 100%
rename from Zend/tests/exception_during_by_reference_magic_get.phpt
rename to Zend/tests/exceptions/exception_during_by_reference_magic_get.phpt
diff --git a/Zend/tests/exception_during_include_stat.phpt b/Zend/tests/exceptions/exception_during_include_stat.phpt
similarity index 100%
rename from Zend/tests/exception_during_include_stat.phpt
rename to Zend/tests/exceptions/exception_during_include_stat.phpt
diff --git a/Zend/tests/exception_during_property_assign_op.phpt b/Zend/tests/exceptions/exception_during_property_assign_op.phpt
similarity index 100%
rename from Zend/tests/exception_during_property_assign_op.phpt
rename to Zend/tests/exceptions/exception_during_property_assign_op.phpt
diff --git a/Zend/tests/exception_during_variance_autoload.phpt b/Zend/tests/exceptions/exception_during_variance_autoload.phpt
similarity index 100%
rename from Zend/tests/exception_during_variance_autoload.phpt
rename to Zend/tests/exceptions/exception_during_variance_autoload.phpt
diff --git a/Zend/tests/exception_during_variance_autoload_2.phpt b/Zend/tests/exceptions/exception_during_variance_autoload_2.phpt
similarity index 100%
rename from Zend/tests/exception_during_variance_autoload_2.phpt
rename to Zend/tests/exceptions/exception_during_variance_autoload_2.phpt
diff --git a/Zend/tests/exception_from_toString.phpt b/Zend/tests/exceptions/exception_from_toString.phpt
similarity index 100%
rename from Zend/tests/exception_from_toString.phpt
rename to Zend/tests/exceptions/exception_from_toString.phpt
diff --git a/Zend/tests/exception_getters_with_ref_props.phpt b/Zend/tests/exceptions/exception_getters_with_ref_props.phpt
similarity index 100%
rename from Zend/tests/exception_getters_with_ref_props.phpt
rename to Zend/tests/exceptions/exception_getters_with_ref_props.phpt
diff --git a/Zend/tests/bug40815.phpt b/Zend/tests/exceptions/exception_handler/bug40815.phpt
similarity index 100%
rename from Zend/tests/bug40815.phpt
rename to Zend/tests/exceptions/exception_handler/bug40815.phpt
diff --git a/Zend/tests/bug47714.phpt b/Zend/tests/exceptions/exception_handler/bug47714.phpt
similarity index 100%
rename from Zend/tests/bug47714.phpt
rename to Zend/tests/exceptions/exception_handler/bug47714.phpt
diff --git a/Zend/tests/exception_handler_001.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_001.phpt
similarity index 100%
rename from Zend/tests/exception_handler_001.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_001.phpt
diff --git a/Zend/tests/exception_handler_002.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_002.phpt
similarity index 100%
rename from Zend/tests/exception_handler_002.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_002.phpt
diff --git a/Zend/tests/exception_handler_003.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_003.phpt
similarity index 100%
rename from Zend/tests/exception_handler_003.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_003.phpt
diff --git a/Zend/tests/exception_handler_004.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_004.phpt
similarity index 100%
rename from Zend/tests/exception_handler_004.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_004.phpt
diff --git a/Zend/tests/exception_handler_005.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_005.phpt
similarity index 100%
rename from Zend/tests/exception_handler_005.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_005.phpt
diff --git a/Zend/tests/exception_handler_006.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_006.phpt
similarity index 100%
rename from Zend/tests/exception_handler_006.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_006.phpt
diff --git a/Zend/tests/exception_handler_007.phpt b/Zend/tests/exceptions/exception_handler/exception_handler_007.phpt
similarity index 100%
rename from Zend/tests/exception_handler_007.phpt
rename to Zend/tests/exceptions/exception_handler/exception_handler_007.phpt
diff --git a/Zend/tests/exit_exception_handler.phpt b/Zend/tests/exceptions/exception_handler/exit_exception_handler.phpt
similarity index 100%
rename from Zend/tests/exit_exception_handler.phpt
rename to Zend/tests/exceptions/exception_handler/exit_exception_handler.phpt
diff --git a/Zend/tests/gh10695_1.phpt b/Zend/tests/exceptions/gh10695_1.phpt
similarity index 100%
rename from Zend/tests/gh10695_1.phpt
rename to Zend/tests/exceptions/gh10695_1.phpt
diff --git a/Zend/tests/gh10695_2.phpt b/Zend/tests/exceptions/gh10695_2.phpt
similarity index 100%
rename from Zend/tests/gh10695_2.phpt
rename to Zend/tests/exceptions/gh10695_2.phpt
diff --git a/Zend/tests/gh10695_3.phpt b/Zend/tests/exceptions/gh10695_3.phpt
similarity index 100%
rename from Zend/tests/gh10695_3.phpt
rename to Zend/tests/exceptions/gh10695_3.phpt
diff --git a/Zend/tests/gh10695_4.phpt b/Zend/tests/exceptions/gh10695_4.phpt
similarity index 100%
rename from Zend/tests/gh10695_4.phpt
rename to Zend/tests/exceptions/gh10695_4.phpt
diff --git a/Zend/tests/gh10695_5.phpt b/Zend/tests/exceptions/gh10695_5.phpt
similarity index 100%
rename from Zend/tests/gh10695_5.phpt
rename to Zend/tests/exceptions/gh10695_5.phpt
diff --git a/Zend/tests/gh10695_6.phpt b/Zend/tests/exceptions/gh10695_6.phpt
similarity index 100%
rename from Zend/tests/gh10695_6.phpt
rename to Zend/tests/exceptions/gh10695_6.phpt
diff --git a/Zend/tests/gh10695_7.phpt b/Zend/tests/exceptions/gh10695_7.phpt
similarity index 100%
rename from Zend/tests/gh10695_7.phpt
rename to Zend/tests/exceptions/gh10695_7.phpt
diff --git a/Zend/tests/gh10810.phpt b/Zend/tests/exceptions/gh10810.phpt
similarity index 100%
rename from Zend/tests/gh10810.phpt
rename to Zend/tests/exceptions/gh10810.phpt
diff --git a/Zend/tests/gh13446_1.phpt b/Zend/tests/exceptions/gh13446_1.phpt
similarity index 100%
rename from Zend/tests/gh13446_1.phpt
rename to Zend/tests/exceptions/gh13446_1.phpt
diff --git a/Zend/tests/gh13446_2.phpt b/Zend/tests/exceptions/gh13446_2.phpt
similarity index 100%
rename from Zend/tests/gh13446_2.phpt
rename to Zend/tests/exceptions/gh13446_2.phpt
diff --git a/Zend/tests/gh13446_3.phpt b/Zend/tests/exceptions/gh13446_3.phpt
similarity index 100%
rename from Zend/tests/gh13446_3.phpt
rename to Zend/tests/exceptions/gh13446_3.phpt
diff --git a/Zend/tests/gh13446_4.phpt b/Zend/tests/exceptions/gh13446_4.phpt
similarity index 100%
rename from Zend/tests/gh13446_4.phpt
rename to Zend/tests/exceptions/gh13446_4.phpt
diff --git a/Zend/tests/gh16188.phpt b/Zend/tests/exceptions/gh16188.phpt
similarity index 100%
rename from Zend/tests/gh16188.phpt
rename to Zend/tests/exceptions/gh16188.phpt
diff --git a/Zend/tests/bug60978.phpt b/Zend/tests/exit/bug60978.phpt
similarity index 100%
rename from Zend/tests/bug60978.phpt
rename to Zend/tests/exit/bug60978.phpt
diff --git a/Zend/tests/bug79948.inc b/Zend/tests/exit/bug79948.inc
similarity index 100%
rename from Zend/tests/bug79948.inc
rename to Zend/tests/exit/bug79948.inc
diff --git a/Zend/tests/bug79948.phpt b/Zend/tests/exit/bug79948.phpt
similarity index 100%
rename from Zend/tests/bug79948.phpt
rename to Zend/tests/exit/bug79948.phpt
diff --git a/Zend/tests/gh15108-001.phpt b/Zend/tests/fibers/gh15108-001.phpt
similarity index 100%
rename from Zend/tests/gh15108-001.phpt
rename to Zend/tests/fibers/gh15108-001.phpt
diff --git a/Zend/tests/gh15108-002.phpt b/Zend/tests/fibers/gh15108-002.phpt
similarity index 100%
rename from Zend/tests/gh15108-002.phpt
rename to Zend/tests/fibers/gh15108-002.phpt
diff --git a/Zend/tests/gh15108-003.phpt b/Zend/tests/fibers/gh15108-003.phpt
similarity index 100%
rename from Zend/tests/gh15108-003.phpt
rename to Zend/tests/fibers/gh15108-003.phpt
diff --git a/Zend/tests/gh15108-004.phpt b/Zend/tests/fibers/gh15108-004.phpt
similarity index 100%
rename from Zend/tests/gh15108-004.phpt
rename to Zend/tests/fibers/gh15108-004.phpt
diff --git a/Zend/tests/gh15108-005.phpt b/Zend/tests/fibers/gh15108-005.phpt
similarity index 100%
rename from Zend/tests/gh15108-005.phpt
rename to Zend/tests/fibers/gh15108-005.phpt
diff --git a/Zend/tests/gh15108-006.phpt b/Zend/tests/fibers/gh15108-006.phpt
similarity index 100%
rename from Zend/tests/gh15108-006.phpt
rename to Zend/tests/fibers/gh15108-006.phpt
diff --git a/Zend/tests/gh15108-007.phpt b/Zend/tests/fibers/gh15108-007.phpt
similarity index 100%
rename from Zend/tests/gh15108-007.phpt
rename to Zend/tests/fibers/gh15108-007.phpt
diff --git a/Zend/tests/gh9916-001.phpt b/Zend/tests/fibers/gh9916-001.phpt
similarity index 100%
rename from Zend/tests/gh9916-001.phpt
rename to Zend/tests/fibers/gh9916-001.phpt
diff --git a/Zend/tests/gh9916-002.phpt b/Zend/tests/fibers/gh9916-002.phpt
similarity index 100%
rename from Zend/tests/gh9916-002.phpt
rename to Zend/tests/fibers/gh9916-002.phpt
diff --git a/Zend/tests/gh9916-003.phpt b/Zend/tests/fibers/gh9916-003.phpt
similarity index 100%
rename from Zend/tests/gh9916-003.phpt
rename to Zend/tests/fibers/gh9916-003.phpt
diff --git a/Zend/tests/gh9916-004.phpt b/Zend/tests/fibers/gh9916-004.phpt
similarity index 100%
rename from Zend/tests/gh9916-004.phpt
rename to Zend/tests/fibers/gh9916-004.phpt
diff --git a/Zend/tests/gh9916-005.phpt b/Zend/tests/fibers/gh9916-005.phpt
similarity index 100%
rename from Zend/tests/gh9916-005.phpt
rename to Zend/tests/fibers/gh9916-005.phpt
diff --git a/Zend/tests/gh9916-006.phpt b/Zend/tests/fibers/gh9916-006.phpt
similarity index 100%
rename from Zend/tests/gh9916-006.phpt
rename to Zend/tests/fibers/gh9916-006.phpt
diff --git a/Zend/tests/gh9916-007.phpt b/Zend/tests/fibers/gh9916-007.phpt
similarity index 100%
rename from Zend/tests/gh9916-007.phpt
rename to Zend/tests/fibers/gh9916-007.phpt
diff --git a/Zend/tests/gh9916-008.phpt b/Zend/tests/fibers/gh9916-008.phpt
similarity index 100%
rename from Zend/tests/gh9916-008.phpt
rename to Zend/tests/fibers/gh9916-008.phpt
diff --git a/Zend/tests/gh9916-009.phpt b/Zend/tests/fibers/gh9916-009.phpt
similarity index 100%
rename from Zend/tests/gh9916-009.phpt
rename to Zend/tests/fibers/gh9916-009.phpt
diff --git a/Zend/tests/gh9916-010.phpt b/Zend/tests/fibers/gh9916-010.phpt
similarity index 100%
rename from Zend/tests/gh9916-010.phpt
rename to Zend/tests/fibers/gh9916-010.phpt
diff --git a/Zend/tests/gh9916-011.phpt b/Zend/tests/fibers/gh9916-011.phpt
similarity index 100%
rename from Zend/tests/gh9916-011.phpt
rename to Zend/tests/fibers/gh9916-011.phpt
diff --git a/Zend/tests/gh9916-012.phpt b/Zend/tests/fibers/gh9916-012.phpt
similarity index 100%
rename from Zend/tests/gh9916-012.phpt
rename to Zend/tests/fibers/gh9916-012.phpt
diff --git a/Zend/tests/first_class_callable/constexpr/attributes.phpt b/Zend/tests/first_class_callable/constexpr/attributes.phpt
new file mode 100644
index 0000000000000..b011a7f678137
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/attributes.phpt
@@ -0,0 +1,50 @@
+--TEST--
+Allow defining FCC in attributes
+--EXTENSIONS--
+reflection
+--FILE--
+getAttributes() as $reflectionAttribute) {
+ var_dump($reflectionAttribute->newInstance());
+}
+
+?>
+--EXPECTF--
+string(3) "cba"
+object(Attr)#%d (1) {
+ ["value"]=>
+ object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+ }
+}
+int(3)
+object(Attr)#%d (1) {
+ ["value"]=>
+ object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strlen"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+ }
+}
diff --git a/Zend/tests/first_class_callable/constexpr/attributes_ast_printing.phpt b/Zend/tests/first_class_callable/constexpr/attributes_ast_printing.phpt
new file mode 100644
index 0000000000000..2916c3dfff86d
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/attributes_ast_printing.phpt
@@ -0,0 +1,33 @@
+--TEST--
+AST printing for FCC in attributes
+--FILE--
+getMessage(), "\n";
+}
+
+try {
+ \assert(
+ !
+ new #[Attr(strrev(...))]
+ class {}
+ );
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+assert(!#[Attr(strrev(...))] function () {
+})
+assert(!new #[Attr(strrev(...))] class {
+})
diff --git a/Zend/tests/first_class_callable/constexpr/attributes_ast_printing_runtime.phpt b/Zend/tests/first_class_callable/constexpr/attributes_ast_printing_runtime.phpt
new file mode 100644
index 0000000000000..8ee0acbb6f0e4
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/attributes_ast_printing_runtime.phpt
@@ -0,0 +1,27 @@
+--TEST--
+AST printing for FCC in attributes at runtime
+--FILE--
+getAttributes() as $attribute) {
+ echo $attribute;
+}
+
+?>
+--EXPECT--
+Attribute [ Test\Attr ] {
+ - Arguments [4] {
+ Argument #0 [ Test\strrev(...) ]
+ Argument #1 [ \strrev(...) ]
+ Argument #2 [ \Test\Clazz::foo(...) ]
+ Argument #3 [ self::foo(...) ]
+ }
+}
diff --git a/Zend/tests/first_class_callable/constexpr/attributes_scope_001.phpt b/Zend/tests/first_class_callable/constexpr/attributes_scope_001.phpt
new file mode 100644
index 0000000000000..6e75004624191
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/attributes_scope_001.phpt
@@ -0,0 +1,28 @@
+--TEST--
+FCC in attribute may access private methods
+--EXTENSIONS--
+reflection
+--FILE--
+getAttributes() as $reflectionAttribute) {
+ ($reflectionAttribute->newInstance()->value)('abc');
+}
+
+?>
+--EXPECT--
+Called C::myMethod
+string(3) "abc"
diff --git a/Zend/tests/first_class_callable/constexpr/attributes_scope_002.phpt b/Zend/tests/first_class_callable/constexpr/attributes_scope_002.phpt
new file mode 100644
index 0000000000000..f93240c5fac8e
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/attributes_scope_002.phpt
@@ -0,0 +1,34 @@
+--TEST--
+FCC in attribute may not access unrelated private methods
+--EXTENSIONS--
+reflection
+--FILE--
+getAttributes() as $reflectionAttribute) {
+ ($reflectionAttribute->newInstance()->value)('abc');
+}
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Call to private method E::myMethod() from scope C in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionAttribute->newInstance()
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/autoload.phpt b/Zend/tests/first_class_callable/constexpr/autoload.phpt
new file mode 100644
index 0000000000000..2dd2561f9a369
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/autoload.phpt
@@ -0,0 +1,31 @@
+--TEST--
+FCC in const expression triggers autoloader.
+--FILE--
+
+--EXPECTF--
+Autoloading AutoloadedClass
+object(Closure)#%d (1) {
+ ["function"]=>
+ string(16) "withStaticMethod"
+}
+Called AutoloadedClass::withStaticMethod
diff --git a/Zend/tests/first_class_callable/constexpr/basic.phpt b/Zend/tests/first_class_callable/constexpr/basic.phpt
new file mode 100644
index 0000000000000..ce60cba1aa84f
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/basic.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Allow defining FCC in const expressions.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(%d) "%s"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/case_insensitive.phpt b/Zend/tests/first_class_callable/constexpr/case_insensitive.phpt
new file mode 100644
index 0000000000000..1feaa718793de
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/case_insensitive.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Allow defining FCC in const expressions with case-insensitive names.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(%d) "%s"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/class_const.phpt b/Zend/tests/first_class_callable/constexpr/class_const.phpt
new file mode 100644
index 0000000000000..ab0ea29d50369
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/class_const.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Allow defining FCC in class constants.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/complex_array.phpt b/Zend/tests/first_class_callable/constexpr/complex_array.phpt
new file mode 100644
index 0000000000000..388d09d056a42
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/complex_array.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Allow defining FCC wrapped in an array in const expressions.
+--FILE--
+
+--EXPECTF--
+array(2) {
+ [0]=>
+ object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+ }
+ [1]=>
+ object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strlen"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+ }
+}
+string(3) "cba"
+int(3)
diff --git a/Zend/tests/first_class_callable/constexpr/default_args.phpt b/Zend/tests/first_class_callable/constexpr/default_args.phpt
new file mode 100644
index 0000000000000..a35efc8961c17
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/default_args.phpt
@@ -0,0 +1,18 @@
+--TEST--
+FCC in default argument
+--FILE--
+
+--EXPECT--
+string(3) "cba"
+int(3)
diff --git a/Zend/tests/first_class_callable/constexpr/error_abstract.phpt b/Zend/tests/first_class_callable/constexpr/error_abstract.phpt
new file mode 100644
index 0000000000000..877f8f3c0b463
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_abstract.phpt
@@ -0,0 +1,20 @@
+--TEST--
+FCC in initializer errors for FCC on abstract method
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot call abstract method Foo::myMethod() in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_dynamic_001.phpt b/Zend/tests/first_class_callable/constexpr/error_dynamic_001.phpt
new file mode 100644
index 0000000000000..44821cf6e60ad
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_dynamic_001.phpt
@@ -0,0 +1,12 @@
+--TEST--
+FCC in initializer errors for FCC on variable.
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use dynamic function name in constant expression in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_dynamic_002.phpt b/Zend/tests/first_class_callable/constexpr/error_dynamic_002.phpt
new file mode 100644
index 0000000000000..e373ff51620d5
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_dynamic_002.phpt
@@ -0,0 +1,12 @@
+--TEST--
+FCC in initializer errors for FCC on Closure.
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use dynamic function name in constant expression in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_dynamic_003.phpt b/Zend/tests/first_class_callable/constexpr/error_dynamic_003.phpt
new file mode 100644
index 0000000000000..41090d89556f3
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_dynamic_003.phpt
@@ -0,0 +1,14 @@
+--TEST--
+FCC in initializer errors for FCC on Constant.
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use dynamic function name in constant expression in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_dynamic_004.phpt b/Zend/tests/first_class_callable/constexpr/error_dynamic_004.phpt
new file mode 100644
index 0000000000000..f42c06cf86328
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_dynamic_004.phpt
@@ -0,0 +1,20 @@
+--TEST--
+FCC in initializer errors for FCC on 'static::'.
+--FILE--
+
+--EXPECTF--
+Fatal error: "static" is not allowed in compile-time constants in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_dynamic_005.phpt b/Zend/tests/first_class_callable/constexpr/error_dynamic_005.phpt
new file mode 100644
index 0000000000000..e13527444f1ca
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_dynamic_005.phpt
@@ -0,0 +1,12 @@
+--TEST--
+FCC in initializer errors for FCC on integer expression
+--FILE--
+
+--EXPECTF--
+Fatal error: Illegal function name in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_instance_call.phpt b/Zend/tests/first_class_callable/constexpr/error_instance_call.phpt
new file mode 100644
index 0000000000000..cfee7c40b7844
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_instance_call.phpt
@@ -0,0 +1,20 @@
+--TEST--
+FCC in initializer errors for FCC on instance call.
+--FILE--
+myMethod(...);
+
+var_dump(Closure);
+(Closure)("abc");
+
+?>
+--EXPECTF--
+Fatal error: Constant expression contains invalid operations in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_magic_callStatic.phpt b/Zend/tests/first_class_callable/constexpr/error_magic_callStatic.phpt
new file mode 100644
index 0000000000000..ea3d4822118ce
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_magic_callStatic.phpt
@@ -0,0 +1,23 @@
+--TEST--
+FCC in initializer errors for FCC on __callStatic() fallback.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Creating a callable for the magic __callStatic() method is not supported in constant expressions in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_namespace_no_fallback_001.phpt b/Zend/tests/first_class_callable/constexpr/error_namespace_no_fallback_001.phpt
new file mode 100644
index 0000000000000..b80a222c2941d
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_namespace_no_fallback_001.phpt
@@ -0,0 +1,18 @@
+--TEST--
+FCC in initializer errors for missing function with FQN matching global function.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Call to undefined function Foo\strrev() in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_namespace_no_fallback_002.phpt b/Zend/tests/first_class_callable/constexpr/error_namespace_no_fallback_002.phpt
new file mode 100644
index 0000000000000..62bc7e9e0b8bf
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_namespace_no_fallback_002.phpt
@@ -0,0 +1,18 @@
+--TEST--
+FCC in initializer errors for missing function with FQN matching global function.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Call to undefined function Foo\strrev() in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_static_call_instance_method.phpt b/Zend/tests/first_class_callable/constexpr/error_static_call_instance_method.phpt
new file mode 100644
index 0000000000000..9a6b27b232e6c
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_static_call_instance_method.phpt
@@ -0,0 +1,23 @@
+--TEST--
+FCC in initializer errors for static reference to instance method.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Non-static method Foo::myMethod() cannot be called statically in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_001.phpt b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_001.phpt
new file mode 100644
index 0000000000000..efffa4a1d366a
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_001.phpt
@@ -0,0 +1,31 @@
+--TEST--
+FCC in initializer errors for static reference to instance method.
+--FILE--
+
+--EXPECTF--
+Deprecated: Calling static trait method Foo::myMethod is deprecated, it should only be called on a class using the trait in %s on line %d
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(8) "myMethod"
+ ["parameter"]=>
+ array(1) {
+ ["$foo"]=>
+ string(10) ""
+ }
+}
+Called Foo::myMethod
+string(3) "abc"
diff --git a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt
new file mode 100644
index 0000000000000..d36f7c97651e0
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt
@@ -0,0 +1,31 @@
+--TEST--
+FCC in initializer errors for static reference to instance method (Exception).
+--FILE--
+getMessage(), PHP_EOL;
+}
+
+
+?>
+--EXPECT--
+Caught: Calling static trait method Foo::myMethod is deprecated, it should only be called on a class using the trait
diff --git a/Zend/tests/first_class_callable/constexpr/error_unknown_class.phpt b/Zend/tests/first_class_callable/constexpr/error_unknown_class.phpt
new file mode 100644
index 0000000000000..39ce1736ddecd
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_unknown_class.phpt
@@ -0,0 +1,15 @@
+--TEST--
+FCC in initializer errors for missing class.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Class "ThisClassNotDoesExist" not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_unknown_function.phpt b/Zend/tests/first_class_callable/constexpr/error_unknown_function.phpt
new file mode 100644
index 0000000000000..38b291d9907ad
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_unknown_function.phpt
@@ -0,0 +1,15 @@
+--TEST--
+FCC in initializer errors for missing function.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Call to undefined function this_function_does_not_exist() in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/error_unknown_method.phpt b/Zend/tests/first_class_callable/constexpr/error_unknown_method.phpt
new file mode 100644
index 0000000000000..345d67ee74dae
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/error_unknown_method.phpt
@@ -0,0 +1,17 @@
+--TEST--
+FCC in initializer errors for missing method.
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Error: Call to undefined method ThisClassDoesExist::thisMethodDoesNotExist() in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/namespace_001.phpt b/Zend/tests/first_class_callable/constexpr/namespace_001.phpt
new file mode 100644
index 0000000000000..f920844957894
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/namespace_001.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Allow defining FCC in const expressions in a namespace.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(%d) "%s"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/namespace_002.phpt b/Zend/tests/first_class_callable/constexpr/namespace_002.phpt
new file mode 100644
index 0000000000000..17d70ad511af7
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/namespace_002.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Allow defining FCC in const expressions in a namespace with global function fallback.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(%d) "%s"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/namespace_003.phpt b/Zend/tests/first_class_callable/constexpr/namespace_003.phpt
new file mode 100644
index 0000000000000..2fe1d45296ce7
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/namespace_003.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Allow defining FCC in const expressions in a namespace with function matching a global function.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(%d) "%s"
+ ["parameter"]=>
+ array(1) {
+ ["$value"]=>
+ string(10) ""
+ }
+}
+string(18) "not the global one"
diff --git a/Zend/tests/first_class_callable/constexpr/namespace_004.phpt b/Zend/tests/first_class_callable/constexpr/namespace_004.phpt
new file mode 100644
index 0000000000000..0fc23422199d5
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/namespace_004.phpt
@@ -0,0 +1,68 @@
+--TEST--
+Allow defining FCC in const expressions in a namespace with function matching a global function later.
+--FILE--
+ 0) {
+ function strrev(string $value) {
+ return 'not the global one';
+ }
+}
+
+foo();
+
+?>
+--EXPECTF--
+object(Closure)#1 (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
+object(Closure)#2 (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
+object(Closure)#2 (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
+object(Closure)#1 (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/property_initializer.phpt b/Zend/tests/first_class_callable/constexpr/property_initializer.phpt
new file mode 100644
index 0000000000000..53cae06216bd1
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/property_initializer.phpt
@@ -0,0 +1,26 @@
+--TEST--
+FCC in property initializer
+--FILE--
+d);
+var_dump(($c->d)("abc"));
+
+
+?>
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/property_initializer_scope_001.phpt b/Zend/tests/first_class_callable/constexpr/property_initializer_scope_001.phpt
new file mode 100644
index 0000000000000..67428d1c6470e
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/property_initializer_scope_001.phpt
@@ -0,0 +1,32 @@
+--TEST--
+FCC in property initializer may access private methods.
+--FILE--
+d);
+var_dump(($c->d)("abc"));
+
+?>
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(11) "C::myMethod"
+ ["parameter"]=>
+ array(1) {
+ ["$foo"]=>
+ string(10) ""
+ }
+}
+Called C::myMethod
+string(3) "abc"
+NULL
diff --git a/Zend/tests/first_class_callable/constexpr/property_initializer_scope_002.phpt b/Zend/tests/first_class_callable/constexpr/property_initializer_scope_002.phpt
new file mode 100644
index 0000000000000..ecee256012ece
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/property_initializer_scope_002.phpt
@@ -0,0 +1,27 @@
+--TEST--
+FCC in property initializer may not access unrelated private methods.
+--FILE--
+d);
+var_dump(($c->d)("abc"));
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Call to private method E::myMethod() from scope C in %s:%d
+Stack trace:
+#0 %s(%d): [constant expression]()
+#1 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/first_class_callable/constexpr/property_initializer_scope_003.phpt b/Zend/tests/first_class_callable/constexpr/property_initializer_scope_003.phpt
new file mode 100644
index 0000000000000..3959efbd16a05
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/property_initializer_scope_003.phpt
@@ -0,0 +1,35 @@
+--TEST--
+FCC in property initializer may access protected methods of parent.
+--FILE--
+d);
+var_dump(($c->d)("abc"));
+
+?>
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(11) "C::myMethod"
+ ["parameter"]=>
+ array(1) {
+ ["$foo"]=>
+ string(10) ""
+ }
+}
+Called P::myMethod
+string(3) "abc"
+NULL
diff --git a/Zend/tests/first_class_callable/constexpr/static_call.phpt b/Zend/tests/first_class_callable/constexpr/static_call.phpt
new file mode 100644
index 0000000000000..26761a041a4cc
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/static_call.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Allow defining FCC for static methods in const expressions.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(8) "myMethod"
+ ["parameter"]=>
+ array(1) {
+ ["$foo"]=>
+ string(10) ""
+ }
+}
+Called Foo::myMethod
+string(3) "abc"
diff --git a/Zend/tests/first_class_callable/constexpr/static_call_self.phpt b/Zend/tests/first_class_callable/constexpr/static_call_self.phpt
new file mode 100644
index 0000000000000..6cbf1726caa91
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/static_call_self.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Allow defining FCC for static methods referenced by 'self::' in const expressions.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(13) "Foo::myMethod"
+ ["parameter"]=>
+ array(1) {
+ ["$foo"]=>
+ string(10) ""
+ }
+}
+Called Foo::myMethod
+string(3) "abc"
diff --git a/Zend/tests/first_class_callable/constexpr/static_property_initializer.phpt b/Zend/tests/first_class_callable/constexpr/static_property_initializer.phpt
new file mode 100644
index 0000000000000..d46b88aae9be0
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/static_property_initializer.phpt
@@ -0,0 +1,25 @@
+--TEST--
+FCC in static property initializer
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(6) "strrev"
+ ["parameter"]=>
+ array(1) {
+ ["$string"]=>
+ string(10) ""
+ }
+}
+string(3) "cba"
diff --git a/Zend/tests/first_class_callable/constexpr/userland.phpt b/Zend/tests/first_class_callable/constexpr/userland.phpt
new file mode 100644
index 0000000000000..6ead1e51ff31c
--- /dev/null
+++ b/Zend/tests/first_class_callable/constexpr/userland.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Allow defining FCC for userland functions in const expressions.
+--FILE--
+
+--EXPECTF--
+object(Closure)#%d (2) {
+ ["function"]=>
+ string(11) "my_function"
+ ["parameter"]=>
+ array(1) {
+ ["$foo"]=>
+ string(10) ""
+ }
+}
+Called my_function
+string(3) "abc"
diff --git a/Zend/tests/first_class_callable_001.phpt b/Zend/tests/first_class_callable/first_class_callable_001.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_001.phpt
rename to Zend/tests/first_class_callable/first_class_callable_001.phpt
diff --git a/Zend/tests/first_class_callable_002.phpt b/Zend/tests/first_class_callable/first_class_callable_002.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_002.phpt
rename to Zend/tests/first_class_callable/first_class_callable_002.phpt
diff --git a/Zend/tests/first_class_callable_003.phpt b/Zend/tests/first_class_callable/first_class_callable_003.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_003.phpt
rename to Zend/tests/first_class_callable/first_class_callable_003.phpt
diff --git a/Zend/tests/first_class_callable_004.phpt b/Zend/tests/first_class_callable/first_class_callable_004.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_004.phpt
rename to Zend/tests/first_class_callable/first_class_callable_004.phpt
diff --git a/Zend/tests/first_class_callable_005.phpt b/Zend/tests/first_class_callable/first_class_callable_005.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_005.phpt
rename to Zend/tests/first_class_callable/first_class_callable_005.phpt
diff --git a/Zend/tests/first_class_callable_006.phpt b/Zend/tests/first_class_callable/first_class_callable_006.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_006.phpt
rename to Zend/tests/first_class_callable/first_class_callable_006.phpt
diff --git a/Zend/tests/first_class_callable_007.phpt b/Zend/tests/first_class_callable/first_class_callable_007.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_007.phpt
rename to Zend/tests/first_class_callable/first_class_callable_007.phpt
diff --git a/Zend/tests/first_class_callable_008.phpt b/Zend/tests/first_class_callable/first_class_callable_008.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_008.phpt
rename to Zend/tests/first_class_callable/first_class_callable_008.phpt
diff --git a/Zend/tests/first_class_callable_009.phpt b/Zend/tests/first_class_callable/first_class_callable_009.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_009.phpt
rename to Zend/tests/first_class_callable/first_class_callable_009.phpt
diff --git a/Zend/tests/first_class_callable_010.phpt b/Zend/tests/first_class_callable/first_class_callable_010.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_010.phpt
rename to Zend/tests/first_class_callable/first_class_callable_010.phpt
diff --git a/Zend/tests/first_class_callable_011.phpt b/Zend/tests/first_class_callable/first_class_callable_011.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_011.phpt
rename to Zend/tests/first_class_callable/first_class_callable_011.phpt
diff --git a/Zend/tests/first_class_callable_012.phpt b/Zend/tests/first_class_callable/first_class_callable_012.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_012.phpt
rename to Zend/tests/first_class_callable/first_class_callable_012.phpt
diff --git a/Zend/tests/first_class_callable_013.phpt b/Zend/tests/first_class_callable/first_class_callable_013.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_013.phpt
rename to Zend/tests/first_class_callable/first_class_callable_013.phpt
diff --git a/Zend/tests/first_class_callable_014.phpt b/Zend/tests/first_class_callable/first_class_callable_014.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_014.phpt
rename to Zend/tests/first_class_callable/first_class_callable_014.phpt
diff --git a/Zend/tests/first_class_callable_015.phpt b/Zend/tests/first_class_callable/first_class_callable_015.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_015.phpt
rename to Zend/tests/first_class_callable/first_class_callable_015.phpt
diff --git a/Zend/tests/first_class_callable_015_strict.inc b/Zend/tests/first_class_callable/first_class_callable_015_strict.inc
similarity index 100%
rename from Zend/tests/first_class_callable_015_strict.inc
rename to Zend/tests/first_class_callable/first_class_callable_015_strict.inc
diff --git a/Zend/tests/first_class_callable_015_weak.inc b/Zend/tests/first_class_callable/first_class_callable_015_weak.inc
similarity index 100%
rename from Zend/tests/first_class_callable_015_weak.inc
rename to Zend/tests/first_class_callable/first_class_callable_015_weak.inc
diff --git a/Zend/tests/first_class_callable_016.phpt b/Zend/tests/first_class_callable/first_class_callable_016.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_016.phpt
rename to Zend/tests/first_class_callable/first_class_callable_016.phpt
diff --git a/Zend/tests/first_class_callable_assert.phpt b/Zend/tests/first_class_callable/first_class_callable_assert.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_assert.phpt
rename to Zend/tests/first_class_callable/first_class_callable_assert.phpt
diff --git a/Zend/tests/first_class_callable_assert2.phpt b/Zend/tests/first_class_callable/first_class_callable_assert2.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_assert2.phpt
rename to Zend/tests/first_class_callable/first_class_callable_assert2.phpt
diff --git a/Zend/tests/first_class_callable_assert3.phpt b/Zend/tests/first_class_callable/first_class_callable_assert3.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_assert3.phpt
rename to Zend/tests/first_class_callable/first_class_callable_assert3.phpt
diff --git a/Zend/tests/first_class_callable_dynamic.phpt b/Zend/tests/first_class_callable/first_class_callable_dynamic.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_dynamic.phpt
rename to Zend/tests/first_class_callable/first_class_callable_dynamic.phpt
diff --git a/Zend/tests/first_class_callable_errors.phpt b/Zend/tests/first_class_callable/first_class_callable_errors.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_errors.phpt
rename to Zend/tests/first_class_callable/first_class_callable_errors.phpt
diff --git a/Zend/tests/first_class_callable_optimization.phpt b/Zend/tests/first_class_callable/first_class_callable_optimization.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_optimization.phpt
rename to Zend/tests/first_class_callable/first_class_callable_optimization.phpt
diff --git a/Zend/tests/first_class_callable_refs.phpt b/Zend/tests/first_class_callable/first_class_callable_refs.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_refs.phpt
rename to Zend/tests/first_class_callable/first_class_callable_refs.phpt
diff --git a/Zend/tests/first_class_callable_signature.phpt b/Zend/tests/first_class_callable/first_class_callable_signature.phpt
similarity index 100%
rename from Zend/tests/first_class_callable_signature.phpt
rename to Zend/tests/first_class_callable/first_class_callable_signature.phpt
diff --git a/Zend/tests/first_class_callable/gh18062.phpt b/Zend/tests/first_class_callable/gh18062.phpt
new file mode 100644
index 0000000000000..3865dd77adba8
--- /dev/null
+++ b/Zend/tests/first_class_callable/gh18062.phpt
@@ -0,0 +1,32 @@
+--TEST--
+First Class Callable returns correct name from is_callable()
+--FILE--
+
+--EXPECT--
+string(9) "some_func"
+string(13) "Foo::__invoke"
+string(8) "Foo::bar"
diff --git a/Zend/tests/bug33171.phpt b/Zend/tests/foreach/bug33171.phpt
similarity index 100%
rename from Zend/tests/bug33171.phpt
rename to Zend/tests/foreach/bug33171.phpt
diff --git a/Zend/tests/bug34065.phpt b/Zend/tests/foreach/bug34065.phpt
similarity index 100%
rename from Zend/tests/bug34065.phpt
rename to Zend/tests/foreach/bug34065.phpt
diff --git a/Zend/tests/bug34310.phpt b/Zend/tests/foreach/bug34310.phpt
similarity index 100%
rename from Zend/tests/bug34310.phpt
rename to Zend/tests/foreach/bug34310.phpt
diff --git a/Zend/tests/bug34467.phpt b/Zend/tests/foreach/bug34467.phpt
similarity index 100%
rename from Zend/tests/bug34467.phpt
rename to Zend/tests/foreach/bug34467.phpt
diff --git a/Zend/tests/bug34873.phpt b/Zend/tests/foreach/bug34873.phpt
similarity index 100%
rename from Zend/tests/bug34873.phpt
rename to Zend/tests/foreach/bug34873.phpt
diff --git a/Zend/tests/bug35106.phpt b/Zend/tests/foreach/bug35106.phpt
similarity index 100%
rename from Zend/tests/bug35106.phpt
rename to Zend/tests/foreach/bug35106.phpt
diff --git a/Zend/tests/bug37046.phpt b/Zend/tests/foreach/bug37046.phpt
similarity index 100%
rename from Zend/tests/bug37046.phpt
rename to Zend/tests/foreach/bug37046.phpt
diff --git a/Zend/tests/bug39017.phpt b/Zend/tests/foreach/bug39017.phpt
similarity index 100%
rename from Zend/tests/bug39017.phpt
rename to Zend/tests/foreach/bug39017.phpt
diff --git a/Zend/tests/bug39036.phpt b/Zend/tests/foreach/bug39036.phpt
similarity index 100%
rename from Zend/tests/bug39036.phpt
rename to Zend/tests/foreach/bug39036.phpt
diff --git a/Zend/tests/bug39825.phpt b/Zend/tests/foreach/bug39825.phpt
similarity index 100%
rename from Zend/tests/bug39825.phpt
rename to Zend/tests/foreach/bug39825.phpt
diff --git a/Zend/tests/bug39990.phpt b/Zend/tests/foreach/bug39990.phpt
similarity index 100%
rename from Zend/tests/bug39990.phpt
rename to Zend/tests/foreach/bug39990.phpt
diff --git a/Zend/tests/bug40705.phpt b/Zend/tests/foreach/bug40705.phpt
similarity index 100%
rename from Zend/tests/bug40705.phpt
rename to Zend/tests/foreach/bug40705.phpt
diff --git a/Zend/tests/bug41351.phpt b/Zend/tests/foreach/bug41351.phpt
similarity index 100%
rename from Zend/tests/bug41351.phpt
rename to Zend/tests/foreach/bug41351.phpt
diff --git a/Zend/tests/bug41351_2.phpt b/Zend/tests/foreach/bug41351_2.phpt
similarity index 100%
rename from Zend/tests/bug41351_2.phpt
rename to Zend/tests/foreach/bug41351_2.phpt
diff --git a/Zend/tests/bug41351_3.phpt b/Zend/tests/foreach/bug41351_3.phpt
similarity index 100%
rename from Zend/tests/bug41351_3.phpt
rename to Zend/tests/foreach/bug41351_3.phpt
diff --git a/Zend/tests/bug41929.phpt b/Zend/tests/foreach/bug41929.phpt
similarity index 100%
rename from Zend/tests/bug41929.phpt
rename to Zend/tests/foreach/bug41929.phpt
diff --git a/Zend/tests/bug67633.phpt b/Zend/tests/foreach/bug67633.phpt
similarity index 100%
rename from Zend/tests/bug67633.phpt
rename to Zend/tests/foreach/bug67633.phpt
diff --git a/Zend/tests/bug68215.phpt b/Zend/tests/foreach/bug68215.phpt
similarity index 100%
rename from Zend/tests/bug68215.phpt
rename to Zend/tests/foreach/bug68215.phpt
diff --git a/Zend/tests/bug73792.phpt b/Zend/tests/foreach/bug73792.phpt
similarity index 100%
rename from Zend/tests/bug73792.phpt
rename to Zend/tests/foreach/bug73792.phpt
diff --git a/Zend/tests/bug76800.phpt b/Zend/tests/foreach/bug76800.phpt
similarity index 100%
rename from Zend/tests/bug76800.phpt
rename to Zend/tests/foreach/bug76800.phpt
diff --git a/Zend/tests/foreach.phpt b/Zend/tests/foreach/foreach.phpt
similarity index 100%
rename from Zend/tests/foreach.phpt
rename to Zend/tests/foreach/foreach.phpt
diff --git a/Zend/tests/foreach_002.phpt b/Zend/tests/foreach/foreach_002.phpt
similarity index 100%
rename from Zend/tests/foreach_002.phpt
rename to Zend/tests/foreach/foreach_002.phpt
diff --git a/Zend/tests/foreach_003.phpt b/Zend/tests/foreach/foreach_003.phpt
similarity index 100%
rename from Zend/tests/foreach_003.phpt
rename to Zend/tests/foreach/foreach_003.phpt
diff --git a/Zend/tests/foreach_005.phpt b/Zend/tests/foreach/foreach_005.phpt
similarity index 100%
rename from Zend/tests/foreach_005.phpt
rename to Zend/tests/foreach/foreach_005.phpt
diff --git a/Zend/tests/foreach_006.phpt b/Zend/tests/foreach/foreach_006.phpt
similarity index 100%
rename from Zend/tests/foreach_006.phpt
rename to Zend/tests/foreach/foreach_006.phpt
diff --git a/Zend/tests/foreach_007.phpt b/Zend/tests/foreach/foreach_007.phpt
similarity index 100%
rename from Zend/tests/foreach_007.phpt
rename to Zend/tests/foreach/foreach_007.phpt
diff --git a/Zend/tests/foreach_008.phpt b/Zend/tests/foreach/foreach_008.phpt
similarity index 100%
rename from Zend/tests/foreach_008.phpt
rename to Zend/tests/foreach/foreach_008.phpt
diff --git a/Zend/tests/foreach_009.phpt b/Zend/tests/foreach/foreach_009.phpt
similarity index 100%
rename from Zend/tests/foreach_009.phpt
rename to Zend/tests/foreach/foreach_009.phpt
diff --git a/Zend/tests/foreach_010.phpt b/Zend/tests/foreach/foreach_010.phpt
similarity index 100%
rename from Zend/tests/foreach_010.phpt
rename to Zend/tests/foreach/foreach_010.phpt
diff --git a/Zend/tests/foreach_011.phpt b/Zend/tests/foreach/foreach_011.phpt
similarity index 100%
rename from Zend/tests/foreach_011.phpt
rename to Zend/tests/foreach/foreach_011.phpt
diff --git a/Zend/tests/foreach_012.phpt b/Zend/tests/foreach/foreach_012.phpt
similarity index 100%
rename from Zend/tests/foreach_012.phpt
rename to Zend/tests/foreach/foreach_012.phpt
diff --git a/Zend/tests/foreach_013.phpt b/Zend/tests/foreach/foreach_013.phpt
similarity index 100%
rename from Zend/tests/foreach_013.phpt
rename to Zend/tests/foreach/foreach_013.phpt
diff --git a/Zend/tests/foreach_014.phpt b/Zend/tests/foreach/foreach_014.phpt
similarity index 100%
rename from Zend/tests/foreach_014.phpt
rename to Zend/tests/foreach/foreach_014.phpt
diff --git a/Zend/tests/foreach_015.phpt b/Zend/tests/foreach/foreach_015.phpt
similarity index 100%
rename from Zend/tests/foreach_015.phpt
rename to Zend/tests/foreach/foreach_015.phpt
diff --git a/Zend/tests/foreach_016.phpt b/Zend/tests/foreach/foreach_016.phpt
similarity index 100%
rename from Zend/tests/foreach_016.phpt
rename to Zend/tests/foreach/foreach_016.phpt
diff --git a/Zend/tests/foreach_017.phpt b/Zend/tests/foreach/foreach_017.phpt
similarity index 100%
rename from Zend/tests/foreach_017.phpt
rename to Zend/tests/foreach/foreach_017.phpt
diff --git a/Zend/tests/foreach_018.phpt b/Zend/tests/foreach/foreach_018.phpt
similarity index 100%
rename from Zend/tests/foreach_018.phpt
rename to Zend/tests/foreach/foreach_018.phpt
diff --git a/Zend/tests/foreach_by_ref_repacking_insert.phpt b/Zend/tests/foreach/foreach_by_ref_repacking_insert.phpt
similarity index 100%
rename from Zend/tests/foreach_by_ref_repacking_insert.phpt
rename to Zend/tests/foreach/foreach_by_ref_repacking_insert.phpt
diff --git a/Zend/tests/foreach_by_ref_to_property.phpt b/Zend/tests/foreach/foreach_by_ref_to_property.phpt
similarity index 100%
rename from Zend/tests/foreach_by_ref_to_property.phpt
rename to Zend/tests/foreach/foreach_by_ref_to_property.phpt
diff --git a/Zend/tests/foreach_empty_loop_leak.phpt b/Zend/tests/foreach/foreach_empty_loop_leak.phpt
similarity index 100%
rename from Zend/tests/foreach_empty_loop_leak.phpt
rename to Zend/tests/foreach/foreach_empty_loop_leak.phpt
diff --git a/Zend/tests/foreach_list_001.phpt b/Zend/tests/foreach/foreach_list_001.phpt
similarity index 100%
rename from Zend/tests/foreach_list_001.phpt
rename to Zend/tests/foreach/foreach_list_001.phpt
diff --git a/Zend/tests/foreach_list_002.phpt b/Zend/tests/foreach/foreach_list_002.phpt
similarity index 100%
rename from Zend/tests/foreach_list_002.phpt
rename to Zend/tests/foreach/foreach_list_002.phpt
diff --git a/Zend/tests/foreach_list_003.phpt b/Zend/tests/foreach/foreach_list_003.phpt
similarity index 100%
rename from Zend/tests/foreach_list_003.phpt
rename to Zend/tests/foreach/foreach_list_003.phpt
diff --git a/Zend/tests/foreach_list_004.phpt b/Zend/tests/foreach/foreach_list_004.phpt
similarity index 100%
rename from Zend/tests/foreach_list_004.phpt
rename to Zend/tests/foreach/foreach_list_004.phpt
diff --git a/Zend/tests/foreach_list_keyed.phpt b/Zend/tests/foreach/foreach_list_keyed.phpt
similarity index 100%
rename from Zend/tests/foreach_list_keyed.phpt
rename to Zend/tests/foreach/foreach_list_keyed.phpt
diff --git a/Zend/tests/foreach_over_null.phpt b/Zend/tests/foreach/foreach_over_null.phpt
similarity index 100%
rename from Zend/tests/foreach_over_null.phpt
rename to Zend/tests/foreach/foreach_over_null.phpt
diff --git a/Zend/tests/foreach_reference.phpt b/Zend/tests/foreach/foreach_reference.phpt
similarity index 100%
rename from Zend/tests/foreach_reference.phpt
rename to Zend/tests/foreach/foreach_reference.phpt
diff --git a/Zend/tests/foreach_shadowed_dyn_property.phpt b/Zend/tests/foreach/foreach_shadowed_dyn_property.phpt
similarity index 100%
rename from Zend/tests/foreach_shadowed_dyn_property.phpt
rename to Zend/tests/foreach/foreach_shadowed_dyn_property.phpt
diff --git a/Zend/tests/foreach_shadowed_property.phpt b/Zend/tests/foreach/foreach_shadowed_property.phpt
similarity index 100%
rename from Zend/tests/foreach_shadowed_property.phpt
rename to Zend/tests/foreach/foreach_shadowed_property.phpt
diff --git a/Zend/tests/foreach_temp_array_expr_with_refs.phpt b/Zend/tests/foreach/foreach_temp_array_expr_with_refs.phpt
similarity index 100%
rename from Zend/tests/foreach_temp_array_expr_with_refs.phpt
rename to Zend/tests/foreach/foreach_temp_array_expr_with_refs.phpt
diff --git a/Zend/tests/foreach_undefined.phpt b/Zend/tests/foreach/foreach_undefined.phpt
similarity index 100%
rename from Zend/tests/foreach_undefined.phpt
rename to Zend/tests/foreach/foreach_undefined.phpt
diff --git a/Zend/tests/foreach_unset_globals.phpt b/Zend/tests/foreach/foreach_unset_globals.phpt
similarity index 100%
rename from Zend/tests/foreach_unset_globals.phpt
rename to Zend/tests/foreach/foreach_unset_globals.phpt
diff --git a/Zend/tests/gh11222.phpt b/Zend/tests/foreach/gh11222.phpt
similarity index 100%
rename from Zend/tests/gh11222.phpt
rename to Zend/tests/foreach/gh11222.phpt
diff --git a/Zend/tests/goto_in_foreach.phpt b/Zend/tests/foreach/goto_in_foreach.phpt
similarity index 100%
rename from Zend/tests/goto_in_foreach.phpt
rename to Zend/tests/foreach/goto_in_foreach.phpt
diff --git a/Zend/tests/this_in_foreach_001.phpt b/Zend/tests/foreach/this_in_foreach_001.phpt
similarity index 100%
rename from Zend/tests/this_in_foreach_001.phpt
rename to Zend/tests/foreach/this_in_foreach_001.phpt
diff --git a/Zend/tests/this_in_foreach_002.phpt b/Zend/tests/foreach/this_in_foreach_002.phpt
similarity index 100%
rename from Zend/tests/this_in_foreach_002.phpt
rename to Zend/tests/foreach/this_in_foreach_002.phpt
diff --git a/Zend/tests/this_in_foreach_003.phpt b/Zend/tests/foreach/this_in_foreach_003.phpt
similarity index 100%
rename from Zend/tests/this_in_foreach_003.phpt
rename to Zend/tests/foreach/this_in_foreach_003.phpt
diff --git a/Zend/tests/this_in_foreach_004.phpt b/Zend/tests/foreach/this_in_foreach_004.phpt
similarity index 100%
rename from Zend/tests/this_in_foreach_004.phpt
rename to Zend/tests/foreach/this_in_foreach_004.phpt
diff --git a/Zend/tests/function_arguments_001.phpt b/Zend/tests/function_arguments/function_arguments_001.phpt
similarity index 100%
rename from Zend/tests/function_arguments_001.phpt
rename to Zend/tests/function_arguments/function_arguments_001.phpt
diff --git a/Zend/tests/function_arguments_002.phpt b/Zend/tests/function_arguments/function_arguments_002.phpt
similarity index 100%
rename from Zend/tests/function_arguments_002.phpt
rename to Zend/tests/function_arguments/function_arguments_002.phpt
diff --git a/Zend/tests/function_arguments_003.phpt b/Zend/tests/function_arguments/function_arguments_003.phpt
similarity index 100%
rename from Zend/tests/function_arguments_003.phpt
rename to Zend/tests/function_arguments/function_arguments_003.phpt
diff --git a/Zend/tests/function_default_argument_cache.phpt b/Zend/tests/function_arguments/function_default_argument_cache.phpt
similarity index 100%
rename from Zend/tests/function_default_argument_cache.phpt
rename to Zend/tests/function_arguments/function_default_argument_cache.phpt
diff --git a/Zend/tests/bug43918.phpt b/Zend/tests/gc/bug43918.phpt
similarity index 100%
rename from Zend/tests/bug43918.phpt
rename to Zend/tests/gc/bug43918.phpt
diff --git a/Zend/tests/bug47343.phpt b/Zend/tests/gc/bug47343.phpt
similarity index 100%
rename from Zend/tests/bug47343.phpt
rename to Zend/tests/gc/bug47343.phpt
diff --git a/Zend/tests/bug54305.phpt b/Zend/tests/gc/bug54305.phpt
similarity index 100%
rename from Zend/tests/bug54305.phpt
rename to Zend/tests/gc/bug54305.phpt
diff --git a/Zend/tests/bug60138.phpt b/Zend/tests/gc/bug60138.phpt
similarity index 100%
rename from Zend/tests/bug60138.phpt
rename to Zend/tests/gc/bug60138.phpt
diff --git a/Zend/tests/bug60139.phpt b/Zend/tests/gc/bug60139.phpt
similarity index 100%
rename from Zend/tests/bug60139.phpt
rename to Zend/tests/gc/bug60139.phpt
diff --git a/Zend/tests/bug63055.phpt b/Zend/tests/gc/bug63055.phpt
similarity index 100%
rename from Zend/tests/bug63055.phpt
rename to Zend/tests/gc/bug63055.phpt
diff --git a/Zend/tests/bug63635.phpt b/Zend/tests/gc/bug63635.phpt
similarity index 100%
rename from Zend/tests/bug63635.phpt
rename to Zend/tests/gc/bug63635.phpt
diff --git a/Zend/tests/bug63734.phpt b/Zend/tests/gc/bug63734.phpt
similarity index 100%
rename from Zend/tests/bug63734.phpt
rename to Zend/tests/gc/bug63734.phpt
diff --git a/Zend/tests/bug64896.phpt b/Zend/tests/gc/bug64896.phpt
similarity index 100%
rename from Zend/tests/bug64896.phpt
rename to Zend/tests/gc/bug64896.phpt
diff --git a/Zend/tests/bug64960.phpt b/Zend/tests/gc/bug64960.phpt
similarity index 100%
rename from Zend/tests/bug64960.phpt
rename to Zend/tests/gc/bug64960.phpt
diff --git a/Zend/tests/bug65372.phpt b/Zend/tests/gc/bug65372.phpt
similarity index 100%
rename from Zend/tests/bug65372.phpt
rename to Zend/tests/gc/bug65372.phpt
diff --git a/Zend/tests/bug67314.phpt b/Zend/tests/gc/bug67314.phpt
similarity index 100%
rename from Zend/tests/bug67314.phpt
rename to Zend/tests/gc/bug67314.phpt
diff --git a/Zend/tests/bug69446.phpt b/Zend/tests/gc/bug69446.phpt
similarity index 100%
rename from Zend/tests/bug69446.phpt
rename to Zend/tests/gc/bug69446.phpt
diff --git a/Zend/tests/bug69446_2.phpt b/Zend/tests/gc/bug69446_2.phpt
similarity index 100%
rename from Zend/tests/bug69446_2.phpt
rename to Zend/tests/gc/bug69446_2.phpt
diff --git a/Zend/tests/bug70805.phpt b/Zend/tests/gc/bug70805.phpt
similarity index 100%
rename from Zend/tests/bug70805.phpt
rename to Zend/tests/gc/bug70805.phpt
diff --git a/Zend/tests/bug70805_1.phpt b/Zend/tests/gc/bug70805_1.phpt
similarity index 100%
rename from Zend/tests/bug70805_1.phpt
rename to Zend/tests/gc/bug70805_1.phpt
diff --git a/Zend/tests/bug70805_2.phpt b/Zend/tests/gc/bug70805_2.phpt
similarity index 100%
rename from Zend/tests/bug70805_2.phpt
rename to Zend/tests/gc/bug70805_2.phpt
diff --git a/Zend/tests/bug72530.phpt b/Zend/tests/gc/bug72530.phpt
similarity index 100%
rename from Zend/tests/bug72530.phpt
rename to Zend/tests/gc/bug72530.phpt
diff --git a/Zend/tests/bug77345_gc_1.phpt b/Zend/tests/gc/bug77345_gc_1.phpt
similarity index 100%
rename from Zend/tests/bug77345_gc_1.phpt
rename to Zend/tests/gc/bug77345_gc_1.phpt
diff --git a/Zend/tests/bug77345_gc_2.phpt b/Zend/tests/gc/bug77345_gc_2.phpt
similarity index 100%
rename from Zend/tests/bug77345_gc_2.phpt
rename to Zend/tests/gc/bug77345_gc_2.phpt
diff --git a/Zend/tests/bug78010.phpt b/Zend/tests/gc/bug78010.phpt
similarity index 100%
rename from Zend/tests/bug78010.phpt
rename to Zend/tests/gc/bug78010.phpt
diff --git a/Zend/tests/bug78379.phpt b/Zend/tests/gc/bug78379.phpt
similarity index 100%
rename from Zend/tests/bug78379.phpt
rename to Zend/tests/gc/bug78379.phpt
diff --git a/Zend/tests/bug78379_2.phpt b/Zend/tests/gc/bug78379_2.phpt
similarity index 100%
rename from Zend/tests/bug78379_2.phpt
rename to Zend/tests/gc/bug78379_2.phpt
diff --git a/Zend/tests/bug78589.phpt b/Zend/tests/gc/bug78589.phpt
similarity index 100%
rename from Zend/tests/bug78589.phpt
rename to Zend/tests/gc/bug78589.phpt
diff --git a/Zend/tests/bug78752.phpt b/Zend/tests/gc/bug78752.phpt
similarity index 100%
rename from Zend/tests/bug78752.phpt
rename to Zend/tests/gc/bug78752.phpt
diff --git a/Zend/tests/bug78999.phpt b/Zend/tests/gc/bug78999.phpt
similarity index 100%
rename from Zend/tests/bug78999.phpt
rename to Zend/tests/gc/bug78999.phpt
diff --git a/Zend/tests/bug79900.phpt b/Zend/tests/gc/bug79900.phpt
similarity index 100%
rename from Zend/tests/bug79900.phpt
rename to Zend/tests/gc/bug79900.phpt
diff --git a/Zend/tests/bug80072.phpt b/Zend/tests/gc/bug80072.phpt
similarity index 100%
rename from Zend/tests/bug80072.phpt
rename to Zend/tests/gc/bug80072.phpt
diff --git a/Zend/tests/gc_001.phpt b/Zend/tests/gc/gc_001.phpt
similarity index 100%
rename from Zend/tests/gc_001.phpt
rename to Zend/tests/gc/gc_001.phpt
diff --git a/Zend/tests/gc_002.phpt b/Zend/tests/gc/gc_002.phpt
similarity index 100%
rename from Zend/tests/gc_002.phpt
rename to Zend/tests/gc/gc_002.phpt
diff --git a/Zend/tests/gc_003.phpt b/Zend/tests/gc/gc_003.phpt
similarity index 100%
rename from Zend/tests/gc_003.phpt
rename to Zend/tests/gc/gc_003.phpt
diff --git a/Zend/tests/gc_004.phpt b/Zend/tests/gc/gc_004.phpt
similarity index 100%
rename from Zend/tests/gc_004.phpt
rename to Zend/tests/gc/gc_004.phpt
diff --git a/Zend/tests/gc_005.phpt b/Zend/tests/gc/gc_005.phpt
similarity index 100%
rename from Zend/tests/gc_005.phpt
rename to Zend/tests/gc/gc_005.phpt
diff --git a/Zend/tests/gc_006.phpt b/Zend/tests/gc/gc_006.phpt
similarity index 100%
rename from Zend/tests/gc_006.phpt
rename to Zend/tests/gc/gc_006.phpt
diff --git a/Zend/tests/gc_007.phpt b/Zend/tests/gc/gc_007.phpt
similarity index 100%
rename from Zend/tests/gc_007.phpt
rename to Zend/tests/gc/gc_007.phpt
diff --git a/Zend/tests/gc_008.phpt b/Zend/tests/gc/gc_008.phpt
similarity index 100%
rename from Zend/tests/gc_008.phpt
rename to Zend/tests/gc/gc_008.phpt
diff --git a/Zend/tests/gc_009.phpt b/Zend/tests/gc/gc_009.phpt
similarity index 100%
rename from Zend/tests/gc_009.phpt
rename to Zend/tests/gc/gc_009.phpt
diff --git a/Zend/tests/gc_011.phpt b/Zend/tests/gc/gc_011.phpt
similarity index 100%
rename from Zend/tests/gc_011.phpt
rename to Zend/tests/gc/gc_011.phpt
diff --git a/Zend/tests/gc_012.phpt b/Zend/tests/gc/gc_012.phpt
similarity index 100%
rename from Zend/tests/gc_012.phpt
rename to Zend/tests/gc/gc_012.phpt
diff --git a/Zend/tests/gc_013.phpt b/Zend/tests/gc/gc_013.phpt
similarity index 100%
rename from Zend/tests/gc_013.phpt
rename to Zend/tests/gc/gc_013.phpt
diff --git a/Zend/tests/gc_014.phpt b/Zend/tests/gc/gc_014.phpt
similarity index 100%
rename from Zend/tests/gc_014.phpt
rename to Zend/tests/gc/gc_014.phpt
diff --git a/Zend/tests/gc_015.phpt b/Zend/tests/gc/gc_015.phpt
similarity index 100%
rename from Zend/tests/gc_015.phpt
rename to Zend/tests/gc/gc_015.phpt
diff --git a/Zend/tests/gc_016.phpt b/Zend/tests/gc/gc_016.phpt
similarity index 100%
rename from Zend/tests/gc_016.phpt
rename to Zend/tests/gc/gc_016.phpt
diff --git a/Zend/tests/gc_017.phpt b/Zend/tests/gc/gc_017.phpt
similarity index 100%
rename from Zend/tests/gc_017.phpt
rename to Zend/tests/gc/gc_017.phpt
diff --git a/Zend/tests/gc_018.phpt b/Zend/tests/gc/gc_018.phpt
similarity index 100%
rename from Zend/tests/gc_018.phpt
rename to Zend/tests/gc/gc_018.phpt
diff --git a/Zend/tests/gc_019.phpt b/Zend/tests/gc/gc_019.phpt
similarity index 100%
rename from Zend/tests/gc_019.phpt
rename to Zend/tests/gc/gc_019.phpt
diff --git a/Zend/tests/gc_020.phpt b/Zend/tests/gc/gc_020.phpt
similarity index 100%
rename from Zend/tests/gc_020.phpt
rename to Zend/tests/gc/gc_020.phpt
diff --git a/Zend/tests/gc_021.phpt b/Zend/tests/gc/gc_021.phpt
similarity index 100%
rename from Zend/tests/gc_021.phpt
rename to Zend/tests/gc/gc_021.phpt
diff --git a/Zend/tests/gc_022.phpt b/Zend/tests/gc/gc_022.phpt
similarity index 100%
rename from Zend/tests/gc_022.phpt
rename to Zend/tests/gc/gc_022.phpt
diff --git a/Zend/tests/gc_023.phpt b/Zend/tests/gc/gc_023.phpt
similarity index 100%
rename from Zend/tests/gc_023.phpt
rename to Zend/tests/gc/gc_023.phpt
diff --git a/Zend/tests/gc_024.phpt b/Zend/tests/gc/gc_024.phpt
similarity index 100%
rename from Zend/tests/gc_024.phpt
rename to Zend/tests/gc/gc_024.phpt
diff --git a/Zend/tests/gc_025.phpt b/Zend/tests/gc/gc_025.phpt
similarity index 100%
rename from Zend/tests/gc_025.phpt
rename to Zend/tests/gc/gc_025.phpt
diff --git a/Zend/tests/gc_026.phpt b/Zend/tests/gc/gc_026.phpt
similarity index 100%
rename from Zend/tests/gc_026.phpt
rename to Zend/tests/gc/gc_026.phpt
diff --git a/Zend/tests/gc_027.phpt b/Zend/tests/gc/gc_027.phpt
similarity index 100%
rename from Zend/tests/gc_027.phpt
rename to Zend/tests/gc/gc_027.phpt
diff --git a/Zend/tests/gc_028.phpt b/Zend/tests/gc/gc_028.phpt
similarity index 100%
rename from Zend/tests/gc_028.phpt
rename to Zend/tests/gc/gc_028.phpt
diff --git a/Zend/tests/gc_029.phpt b/Zend/tests/gc/gc_029.phpt
similarity index 100%
rename from Zend/tests/gc_029.phpt
rename to Zend/tests/gc/gc_029.phpt
diff --git a/Zend/tests/gc_030.phpt b/Zend/tests/gc/gc_030.phpt
similarity index 100%
rename from Zend/tests/gc_030.phpt
rename to Zend/tests/gc/gc_030.phpt
diff --git a/Zend/tests/gc_031.phpt b/Zend/tests/gc/gc_031.phpt
similarity index 100%
rename from Zend/tests/gc_031.phpt
rename to Zend/tests/gc/gc_031.phpt
diff --git a/Zend/tests/gc_032.phpt b/Zend/tests/gc/gc_032.phpt
similarity index 100%
rename from Zend/tests/gc_032.phpt
rename to Zend/tests/gc/gc_032.phpt
diff --git a/Zend/tests/gc_033.phpt b/Zend/tests/gc/gc_033.phpt
similarity index 100%
rename from Zend/tests/gc_033.phpt
rename to Zend/tests/gc/gc_033.phpt
diff --git a/Zend/tests/gc_034.phpt b/Zend/tests/gc/gc_034.phpt
similarity index 100%
rename from Zend/tests/gc_034.phpt
rename to Zend/tests/gc/gc_034.phpt
diff --git a/Zend/tests/gc_035.phpt b/Zend/tests/gc/gc_035.phpt
similarity index 100%
rename from Zend/tests/gc_035.phpt
rename to Zend/tests/gc/gc_035.phpt
diff --git a/Zend/tests/gc_036.phpt b/Zend/tests/gc/gc_036.phpt
similarity index 100%
rename from Zend/tests/gc_036.phpt
rename to Zend/tests/gc/gc_036.phpt
diff --git a/Zend/tests/gc_037.phpt b/Zend/tests/gc/gc_037.phpt
similarity index 100%
rename from Zend/tests/gc_037.phpt
rename to Zend/tests/gc/gc_037.phpt
diff --git a/Zend/tests/gc_038.phpt b/Zend/tests/gc/gc_038.phpt
similarity index 100%
rename from Zend/tests/gc_038.phpt
rename to Zend/tests/gc/gc_038.phpt
diff --git a/Zend/tests/gc_039.phpt b/Zend/tests/gc/gc_039.phpt
similarity index 100%
rename from Zend/tests/gc_039.phpt
rename to Zend/tests/gc/gc_039.phpt
diff --git a/Zend/tests/gc_041.phpt b/Zend/tests/gc/gc_041.phpt
similarity index 100%
rename from Zend/tests/gc_041.phpt
rename to Zend/tests/gc/gc_041.phpt
diff --git a/Zend/tests/gc_042.phpt b/Zend/tests/gc/gc_042.phpt
similarity index 100%
rename from Zend/tests/gc_042.phpt
rename to Zend/tests/gc/gc_042.phpt
diff --git a/Zend/tests/gc_043.phpt b/Zend/tests/gc/gc_043.phpt
similarity index 100%
rename from Zend/tests/gc_043.phpt
rename to Zend/tests/gc/gc_043.phpt
diff --git a/Zend/tests/gc_044.phpt b/Zend/tests/gc/gc_044.phpt
similarity index 100%
rename from Zend/tests/gc_044.phpt
rename to Zend/tests/gc/gc_044.phpt
diff --git a/Zend/tests/gc_045.phpt b/Zend/tests/gc/gc_045.phpt
similarity index 100%
rename from Zend/tests/gc_045.phpt
rename to Zend/tests/gc/gc_045.phpt
diff --git a/Zend/tests/gc_046.phpt b/Zend/tests/gc/gc_046.phpt
similarity index 100%
rename from Zend/tests/gc_046.phpt
rename to Zend/tests/gc/gc_046.phpt
diff --git a/Zend/tests/gc_047.phpt b/Zend/tests/gc/gc_047.phpt
similarity index 100%
rename from Zend/tests/gc_047.phpt
rename to Zend/tests/gc/gc_047.phpt
diff --git a/Zend/tests/gc_048.phpt b/Zend/tests/gc/gc_048.phpt
similarity index 100%
rename from Zend/tests/gc_048.phpt
rename to Zend/tests/gc/gc_048.phpt
diff --git a/Zend/tests/gc_049.phpt b/Zend/tests/gc/gc_049.phpt
similarity index 100%
rename from Zend/tests/gc_049.phpt
rename to Zend/tests/gc/gc_049.phpt
diff --git a/Zend/tests/gc_050.phpt b/Zend/tests/gc/gc_050.phpt
similarity index 100%
rename from Zend/tests/gc_050.phpt
rename to Zend/tests/gc/gc_050.phpt
diff --git a/Zend/tests/bug68775.phpt b/Zend/tests/generators/bug68775.phpt
similarity index 100%
rename from Zend/tests/bug68775.phpt
rename to Zend/tests/generators/bug68775.phpt
diff --git a/Zend/tests/bug69740.phpt b/Zend/tests/generators/bug69740.phpt
similarity index 100%
rename from Zend/tests/bug69740.phpt
rename to Zend/tests/generators/bug69740.phpt
diff --git a/Zend/tests/bug69989_1.phpt b/Zend/tests/generators/bug69989_1.phpt
similarity index 100%
rename from Zend/tests/bug69989_1.phpt
rename to Zend/tests/generators/bug69989_1.phpt
diff --git a/Zend/tests/bug69989_2.phpt b/Zend/tests/generators/bug69989_2.phpt
similarity index 100%
rename from Zend/tests/bug69989_2.phpt
rename to Zend/tests/generators/bug69989_2.phpt
diff --git a/Zend/tests/bug69989_3.phpt b/Zend/tests/generators/bug69989_3.phpt
similarity index 100%
rename from Zend/tests/bug69989_3.phpt
rename to Zend/tests/generators/bug69989_3.phpt
diff --git a/Zend/tests/bug71980.phpt b/Zend/tests/generators/bug71980.phpt
similarity index 100%
rename from Zend/tests/bug71980.phpt
rename to Zend/tests/generators/bug71980.phpt
diff --git a/Zend/tests/bug72373.phpt b/Zend/tests/generators/bug72373.phpt
similarity index 100%
rename from Zend/tests/bug72373.phpt
rename to Zend/tests/generators/bug72373.phpt
diff --git a/Zend/tests/bug74840.phpt b/Zend/tests/generators/bug74840.phpt
similarity index 100%
rename from Zend/tests/bug74840.phpt
rename to Zend/tests/generators/bug74840.phpt
diff --git a/Zend/tests/bug75786.phpt b/Zend/tests/generators/bug75786.phpt
similarity index 100%
rename from Zend/tests/bug75786.phpt
rename to Zend/tests/generators/bug75786.phpt
diff --git a/Zend/tests/bug76946.phpt b/Zend/tests/generators/bug76946.phpt
similarity index 100%
rename from Zend/tests/bug76946.phpt
rename to Zend/tests/generators/bug76946.phpt
diff --git a/Zend/tests/bug79657.phpt b/Zend/tests/generators/bug79657.phpt
similarity index 100%
rename from Zend/tests/bug79657.phpt
rename to Zend/tests/generators/bug79657.phpt
diff --git a/Zend/tests/bug79927.phpt b/Zend/tests/generators/bug79927.phpt
similarity index 100%
rename from Zend/tests/bug79927.phpt
rename to Zend/tests/generators/bug79927.phpt
diff --git a/Zend/tests/gh15275-001.phpt b/Zend/tests/generators/gh15275-001.phpt
similarity index 100%
rename from Zend/tests/gh15275-001.phpt
rename to Zend/tests/generators/gh15275-001.phpt
diff --git a/Zend/tests/gh15275-002.phpt b/Zend/tests/generators/gh15275-002.phpt
similarity index 100%
rename from Zend/tests/gh15275-002.phpt
rename to Zend/tests/generators/gh15275-002.phpt
diff --git a/Zend/tests/gh15275-003.phpt b/Zend/tests/generators/gh15275-003.phpt
similarity index 100%
rename from Zend/tests/gh15275-003.phpt
rename to Zend/tests/generators/gh15275-003.phpt
diff --git a/Zend/tests/gh15275-004.phpt b/Zend/tests/generators/gh15275-004.phpt
similarity index 100%
rename from Zend/tests/gh15275-004.phpt
rename to Zend/tests/generators/gh15275-004.phpt
diff --git a/Zend/tests/gh15275-005.phpt b/Zend/tests/generators/gh15275-005.phpt
similarity index 100%
rename from Zend/tests/gh15275-005.phpt
rename to Zend/tests/generators/gh15275-005.phpt
diff --git a/Zend/tests/gh15275-006.phpt b/Zend/tests/generators/gh15275-006.phpt
similarity index 100%
rename from Zend/tests/gh15275-006.phpt
rename to Zend/tests/generators/gh15275-006.phpt
diff --git a/Zend/tests/gh15330-001.phpt b/Zend/tests/generators/gh15330-001.phpt
similarity index 100%
rename from Zend/tests/gh15330-001.phpt
rename to Zend/tests/generators/gh15330-001.phpt
diff --git a/Zend/tests/gh15330-002.phpt b/Zend/tests/generators/gh15330-002.phpt
similarity index 100%
rename from Zend/tests/gh15330-002.phpt
rename to Zend/tests/generators/gh15330-002.phpt
diff --git a/Zend/tests/gh15330-003.phpt b/Zend/tests/generators/gh15330-003.phpt
similarity index 100%
rename from Zend/tests/gh15330-003.phpt
rename to Zend/tests/generators/gh15330-003.phpt
diff --git a/Zend/tests/gh15330-004.phpt b/Zend/tests/generators/gh15330-004.phpt
similarity index 100%
rename from Zend/tests/gh15330-004.phpt
rename to Zend/tests/generators/gh15330-004.phpt
diff --git a/Zend/tests/gh15330-005.phpt b/Zend/tests/generators/gh15330-005.phpt
similarity index 100%
rename from Zend/tests/gh15330-005.phpt
rename to Zend/tests/generators/gh15330-005.phpt
diff --git a/Zend/tests/gh15330-006.phpt b/Zend/tests/generators/gh15330-006.phpt
similarity index 100%
rename from Zend/tests/gh15330-006.phpt
rename to Zend/tests/generators/gh15330-006.phpt
diff --git a/Zend/tests/generators/gh15851.phpt b/Zend/tests/generators/gh15851.phpt
index 8a7fa6294e255..dfeb7d81ed0d1 100644
--- a/Zend/tests/generators/gh15851.phpt
+++ b/Zend/tests/generators/gh15851.phpt
@@ -1,5 +1,5 @@
--TEST--
-GH-15851: Access on NULL when printing backtrace with freed generator
+GH-15851: Access on NULL when printing backtrace during cleanup of nested generator frame
--FILE--
handle(...));
+var_dump(get_error_handler() === $f);
+
+echo "\nClosure\n";
+set_error_handler($f = function () {});
+var_dump(get_error_handler() === $f);
+
+echo "\nInvokable\n";
+set_error_handler($object = new Invokable());
+var_dump(get_error_handler() === $object);
+
+echo "\nStable return value\n";
+var_dump(get_error_handler() === get_error_handler());
+
+?>
+==DONE==
+--EXPECT--
+No error handler
+bool(true)
+
+Function string
+bool(true)
+
+NULL
+bool(true)
+
+Static method array
+bool(true)
+
+Static method string
+bool(true)
+
+Instance method array
+bool(true)
+
+First class callable method
+bool(true)
+
+Closure
+bool(true)
+
+Invokable
+bool(true)
+
+Stable return value
+bool(true)
+==DONE==
diff --git a/Zend/tests/get_exception_handler.phpt b/Zend/tests/get_exception_handler.phpt
new file mode 100644
index 0000000000000..05f43db2c0f4c
--- /dev/null
+++ b/Zend/tests/get_exception_handler.phpt
@@ -0,0 +1,87 @@
+--TEST--
+get_exception_handler()
+--FILE--
+handle(...));
+var_dump(get_exception_handler() === $f);
+
+echo "\nClosure\n";
+set_exception_handler($f = function () {});
+var_dump(get_exception_handler() === $f);
+
+echo "\nInvokable\n";
+set_exception_handler($object = new Invokable());
+var_dump(get_exception_handler() === $object);
+
+echo "\nStable return value\n";
+var_dump(get_exception_handler() === get_exception_handler());
+
+?>==DONE==
+--EXPECT--
+No exception handler
+bool(true)
+
+Function string
+bool(true)
+
+NULL
+bool(true)
+
+Static method array
+bool(true)
+
+Static method string
+bool(true)
+
+Instance method array
+bool(true)
+
+First class callable method
+bool(true)
+
+Closure
+bool(true)
+
+Invokable
+bool(true)
+
+Stable return value
+bool(true)
+==DONE==
diff --git a/Zend/tests/gh18026.phpt b/Zend/tests/gh18026.phpt
new file mode 100644
index 0000000000000..0e33e349b12fe
--- /dev/null
+++ b/Zend/tests/gh18026.phpt
@@ -0,0 +1,12 @@
+--TEST--
+GH-18026: Confusing "amp" reference in parser error
+--FILE--
+
+--EXPECTF--
+Parse error: syntax error, unexpected token ")", expecting token "&" in %s on line %d
diff --git a/Zend/tests/alternative_offset_syntax_compile_error_in_const_expr.phpt b/Zend/tests/grammar/alternative_offset_syntax_compile_error_in_const_expr.phpt
similarity index 100%
rename from Zend/tests/alternative_offset_syntax_compile_error_in_const_expr.phpt
rename to Zend/tests/grammar/alternative_offset_syntax_compile_error_in_const_expr.phpt
diff --git a/Zend/tests/alternative_offset_syntax_compile_error_outside_const_expr.phpt b/Zend/tests/grammar/alternative_offset_syntax_compile_error_outside_const_expr.phpt
similarity index 100%
rename from Zend/tests/alternative_offset_syntax_compile_error_outside_const_expr.phpt
rename to Zend/tests/grammar/alternative_offset_syntax_compile_error_outside_const_expr.phpt
diff --git a/Zend/tests/alternative_offset_syntax_in_encaps_string.phpt b/Zend/tests/grammar/alternative_offset_syntax_in_encaps_string.phpt
similarity index 100%
rename from Zend/tests/alternative_offset_syntax_in_encaps_string.phpt
rename to Zend/tests/grammar/alternative_offset_syntax_in_encaps_string.phpt
diff --git a/Zend/tests/bug31341.phpt b/Zend/tests/grammar/bug31341.phpt
similarity index 100%
rename from Zend/tests/bug31341.phpt
rename to Zend/tests/grammar/bug31341.phpt
diff --git a/Zend/tests/bug35411.phpt b/Zend/tests/grammar/bug35411.phpt
similarity index 100%
rename from Zend/tests/bug35411.phpt
rename to Zend/tests/grammar/bug35411.phpt
diff --git a/Zend/tests/bug41401.phpt b/Zend/tests/grammar/bug41401.phpt
similarity index 100%
rename from Zend/tests/bug41401.phpt
rename to Zend/tests/grammar/bug41401.phpt
diff --git a/Zend/tests/bug45147.phpt b/Zend/tests/grammar/bug45147.phpt
similarity index 100%
rename from Zend/tests/bug45147.phpt
rename to Zend/tests/grammar/bug45147.phpt
diff --git a/Zend/tests/bug55445.phpt b/Zend/tests/grammar/bug55445.phpt
similarity index 100%
rename from Zend/tests/bug55445.phpt
rename to Zend/tests/grammar/bug55445.phpt
diff --git a/Zend/tests/bug60099.phpt b/Zend/tests/grammar/bug60099.phpt
similarity index 100%
rename from Zend/tests/bug60099.phpt
rename to Zend/tests/grammar/bug60099.phpt
diff --git a/Zend/tests/bug61095.phpt b/Zend/tests/grammar/bug61095.phpt
similarity index 100%
rename from Zend/tests/bug61095.phpt
rename to Zend/tests/grammar/bug61095.phpt
diff --git a/Zend/tests/bug61225.phpt b/Zend/tests/grammar/bug61225.phpt
similarity index 100%
rename from Zend/tests/bug61225.phpt
rename to Zend/tests/grammar/bug61225.phpt
diff --git a/Zend/tests/bug61681.phpt b/Zend/tests/grammar/bug61681.phpt
similarity index 100%
rename from Zend/tests/bug61681.phpt
rename to Zend/tests/grammar/bug61681.phpt
diff --git a/Zend/tests/bug70430.phpt b/Zend/tests/grammar/bug70430.phpt
similarity index 100%
rename from Zend/tests/bug70430.phpt
rename to Zend/tests/grammar/bug70430.phpt
diff --git a/Zend/tests/bug77993.phpt b/Zend/tests/grammar/bug77993.phpt
similarity index 100%
rename from Zend/tests/bug77993.phpt
rename to Zend/tests/grammar/bug77993.phpt
diff --git a/Zend/tests/bug78363.phpt b/Zend/tests/grammar/bug78363.phpt
similarity index 100%
rename from Zend/tests/bug78363.phpt
rename to Zend/tests/grammar/bug78363.phpt
diff --git a/Zend/tests/gh14961.phpt b/Zend/tests/grammar/gh14961.phpt
similarity index 100%
rename from Zend/tests/gh14961.phpt
rename to Zend/tests/grammar/gh14961.phpt
diff --git a/Zend/tests/readonly_function.phpt b/Zend/tests/grammar/readonly_function.phpt
similarity index 100%
rename from Zend/tests/readonly_function.phpt
rename to Zend/tests/grammar/readonly_function.phpt
diff --git a/Zend/tests/ns_trailing_comma_01.phpt b/Zend/tests/group_use/ns_trailing_comma_01.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_01.phpt
rename to Zend/tests/group_use/ns_trailing_comma_01.phpt
diff --git a/Zend/tests/ns_trailing_comma_02.phpt b/Zend/tests/group_use/ns_trailing_comma_02.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_02.phpt
rename to Zend/tests/group_use/ns_trailing_comma_02.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_01.phpt b/Zend/tests/group_use/ns_trailing_comma_error_01.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_01.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_01.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_02.phpt b/Zend/tests/group_use/ns_trailing_comma_error_02.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_02.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_02.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_03.phpt b/Zend/tests/group_use/ns_trailing_comma_error_03.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_03.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_03.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_04.phpt b/Zend/tests/group_use/ns_trailing_comma_error_04.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_04.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_04.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_05.phpt b/Zend/tests/group_use/ns_trailing_comma_error_05.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_05.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_05.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_06.phpt b/Zend/tests/group_use/ns_trailing_comma_error_06.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_06.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_06.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_07.phpt b/Zend/tests/group_use/ns_trailing_comma_error_07.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_07.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_07.phpt
diff --git a/Zend/tests/ns_trailing_comma_error_08.phpt b/Zend/tests/group_use/ns_trailing_comma_error_08.phpt
similarity index 100%
rename from Zend/tests/ns_trailing_comma_error_08.phpt
rename to Zend/tests/group_use/ns_trailing_comma_error_08.phpt
diff --git a/Zend/tests/bug35655.phpt b/Zend/tests/heredoc_nowdoc/bug35655.phpt
similarity index 100%
rename from Zend/tests/bug35655.phpt
rename to Zend/tests/heredoc_nowdoc/bug35655.phpt
diff --git a/Zend/tests/bug36037.phpt b/Zend/tests/heredoc_nowdoc/bug36037.phpt
similarity index 100%
rename from Zend/tests/bug36037.phpt
rename to Zend/tests/heredoc_nowdoc/bug36037.phpt
diff --git a/Zend/tests/bug44830.phpt b/Zend/tests/heredoc_nowdoc/bug44830.phpt
similarity index 100%
rename from Zend/tests/bug44830.phpt
rename to Zend/tests/heredoc_nowdoc/bug44830.phpt
diff --git a/Zend/tests/bug47516.phpt b/Zend/tests/heredoc_nowdoc/bug47516.phpt
similarity index 100%
rename from Zend/tests/bug47516.phpt
rename to Zend/tests/heredoc_nowdoc/bug47516.phpt
diff --git a/Zend/tests/bug76439.phpt b/Zend/tests/heredoc_nowdoc/bug76439.phpt
similarity index 100%
rename from Zend/tests/bug76439.phpt
rename to Zend/tests/heredoc_nowdoc/bug76439.phpt
diff --git a/Zend/tests/bug76439_2.phpt b/Zend/tests/heredoc_nowdoc/bug76439_2.phpt
similarity index 100%
rename from Zend/tests/bug76439_2.phpt
rename to Zend/tests/heredoc_nowdoc/bug76439_2.phpt
diff --git a/Zend/tests/bug79934.phpt b/Zend/tests/heredoc_nowdoc/bug79934.phpt
similarity index 100%
rename from Zend/tests/bug79934.phpt
rename to Zend/tests/heredoc_nowdoc/bug79934.phpt
diff --git a/Zend/tests/flexible-heredoc-complex-test1.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test1.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-complex-test1.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test1.phpt
diff --git a/Zend/tests/flexible-heredoc-complex-test2.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test2.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-complex-test2.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test2.phpt
diff --git a/Zend/tests/flexible-heredoc-complex-test3.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test3.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-complex-test3.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test3.phpt
diff --git a/Zend/tests/flexible-heredoc-complex-test4.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test4.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-complex-test4.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-complex-test4.phpt
diff --git a/Zend/tests/flexible-heredoc-error1.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error1.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error1.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error1.phpt
diff --git a/Zend/tests/flexible-heredoc-error10.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error10.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error10.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error10.phpt
diff --git a/Zend/tests/flexible-heredoc-error11.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error11.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error11.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error11.phpt
diff --git a/Zend/tests/flexible-heredoc-error12.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error12.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error12.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error12.phpt
diff --git a/Zend/tests/flexible-heredoc-error13.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error13.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error13.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error13.phpt
diff --git a/Zend/tests/flexible-heredoc-error2.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error2.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error2.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error2.phpt
diff --git a/Zend/tests/flexible-heredoc-error3.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error3.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error3.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error3.phpt
diff --git a/Zend/tests/flexible-heredoc-error4.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error4.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error4.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error4.phpt
diff --git a/Zend/tests/flexible-heredoc-error5.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error5.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error5.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error5.phpt
diff --git a/Zend/tests/flexible-heredoc-error6.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error6.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error6.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error6.phpt
diff --git a/Zend/tests/flexible-heredoc-error7.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error7.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error7.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error7.phpt
diff --git a/Zend/tests/flexible-heredoc-error8.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error8.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error8.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error8.phpt
diff --git a/Zend/tests/flexible-heredoc-error9.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-error9.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-error9.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-error9.phpt
diff --git a/Zend/tests/flexible-heredoc-nowdoc-lineno.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-nowdoc-lineno.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-nowdoc-lineno.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-nowdoc-lineno.phpt
diff --git a/Zend/tests/flexible-heredoc-nowdoc.phpt b/Zend/tests/heredoc_nowdoc/flexible-heredoc-nowdoc.phpt
similarity index 100%
rename from Zend/tests/flexible-heredoc-nowdoc.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-heredoc-nowdoc.phpt
diff --git a/Zend/tests/flexible-nowdoc-error1.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error1.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error1.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error1.phpt
diff --git a/Zend/tests/flexible-nowdoc-error2.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error2.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error2.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error2.phpt
diff --git a/Zend/tests/flexible-nowdoc-error3.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error3.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error3.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error3.phpt
diff --git a/Zend/tests/flexible-nowdoc-error4.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error4.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error4.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error4.phpt
diff --git a/Zend/tests/flexible-nowdoc-error5.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error5.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error5.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error5.phpt
diff --git a/Zend/tests/flexible-nowdoc-error6.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error6.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error6.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error6.phpt
diff --git a/Zend/tests/flexible-nowdoc-error7.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error7.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error7.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error7.phpt
diff --git a/Zend/tests/flexible-nowdoc-error8.phpt b/Zend/tests/heredoc_nowdoc/flexible-nowdoc-error8.phpt
similarity index 100%
rename from Zend/tests/flexible-nowdoc-error8.phpt
rename to Zend/tests/heredoc_nowdoc/flexible-nowdoc-error8.phpt
diff --git a/Zend/tests/gh16630.phpt b/Zend/tests/heredoc_nowdoc/gh16630.phpt
similarity index 100%
rename from Zend/tests/gh16630.phpt
rename to Zend/tests/heredoc_nowdoc/gh16630.phpt
diff --git a/Zend/tests/heredoc_001.phpt b/Zend/tests/heredoc_nowdoc/heredoc_001.phpt
similarity index 100%
rename from Zend/tests/heredoc_001.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_001.phpt
diff --git a/Zend/tests/heredoc_002.phpt b/Zend/tests/heredoc_nowdoc/heredoc_002.phpt
similarity index 100%
rename from Zend/tests/heredoc_002.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_002.phpt
diff --git a/Zend/tests/heredoc_003.phpt b/Zend/tests/heredoc_nowdoc/heredoc_003.phpt
similarity index 100%
rename from Zend/tests/heredoc_003.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_003.phpt
diff --git a/Zend/tests/heredoc_004.phpt b/Zend/tests/heredoc_nowdoc/heredoc_004.phpt
similarity index 100%
rename from Zend/tests/heredoc_004.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_004.phpt
diff --git a/Zend/tests/heredoc_005.phpt b/Zend/tests/heredoc_nowdoc/heredoc_005.phpt
similarity index 100%
rename from Zend/tests/heredoc_005.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_005.phpt
diff --git a/Zend/tests/heredoc_006.phpt b/Zend/tests/heredoc_nowdoc/heredoc_006.phpt
similarity index 100%
rename from Zend/tests/heredoc_006.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_006.phpt
diff --git a/Zend/tests/heredoc_007.phpt b/Zend/tests/heredoc_nowdoc/heredoc_007.phpt
similarity index 100%
rename from Zend/tests/heredoc_007.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_007.phpt
diff --git a/Zend/tests/heredoc_008.phpt b/Zend/tests/heredoc_nowdoc/heredoc_008.phpt
similarity index 100%
rename from Zend/tests/heredoc_008.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_008.phpt
diff --git a/Zend/tests/heredoc_011.phpt b/Zend/tests/heredoc_nowdoc/heredoc_011.phpt
similarity index 100%
rename from Zend/tests/heredoc_011.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_011.phpt
diff --git a/Zend/tests/heredoc_012.phpt b/Zend/tests/heredoc_nowdoc/heredoc_012.phpt
similarity index 100%
rename from Zend/tests/heredoc_012.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_012.phpt
diff --git a/Zend/tests/heredoc_013.phpt b/Zend/tests/heredoc_nowdoc/heredoc_013.phpt
similarity index 100%
rename from Zend/tests/heredoc_013.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_013.phpt
diff --git a/Zend/tests/heredoc_014.phpt b/Zend/tests/heredoc_nowdoc/heredoc_014.phpt
similarity index 100%
rename from Zend/tests/heredoc_014.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_014.phpt
diff --git a/Zend/tests/heredoc_015.phpt b/Zend/tests/heredoc_nowdoc/heredoc_015.phpt
similarity index 100%
rename from Zend/tests/heredoc_015.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_015.phpt
diff --git a/Zend/tests/heredoc_016.phpt b/Zend/tests/heredoc_nowdoc/heredoc_016.phpt
similarity index 100%
rename from Zend/tests/heredoc_016.phpt
rename to Zend/tests/heredoc_nowdoc/heredoc_016.phpt
diff --git a/Zend/tests/nowdoc.inc b/Zend/tests/heredoc_nowdoc/nowdoc.inc
similarity index 100%
rename from Zend/tests/nowdoc.inc
rename to Zend/tests/heredoc_nowdoc/nowdoc.inc
diff --git a/Zend/tests/nowdoc_001.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_001.phpt
similarity index 100%
rename from Zend/tests/nowdoc_001.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_001.phpt
diff --git a/Zend/tests/nowdoc_002.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_002.phpt
similarity index 100%
rename from Zend/tests/nowdoc_002.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_002.phpt
diff --git a/Zend/tests/nowdoc_003.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_003.phpt
similarity index 100%
rename from Zend/tests/nowdoc_003.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_003.phpt
diff --git a/Zend/tests/nowdoc_004.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_004.phpt
similarity index 100%
rename from Zend/tests/nowdoc_004.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_004.phpt
diff --git a/Zend/tests/nowdoc_005.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_005.phpt
similarity index 100%
rename from Zend/tests/nowdoc_005.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_005.phpt
diff --git a/Zend/tests/nowdoc_006.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_006.phpt
similarity index 100%
rename from Zend/tests/nowdoc_006.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_006.phpt
diff --git a/Zend/tests/nowdoc_007.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_007.phpt
similarity index 100%
rename from Zend/tests/nowdoc_007.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_007.phpt
diff --git a/Zend/tests/nowdoc_008.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_008.phpt
similarity index 100%
rename from Zend/tests/nowdoc_008.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_008.phpt
diff --git a/Zend/tests/nowdoc_011.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_011.phpt
similarity index 100%
rename from Zend/tests/nowdoc_011.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_011.phpt
diff --git a/Zend/tests/nowdoc_012.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_012.phpt
similarity index 100%
rename from Zend/tests/nowdoc_012.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_012.phpt
diff --git a/Zend/tests/nowdoc_013.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_013.phpt
similarity index 100%
rename from Zend/tests/nowdoc_013.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_013.phpt
diff --git a/Zend/tests/nowdoc_014.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_014.phpt
similarity index 100%
rename from Zend/tests/nowdoc_014.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_014.phpt
diff --git a/Zend/tests/nowdoc_015.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_015.phpt
similarity index 100%
rename from Zend/tests/nowdoc_015.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_015.phpt
diff --git a/Zend/tests/nowdoc_016.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_016.phpt
similarity index 100%
rename from Zend/tests/nowdoc_016.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_016.phpt
diff --git a/Zend/tests/nowdoc_017.phpt b/Zend/tests/heredoc_nowdoc/nowdoc_017.phpt
similarity index 100%
rename from Zend/tests/nowdoc_017.phpt
rename to Zend/tests/heredoc_nowdoc/nowdoc_017.phpt
diff --git a/Zend/tests/warning_during_heredoc_scan_ahead.phpt b/Zend/tests/heredoc_nowdoc/warning_during_heredoc_scan_ahead.phpt
similarity index 100%
rename from Zend/tests/warning_during_heredoc_scan_ahead.phpt
rename to Zend/tests/heredoc_nowdoc/warning_during_heredoc_scan_ahead.phpt
diff --git a/Zend/tests/oss_fuzz_63802.phpt b/Zend/tests/in-de-crement/oss_fuzz_63802.phpt
similarity index 100%
rename from Zend/tests/oss_fuzz_63802.phpt
rename to Zend/tests/in-de-crement/oss_fuzz_63802.phpt
diff --git a/Zend/tests/post_inc_without_use.phpt b/Zend/tests/in-de-crement/post_inc_without_use.phpt
similarity index 100%
rename from Zend/tests/post_inc_without_use.phpt
rename to Zend/tests/in-de-crement/post_inc_without_use.phpt
diff --git a/Zend/tests/indirect_call_array_001.phpt b/Zend/tests/indirect_function_call/indirect_call_array_001.phpt
similarity index 100%
rename from Zend/tests/indirect_call_array_001.phpt
rename to Zend/tests/indirect_function_call/indirect_call_array_001.phpt
diff --git a/Zend/tests/indirect_call_array_002.phpt b/Zend/tests/indirect_function_call/indirect_call_array_002.phpt
similarity index 100%
rename from Zend/tests/indirect_call_array_002.phpt
rename to Zend/tests/indirect_function_call/indirect_call_array_002.phpt
diff --git a/Zend/tests/indirect_call_array_003.phpt b/Zend/tests/indirect_function_call/indirect_call_array_003.phpt
similarity index 100%
rename from Zend/tests/indirect_call_array_003.phpt
rename to Zend/tests/indirect_function_call/indirect_call_array_003.phpt
diff --git a/Zend/tests/indirect_call_array_004.phpt b/Zend/tests/indirect_function_call/indirect_call_array_004.phpt
similarity index 100%
rename from Zend/tests/indirect_call_array_004.phpt
rename to Zend/tests/indirect_function_call/indirect_call_array_004.phpt
diff --git a/Zend/tests/indirect_call_array_005.phpt b/Zend/tests/indirect_function_call/indirect_call_array_005.phpt
similarity index 100%
rename from Zend/tests/indirect_call_array_005.phpt
rename to Zend/tests/indirect_function_call/indirect_call_array_005.phpt
diff --git a/Zend/tests/indirect_call_from_constant.phpt b/Zend/tests/indirect_function_call/indirect_call_from_constant.phpt
similarity index 100%
rename from Zend/tests/indirect_call_from_constant.phpt
rename to Zend/tests/indirect_function_call/indirect_call_from_constant.phpt
diff --git a/Zend/tests/indirect_call_string_001.phpt b/Zend/tests/indirect_function_call/indirect_call_string_001.phpt
similarity index 100%
rename from Zend/tests/indirect_call_string_001.phpt
rename to Zend/tests/indirect_function_call/indirect_call_string_001.phpt
diff --git a/Zend/tests/indirect_call_string_002.phpt b/Zend/tests/indirect_function_call/indirect_call_string_002.phpt
similarity index 100%
rename from Zend/tests/indirect_call_string_002.phpt
rename to Zend/tests/indirect_function_call/indirect_call_string_002.phpt
diff --git a/Zend/tests/indirect_call_string_003.phpt b/Zend/tests/indirect_function_call/indirect_call_string_003.phpt
similarity index 100%
rename from Zend/tests/indirect_call_string_003.phpt
rename to Zend/tests/indirect_function_call/indirect_call_string_003.phpt
diff --git a/Zend/tests/indirect_method_call_001.phpt b/Zend/tests/indirect_function_call/indirect_method_call_001.phpt
similarity index 100%
rename from Zend/tests/indirect_method_call_001.phpt
rename to Zend/tests/indirect_function_call/indirect_method_call_001.phpt
diff --git a/Zend/tests/indirect_method_call_002.phpt b/Zend/tests/indirect_function_call/indirect_method_call_002.phpt
similarity index 100%
rename from Zend/tests/indirect_method_call_002.phpt
rename to Zend/tests/indirect_function_call/indirect_method_call_002.phpt
diff --git a/Zend/tests/indirect_method_call_003.phpt b/Zend/tests/indirect_function_call/indirect_method_call_003.phpt
similarity index 100%
rename from Zend/tests/indirect_method_call_003.phpt
rename to Zend/tests/indirect_function_call/indirect_method_call_003.phpt
diff --git a/Zend/tests/indirect_method_call_004.phpt b/Zend/tests/indirect_function_call/indirect_method_call_004.phpt
similarity index 100%
rename from Zend/tests/indirect_method_call_004.phpt
rename to Zend/tests/indirect_function_call/indirect_method_call_004.phpt
diff --git a/Zend/tests/indirect_method_call_005.phpt b/Zend/tests/indirect_function_call/indirect_method_call_005.phpt
similarity index 100%
rename from Zend/tests/indirect_method_call_005.phpt
rename to Zend/tests/indirect_function_call/indirect_method_call_005.phpt
diff --git a/Zend/tests/abstract_inheritance_001.phpt b/Zend/tests/inheritance/abstract_inheritance_001.phpt
similarity index 100%
rename from Zend/tests/abstract_inheritance_001.phpt
rename to Zend/tests/inheritance/abstract_inheritance_001.phpt
diff --git a/Zend/tests/abstract_inheritance_002.phpt b/Zend/tests/inheritance/abstract_inheritance_002.phpt
similarity index 100%
rename from Zend/tests/abstract_inheritance_002.phpt
rename to Zend/tests/inheritance/abstract_inheritance_002.phpt
diff --git a/Zend/tests/abstract_inheritance_003.phpt b/Zend/tests/inheritance/abstract_inheritance_003.phpt
similarity index 100%
rename from Zend/tests/abstract_inheritance_003.phpt
rename to Zend/tests/inheritance/abstract_inheritance_003.phpt
diff --git a/Zend/tests/argument_restriction_001.phpt b/Zend/tests/inheritance/argument_restriction_001.phpt
similarity index 100%
rename from Zend/tests/argument_restriction_001.phpt
rename to Zend/tests/inheritance/argument_restriction_001.phpt
diff --git a/Zend/tests/argument_restriction_002.phpt b/Zend/tests/inheritance/argument_restriction_002.phpt
similarity index 100%
rename from Zend/tests/argument_restriction_002.phpt
rename to Zend/tests/inheritance/argument_restriction_002.phpt
diff --git a/Zend/tests/argument_restriction_003.phpt b/Zend/tests/inheritance/argument_restriction_003.phpt
similarity index 100%
rename from Zend/tests/argument_restriction_003.phpt
rename to Zend/tests/inheritance/argument_restriction_003.phpt
diff --git a/Zend/tests/argument_restriction_004.phpt b/Zend/tests/inheritance/argument_restriction_004.phpt
similarity index 100%
rename from Zend/tests/argument_restriction_004.phpt
rename to Zend/tests/inheritance/argument_restriction_004.phpt
diff --git a/Zend/tests/argument_restriction_005.phpt b/Zend/tests/inheritance/argument_restriction_005.phpt
similarity index 100%
rename from Zend/tests/argument_restriction_005.phpt
rename to Zend/tests/inheritance/argument_restriction_005.phpt
diff --git a/Zend/tests/argument_restriction_006.phpt b/Zend/tests/inheritance/argument_restriction_006.phpt
similarity index 100%
rename from Zend/tests/argument_restriction_006.phpt
rename to Zend/tests/inheritance/argument_restriction_006.phpt
diff --git a/Zend/tests/bug22725.phpt b/Zend/tests/inheritance/bug22725.phpt
similarity index 100%
rename from Zend/tests/bug22725.phpt
rename to Zend/tests/inheritance/bug22725.phpt
diff --git a/Zend/tests/bug29674.phpt b/Zend/tests/inheritance/bug29674.phpt
similarity index 100%
rename from Zend/tests/bug29674.phpt
rename to Zend/tests/inheritance/bug29674.phpt
diff --git a/Zend/tests/bug29689.phpt b/Zend/tests/inheritance/bug29689.phpt
similarity index 100%
rename from Zend/tests/bug29689.phpt
rename to Zend/tests/inheritance/bug29689.phpt
diff --git a/Zend/tests/bug30451.phpt b/Zend/tests/inheritance/bug30451.phpt
similarity index 100%
rename from Zend/tests/bug30451.phpt
rename to Zend/tests/inheritance/bug30451.phpt
diff --git a/Zend/tests/bug33277.phpt b/Zend/tests/inheritance/bug33277.phpt
similarity index 100%
rename from Zend/tests/bug33277.phpt
rename to Zend/tests/inheritance/bug33277.phpt
diff --git a/Zend/tests/bug37212.phpt b/Zend/tests/inheritance/bug37212.phpt
similarity index 100%
rename from Zend/tests/bug37212.phpt
rename to Zend/tests/inheritance/bug37212.phpt
diff --git a/Zend/tests/bug37632.phpt b/Zend/tests/inheritance/bug37632.phpt
similarity index 100%
rename from Zend/tests/bug37632.phpt
rename to Zend/tests/inheritance/bug37632.phpt
diff --git a/Zend/tests/bug38772.phpt b/Zend/tests/inheritance/bug38772.phpt
similarity index 100%
rename from Zend/tests/bug38772.phpt
rename to Zend/tests/inheritance/bug38772.phpt
diff --git a/Zend/tests/bug39721.phpt b/Zend/tests/inheritance/bug39721.phpt
similarity index 100%
rename from Zend/tests/bug39721.phpt
rename to Zend/tests/inheritance/bug39721.phpt
diff --git a/Zend/tests/bug41961.phpt b/Zend/tests/inheritance/bug41961.phpt
similarity index 100%
rename from Zend/tests/bug41961.phpt
rename to Zend/tests/inheritance/bug41961.phpt
diff --git a/Zend/tests/bug42211.phpt b/Zend/tests/inheritance/bug42211.phpt
similarity index 100%
rename from Zend/tests/bug42211.phpt
rename to Zend/tests/inheritance/bug42211.phpt
diff --git a/Zend/tests/bug43200.phpt b/Zend/tests/inheritance/bug43200.phpt
similarity index 100%
rename from Zend/tests/bug43200.phpt
rename to Zend/tests/inheritance/bug43200.phpt
diff --git a/Zend/tests/bug43200_2.phpt b/Zend/tests/inheritance/bug43200_2.phpt
similarity index 100%
rename from Zend/tests/bug43200_2.phpt
rename to Zend/tests/inheritance/bug43200_2.phpt
diff --git a/Zend/tests/bug43703.phpt b/Zend/tests/inheritance/bug43703.phpt
similarity index 100%
rename from Zend/tests/bug43703.phpt
rename to Zend/tests/inheritance/bug43703.phpt
diff --git a/Zend/tests/bug44141.phpt b/Zend/tests/inheritance/bug44141.phpt
similarity index 100%
rename from Zend/tests/bug44141.phpt
rename to Zend/tests/inheritance/bug44141.phpt
diff --git a/Zend/tests/bug44414.phpt b/Zend/tests/inheritance/bug44414.phpt
similarity index 100%
rename from Zend/tests/bug44414.phpt
rename to Zend/tests/inheritance/bug44414.phpt
diff --git a/Zend/tests/bug48215.phpt b/Zend/tests/inheritance/bug48215.phpt
similarity index 100%
rename from Zend/tests/bug48215.phpt
rename to Zend/tests/inheritance/bug48215.phpt
diff --git a/Zend/tests/bug48215_2.phpt b/Zend/tests/inheritance/bug48215_2.phpt
similarity index 100%
rename from Zend/tests/bug48215_2.phpt
rename to Zend/tests/inheritance/bug48215_2.phpt
diff --git a/Zend/tests/bug50810.phpt b/Zend/tests/inheritance/bug50810.phpt
similarity index 100%
rename from Zend/tests/bug50810.phpt
rename to Zend/tests/inheritance/bug50810.phpt
diff --git a/Zend/tests/bug53727.phpt b/Zend/tests/inheritance/bug53727.phpt
similarity index 100%
rename from Zend/tests/bug53727.phpt
rename to Zend/tests/inheritance/bug53727.phpt
diff --git a/Zend/tests/bug60161.phpt b/Zend/tests/inheritance/bug60161.phpt
similarity index 100%
rename from Zend/tests/bug60161.phpt
rename to Zend/tests/inheritance/bug60161.phpt
diff --git a/Zend/tests/bug60444.phpt b/Zend/tests/inheritance/bug60444.phpt
similarity index 100%
rename from Zend/tests/bug60444.phpt
rename to Zend/tests/inheritance/bug60444.phpt
diff --git a/Zend/tests/bug61761.phpt b/Zend/tests/inheritance/bug61761.phpt
similarity index 100%
rename from Zend/tests/bug61761.phpt
rename to Zend/tests/inheritance/bug61761.phpt
diff --git a/Zend/tests/bug62609.phpt b/Zend/tests/inheritance/bug62609.phpt
similarity index 100%
rename from Zend/tests/bug62609.phpt
rename to Zend/tests/inheritance/bug62609.phpt
diff --git a/Zend/tests/bug62609_2.phpt b/Zend/tests/inheritance/bug62609_2.phpt
similarity index 100%
rename from Zend/tests/bug62609_2.phpt
rename to Zend/tests/inheritance/bug62609_2.phpt
diff --git a/Zend/tests/bug62814.phpt b/Zend/tests/inheritance/bug62814.phpt
similarity index 100%
rename from Zend/tests/bug62814.phpt
rename to Zend/tests/inheritance/bug62814.phpt
diff --git a/Zend/tests/bug62956.phpt b/Zend/tests/inheritance/bug62956.phpt
similarity index 100%
rename from Zend/tests/bug62956.phpt
rename to Zend/tests/inheritance/bug62956.phpt
diff --git a/Zend/tests/bug63468.phpt b/Zend/tests/inheritance/bug63468.phpt
similarity index 100%
rename from Zend/tests/bug63468.phpt
rename to Zend/tests/inheritance/bug63468.phpt
diff --git a/Zend/tests/bug63816.phpt b/Zend/tests/inheritance/bug63816.phpt
similarity index 100%
rename from Zend/tests/bug63816.phpt
rename to Zend/tests/inheritance/bug63816.phpt
diff --git a/Zend/tests/bug66286.phpt b/Zend/tests/inheritance/bug66286.phpt
similarity index 100%
rename from Zend/tests/bug66286.phpt
rename to Zend/tests/inheritance/bug66286.phpt
diff --git a/Zend/tests/bug70873.phpt b/Zend/tests/inheritance/bug70873.phpt
similarity index 100%
rename from Zend/tests/bug70873.phpt
rename to Zend/tests/inheritance/bug70873.phpt
diff --git a/Zend/tests/bug70957.phpt b/Zend/tests/inheritance/bug70957.phpt
similarity index 82%
rename from Zend/tests/bug70957.phpt
rename to Zend/tests/inheritance/bug70957.phpt
index 9341fcd288d95..6e5a51d9edea4 100644
--- a/Zend/tests/bug70957.phpt
+++ b/Zend/tests/inheritance/bug70957.phpt
@@ -19,4 +19,4 @@ class B extends Foo
}
?>
--EXPECTF--
-Fatal error: Declaration of T::bar() must be compatible with Foo::bar($a = 'Foo') in %s on line %d
+Fatal error: Declaration of B::bar() must be compatible with Foo::bar($a = 'Foo') in %s on line %d
diff --git a/Zend/tests/bug70997.phpt b/Zend/tests/inheritance/bug70997.phpt
similarity index 100%
rename from Zend/tests/bug70997.phpt
rename to Zend/tests/inheritance/bug70997.phpt
diff --git a/Zend/tests/bug71248.phpt b/Zend/tests/inheritance/bug71248.phpt
similarity index 100%
rename from Zend/tests/bug71248.phpt
rename to Zend/tests/inheritance/bug71248.phpt
diff --git a/Zend/tests/bug71414.phpt b/Zend/tests/inheritance/bug71414.phpt
similarity index 100%
rename from Zend/tests/bug71414.phpt
rename to Zend/tests/inheritance/bug71414.phpt
diff --git a/Zend/tests/bug71428.1.phpt b/Zend/tests/inheritance/bug71428.1.phpt
similarity index 100%
rename from Zend/tests/bug71428.1.phpt
rename to Zend/tests/inheritance/bug71428.1.phpt
diff --git a/Zend/tests/bug71428.2.phpt b/Zend/tests/inheritance/bug71428.2.phpt
similarity index 100%
rename from Zend/tests/bug71428.2.phpt
rename to Zend/tests/inheritance/bug71428.2.phpt
diff --git a/Zend/tests/bug71428.3.phpt b/Zend/tests/inheritance/bug71428.3.phpt
similarity index 100%
rename from Zend/tests/bug71428.3.phpt
rename to Zend/tests/inheritance/bug71428.3.phpt
diff --git a/Zend/tests/bug72119.phpt b/Zend/tests/inheritance/bug72119.phpt
similarity index 100%
rename from Zend/tests/bug72119.phpt
rename to Zend/tests/inheritance/bug72119.phpt
diff --git a/Zend/tests/bug72496.phpt b/Zend/tests/inheritance/bug72496.phpt
similarity index 100%
rename from Zend/tests/bug72496.phpt
rename to Zend/tests/inheritance/bug72496.phpt
diff --git a/Zend/tests/bug73987.phpt b/Zend/tests/inheritance/bug73987.phpt
similarity index 100%
rename from Zend/tests/bug73987.phpt
rename to Zend/tests/inheritance/bug73987.phpt
diff --git a/Zend/tests/bug73987_1.phpt b/Zend/tests/inheritance/bug73987_1.phpt
similarity index 100%
rename from Zend/tests/bug73987_1.phpt
rename to Zend/tests/inheritance/bug73987_1.phpt
diff --git a/Zend/tests/bug73987_2.phpt b/Zend/tests/inheritance/bug73987_2.phpt
similarity index 100%
rename from Zend/tests/bug73987_2.phpt
rename to Zend/tests/inheritance/bug73987_2.phpt
diff --git a/Zend/tests/bug73987_3.phpt b/Zend/tests/inheritance/bug73987_3.phpt
similarity index 100%
rename from Zend/tests/bug73987_3.phpt
rename to Zend/tests/inheritance/bug73987_3.phpt
diff --git a/Zend/tests/bug78344.phpt b/Zend/tests/inheritance/bug78344.phpt
similarity index 100%
rename from Zend/tests/bug78344.phpt
rename to Zend/tests/inheritance/bug78344.phpt
diff --git a/Zend/tests/bug79862.phpt b/Zend/tests/inheritance/bug79862.phpt
similarity index 100%
rename from Zend/tests/bug79862.phpt
rename to Zend/tests/inheritance/bug79862.phpt
diff --git a/Zend/tests/bug80126.phpt b/Zend/tests/inheritance/bug80126.phpt
similarity index 100%
rename from Zend/tests/bug80126.phpt
rename to Zend/tests/inheritance/bug80126.phpt
diff --git a/Zend/tests/bug80126_2.phpt b/Zend/tests/inheritance/bug80126_2.phpt
similarity index 100%
rename from Zend/tests/bug80126_2.phpt
rename to Zend/tests/inheritance/bug80126_2.phpt
diff --git a/Zend/tests/bug80391.phpt b/Zend/tests/inheritance/bug80391.phpt
similarity index 100%
rename from Zend/tests/bug80391.phpt
rename to Zend/tests/inheritance/bug80391.phpt
diff --git a/Zend/tests/constructor_abstract_grantparent.phpt b/Zend/tests/inheritance/constructor_abstract_grantparent.phpt
similarity index 100%
rename from Zend/tests/constructor_abstract_grantparent.phpt
rename to Zend/tests/inheritance/constructor_abstract_grantparent.phpt
diff --git a/Zend/tests/deprecation_to_exception_during_inheritance.phpt b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt
similarity index 100%
rename from Zend/tests/deprecation_to_exception_during_inheritance.phpt
rename to Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt
diff --git a/Zend/tests/gh15907.phpt b/Zend/tests/inheritance/gh15907.phpt
similarity index 100%
rename from Zend/tests/gh15907.phpt
rename to Zend/tests/inheritance/gh15907.phpt
diff --git a/Zend/tests/gh16508.phpt b/Zend/tests/inheritance/gh16508.phpt
similarity index 78%
rename from Zend/tests/gh16508.phpt
rename to Zend/tests/inheritance/gh16508.phpt
index e5b89b6029040..2330cf083ce77 100644
--- a/Zend/tests/gh16508.phpt
+++ b/Zend/tests/inheritance/gh16508.phpt
@@ -17,4 +17,4 @@ abstract class Test {
?>
--EXPECTF--
-Fatal error: Class Test2 contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Test::foo) in %s on line 5
+Fatal error: Class Test2 contains 1 abstract method and must therefore be declared abstract or implement the remaining method (Test::foo) in %s on line 5
diff --git a/Zend/tests/gh9407.phpt b/Zend/tests/inheritance/gh9407.phpt
similarity index 100%
rename from Zend/tests/gh9407.phpt
rename to Zend/tests/inheritance/gh9407.phpt
diff --git a/Zend/tests/grandparent_prototype.phpt b/Zend/tests/inheritance/grandparent_prototype.phpt
similarity index 100%
rename from Zend/tests/grandparent_prototype.phpt
rename to Zend/tests/inheritance/grandparent_prototype.phpt
diff --git a/Zend/tests/inherit_internal_static.phpt b/Zend/tests/inheritance/inherit_internal_static.phpt
similarity index 100%
rename from Zend/tests/inherit_internal_static.phpt
rename to Zend/tests/inheritance/inherit_internal_static.phpt
diff --git a/Zend/tests/interface_constructor_prototype_001.phpt b/Zend/tests/inheritance/interface_constructor_prototype_001.phpt
similarity index 100%
rename from Zend/tests/interface_constructor_prototype_001.phpt
rename to Zend/tests/inheritance/interface_constructor_prototype_001.phpt
diff --git a/Zend/tests/interface_constructor_prototype_002.phpt b/Zend/tests/inheritance/interface_constructor_prototype_002.phpt
similarity index 100%
rename from Zend/tests/interface_constructor_prototype_002.phpt
rename to Zend/tests/inheritance/interface_constructor_prototype_002.phpt
diff --git a/Zend/tests/unused_shared_static_variables.phpt b/Zend/tests/inheritance/unused_shared_static_variables.phpt
similarity index 100%
rename from Zend/tests/unused_shared_static_variables.phpt
rename to Zend/tests/inheritance/unused_shared_static_variables.phpt
diff --git a/Zend/tests/update_consts_shadowed_private_prop.phpt b/Zend/tests/inheritance/update_consts_shadowed_private_prop.phpt
similarity index 100%
rename from Zend/tests/update_consts_shadowed_private_prop.phpt
rename to Zend/tests/inheritance/update_consts_shadowed_private_prop.phpt
diff --git a/Zend/tests/bug29883.phpt b/Zend/tests/isset/bug29883.phpt
similarity index 100%
rename from Zend/tests/bug29883.phpt
rename to Zend/tests/isset/bug29883.phpt
diff --git a/Zend/tests/bug62680.phpt b/Zend/tests/isset/bug62680.phpt
similarity index 100%
rename from Zend/tests/bug62680.phpt
rename to Zend/tests/isset/bug62680.phpt
diff --git a/Zend/tests/bug63982.phpt b/Zend/tests/isset/bug63982.phpt
similarity index 100%
rename from Zend/tests/bug63982.phpt
rename to Zend/tests/isset/bug63982.phpt
diff --git a/Zend/tests/bug68162.phpt b/Zend/tests/isset/bug68162.phpt
similarity index 100%
rename from Zend/tests/bug68162.phpt
rename to Zend/tests/isset/bug68162.phpt
diff --git a/Zend/tests/bug74836.phpt b/Zend/tests/isset/bug74836.phpt
similarity index 100%
rename from Zend/tests/bug74836.phpt
rename to Zend/tests/isset/bug74836.phpt
diff --git a/Zend/tests/bug80030.phpt b/Zend/tests/isset/bug80030.phpt
similarity index 100%
rename from Zend/tests/bug80030.phpt
rename to Zend/tests/isset/bug80030.phpt
diff --git a/Zend/tests/isset_001.phpt b/Zend/tests/isset/isset_001.phpt
similarity index 100%
rename from Zend/tests/isset_001.phpt
rename to Zend/tests/isset/isset_001.phpt
diff --git a/Zend/tests/isset_002.phpt b/Zend/tests/isset/isset_002.phpt
similarity index 100%
rename from Zend/tests/isset_002.phpt
rename to Zend/tests/isset/isset_002.phpt
diff --git a/Zend/tests/isset_003.phpt b/Zend/tests/isset/isset_003.phpt
similarity index 100%
rename from Zend/tests/isset_003.phpt
rename to Zend/tests/isset/isset_003.phpt
diff --git a/Zend/tests/isset_array.phpt b/Zend/tests/isset/isset_array.phpt
similarity index 100%
rename from Zend/tests/isset_array.phpt
rename to Zend/tests/isset/isset_array.phpt
diff --git a/Zend/tests/isset_expr_error.phpt b/Zend/tests/isset/isset_expr_error.phpt
similarity index 100%
rename from Zend/tests/isset_expr_error.phpt
rename to Zend/tests/isset/isset_expr_error.phpt
diff --git a/Zend/tests/isset_func_error.phpt b/Zend/tests/isset/isset_func_error.phpt
similarity index 100%
rename from Zend/tests/isset_func_error.phpt
rename to Zend/tests/isset/isset_func_error.phpt
diff --git a/Zend/tests/isset_str_offset.phpt b/Zend/tests/isset/isset_str_offset.phpt
similarity index 100%
rename from Zend/tests/isset_str_offset.phpt
rename to Zend/tests/isset/isset_str_offset.phpt
diff --git a/Zend/tests/this_in_isset.phpt b/Zend/tests/isset/this_in_isset.phpt
similarity index 100%
rename from Zend/tests/this_in_isset.phpt
rename to Zend/tests/isset/this_in_isset.phpt
diff --git a/Zend/tests/jump01.phpt b/Zend/tests/jump/jump01.phpt
similarity index 100%
rename from Zend/tests/jump01.phpt
rename to Zend/tests/jump/jump01.phpt
diff --git a/Zend/tests/jump02.phpt b/Zend/tests/jump/jump02.phpt
similarity index 100%
rename from Zend/tests/jump02.phpt
rename to Zend/tests/jump/jump02.phpt
diff --git a/Zend/tests/jump03.phpt b/Zend/tests/jump/jump03.phpt
similarity index 100%
rename from Zend/tests/jump03.phpt
rename to Zend/tests/jump/jump03.phpt
diff --git a/Zend/tests/jump04.phpt b/Zend/tests/jump/jump04.phpt
similarity index 100%
rename from Zend/tests/jump04.phpt
rename to Zend/tests/jump/jump04.phpt
diff --git a/Zend/tests/jump05.phpt b/Zend/tests/jump/jump05.phpt
similarity index 100%
rename from Zend/tests/jump05.phpt
rename to Zend/tests/jump/jump05.phpt
diff --git a/Zend/tests/jump06.phpt b/Zend/tests/jump/jump06.phpt
similarity index 100%
rename from Zend/tests/jump06.phpt
rename to Zend/tests/jump/jump06.phpt
diff --git a/Zend/tests/jump07.phpt b/Zend/tests/jump/jump07.phpt
similarity index 100%
rename from Zend/tests/jump07.phpt
rename to Zend/tests/jump/jump07.phpt
diff --git a/Zend/tests/jump08.phpt b/Zend/tests/jump/jump08.phpt
similarity index 100%
rename from Zend/tests/jump08.phpt
rename to Zend/tests/jump/jump08.phpt
diff --git a/Zend/tests/jump09.phpt b/Zend/tests/jump/jump09.phpt
similarity index 100%
rename from Zend/tests/jump09.phpt
rename to Zend/tests/jump/jump09.phpt
diff --git a/Zend/tests/jump10.phpt b/Zend/tests/jump/jump10.phpt
similarity index 100%
rename from Zend/tests/jump10.phpt
rename to Zend/tests/jump/jump10.phpt
diff --git a/Zend/tests/jump11.phpt b/Zend/tests/jump/jump11.phpt
similarity index 89%
rename from Zend/tests/jump11.phpt
rename to Zend/tests/jump/jump11.phpt
index 4c4c4f30a20f3..7b6b9170c72e9 100644
--- a/Zend/tests/jump11.phpt
+++ b/Zend/tests/jump/jump11.phpt
@@ -1,5 +1,5 @@
--TEST--
-jump 08: goto inside switch in constructor
+jump 11: goto inside switch in constructor
--FILE--
diff --git a/Zend/tests/magic_methods_inheritance_rules.phpt b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt
similarity index 100%
rename from Zend/tests/magic_methods_inheritance_rules.phpt
rename to Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt
diff --git a/Zend/tests/magic_methods_inheritance_rules_non_trivial_01.phpt b/Zend/tests/magic_methods/magic_methods_inheritance_rules_non_trivial_01.phpt
similarity index 100%
rename from Zend/tests/magic_methods_inheritance_rules_non_trivial_01.phpt
rename to Zend/tests/magic_methods/magic_methods_inheritance_rules_non_trivial_01.phpt
diff --git a/Zend/tests/magic_methods_inheritance_rules_non_trivial_02.phpt b/Zend/tests/magic_methods/magic_methods_inheritance_rules_non_trivial_02.phpt
similarity index 100%
rename from Zend/tests/magic_methods_inheritance_rules_non_trivial_02.phpt
rename to Zend/tests/magic_methods/magic_methods_inheritance_rules_non_trivial_02.phpt
diff --git a/Zend/tests/magic_methods_serialize.phpt b/Zend/tests/magic_methods/magic_methods_serialize.phpt
similarity index 100%
rename from Zend/tests/magic_methods_serialize.phpt
rename to Zend/tests/magic_methods/magic_methods_serialize.phpt
diff --git a/Zend/tests/magic_methods_set_state.phpt b/Zend/tests/magic_methods/magic_methods_set_state.phpt
similarity index 100%
rename from Zend/tests/magic_methods_set_state.phpt
rename to Zend/tests/magic_methods/magic_methods_set_state.phpt
diff --git a/Zend/tests/magic_methods_sleep.phpt b/Zend/tests/magic_methods/magic_methods_sleep.phpt
similarity index 100%
rename from Zend/tests/magic_methods_sleep.phpt
rename to Zend/tests/magic_methods/magic_methods_sleep.phpt
diff --git a/Zend/tests/magic_methods_unserialize.phpt b/Zend/tests/magic_methods/magic_methods_unserialize.phpt
similarity index 100%
rename from Zend/tests/magic_methods_unserialize.phpt
rename to Zend/tests/magic_methods/magic_methods_unserialize.phpt
diff --git a/Zend/tests/magic_methods_wakeup.phpt b/Zend/tests/magic_methods/magic_methods_wakeup.phpt
similarity index 100%
rename from Zend/tests/magic_methods_wakeup.phpt
rename to Zend/tests/magic_methods/magic_methods_wakeup.phpt
diff --git a/Zend/tests/overloaded_prop_assign_op_refs.phpt b/Zend/tests/magic_methods/overloaded_prop_assign_op_refs.phpt
similarity index 100%
rename from Zend/tests/overloaded_prop_assign_op_refs.phpt
rename to Zend/tests/magic_methods/overloaded_prop_assign_op_refs.phpt
diff --git a/Zend/tests/stringable_automatic_implementation.phpt b/Zend/tests/magic_methods/stringable_automatic_implementation.phpt
similarity index 100%
rename from Zend/tests/stringable_automatic_implementation.phpt
rename to Zend/tests/magic_methods/stringable_automatic_implementation.phpt
diff --git a/Zend/tests/stringable_internal_class.phpt b/Zend/tests/magic_methods/stringable_internal_class.phpt
similarity index 100%
rename from Zend/tests/stringable_internal_class.phpt
rename to Zend/tests/magic_methods/stringable_internal_class.phpt
diff --git a/Zend/tests/stringable_trait.phpt b/Zend/tests/magic_methods/stringable_trait.phpt
similarity index 100%
rename from Zend/tests/stringable_trait.phpt
rename to Zend/tests/magic_methods/stringable_trait.phpt
diff --git a/Zend/tests/stringable_trait_invalid.phpt b/Zend/tests/magic_methods/stringable_trait_invalid.phpt
similarity index 100%
rename from Zend/tests/stringable_trait_invalid.phpt
rename to Zend/tests/magic_methods/stringable_trait_invalid.phpt
diff --git a/Zend/tests/trampoline_closure_named_arguments.phpt b/Zend/tests/magic_methods/trampoline_closure_named_arguments.phpt
similarity index 100%
rename from Zend/tests/trampoline_closure_named_arguments.phpt
rename to Zend/tests/magic_methods/trampoline_closure_named_arguments.phpt
diff --git a/Zend/tests/match/048.phpt b/Zend/tests/match/048.phpt
new file mode 100644
index 0000000000000..e5c6bb2e0bcbe
--- /dev/null
+++ b/Zend/tests/match/048.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Match expression error messages (exception_string_param_max_len=0)
+--INI--
+zend.exception_string_param_max_len=0
+--FILE--
+getMessage() . PHP_EOL;
+ }
+}
+
+test(null);
+test(1);
+test(5.5);
+test(5.0);
+test("foo");
+test(true);
+test(false);
+test([1, 2, 3]);
+test(new Beep());
+// Testing long strings.
+test(str_repeat('e', 100));
+test(str_repeat("e\n", 100));
+?>
+--EXPECT--
+Unhandled match case NULL
+Unhandled match case 1
+Unhandled match case 5.5
+Unhandled match case 5.0
+Unhandled match case of type string
+Unhandled match case true
+Unhandled match case false
+Unhandled match case of type array
+Unhandled match case of type Beep
+Unhandled match case of type string
+Unhandled match case of type string
diff --git a/Zend/tests/declare_already_in_use.phpt b/Zend/tests/name_collision/declare_already_in_use.phpt
similarity index 100%
rename from Zend/tests/declare_already_in_use.phpt
rename to Zend/tests/name_collision/declare_already_in_use.phpt
diff --git a/Zend/tests/delayed_early_binding_redeclaration-1.inc b/Zend/tests/name_collision/delayed_early_binding_redeclaration-1.inc
similarity index 100%
rename from Zend/tests/delayed_early_binding_redeclaration-1.inc
rename to Zend/tests/name_collision/delayed_early_binding_redeclaration-1.inc
diff --git a/Zend/tests/delayed_early_binding_redeclaration-2.inc b/Zend/tests/name_collision/delayed_early_binding_redeclaration-2.inc
similarity index 100%
rename from Zend/tests/delayed_early_binding_redeclaration-2.inc
rename to Zend/tests/name_collision/delayed_early_binding_redeclaration-2.inc
diff --git a/Zend/tests/delayed_early_binding_redeclaration.phpt b/Zend/tests/name_collision/delayed_early_binding_redeclaration.phpt
similarity index 100%
rename from Zend/tests/delayed_early_binding_redeclaration.phpt
rename to Zend/tests/name_collision/delayed_early_binding_redeclaration.phpt
diff --git a/Zend/tests/name_collision_01.phpt b/Zend/tests/name_collision/name_collision_01.phpt
similarity index 100%
rename from Zend/tests/name_collision_01.phpt
rename to Zend/tests/name_collision/name_collision_01.phpt
diff --git a/Zend/tests/name_collision_02.phpt b/Zend/tests/name_collision/name_collision_02.phpt
similarity index 100%
rename from Zend/tests/name_collision_02.phpt
rename to Zend/tests/name_collision/name_collision_02.phpt
diff --git a/Zend/tests/name_collision_03.phpt b/Zend/tests/name_collision/name_collision_03.phpt
similarity index 100%
rename from Zend/tests/name_collision_03.phpt
rename to Zend/tests/name_collision/name_collision_03.phpt
diff --git a/Zend/tests/name_collision_04.phpt b/Zend/tests/name_collision/name_collision_04.phpt
similarity index 100%
rename from Zend/tests/name_collision_04.phpt
rename to Zend/tests/name_collision/name_collision_04.phpt
diff --git a/Zend/tests/name_collision_05.phpt b/Zend/tests/name_collision/name_collision_05.phpt
similarity index 100%
rename from Zend/tests/name_collision_05.phpt
rename to Zend/tests/name_collision/name_collision_05.phpt
diff --git a/Zend/tests/name_collision_06.phpt b/Zend/tests/name_collision/name_collision_06.phpt
similarity index 100%
rename from Zend/tests/name_collision_06.phpt
rename to Zend/tests/name_collision/name_collision_06.phpt
diff --git a/Zend/tests/name_collision_07.phpt b/Zend/tests/name_collision/name_collision_07.phpt
similarity index 100%
rename from Zend/tests/name_collision_07.phpt
rename to Zend/tests/name_collision/name_collision_07.phpt
diff --git a/Zend/tests/name_collision_08.phpt b/Zend/tests/name_collision/name_collision_08.phpt
similarity index 100%
rename from Zend/tests/name_collision_08.phpt
rename to Zend/tests/name_collision/name_collision_08.phpt
diff --git a/Zend/tests/name_collision_09.phpt b/Zend/tests/name_collision/name_collision_09.phpt
similarity index 100%
rename from Zend/tests/name_collision_09.phpt
rename to Zend/tests/name_collision/name_collision_09.phpt
diff --git a/Zend/tests/bug80096.phpt b/Zend/tests/named_params/bug80096.phpt
similarity index 100%
rename from Zend/tests/bug80096.phpt
rename to Zend/tests/named_params/bug80096.phpt
diff --git a/Zend/tests/bug42802.phpt b/Zend/tests/namespaces/bug42802.phpt
similarity index 100%
rename from Zend/tests/bug42802.phpt
rename to Zend/tests/namespaces/bug42802.phpt
diff --git a/Zend/tests/bug42819.phpt b/Zend/tests/namespaces/bug42819.phpt
similarity index 100%
rename from Zend/tests/bug42819.phpt
rename to Zend/tests/namespaces/bug42819.phpt
diff --git a/Zend/tests/bug46813.phpt b/Zend/tests/namespaces/bug46813.phpt
similarity index 100%
rename from Zend/tests/bug46813.phpt
rename to Zend/tests/namespaces/bug46813.phpt
diff --git a/Zend/tests/bug47593.phpt b/Zend/tests/namespaces/bug47593.phpt
similarity index 100%
rename from Zend/tests/bug47593.phpt
rename to Zend/tests/namespaces/bug47593.phpt
diff --git a/Zend/tests/bug48912.phpt b/Zend/tests/namespaces/bug48912.phpt
similarity index 100%
rename from Zend/tests/bug48912.phpt
rename to Zend/tests/namespaces/bug48912.phpt
diff --git a/Zend/tests/bug77376.phpt b/Zend/tests/namespaces/bug77376.phpt
similarity index 100%
rename from Zend/tests/bug77376.phpt
rename to Zend/tests/namespaces/bug77376.phpt
diff --git a/Zend/tests/gh11152.phpt b/Zend/tests/namespaces/gh11152.phpt
similarity index 100%
rename from Zend/tests/gh11152.phpt
rename to Zend/tests/namespaces/gh11152.phpt
diff --git a/Zend/tests/namespace_first_stmt_nop.phpt b/Zend/tests/namespaces/namespace_first_stmt_nop.phpt
similarity index 100%
rename from Zend/tests/namespace_first_stmt_nop.phpt
rename to Zend/tests/namespaces/namespace_first_stmt_nop.phpt
diff --git a/Zend/tests/namespace_name_namespace.phpt b/Zend/tests/namespaces/namespace_name_namespace.phpt
similarity index 100%
rename from Zend/tests/namespace_name_namespace.phpt
rename to Zend/tests/namespaces/namespace_name_namespace.phpt
diff --git a/Zend/tests/namespace_name_namespace_start.phpt b/Zend/tests/namespaces/namespace_name_namespace_start.phpt
similarity index 100%
rename from Zend/tests/namespace_name_namespace_start.phpt
rename to Zend/tests/namespaces/namespace_name_namespace_start.phpt
diff --git a/Zend/tests/namespace_name_reserved_keywords.phpt b/Zend/tests/namespaces/namespace_name_reserved_keywords.phpt
similarity index 100%
rename from Zend/tests/namespace_name_reserved_keywords.phpt
rename to Zend/tests/namespaces/namespace_name_reserved_keywords.phpt
diff --git a/Zend/tests/namespaced_name_whitespace.phpt b/Zend/tests/namespaces/namespaced_name_whitespace.phpt
similarity index 100%
rename from Zend/tests/namespaced_name_whitespace.phpt
rename to Zend/tests/namespaces/namespaced_name_whitespace.phpt
diff --git a/Zend/tests/ns_001.phpt b/Zend/tests/namespaces/ns_001.phpt
similarity index 100%
rename from Zend/tests/ns_001.phpt
rename to Zend/tests/namespaces/ns_001.phpt
diff --git a/Zend/tests/ns_002.phpt b/Zend/tests/namespaces/ns_002.phpt
similarity index 100%
rename from Zend/tests/ns_002.phpt
rename to Zend/tests/namespaces/ns_002.phpt
diff --git a/Zend/tests/ns_003.phpt b/Zend/tests/namespaces/ns_003.phpt
similarity index 100%
rename from Zend/tests/ns_003.phpt
rename to Zend/tests/namespaces/ns_003.phpt
diff --git a/Zend/tests/ns_004.phpt b/Zend/tests/namespaces/ns_004.phpt
similarity index 100%
rename from Zend/tests/ns_004.phpt
rename to Zend/tests/namespaces/ns_004.phpt
diff --git a/Zend/tests/ns_005.phpt b/Zend/tests/namespaces/ns_005.phpt
similarity index 100%
rename from Zend/tests/ns_005.phpt
rename to Zend/tests/namespaces/ns_005.phpt
diff --git a/Zend/tests/ns_006.phpt b/Zend/tests/namespaces/ns_006.phpt
similarity index 100%
rename from Zend/tests/ns_006.phpt
rename to Zend/tests/namespaces/ns_006.phpt
diff --git a/Zend/tests/ns_007.phpt b/Zend/tests/namespaces/ns_007.phpt
similarity index 100%
rename from Zend/tests/ns_007.phpt
rename to Zend/tests/namespaces/ns_007.phpt
diff --git a/Zend/tests/ns_008.phpt b/Zend/tests/namespaces/ns_008.phpt
similarity index 100%
rename from Zend/tests/ns_008.phpt
rename to Zend/tests/namespaces/ns_008.phpt
diff --git a/Zend/tests/ns_009.phpt b/Zend/tests/namespaces/ns_009.phpt
similarity index 100%
rename from Zend/tests/ns_009.phpt
rename to Zend/tests/namespaces/ns_009.phpt
diff --git a/Zend/tests/ns_010.phpt b/Zend/tests/namespaces/ns_010.phpt
similarity index 100%
rename from Zend/tests/ns_010.phpt
rename to Zend/tests/namespaces/ns_010.phpt
diff --git a/Zend/tests/ns_011.phpt b/Zend/tests/namespaces/ns_011.phpt
similarity index 100%
rename from Zend/tests/ns_011.phpt
rename to Zend/tests/namespaces/ns_011.phpt
diff --git a/Zend/tests/ns_012.phpt b/Zend/tests/namespaces/ns_012.phpt
similarity index 100%
rename from Zend/tests/ns_012.phpt
rename to Zend/tests/namespaces/ns_012.phpt
diff --git a/Zend/tests/ns_013.phpt b/Zend/tests/namespaces/ns_013.phpt
similarity index 100%
rename from Zend/tests/ns_013.phpt
rename to Zend/tests/namespaces/ns_013.phpt
diff --git a/Zend/tests/ns_014.phpt b/Zend/tests/namespaces/ns_014.phpt
similarity index 100%
rename from Zend/tests/ns_014.phpt
rename to Zend/tests/namespaces/ns_014.phpt
diff --git a/Zend/tests/ns_015.phpt b/Zend/tests/namespaces/ns_015.phpt
similarity index 100%
rename from Zend/tests/ns_015.phpt
rename to Zend/tests/namespaces/ns_015.phpt
diff --git a/Zend/tests/ns_016.phpt b/Zend/tests/namespaces/ns_016.phpt
similarity index 100%
rename from Zend/tests/ns_016.phpt
rename to Zend/tests/namespaces/ns_016.phpt
diff --git a/Zend/tests/ns_017.phpt b/Zend/tests/namespaces/ns_017.phpt
similarity index 100%
rename from Zend/tests/ns_017.phpt
rename to Zend/tests/namespaces/ns_017.phpt
diff --git a/Zend/tests/ns_018.phpt b/Zend/tests/namespaces/ns_018.phpt
similarity index 100%
rename from Zend/tests/ns_018.phpt
rename to Zend/tests/namespaces/ns_018.phpt
diff --git a/Zend/tests/ns_019.phpt b/Zend/tests/namespaces/ns_019.phpt
similarity index 100%
rename from Zend/tests/ns_019.phpt
rename to Zend/tests/namespaces/ns_019.phpt
diff --git a/Zend/tests/ns_020.phpt b/Zend/tests/namespaces/ns_020.phpt
similarity index 100%
rename from Zend/tests/ns_020.phpt
rename to Zend/tests/namespaces/ns_020.phpt
diff --git a/Zend/tests/ns_021.phpt b/Zend/tests/namespaces/ns_021.phpt
similarity index 100%
rename from Zend/tests/ns_021.phpt
rename to Zend/tests/namespaces/ns_021.phpt
diff --git a/Zend/tests/ns_022.inc b/Zend/tests/namespaces/ns_022.inc
similarity index 100%
rename from Zend/tests/ns_022.inc
rename to Zend/tests/namespaces/ns_022.inc
diff --git a/Zend/tests/ns_022.phpt b/Zend/tests/namespaces/ns_022.phpt
similarity index 100%
rename from Zend/tests/ns_022.phpt
rename to Zend/tests/namespaces/ns_022.phpt
diff --git a/Zend/tests/ns_023.phpt b/Zend/tests/namespaces/ns_023.phpt
similarity index 100%
rename from Zend/tests/ns_023.phpt
rename to Zend/tests/namespaces/ns_023.phpt
diff --git a/Zend/tests/ns_024.phpt b/Zend/tests/namespaces/ns_024.phpt
similarity index 100%
rename from Zend/tests/ns_024.phpt
rename to Zend/tests/namespaces/ns_024.phpt
diff --git a/Zend/tests/ns_025.phpt b/Zend/tests/namespaces/ns_025.phpt
similarity index 100%
rename from Zend/tests/ns_025.phpt
rename to Zend/tests/namespaces/ns_025.phpt
diff --git a/Zend/tests/ns_026.phpt b/Zend/tests/namespaces/ns_026.phpt
similarity index 100%
rename from Zend/tests/ns_026.phpt
rename to Zend/tests/namespaces/ns_026.phpt
diff --git a/Zend/tests/ns_027.inc b/Zend/tests/namespaces/ns_027.inc
similarity index 100%
rename from Zend/tests/ns_027.inc
rename to Zend/tests/namespaces/ns_027.inc
diff --git a/Zend/tests/ns_027.phpt b/Zend/tests/namespaces/ns_027.phpt
similarity index 100%
rename from Zend/tests/ns_027.phpt
rename to Zend/tests/namespaces/ns_027.phpt
diff --git a/Zend/tests/ns_028.inc b/Zend/tests/namespaces/ns_028.inc
similarity index 100%
rename from Zend/tests/ns_028.inc
rename to Zend/tests/namespaces/ns_028.inc
diff --git a/Zend/tests/ns_028.phpt b/Zend/tests/namespaces/ns_028.phpt
similarity index 100%
rename from Zend/tests/ns_028.phpt
rename to Zend/tests/namespaces/ns_028.phpt
diff --git a/Zend/tests/ns_029.phpt b/Zend/tests/namespaces/ns_029.phpt
similarity index 100%
rename from Zend/tests/ns_029.phpt
rename to Zend/tests/namespaces/ns_029.phpt
diff --git a/Zend/tests/ns_030.phpt b/Zend/tests/namespaces/ns_030.phpt
similarity index 100%
rename from Zend/tests/ns_030.phpt
rename to Zend/tests/namespaces/ns_030.phpt
diff --git a/Zend/tests/ns_031.phpt b/Zend/tests/namespaces/ns_031.phpt
similarity index 100%
rename from Zend/tests/ns_031.phpt
rename to Zend/tests/namespaces/ns_031.phpt
diff --git a/Zend/tests/ns_032.phpt b/Zend/tests/namespaces/ns_032.phpt
similarity index 100%
rename from Zend/tests/ns_032.phpt
rename to Zend/tests/namespaces/ns_032.phpt
diff --git a/Zend/tests/ns_033.phpt b/Zend/tests/namespaces/ns_033.phpt
similarity index 100%
rename from Zend/tests/ns_033.phpt
rename to Zend/tests/namespaces/ns_033.phpt
diff --git a/Zend/tests/ns_034.phpt b/Zend/tests/namespaces/ns_034.phpt
similarity index 100%
rename from Zend/tests/ns_034.phpt
rename to Zend/tests/namespaces/ns_034.phpt
diff --git a/Zend/tests/ns_035.phpt b/Zend/tests/namespaces/ns_035.phpt
similarity index 100%
rename from Zend/tests/ns_035.phpt
rename to Zend/tests/namespaces/ns_035.phpt
diff --git a/Zend/tests/ns_036.phpt b/Zend/tests/namespaces/ns_036.phpt
similarity index 100%
rename from Zend/tests/ns_036.phpt
rename to Zend/tests/namespaces/ns_036.phpt
diff --git a/Zend/tests/ns_037.phpt b/Zend/tests/namespaces/ns_037.phpt
similarity index 100%
rename from Zend/tests/ns_037.phpt
rename to Zend/tests/namespaces/ns_037.phpt
diff --git a/Zend/tests/ns_038.phpt b/Zend/tests/namespaces/ns_038.phpt
similarity index 100%
rename from Zend/tests/ns_038.phpt
rename to Zend/tests/namespaces/ns_038.phpt
diff --git a/Zend/tests/ns_039.phpt b/Zend/tests/namespaces/ns_039.phpt
similarity index 100%
rename from Zend/tests/ns_039.phpt
rename to Zend/tests/namespaces/ns_039.phpt
diff --git a/Zend/tests/ns_040.phpt b/Zend/tests/namespaces/ns_040.phpt
similarity index 100%
rename from Zend/tests/ns_040.phpt
rename to Zend/tests/namespaces/ns_040.phpt
diff --git a/Zend/tests/ns_041.phpt b/Zend/tests/namespaces/ns_041.phpt
similarity index 100%
rename from Zend/tests/ns_041.phpt
rename to Zend/tests/namespaces/ns_041.phpt
diff --git a/Zend/tests/ns_042.phpt b/Zend/tests/namespaces/ns_042.phpt
similarity index 100%
rename from Zend/tests/ns_042.phpt
rename to Zend/tests/namespaces/ns_042.phpt
diff --git a/Zend/tests/ns_043.phpt b/Zend/tests/namespaces/ns_043.phpt
similarity index 100%
rename from Zend/tests/ns_043.phpt
rename to Zend/tests/namespaces/ns_043.phpt
diff --git a/Zend/tests/ns_044.phpt b/Zend/tests/namespaces/ns_044.phpt
similarity index 100%
rename from Zend/tests/ns_044.phpt
rename to Zend/tests/namespaces/ns_044.phpt
diff --git a/Zend/tests/ns_045.phpt b/Zend/tests/namespaces/ns_045.phpt
similarity index 100%
rename from Zend/tests/ns_045.phpt
rename to Zend/tests/namespaces/ns_045.phpt
diff --git a/Zend/tests/ns_046.phpt b/Zend/tests/namespaces/ns_046.phpt
similarity index 100%
rename from Zend/tests/ns_046.phpt
rename to Zend/tests/namespaces/ns_046.phpt
diff --git a/Zend/tests/ns_047.phpt b/Zend/tests/namespaces/ns_047.phpt
similarity index 100%
rename from Zend/tests/ns_047.phpt
rename to Zend/tests/namespaces/ns_047.phpt
diff --git a/Zend/tests/ns_048.phpt b/Zend/tests/namespaces/ns_048.phpt
similarity index 100%
rename from Zend/tests/ns_048.phpt
rename to Zend/tests/namespaces/ns_048.phpt
diff --git a/Zend/tests/ns_049.phpt b/Zend/tests/namespaces/ns_049.phpt
similarity index 100%
rename from Zend/tests/ns_049.phpt
rename to Zend/tests/namespaces/ns_049.phpt
diff --git a/Zend/tests/ns_050.phpt b/Zend/tests/namespaces/ns_050.phpt
similarity index 100%
rename from Zend/tests/ns_050.phpt
rename to Zend/tests/namespaces/ns_050.phpt
diff --git a/Zend/tests/ns_051.phpt b/Zend/tests/namespaces/ns_051.phpt
similarity index 100%
rename from Zend/tests/ns_051.phpt
rename to Zend/tests/namespaces/ns_051.phpt
diff --git a/Zend/tests/ns_052.phpt b/Zend/tests/namespaces/ns_052.phpt
similarity index 100%
rename from Zend/tests/ns_052.phpt
rename to Zend/tests/namespaces/ns_052.phpt
diff --git a/Zend/tests/ns_053.phpt b/Zend/tests/namespaces/ns_053.phpt
similarity index 100%
rename from Zend/tests/ns_053.phpt
rename to Zend/tests/namespaces/ns_053.phpt
diff --git a/Zend/tests/ns_054.phpt b/Zend/tests/namespaces/ns_054.phpt
similarity index 100%
rename from Zend/tests/ns_054.phpt
rename to Zend/tests/namespaces/ns_054.phpt
diff --git a/Zend/tests/ns_055.phpt b/Zend/tests/namespaces/ns_055.phpt
similarity index 100%
rename from Zend/tests/ns_055.phpt
rename to Zend/tests/namespaces/ns_055.phpt
diff --git a/Zend/tests/ns_056.phpt b/Zend/tests/namespaces/ns_056.phpt
similarity index 100%
rename from Zend/tests/ns_056.phpt
rename to Zend/tests/namespaces/ns_056.phpt
diff --git a/Zend/tests/ns_057.phpt b/Zend/tests/namespaces/ns_057.phpt
similarity index 100%
rename from Zend/tests/ns_057.phpt
rename to Zend/tests/namespaces/ns_057.phpt
diff --git a/Zend/tests/ns_058.phpt b/Zend/tests/namespaces/ns_058.phpt
similarity index 100%
rename from Zend/tests/ns_058.phpt
rename to Zend/tests/namespaces/ns_058.phpt
diff --git a/Zend/tests/ns_059.phpt b/Zend/tests/namespaces/ns_059.phpt
similarity index 100%
rename from Zend/tests/ns_059.phpt
rename to Zend/tests/namespaces/ns_059.phpt
diff --git a/Zend/tests/ns_060.phpt b/Zend/tests/namespaces/ns_060.phpt
similarity index 100%
rename from Zend/tests/ns_060.phpt
rename to Zend/tests/namespaces/ns_060.phpt
diff --git a/Zend/tests/ns_061.phpt b/Zend/tests/namespaces/ns_061.phpt
similarity index 100%
rename from Zend/tests/ns_061.phpt
rename to Zend/tests/namespaces/ns_061.phpt
diff --git a/Zend/tests/ns_062.phpt b/Zend/tests/namespaces/ns_062.phpt
similarity index 100%
rename from Zend/tests/ns_062.phpt
rename to Zend/tests/namespaces/ns_062.phpt
diff --git a/Zend/tests/ns_063.phpt b/Zend/tests/namespaces/ns_063.phpt
similarity index 100%
rename from Zend/tests/ns_063.phpt
rename to Zend/tests/namespaces/ns_063.phpt
diff --git a/Zend/tests/ns_064.phpt b/Zend/tests/namespaces/ns_064.phpt
similarity index 100%
rename from Zend/tests/ns_064.phpt
rename to Zend/tests/namespaces/ns_064.phpt
diff --git a/Zend/tests/ns_065.inc b/Zend/tests/namespaces/ns_065.inc
similarity index 100%
rename from Zend/tests/ns_065.inc
rename to Zend/tests/namespaces/ns_065.inc
diff --git a/Zend/tests/ns_065.phpt b/Zend/tests/namespaces/ns_065.phpt
similarity index 100%
rename from Zend/tests/ns_065.phpt
rename to Zend/tests/namespaces/ns_065.phpt
diff --git a/Zend/tests/ns_066.phpt b/Zend/tests/namespaces/ns_066.phpt
similarity index 100%
rename from Zend/tests/ns_066.phpt
rename to Zend/tests/namespaces/ns_066.phpt
diff --git a/Zend/tests/ns_067.inc b/Zend/tests/namespaces/ns_067.inc
similarity index 100%
rename from Zend/tests/ns_067.inc
rename to Zend/tests/namespaces/ns_067.inc
diff --git a/Zend/tests/ns_067.phpt b/Zend/tests/namespaces/ns_067.phpt
similarity index 100%
rename from Zend/tests/ns_067.phpt
rename to Zend/tests/namespaces/ns_067.phpt
diff --git a/Zend/tests/ns_068.phpt b/Zend/tests/namespaces/ns_068.phpt
similarity index 100%
rename from Zend/tests/ns_068.phpt
rename to Zend/tests/namespaces/ns_068.phpt
diff --git a/Zend/tests/ns_069.inc b/Zend/tests/namespaces/ns_069.inc
similarity index 100%
rename from Zend/tests/ns_069.inc
rename to Zend/tests/namespaces/ns_069.inc
diff --git a/Zend/tests/ns_069.phpt b/Zend/tests/namespaces/ns_069.phpt
similarity index 100%
rename from Zend/tests/ns_069.phpt
rename to Zend/tests/namespaces/ns_069.phpt
diff --git a/Zend/tests/ns_070.phpt b/Zend/tests/namespaces/ns_070.phpt
similarity index 100%
rename from Zend/tests/ns_070.phpt
rename to Zend/tests/namespaces/ns_070.phpt
diff --git a/Zend/tests/ns_071.phpt b/Zend/tests/namespaces/ns_071.phpt
similarity index 100%
rename from Zend/tests/ns_071.phpt
rename to Zend/tests/namespaces/ns_071.phpt
diff --git a/Zend/tests/ns_072.phpt b/Zend/tests/namespaces/ns_072.phpt
similarity index 100%
rename from Zend/tests/ns_072.phpt
rename to Zend/tests/namespaces/ns_072.phpt
diff --git a/Zend/tests/ns_073.phpt b/Zend/tests/namespaces/ns_073.phpt
similarity index 100%
rename from Zend/tests/ns_073.phpt
rename to Zend/tests/namespaces/ns_073.phpt
diff --git a/Zend/tests/ns_074.phpt b/Zend/tests/namespaces/ns_074.phpt
similarity index 100%
rename from Zend/tests/ns_074.phpt
rename to Zend/tests/namespaces/ns_074.phpt
diff --git a/Zend/tests/ns_075.phpt b/Zend/tests/namespaces/ns_075.phpt
similarity index 100%
rename from Zend/tests/ns_075.phpt
rename to Zend/tests/namespaces/ns_075.phpt
diff --git a/Zend/tests/ns_076.phpt b/Zend/tests/namespaces/ns_076.phpt
similarity index 100%
rename from Zend/tests/ns_076.phpt
rename to Zend/tests/namespaces/ns_076.phpt
diff --git a/Zend/tests/ns_077_1.phpt b/Zend/tests/namespaces/ns_077_1.phpt
similarity index 100%
rename from Zend/tests/ns_077_1.phpt
rename to Zend/tests/namespaces/ns_077_1.phpt
diff --git a/Zend/tests/ns_077_2.phpt b/Zend/tests/namespaces/ns_077_2.phpt
similarity index 100%
rename from Zend/tests/ns_077_2.phpt
rename to Zend/tests/namespaces/ns_077_2.phpt
diff --git a/Zend/tests/ns_077_3.phpt b/Zend/tests/namespaces/ns_077_3.phpt
similarity index 100%
rename from Zend/tests/ns_077_3.phpt
rename to Zend/tests/namespaces/ns_077_3.phpt
diff --git a/Zend/tests/ns_077_4.phpt b/Zend/tests/namespaces/ns_077_4.phpt
similarity index 100%
rename from Zend/tests/ns_077_4.phpt
rename to Zend/tests/namespaces/ns_077_4.phpt
diff --git a/Zend/tests/ns_077_5.phpt b/Zend/tests/namespaces/ns_077_5.phpt
similarity index 100%
rename from Zend/tests/ns_077_5.phpt
rename to Zend/tests/namespaces/ns_077_5.phpt
diff --git a/Zend/tests/ns_077_7.phpt b/Zend/tests/namespaces/ns_077_7.phpt
similarity index 100%
rename from Zend/tests/ns_077_7.phpt
rename to Zend/tests/namespaces/ns_077_7.phpt
diff --git a/Zend/tests/ns_077_8.phpt b/Zend/tests/namespaces/ns_077_8.phpt
similarity index 100%
rename from Zend/tests/ns_077_8.phpt
rename to Zend/tests/namespaces/ns_077_8.phpt
diff --git a/Zend/tests/ns_078.phpt b/Zend/tests/namespaces/ns_078.phpt
similarity index 100%
rename from Zend/tests/ns_078.phpt
rename to Zend/tests/namespaces/ns_078.phpt
diff --git a/Zend/tests/ns_079.phpt b/Zend/tests/namespaces/ns_079.phpt
similarity index 100%
rename from Zend/tests/ns_079.phpt
rename to Zend/tests/namespaces/ns_079.phpt
diff --git a/Zend/tests/ns_080.phpt b/Zend/tests/namespaces/ns_080.phpt
similarity index 100%
rename from Zend/tests/ns_080.phpt
rename to Zend/tests/namespaces/ns_080.phpt
diff --git a/Zend/tests/ns_081.phpt b/Zend/tests/namespaces/ns_081.phpt
similarity index 100%
rename from Zend/tests/ns_081.phpt
rename to Zend/tests/namespaces/ns_081.phpt
diff --git a/Zend/tests/ns_082.phpt b/Zend/tests/namespaces/ns_082.phpt
similarity index 100%
rename from Zend/tests/ns_082.phpt
rename to Zend/tests/namespaces/ns_082.phpt
diff --git a/Zend/tests/ns_083.phpt b/Zend/tests/namespaces/ns_083.phpt
similarity index 100%
rename from Zend/tests/ns_083.phpt
rename to Zend/tests/namespaces/ns_083.phpt
diff --git a/Zend/tests/ns_084.phpt b/Zend/tests/namespaces/ns_084.phpt
similarity index 100%
rename from Zend/tests/ns_084.phpt
rename to Zend/tests/namespaces/ns_084.phpt
diff --git a/Zend/tests/ns_085.phpt b/Zend/tests/namespaces/ns_085.phpt
similarity index 100%
rename from Zend/tests/ns_085.phpt
rename to Zend/tests/namespaces/ns_085.phpt
diff --git a/Zend/tests/ns_086.phpt b/Zend/tests/namespaces/ns_086.phpt
similarity index 100%
rename from Zend/tests/ns_086.phpt
rename to Zend/tests/namespaces/ns_086.phpt
diff --git a/Zend/tests/ns_087.phpt b/Zend/tests/namespaces/ns_087.phpt
similarity index 100%
rename from Zend/tests/ns_087.phpt
rename to Zend/tests/namespaces/ns_087.phpt
diff --git a/Zend/tests/ns_088.phpt b/Zend/tests/namespaces/ns_088.phpt
similarity index 100%
rename from Zend/tests/ns_088.phpt
rename to Zend/tests/namespaces/ns_088.phpt
diff --git a/Zend/tests/ns_089.phpt b/Zend/tests/namespaces/ns_089.phpt
similarity index 100%
rename from Zend/tests/ns_089.phpt
rename to Zend/tests/namespaces/ns_089.phpt
diff --git a/Zend/tests/ns_090.phpt b/Zend/tests/namespaces/ns_090.phpt
similarity index 100%
rename from Zend/tests/ns_090.phpt
rename to Zend/tests/namespaces/ns_090.phpt
diff --git a/Zend/tests/ns_091.phpt b/Zend/tests/namespaces/ns_091.phpt
similarity index 100%
rename from Zend/tests/ns_091.phpt
rename to Zend/tests/namespaces/ns_091.phpt
diff --git a/Zend/tests/ns_092.phpt b/Zend/tests/namespaces/ns_092.phpt
similarity index 100%
rename from Zend/tests/ns_092.phpt
rename to Zend/tests/namespaces/ns_092.phpt
diff --git a/Zend/tests/ns_093.phpt b/Zend/tests/namespaces/ns_093.phpt
similarity index 100%
rename from Zend/tests/ns_093.phpt
rename to Zend/tests/namespaces/ns_093.phpt
diff --git a/Zend/tests/ns_094.phpt b/Zend/tests/namespaces/ns_094.phpt
similarity index 100%
rename from Zend/tests/ns_094.phpt
rename to Zend/tests/namespaces/ns_094.phpt
diff --git a/Zend/tests/ns_095.phpt b/Zend/tests/namespaces/ns_095.phpt
similarity index 100%
rename from Zend/tests/ns_095.phpt
rename to Zend/tests/namespaces/ns_095.phpt
diff --git a/Zend/tests/ns_096.phpt b/Zend/tests/namespaces/ns_096.phpt
similarity index 100%
rename from Zend/tests/ns_096.phpt
rename to Zend/tests/namespaces/ns_096.phpt
diff --git a/Zend/tests/new_oom.phpt b/Zend/tests/new_oom.phpt
index a424e12b4eab9..6d4ba3d760b40 100644
--- a/Zend/tests/new_oom.phpt
+++ b/Zend/tests/new_oom.phpt
@@ -13,7 +13,7 @@ $php = PHP_BINARY;
foreach (get_declared_classes() as $class) {
$output = shell_exec("$php --no-php-ini $file $class 2>&1");
- if ($output && preg_match('(^\nFatal error: Allowed memory size of [0-9]+ bytes exhausted[^\r\n]* \(tried to allocate [0-9]+ bytes\) in [^\r\n]+ on line [0-9]+$)', $output) !== 1) {
+ if ($output && preg_match('(^\nFatal error: Allowed memory size of [0-9]+ bytes exhausted[^\r\n]* \(tried to allocate [0-9]+ bytes\) in [^\r\n]+ on line [0-9]+\nStack trace:\n(#[0-9]+ [^\r\n]+\n)+$)', $output) !== 1) {
echo "Class $class failed\n";
echo $output, "\n";
}
diff --git a/Zend/tests/bug81216.phpt b/Zend/tests/nullsafe_operator/bug81216.phpt
similarity index 100%
rename from Zend/tests/bug81216.phpt
rename to Zend/tests/nullsafe_operator/bug81216.phpt
diff --git a/Zend/tests/bug81216_2.phpt b/Zend/tests/nullsafe_operator/bug81216_2.phpt
similarity index 100%
rename from Zend/tests/bug81216_2.phpt
rename to Zend/tests/nullsafe_operator/bug81216_2.phpt
diff --git a/Zend/tests/oss-fuzz-69765.phpt b/Zend/tests/nullsafe_operator/oss-fuzz-69765.phpt
similarity index 100%
rename from Zend/tests/oss-fuzz-69765.phpt
rename to Zend/tests/nullsafe_operator/oss-fuzz-69765.phpt
diff --git a/Zend/tests/oss_fuzz_60011_1.phpt b/Zend/tests/nullsafe_operator/oss_fuzz_60011_1.phpt
similarity index 100%
rename from Zend/tests/oss_fuzz_60011_1.phpt
rename to Zend/tests/nullsafe_operator/oss_fuzz_60011_1.phpt
diff --git a/Zend/tests/oss_fuzz_60011_2.phpt b/Zend/tests/nullsafe_operator/oss_fuzz_60011_2.phpt
similarity index 100%
rename from Zend/tests/oss_fuzz_60011_2.phpt
rename to Zend/tests/nullsafe_operator/oss_fuzz_60011_2.phpt
diff --git a/Zend/tests/bug78454_1.phpt b/Zend/tests/numeric_literal_separator/bug78454_1.phpt
similarity index 100%
rename from Zend/tests/bug78454_1.phpt
rename to Zend/tests/numeric_literal_separator/bug78454_1.phpt
diff --git a/Zend/tests/bug78454_2.phpt b/Zend/tests/numeric_literal_separator/bug78454_2.phpt
similarity index 100%
rename from Zend/tests/bug78454_2.phpt
rename to Zend/tests/numeric_literal_separator/bug78454_2.phpt
diff --git a/Zend/tests/numeric_literal_separator_001.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_001.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_001.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_001.phpt
diff --git a/Zend/tests/numeric_literal_separator_002.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_002.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_002.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_002.phpt
diff --git a/Zend/tests/numeric_literal_separator_003.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_003.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_003.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_003.phpt
diff --git a/Zend/tests/numeric_literal_separator_004.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_004.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_004.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_004.phpt
diff --git a/Zend/tests/numeric_literal_separator_005.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_005.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_005.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_005.phpt
diff --git a/Zend/tests/numeric_literal_separator_006.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_006.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_006.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_006.phpt
diff --git a/Zend/tests/numeric_literal_separator_007.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_007.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_007.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_007.phpt
diff --git a/Zend/tests/numeric_literal_separator_008.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_008.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_008.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_008.phpt
diff --git a/Zend/tests/numeric_literal_separator_009.phpt b/Zend/tests/numeric_literal_separator/numeric_literal_separator_009.phpt
similarity index 100%
rename from Zend/tests/numeric_literal_separator_009.phpt
rename to Zend/tests/numeric_literal_separator/numeric_literal_separator_009.phpt
diff --git a/Zend/tests/objects_001.phpt b/Zend/tests/objects/objects_001.phpt
similarity index 100%
rename from Zend/tests/objects_001.phpt
rename to Zend/tests/objects/objects_001.phpt
diff --git a/Zend/tests/objects_002.phpt b/Zend/tests/objects/objects_002.phpt
similarity index 100%
rename from Zend/tests/objects_002.phpt
rename to Zend/tests/objects/objects_002.phpt
diff --git a/Zend/tests/objects_003.phpt b/Zend/tests/objects/objects_003.phpt
similarity index 100%
rename from Zend/tests/objects_003.phpt
rename to Zend/tests/objects/objects_003.phpt
diff --git a/Zend/tests/objects_004.phpt b/Zend/tests/objects/objects_004.phpt
similarity index 100%
rename from Zend/tests/objects_004.phpt
rename to Zend/tests/objects/objects_004.phpt
diff --git a/Zend/tests/objects_005.phpt b/Zend/tests/objects/objects_005.phpt
similarity index 100%
rename from Zend/tests/objects_005.phpt
rename to Zend/tests/objects/objects_005.phpt
diff --git a/Zend/tests/objects_006.phpt b/Zend/tests/objects/objects_006.phpt
similarity index 100%
rename from Zend/tests/objects_006.phpt
rename to Zend/tests/objects/objects_006.phpt
diff --git a/Zend/tests/objects_007.phpt b/Zend/tests/objects/objects_007.phpt
similarity index 100%
rename from Zend/tests/objects_007.phpt
rename to Zend/tests/objects/objects_007.phpt
diff --git a/Zend/tests/objects_008.phpt b/Zend/tests/objects/objects_008.phpt
similarity index 100%
rename from Zend/tests/objects_008.phpt
rename to Zend/tests/objects/objects_008.phpt
diff --git a/Zend/tests/objects_009.phpt b/Zend/tests/objects/objects_009.phpt
similarity index 100%
rename from Zend/tests/objects_009.phpt
rename to Zend/tests/objects/objects_009.phpt
diff --git a/Zend/tests/objects_010.phpt b/Zend/tests/objects/objects_010.phpt
similarity index 100%
rename from Zend/tests/objects_010.phpt
rename to Zend/tests/objects/objects_010.phpt
diff --git a/Zend/tests/objects_011.phpt b/Zend/tests/objects/objects_011.phpt
similarity index 100%
rename from Zend/tests/objects_011.phpt
rename to Zend/tests/objects/objects_011.phpt
diff --git a/Zend/tests/objects_012.phpt b/Zend/tests/objects/objects_012.phpt
similarity index 100%
rename from Zend/tests/objects_012.phpt
rename to Zend/tests/objects/objects_012.phpt
diff --git a/Zend/tests/objects_013.phpt b/Zend/tests/objects/objects_013.phpt
similarity index 100%
rename from Zend/tests/objects_013.phpt
rename to Zend/tests/objects/objects_013.phpt
diff --git a/Zend/tests/objects_014.phpt b/Zend/tests/objects/objects_014.phpt
similarity index 100%
rename from Zend/tests/objects_014.phpt
rename to Zend/tests/objects/objects_014.phpt
diff --git a/Zend/tests/objects_015.phpt b/Zend/tests/objects/objects_015.phpt
similarity index 100%
rename from Zend/tests/objects_015.phpt
rename to Zend/tests/objects/objects_015.phpt
diff --git a/Zend/tests/objects_017.phpt b/Zend/tests/objects/objects_017.phpt
similarity index 100%
rename from Zend/tests/objects_017.phpt
rename to Zend/tests/objects/objects_017.phpt
diff --git a/Zend/tests/objects_018.phpt b/Zend/tests/objects/objects_018.phpt
similarity index 100%
rename from Zend/tests/objects_018.phpt
rename to Zend/tests/objects/objects_018.phpt
diff --git a/Zend/tests/objects_019.phpt b/Zend/tests/objects/objects_019.phpt
similarity index 100%
rename from Zend/tests/objects_019.phpt
rename to Zend/tests/objects/objects_019.phpt
diff --git a/Zend/tests/objects_021.phpt b/Zend/tests/objects/objects_021.phpt
similarity index 100%
rename from Zend/tests/objects_021.phpt
rename to Zend/tests/objects/objects_021.phpt
diff --git a/Zend/tests/objects_022.phpt b/Zend/tests/objects/objects_022.phpt
similarity index 100%
rename from Zend/tests/objects_022.phpt
rename to Zend/tests/objects/objects_022.phpt
diff --git a/Zend/tests/objects_023.phpt b/Zend/tests/objects/objects_023.phpt
similarity index 100%
rename from Zend/tests/objects_023.phpt
rename to Zend/tests/objects/objects_023.phpt
diff --git a/Zend/tests/objects_024.phpt b/Zend/tests/objects/objects_024.phpt
similarity index 100%
rename from Zend/tests/objects_024.phpt
rename to Zend/tests/objects/objects_024.phpt
diff --git a/Zend/tests/objects_025.phpt b/Zend/tests/objects/objects_025.phpt
similarity index 100%
rename from Zend/tests/objects_025.phpt
rename to Zend/tests/objects/objects_025.phpt
diff --git a/Zend/tests/objects_026.phpt b/Zend/tests/objects/objects_026.phpt
similarity index 100%
rename from Zend/tests/objects_026.phpt
rename to Zend/tests/objects/objects_026.phpt
diff --git a/Zend/tests/objects_027.phpt b/Zend/tests/objects/objects_027.phpt
similarity index 100%
rename from Zend/tests/objects_027.phpt
rename to Zend/tests/objects/objects_027.phpt
diff --git a/Zend/tests/objects_028.phpt b/Zend/tests/objects/objects_028.phpt
similarity index 100%
rename from Zend/tests/objects_028.phpt
rename to Zend/tests/objects/objects_028.phpt
diff --git a/Zend/tests/objects_029.phpt b/Zend/tests/objects/objects_029.phpt
similarity index 100%
rename from Zend/tests/objects_029.phpt
rename to Zend/tests/objects/objects_029.phpt
diff --git a/Zend/tests/objects_030.phpt b/Zend/tests/objects/objects_030.phpt
similarity index 100%
rename from Zend/tests/objects_030.phpt
rename to Zend/tests/objects/objects_030.phpt
diff --git a/Zend/tests/objects_031.phpt b/Zend/tests/objects/objects_031.phpt
similarity index 100%
rename from Zend/tests/objects_031.phpt
rename to Zend/tests/objects/objects_031.phpt
diff --git a/Zend/tests/objects_032.phpt b/Zend/tests/objects/objects_032.phpt
similarity index 100%
rename from Zend/tests/objects_032.phpt
rename to Zend/tests/objects/objects_032.phpt
diff --git a/Zend/tests/objects_033.phpt b/Zend/tests/objects/objects_033.phpt
similarity index 100%
rename from Zend/tests/objects_033.phpt
rename to Zend/tests/objects/objects_033.phpt
diff --git a/Zend/tests/objects_034.phpt b/Zend/tests/objects/objects_034.phpt
similarity index 100%
rename from Zend/tests/objects_034.phpt
rename to Zend/tests/objects/objects_034.phpt
diff --git a/Zend/tests/objects_035.phpt b/Zend/tests/objects/objects_035.phpt
similarity index 100%
rename from Zend/tests/objects_035.phpt
rename to Zend/tests/objects/objects_035.phpt
diff --git a/Zend/tests/oss_fuzz_410939023.phpt b/Zend/tests/oss_fuzz_410939023.phpt
new file mode 100644
index 0000000000000..9c024bf5b5b06
--- /dev/null
+++ b/Zend/tests/oss_fuzz_410939023.phpt
@@ -0,0 +1,11 @@
+--TEST--
+OSS-Fuzz #410939023: Use of magic const within const expr cast
+--FILE--
+
+--EXPECT--
+string(0) ""
diff --git a/Zend/tests/bug68446.phpt b/Zend/tests/parameter_default_values/bug68446.phpt
similarity index 100%
rename from Zend/tests/bug68446.phpt
rename to Zend/tests/parameter_default_values/bug68446.phpt
diff --git a/Zend/tests/bug69767.phpt b/Zend/tests/parameter_default_values/bug69767.phpt
similarity index 100%
rename from Zend/tests/bug69767.phpt
rename to Zend/tests/parameter_default_values/bug69767.phpt
diff --git a/Zend/tests/property_hooks/abstract_get_set_readonly.phpt b/Zend/tests/property_hooks/abstract_get_set_readonly.phpt
index 644ffb474960b..e8cb95019cc08 100644
--- a/Zend/tests/property_hooks/abstract_get_set_readonly.phpt
+++ b/Zend/tests/property_hooks/abstract_get_set_readonly.phpt
@@ -10,4 +10,4 @@ class C extends P {
}
?>
--EXPECTF--
-Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (P::$prop::set) in %s on line %d
+Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining method (P::$prop::set) in %s on line %d
diff --git a/Zend/tests/property_hooks/abstract_hook_in_non_abstract_class.phpt b/Zend/tests/property_hooks/abstract_hook_in_non_abstract_class.phpt
index fe27f3ecebc5e..d3b8396a55f0a 100644
--- a/Zend/tests/property_hooks/abstract_hook_in_non_abstract_class.phpt
+++ b/Zend/tests/property_hooks/abstract_hook_in_non_abstract_class.phpt
@@ -12,4 +12,4 @@ class Test {
?>
--EXPECTF--
-Fatal error: Class Test contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Test::$prop::get) in %s on line %d
+Fatal error: Class Test contains 1 abstract method and must therefore be declared abstract or implement the remaining method (Test::$prop::get) in %s on line %d
diff --git a/Zend/tests/property_hooks/abstract_hook_not_implemented.phpt b/Zend/tests/property_hooks/abstract_hook_not_implemented.phpt
index bb94c0650b01a..24fd4f1ef1ed5 100644
--- a/Zend/tests/property_hooks/abstract_hook_not_implemented.phpt
+++ b/Zend/tests/property_hooks/abstract_hook_not_implemented.phpt
@@ -14,4 +14,4 @@ class B extends A {}
?>
--EXPECTF--
-Fatal error: Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (A::$prop::get) in %s on line %d
+Fatal error: Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining method (A::$prop::get) in %s on line %d
diff --git a/Zend/tests/gh15240.phpt b/Zend/tests/property_hooks/gh15240.phpt
similarity index 100%
rename from Zend/tests/gh15240.phpt
rename to Zend/tests/property_hooks/gh15240.phpt
diff --git a/Zend/tests/gh16615_001.phpt b/Zend/tests/property_hooks/gh16615_001.phpt
similarity index 100%
rename from Zend/tests/gh16615_001.phpt
rename to Zend/tests/property_hooks/gh16615_001.phpt
diff --git a/Zend/tests/gh16615_002.phpt b/Zend/tests/property_hooks/gh16615_002.phpt
similarity index 100%
rename from Zend/tests/gh16615_002.phpt
rename to Zend/tests/property_hooks/gh16615_002.phpt
diff --git a/Zend/tests/gh16725.phpt b/Zend/tests/property_hooks/gh16725.phpt
similarity index 100%
rename from Zend/tests/gh16725.phpt
rename to Zend/tests/property_hooks/gh16725.phpt
diff --git a/Zend/tests/property_hooks/invalid_abstract_indirect.phpt b/Zend/tests/property_hooks/invalid_abstract_indirect.phpt
index 834440a7f6885..b04d44c8c8a85 100644
--- a/Zend/tests/property_hooks/invalid_abstract_indirect.phpt
+++ b/Zend/tests/property_hooks/invalid_abstract_indirect.phpt
@@ -13,4 +13,4 @@ class B extends A {
?>
--EXPECTF--
-Fatal error: Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (A::$prop::get) in %s on line %d
+Fatal error: Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining method (A::$prop::get) in %s on line %d
diff --git a/Zend/tests/property_hooks/invalid_abstract_indirect_2.phpt b/Zend/tests/property_hooks/invalid_abstract_indirect_2.phpt
index 28938c17540b5..8526562c2adf5 100644
--- a/Zend/tests/property_hooks/invalid_abstract_indirect_2.phpt
+++ b/Zend/tests/property_hooks/invalid_abstract_indirect_2.phpt
@@ -12,4 +12,4 @@ class B extends A {
?>
--EXPECTF--
-Fatal error: Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (A::$prop::get) in %s on line %d
+Fatal error: Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining method (A::$prop::get) in %s on line %d
diff --git a/Zend/tests/oss-fuzz-391975641.phpt b/Zend/tests/property_hooks/oss-fuzz-391975641.phpt
similarity index 100%
rename from Zend/tests/oss-fuzz-391975641.phpt
rename to Zend/tests/property_hooks/oss-fuzz-391975641.phpt
diff --git a/Zend/tests/gh10377.phpt b/Zend/tests/readonly_classes/gh10377.phpt
similarity index 100%
rename from Zend/tests/gh10377.phpt
rename to Zend/tests/readonly_classes/gh10377.phpt
diff --git a/Zend/tests/gh10377_1.phpt b/Zend/tests/readonly_classes/gh10377_1.phpt
similarity index 100%
rename from Zend/tests/gh10377_1.phpt
rename to Zend/tests/readonly_classes/gh10377_1.phpt
diff --git a/Zend/tests/gh10377_2.phpt b/Zend/tests/readonly_classes/gh10377_2.phpt
similarity index 100%
rename from Zend/tests/gh10377_2.phpt
rename to Zend/tests/readonly_classes/gh10377_2.phpt
diff --git a/Zend/tests/gh10377_3.phpt b/Zend/tests/readonly_classes/gh10377_3.phpt
similarity index 100%
rename from Zend/tests/gh10377_3.phpt
rename to Zend/tests/readonly_classes/gh10377_3.phpt
diff --git a/Zend/tests/gh10377_4.phpt b/Zend/tests/readonly_classes/gh10377_4.phpt
similarity index 100%
rename from Zend/tests/gh10377_4.phpt
rename to Zend/tests/readonly_classes/gh10377_4.phpt
diff --git a/Zend/tests/bug34045.phpt b/Zend/tests/serialize/bug34045.phpt
similarity index 100%
rename from Zend/tests/bug34045.phpt
rename to Zend/tests/serialize/bug34045.phpt
diff --git a/Zend/tests/bug64354.phpt b/Zend/tests/serialize/bug64354.phpt
similarity index 100%
rename from Zend/tests/bug64354.phpt
rename to Zend/tests/serialize/bug64354.phpt
diff --git a/Zend/tests/bug69761.phpt b/Zend/tests/serialize/bug69761.phpt
similarity index 100%
rename from Zend/tests/bug69761.phpt
rename to Zend/tests/serialize/bug69761.phpt
diff --git a/Zend/tests/bug70121.phpt b/Zend/tests/serialize/bug70121.phpt
similarity index 100%
rename from Zend/tests/bug70121.phpt
rename to Zend/tests/serialize/bug70121.phpt
diff --git a/Zend/tests/bug70187.phpt b/Zend/tests/serialize/bug70187.phpt
similarity index 100%
rename from Zend/tests/bug70187.phpt
rename to Zend/tests/serialize/bug70187.phpt
diff --git a/Zend/tests/bug70187_2.phpt b/Zend/tests/serialize/bug70187_2.phpt
similarity index 100%
rename from Zend/tests/bug70187_2.phpt
rename to Zend/tests/serialize/bug70187_2.phpt
diff --git a/Zend/tests/bug70253.phpt b/Zend/tests/serialize/bug70253.phpt
similarity index 100%
rename from Zend/tests/bug70253.phpt
rename to Zend/tests/serialize/bug70253.phpt
diff --git a/Zend/tests/bug71841.phpt b/Zend/tests/serialize/bug71841.phpt
similarity index 100%
rename from Zend/tests/bug71841.phpt
rename to Zend/tests/serialize/bug71841.phpt
diff --git a/Zend/tests/bug73900.phpt b/Zend/tests/serialize/bug73900.phpt
similarity index 100%
rename from Zend/tests/bug73900.phpt
rename to Zend/tests/serialize/bug73900.phpt
diff --git a/Zend/tests/bug76502.phpt b/Zend/tests/serialize/bug76502.phpt
similarity index 100%
rename from Zend/tests/bug76502.phpt
rename to Zend/tests/serialize/bug76502.phpt
diff --git a/Zend/tests/serializable_deprecation.phpt b/Zend/tests/serialize/serializable_deprecation.phpt
similarity index 100%
rename from Zend/tests/serializable_deprecation.phpt
rename to Zend/tests/serialize/serializable_deprecation.phpt
diff --git a/Zend/tests/settype_array.phpt b/Zend/tests/settype/settype_array.phpt
similarity index 100%
rename from Zend/tests/settype_array.phpt
rename to Zend/tests/settype/settype_array.phpt
diff --git a/Zend/tests/settype_bool.phpt b/Zend/tests/settype/settype_bool.phpt
similarity index 100%
rename from Zend/tests/settype_bool.phpt
rename to Zend/tests/settype/settype_bool.phpt
diff --git a/Zend/tests/settype_double.phpt b/Zend/tests/settype/settype_double.phpt
similarity index 100%
rename from Zend/tests/settype_double.phpt
rename to Zend/tests/settype/settype_double.phpt
diff --git a/Zend/tests/settype_int.phpt b/Zend/tests/settype/settype_int.phpt
similarity index 100%
rename from Zend/tests/settype_int.phpt
rename to Zend/tests/settype/settype_int.phpt
diff --git a/Zend/tests/settype_null.phpt b/Zend/tests/settype/settype_null.phpt
similarity index 100%
rename from Zend/tests/settype_null.phpt
rename to Zend/tests/settype/settype_null.phpt
diff --git a/Zend/tests/settype_object.phpt b/Zend/tests/settype/settype_object.phpt
similarity index 100%
rename from Zend/tests/settype_object.phpt
rename to Zend/tests/settype/settype_object.phpt
diff --git a/Zend/tests/settype_resource.phpt b/Zend/tests/settype/settype_resource.phpt
similarity index 100%
rename from Zend/tests/settype_resource.phpt
rename to Zend/tests/settype/settype_resource.phpt
diff --git a/Zend/tests/settype_string.phpt b/Zend/tests/settype/settype_string.phpt
similarity index 100%
rename from Zend/tests/settype_string.phpt
rename to Zend/tests/settype/settype_string.phpt
diff --git a/Zend/tests/static_variable.phpt b/Zend/tests/static_variables/static_variable.phpt
similarity index 100%
rename from Zend/tests/static_variable.phpt
rename to Zend/tests/static_variables/static_variable.phpt
diff --git a/Zend/tests/static_variable_func_call.phpt b/Zend/tests/static_variables/static_variable_func_call.phpt
similarity index 100%
rename from Zend/tests/static_variable_func_call.phpt
rename to Zend/tests/static_variables/static_variable_func_call.phpt
diff --git a/Zend/tests/static_variable_in_dynamic_function.phpt b/Zend/tests/static_variables/static_variable_in_dynamic_function.phpt
similarity index 100%
rename from Zend/tests/static_variable_in_dynamic_function.phpt
rename to Zend/tests/static_variables/static_variable_in_dynamic_function.phpt
diff --git a/Zend/tests/static_variable_in_dynamic_function_2.phpt b/Zend/tests/static_variables/static_variable_in_dynamic_function_2.phpt
similarity index 100%
rename from Zend/tests/static_variable_in_dynamic_function_2.phpt
rename to Zend/tests/static_variables/static_variable_in_dynamic_function_2.phpt
diff --git a/Zend/tests/static_variable_in_private_method.phpt b/Zend/tests/static_variables/static_variable_in_private_method.phpt
similarity index 100%
rename from Zend/tests/static_variable_in_private_method.phpt
rename to Zend/tests/static_variables/static_variable_in_private_method.phpt
diff --git a/Zend/tests/static_variable_in_private_trait_method.phpt b/Zend/tests/static_variables/static_variable_in_private_trait_method.phpt
similarity index 100%
rename from Zend/tests/static_variable_in_private_trait_method.phpt
rename to Zend/tests/static_variables/static_variable_in_private_trait_method.phpt
diff --git a/Zend/tests/static_variables_closure_bind.phpt b/Zend/tests/static_variables/static_variables_closure_bind.phpt
similarity index 100%
rename from Zend/tests/static_variables_closure_bind.phpt
rename to Zend/tests/static_variables/static_variables_closure_bind.phpt
diff --git a/Zend/tests/static_variables_destructor.phpt b/Zend/tests/static_variables/static_variables_destructor.phpt
similarity index 100%
rename from Zend/tests/static_variables_destructor.phpt
rename to Zend/tests/static_variables/static_variables_destructor.phpt
diff --git a/Zend/tests/static_variables_global.phpt b/Zend/tests/static_variables/static_variables_global.phpt
similarity index 100%
rename from Zend/tests/static_variables_global.phpt
rename to Zend/tests/static_variables/static_variables_global.phpt
diff --git a/Zend/tests/static_variables_global_2.phpt b/Zend/tests/static_variables/static_variables_global_2.phpt
similarity index 100%
rename from Zend/tests/static_variables_global_2.phpt
rename to Zend/tests/static_variables/static_variables_global_2.phpt
diff --git a/Zend/tests/static_variables_recursive.phpt b/Zend/tests/static_variables/static_variables_recursive.phpt
similarity index 100%
rename from Zend/tests/static_variables_recursive.phpt
rename to Zend/tests/static_variables/static_variables_recursive.phpt
diff --git a/Zend/tests/static_variables_throwing_initializer.phpt b/Zend/tests/static_variables/static_variables_throwing_initializer.phpt
similarity index 100%
rename from Zend/tests/static_variables_throwing_initializer.phpt
rename to Zend/tests/static_variables/static_variables_throwing_initializer.phpt
diff --git a/Zend/tests/static_variables_traits.phpt b/Zend/tests/static_variables/static_variables_traits.phpt
similarity index 100%
rename from Zend/tests/static_variables_traits.phpt
rename to Zend/tests/static_variables/static_variables_traits.phpt
diff --git a/Zend/tests/034.phpt b/Zend/tests/switch/034.phpt
similarity index 100%
rename from Zend/tests/034.phpt
rename to Zend/tests/switch/034.phpt
diff --git a/Zend/tests/bug26281.phpt b/Zend/tests/switch/bug26281.phpt
similarity index 100%
rename from Zend/tests/bug26281.phpt
rename to Zend/tests/switch/bug26281.phpt
diff --git a/Zend/tests/bug26696.phpt b/Zend/tests/switch/bug26696.phpt
similarity index 100%
rename from Zend/tests/bug26696.phpt
rename to Zend/tests/switch/bug26696.phpt
diff --git a/Zend/tests/bug26801.phpt b/Zend/tests/switch/bug26801.phpt
similarity index 100%
rename from Zend/tests/bug26801.phpt
rename to Zend/tests/switch/bug26801.phpt
diff --git a/Zend/tests/bug29944.phpt b/Zend/tests/switch/bug29944.phpt
similarity index 100%
rename from Zend/tests/bug29944.phpt
rename to Zend/tests/switch/bug29944.phpt
diff --git a/Zend/tests/bug38623.phpt b/Zend/tests/switch/bug38623.phpt
similarity index 100%
rename from Zend/tests/bug38623.phpt
rename to Zend/tests/switch/bug38623.phpt
diff --git a/Zend/tests/bug71756.phpt b/Zend/tests/switch/bug71756.phpt
similarity index 100%
rename from Zend/tests/bug71756.phpt
rename to Zend/tests/switch/bug71756.phpt
diff --git a/Zend/tests/bug71914.phpt b/Zend/tests/switch/bug71914.phpt
similarity index 100%
rename from Zend/tests/bug71914.phpt
rename to Zend/tests/switch/bug71914.phpt
diff --git a/Zend/tests/bug72508.phpt b/Zend/tests/switch/bug72508.phpt
similarity index 100%
rename from Zend/tests/bug72508.phpt
rename to Zend/tests/switch/bug72508.phpt
diff --git a/Zend/tests/bug80046.phpt b/Zend/tests/switch/bug80046.phpt
similarity index 100%
rename from Zend/tests/bug80046.phpt
rename to Zend/tests/switch/bug80046.phpt
diff --git a/Zend/tests/continue_targeting_switch_warning.phpt b/Zend/tests/switch/continue_targeting_switch_warning.phpt
similarity index 100%
rename from Zend/tests/continue_targeting_switch_warning.phpt
rename to Zend/tests/switch/continue_targeting_switch_warning.phpt
diff --git a/Zend/tests/switch_on_numeric_strings.phpt b/Zend/tests/switch/switch_on_numeric_strings.phpt
similarity index 100%
rename from Zend/tests/switch_on_numeric_strings.phpt
rename to Zend/tests/switch/switch_on_numeric_strings.phpt
diff --git a/Zend/tests/temporary_cleaning_001.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_001.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_001.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_001.phpt
diff --git a/Zend/tests/temporary_cleaning_002.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_002.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_002.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_002.phpt
diff --git a/Zend/tests/temporary_cleaning_003.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_003.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_003.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_003.phpt
diff --git a/Zend/tests/temporary_cleaning_004.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_004.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_004.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_004.phpt
diff --git a/Zend/tests/temporary_cleaning_005.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_005.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_005.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_005.phpt
diff --git a/Zend/tests/temporary_cleaning_006.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_006.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_006.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_006.phpt
diff --git a/Zend/tests/temporary_cleaning_007.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_007.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_007.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_007.phpt
diff --git a/Zend/tests/temporary_cleaning_008.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_008.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_008.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_008.phpt
diff --git a/Zend/tests/temporary_cleaning_009.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_009.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_009.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_009.phpt
diff --git a/Zend/tests/temporary_cleaning_010.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_010.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_010.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_010.phpt
diff --git a/Zend/tests/temporary_cleaning_011.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_011.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_011.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_011.phpt
diff --git a/Zend/tests/temporary_cleaning_012.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_012.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_012.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_012.phpt
diff --git a/Zend/tests/temporary_cleaning_013.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_013.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_013.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_013.phpt
diff --git a/Zend/tests/temporary_cleaning_014.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_014.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_014.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_014.phpt
diff --git a/Zend/tests/temporary_cleaning_015.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_015.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt
diff --git a/Zend/tests/temporary_cleaning_016.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_016.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt
diff --git a/Zend/tests/temporary_cleaning_017.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_017.phpt
similarity index 100%
rename from Zend/tests/temporary_cleaning_017.phpt
rename to Zend/tests/temporary_cleaning/temporary_cleaning_017.phpt
diff --git a/Zend/tests/030.phpt b/Zend/tests/this-reserved/030.phpt
similarity index 100%
rename from Zend/tests/030.phpt
rename to Zend/tests/this-reserved/030.phpt
diff --git a/Zend/tests/bug34358.phpt b/Zend/tests/this-reserved/bug34358.phpt
similarity index 100%
rename from Zend/tests/bug34358.phpt
rename to Zend/tests/this-reserved/bug34358.phpt
diff --git a/Zend/tests/bug41117_1.phpt b/Zend/tests/this-reserved/bug41117_1.phpt
similarity index 100%
rename from Zend/tests/bug41117_1.phpt
rename to Zend/tests/this-reserved/bug41117_1.phpt
diff --git a/Zend/tests/bug71737.phpt b/Zend/tests/this-reserved/bug71737.phpt
similarity index 100%
rename from Zend/tests/bug71737.phpt
rename to Zend/tests/this-reserved/bug71737.phpt
diff --git a/Zend/tests/this_as_global.phpt b/Zend/tests/this-reserved/this_as_global.phpt
similarity index 100%
rename from Zend/tests/this_as_global.phpt
rename to Zend/tests/this-reserved/this_as_global.phpt
diff --git a/Zend/tests/this_as_lexical_var_error.phpt b/Zend/tests/this-reserved/this_as_lexical_var_error.phpt
similarity index 100%
rename from Zend/tests/this_as_lexical_var_error.phpt
rename to Zend/tests/this-reserved/this_as_lexical_var_error.phpt
diff --git a/Zend/tests/this_as_parameter.phpt b/Zend/tests/this-reserved/this_as_parameter.phpt
similarity index 100%
rename from Zend/tests/this_as_parameter.phpt
rename to Zend/tests/this-reserved/this_as_parameter.phpt
diff --git a/Zend/tests/this_as_static.phpt b/Zend/tests/this-reserved/this_as_static.phpt
similarity index 100%
rename from Zend/tests/this_as_static.phpt
rename to Zend/tests/this-reserved/this_as_static.phpt
diff --git a/Zend/tests/this_in_catch.phpt b/Zend/tests/this-reserved/this_in_catch.phpt
similarity index 100%
rename from Zend/tests/this_in_catch.phpt
rename to Zend/tests/this-reserved/this_in_catch.phpt
diff --git a/Zend/tests/this_in_extract.phpt b/Zend/tests/this-reserved/this_in_extract.phpt
similarity index 100%
rename from Zend/tests/this_in_extract.phpt
rename to Zend/tests/this-reserved/this_in_extract.phpt
diff --git a/Zend/tests/this_reassign.phpt b/Zend/tests/this-reserved/this_reassign.phpt
similarity index 100%
rename from Zend/tests/this_reassign.phpt
rename to Zend/tests/this-reserved/this_reassign.phpt
diff --git a/Zend/tests/bug33318.phpt b/Zend/tests/throw/bug33318.phpt
similarity index 100%
rename from Zend/tests/bug33318.phpt
rename to Zend/tests/throw/bug33318.phpt
diff --git a/Zend/tests/traits/abstract_method_6.phpt b/Zend/tests/traits/abstract_method_6.phpt
index a8a3fc4138689..279022d7f29cf 100644
--- a/Zend/tests/traits/abstract_method_6.phpt
+++ b/Zend/tests/traits/abstract_method_6.phpt
@@ -17,4 +17,4 @@ class D extends C {
?>
--EXPECTF--
-Fatal error: Class C must implement 1 abstract private method (C::method) in %s on line %d
+Fatal error: Class C must implement 1 abstract method (C::method) in %s on line %d
diff --git a/Zend/tests/bug53748.phpt b/Zend/tests/traits/bug53748.phpt
similarity index 100%
rename from Zend/tests/bug53748.phpt
rename to Zend/tests/traits/bug53748.phpt
diff --git a/Zend/tests/bug55086.phpt b/Zend/tests/traits/bug55086.phpt
similarity index 100%
rename from Zend/tests/bug55086.phpt
rename to Zend/tests/traits/bug55086.phpt
diff --git a/Zend/tests/bug55825.phpt b/Zend/tests/traits/bug55825.phpt
similarity index 100%
rename from Zend/tests/bug55825.phpt
rename to Zend/tests/traits/bug55825.phpt
diff --git a/Zend/tests/bug60536_001.phpt b/Zend/tests/traits/bug60536_001.phpt
similarity index 100%
rename from Zend/tests/bug60536_001.phpt
rename to Zend/tests/traits/bug60536_001.phpt
diff --git a/Zend/tests/bug60536_002.phpt b/Zend/tests/traits/bug60536_002.phpt
similarity index 100%
rename from Zend/tests/bug60536_002.phpt
rename to Zend/tests/traits/bug60536_002.phpt
diff --git a/Zend/tests/bug60536_003.phpt b/Zend/tests/traits/bug60536_003.phpt
similarity index 100%
rename from Zend/tests/bug60536_003.phpt
rename to Zend/tests/traits/bug60536_003.phpt
diff --git a/Zend/tests/bug60536_004.phpt b/Zend/tests/traits/bug60536_004.phpt
similarity index 100%
rename from Zend/tests/bug60536_004.phpt
rename to Zend/tests/traits/bug60536_004.phpt
diff --git a/Zend/tests/bug60536_005.phpt b/Zend/tests/traits/bug60536_005.phpt
similarity index 100%
rename from Zend/tests/bug60536_005.phpt
rename to Zend/tests/traits/bug60536_005.phpt
diff --git a/Zend/tests/bug62069.phpt b/Zend/tests/traits/bug62069.phpt
similarity index 100%
rename from Zend/tests/bug62069.phpt
rename to Zend/tests/traits/bug62069.phpt
diff --git a/Zend/tests/bug62069_2.phpt b/Zend/tests/traits/bug62069_2.phpt
similarity index 100%
rename from Zend/tests/bug62069_2.phpt
rename to Zend/tests/traits/bug62069_2.phpt
diff --git a/Zend/tests/bug62358.phpt b/Zend/tests/traits/bug62358.phpt
similarity index 100%
rename from Zend/tests/bug62358.phpt
rename to Zend/tests/traits/bug62358.phpt
diff --git a/Zend/tests/bug62892.phpt b/Zend/tests/traits/bug62892.phpt
similarity index 100%
rename from Zend/tests/bug62892.phpt
rename to Zend/tests/traits/bug62892.phpt
diff --git a/Zend/tests/bug62907.phpt b/Zend/tests/traits/bug62907.phpt
similarity index 100%
rename from Zend/tests/bug62907.phpt
rename to Zend/tests/traits/bug62907.phpt
diff --git a/Zend/tests/bug63219.phpt b/Zend/tests/traits/bug63219.phpt
similarity index 100%
rename from Zend/tests/bug63219.phpt
rename to Zend/tests/traits/bug63219.phpt
diff --git a/Zend/tests/bug63305.phpt b/Zend/tests/traits/bug63305.phpt
similarity index 100%
rename from Zend/tests/bug63305.phpt
rename to Zend/tests/traits/bug63305.phpt
diff --git a/Zend/tests/bug65419.phpt b/Zend/tests/traits/bug65419.phpt
similarity index 100%
rename from Zend/tests/bug65419.phpt
rename to Zend/tests/traits/bug65419.phpt
diff --git a/Zend/tests/bug65579.phpt b/Zend/tests/traits/bug65579.phpt
similarity index 100%
rename from Zend/tests/bug65579.phpt
rename to Zend/tests/traits/bug65579.phpt
diff --git a/Zend/tests/bug69084.phpt b/Zend/tests/traits/bug69084.phpt
similarity index 83%
rename from Zend/tests/bug69084.phpt
rename to Zend/tests/traits/bug69084.phpt
index 2cefcc54320f1..8b10ef2fb53d7 100644
--- a/Zend/tests/bug69084.phpt
+++ b/Zend/tests/traits/bug69084.phpt
@@ -26,4 +26,4 @@ $b->main();
?>
--EXPECTF--
-Fatal error: Class Bar contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Bar::doOtherStuff) in %s on line %d
+Fatal error: Class Bar contains 1 abstract method and must therefore be declared abstract or implement the remaining method (Bar::doOtherStuff) in %s on line %d
diff --git a/Zend/tests/bug69174.phpt b/Zend/tests/traits/bug69174.phpt
similarity index 100%
rename from Zend/tests/bug69174.phpt
rename to Zend/tests/traits/bug69174.phpt
diff --git a/Zend/tests/bug69467.phpt b/Zend/tests/traits/bug69467.phpt
similarity index 100%
rename from Zend/tests/bug69467.phpt
rename to Zend/tests/traits/bug69467.phpt
diff --git a/Zend/tests/bug70156.phpt b/Zend/tests/traits/bug70156.phpt
similarity index 100%
rename from Zend/tests/bug70156.phpt
rename to Zend/tests/traits/bug70156.phpt
diff --git a/Zend/tests/bug70958.phpt b/Zend/tests/traits/bug70958.phpt
similarity index 100%
rename from Zend/tests/bug70958.phpt
rename to Zend/tests/traits/bug70958.phpt
diff --git a/Zend/tests/bug71275.phpt b/Zend/tests/traits/bug71275.phpt
similarity index 100%
rename from Zend/tests/bug71275.phpt
rename to Zend/tests/traits/bug71275.phpt
diff --git a/Zend/tests/bug74269.phpt b/Zend/tests/traits/bug74269.phpt
similarity index 100%
rename from Zend/tests/bug74269.phpt
rename to Zend/tests/traits/bug74269.phpt
diff --git a/Zend/tests/bug77966.phpt b/Zend/tests/traits/bug77966.phpt
similarity index 100%
rename from Zend/tests/bug77966.phpt
rename to Zend/tests/traits/bug77966.phpt
diff --git a/Zend/tests/bug78776.phpt b/Zend/tests/traits/bug78776.phpt
similarity index 93%
rename from Zend/tests/bug78776.phpt
rename to Zend/tests/traits/bug78776.phpt
index 3696d955a3a3e..feecfeb8fddd4 100644
--- a/Zend/tests/bug78776.phpt
+++ b/Zend/tests/traits/bug78776.phpt
@@ -25,4 +25,4 @@ B::createApp();
?>
--EXPECTF--
-Fatal error: Cannot make non static method A::createApp() static in class C in %s on line %d
+Fatal error: Cannot make non static method A::createApp() static in class B in %s on line %d
diff --git a/Zend/tests/bug78787.phpt b/Zend/tests/traits/bug78787.phpt
similarity index 100%
rename from Zend/tests/bug78787.phpt
rename to Zend/tests/traits/bug78787.phpt
diff --git a/Zend/tests/bug80055.phpt b/Zend/tests/traits/bug80055.phpt
similarity index 100%
rename from Zend/tests/bug80055.phpt
rename to Zend/tests/traits/bug80055.phpt
diff --git a/Zend/tests/bug81192.phpt b/Zend/tests/traits/bug81192.phpt
similarity index 83%
rename from Zend/tests/bug81192.phpt
rename to Zend/tests/traits/bug81192.phpt
index 00f6f1d2fbf71..268df826ddfde 100644
--- a/Zend/tests/bug81192.phpt
+++ b/Zend/tests/traits/bug81192.phpt
@@ -17,4 +17,4 @@ class B extends A {
?>
--EXPECTF--
-Fatal error: Declaration of T::foo(): string must be compatible with A::foo(): int in %sbug81192_trait.inc on line 4
+Fatal error: Declaration of B::foo(): string must be compatible with A::foo(): int in %sbug81192_trait.inc on line 4
diff --git a/Zend/tests/bug81192_trait.inc b/Zend/tests/traits/bug81192_trait.inc
similarity index 100%
rename from Zend/tests/bug81192_trait.inc
rename to Zend/tests/traits/bug81192_trait.inc
diff --git a/Zend/tests/traits/bugs/abstract-methods01.phpt b/Zend/tests/traits/bugs/abstract-methods01.phpt
index 721f9657594d8..e50f94ccefeb4 100644
--- a/Zend/tests/traits/bugs/abstract-methods01.phpt
+++ b/Zend/tests/traits/bugs/abstract-methods01.phpt
@@ -16,4 +16,4 @@ $test = new TraitsTest();
$test->hello();
?>
--EXPECTF--
-Fatal error: Class %s contains %d abstract method and must therefore be declared abstract or implement the remaining methods (%s) in %s on line %d
+Fatal error: Class %s contains %d abstract method and must therefore be declared abstract or implement the remaining method (%s) in %s on line %d
diff --git a/Zend/tests/traits/bugs/abstract-methods05.phpt b/Zend/tests/traits/bugs/abstract-methods05.phpt
index 96619eae910b8..e0ffc60c14921 100644
--- a/Zend/tests/traits/bugs/abstract-methods05.phpt
+++ b/Zend/tests/traits/bugs/abstract-methods05.phpt
@@ -22,4 +22,4 @@ class TraitsTest1 {
?>
--EXPECTF--
-Fatal error: Declaration of THelloB::hello() must be compatible with THelloA::hello($a) in %s on line %d
+Fatal error: Declaration of TraitsTest1::hello() must be compatible with THelloA::hello($a) in %s on line %d
diff --git a/Zend/tests/traits/bugs/abstract-methods06.phpt b/Zend/tests/traits/bugs/abstract-methods06.phpt
index 8e2f25b048e0b..82ad805aed538 100644
--- a/Zend/tests/traits/bugs/abstract-methods06.phpt
+++ b/Zend/tests/traits/bugs/abstract-methods06.phpt
@@ -23,4 +23,4 @@ class TraitsTest1 {
?>
--EXPECTF--
-Fatal error: Declaration of THelloB::hello() must be compatible with THelloA::hello($a) in %s on line %d
+Fatal error: Declaration of TraitsTest1::hello() must be compatible with THelloA::hello($a) in %s on line %d
diff --git a/Zend/tests/traits/bugs/missing-trait.phpt b/Zend/tests/traits/bugs/missing-trait.phpt
index 4bf054e889002..0b36c47e771d7 100644
--- a/Zend/tests/traits/bugs/missing-trait.phpt
+++ b/Zend/tests/traits/bugs/missing-trait.phpt
@@ -12,4 +12,7 @@ $test = new TraitsTest();
?>
--EXPECTF--
-Fatal error: Trait "THello" not found in %s on line %d
+Fatal error: Uncaught Error: Trait "THello" not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/class_uses_static.phpt b/Zend/tests/traits/class_uses_static.phpt
similarity index 100%
rename from Zend/tests/class_uses_static.phpt
rename to Zend/tests/traits/class_uses_static.phpt
diff --git a/Zend/tests/traits/constant_015.phpt b/Zend/tests/traits/constant_015.phpt
index 24507ea9e59cd..1b219340506b8 100644
--- a/Zend/tests/traits/constant_015.phpt
+++ b/Zend/tests/traits/constant_015.phpt
@@ -1,5 +1,5 @@
--TEST--
-The same name constant of a trait used in a class that inherits a constant defined in a parent can be defined only if they are compatible.
+Final class constant in parent may not be overridden by child through trait
--FILE--
--EXPECTF--
-Fatal error: BaseClass2 and TestTrait2 define the same constant (Constant) in the composition of DerivedClass2. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
+Fatal error: DerivedClass1::Constant cannot override final constant BaseClass1::Constant in %s on line %d
diff --git a/Zend/tests/traits/error_001.phpt b/Zend/tests/traits/error_001.phpt
deleted file mode 100644
index a7889da41e2b7..0000000000000
--- a/Zend/tests/traits/error_001.phpt
+++ /dev/null
@@ -1,28 +0,0 @@
---TEST--
-Trying to use instanceof for a method twice
---FILE--
-
---EXPECTF--
-Fatal error: Class A cannot extend trait foo in %s on line %d
diff --git a/Zend/tests/traits/error_002.phpt b/Zend/tests/traits/error_002.phpt
index 53ad403a43373..fc9cfc5884c4c 100644
--- a/Zend/tests/traits/error_002.phpt
+++ b/Zend/tests/traits/error_002.phpt
@@ -9,4 +9,7 @@ class A {
?>
--EXPECTF--
-Fatal error: Trait "abc" not found in %s on line %d
+Fatal error: Uncaught Error: Trait "abc" not found in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/traits/error_003.phpt b/Zend/tests/traits/error_003.phpt
index fdfa0ac1fe22b..6a6d2d56d6c41 100644
--- a/Zend/tests/traits/error_003.phpt
+++ b/Zend/tests/traits/error_003.phpt
@@ -12,4 +12,7 @@ class A {
?>
--EXPECTF--
-Fatal error: A cannot use abc - it is not a trait in %s on line %d
+Fatal error: Uncaught Error: A cannot use abc - it is not a trait in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/traits/error_004.phpt b/Zend/tests/traits/error_004.phpt
index d7ff695e047b3..1e339f3d6314d 100644
--- a/Zend/tests/traits/error_004.phpt
+++ b/Zend/tests/traits/error_004.phpt
@@ -12,4 +12,7 @@ class A {
?>
--EXPECTF--
-Fatal error: A cannot use abc - it is not a trait in %s on line %d
+Fatal error: Uncaught Error: A cannot use abc - it is not a trait in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/traits/error_005.phpt b/Zend/tests/traits/error_005.phpt
index 3b8cc32f97584..dae8e1893a68c 100644
--- a/Zend/tests/traits/error_005.phpt
+++ b/Zend/tests/traits/error_005.phpt
@@ -12,4 +12,7 @@ class A {
?>
--EXPECTF--
-Fatal error: A cannot use abc - it is not a trait in %s on line %d
+Fatal error: Uncaught Error: A cannot use abc - it is not a trait in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/traits/error_006.phpt b/Zend/tests/traits/error_006.phpt
index f3ed87123cf29..5d831a9da7f12 100644
--- a/Zend/tests/traits/error_006.phpt
+++ b/Zend/tests/traits/error_006.phpt
@@ -12,4 +12,7 @@ class A {
?>
--EXPECTF--
-Fatal error: A cannot use abc - it is not a trait in %s on line %d
+Fatal error: Uncaught Error: A cannot use abc - it is not a trait in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/gh10935.phpt b/Zend/tests/traits/gh10935.phpt
similarity index 100%
rename from Zend/tests/gh10935.phpt
rename to Zend/tests/traits/gh10935.phpt
diff --git a/Zend/tests/gh12468_1.phpt b/Zend/tests/traits/gh12468_1.phpt
similarity index 100%
rename from Zend/tests/gh12468_1.phpt
rename to Zend/tests/traits/gh12468_1.phpt
diff --git a/Zend/tests/gh12468_2.phpt b/Zend/tests/traits/gh12468_2.phpt
similarity index 100%
rename from Zend/tests/gh12468_2.phpt
rename to Zend/tests/traits/gh12468_2.phpt
diff --git a/Zend/tests/gh14009_001.phpt b/Zend/tests/traits/gh14009_001.phpt
similarity index 100%
rename from Zend/tests/gh14009_001.phpt
rename to Zend/tests/traits/gh14009_001.phpt
diff --git a/Zend/tests/gh14009_002.phpt b/Zend/tests/traits/gh14009_002.phpt
similarity index 100%
rename from Zend/tests/gh14009_002.phpt
rename to Zend/tests/traits/gh14009_002.phpt
diff --git a/Zend/tests/gh14009_003.phpt b/Zend/tests/traits/gh14009_003.phpt
similarity index 100%
rename from Zend/tests/gh14009_003.phpt
rename to Zend/tests/traits/gh14009_003.phpt
diff --git a/Zend/tests/gh14009_004.phpt b/Zend/tests/traits/gh14009_004.phpt
similarity index 100%
rename from Zend/tests/gh14009_004.phpt
rename to Zend/tests/traits/gh14009_004.phpt
diff --git a/Zend/tests/gh14009_005.phpt b/Zend/tests/traits/gh14009_005.phpt
similarity index 100%
rename from Zend/tests/gh14009_005.phpt
rename to Zend/tests/traits/gh14009_005.phpt
diff --git a/Zend/tests/gh14480.phpt b/Zend/tests/traits/gh14480.phpt
similarity index 100%
rename from Zend/tests/gh14480.phpt
rename to Zend/tests/traits/gh14480.phpt
diff --git a/Zend/tests/traits/gh15753.phpt b/Zend/tests/traits/gh15753.phpt
new file mode 100644
index 0000000000000..21e32d96c09e3
--- /dev/null
+++ b/Zend/tests/traits/gh15753.phpt
@@ -0,0 +1,23 @@
+--TEST--
+GH-15753: Trait can override property declared in parent class of used class
+--FILE--
+prop);
+
+?>
+--EXPECT--
+int(2)
diff --git a/Zend/tests/traits/gh16198.phpt b/Zend/tests/traits/gh16198.phpt
new file mode 100644
index 0000000000000..f44926cf5ce24
--- /dev/null
+++ b/Zend/tests/traits/gh16198.phpt
@@ -0,0 +1,36 @@
+--TEST--
+GH-16198: Incorrect trait constant conflict when declared via trait
+--FILE--
+
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/traits/gh16198_2.phpt b/Zend/tests/traits/gh16198_2.phpt
new file mode 100644
index 0000000000000..7599ea1a11b49
--- /dev/null
+++ b/Zend/tests/traits/gh16198_2.phpt
@@ -0,0 +1,27 @@
+--TEST--
+GH-16198: Usage of super in cloned trait method
+--FILE--
+
+--EXPECT--
+string(13) "P::__destruct"
diff --git a/Zend/tests/gh17214.phpt b/Zend/tests/traits/gh17214.phpt
similarity index 100%
rename from Zend/tests/gh17214.phpt
rename to Zend/tests/traits/gh17214.phpt
diff --git a/Zend/tests/traits/gh17959.phpt b/Zend/tests/traits/gh17959.phpt
new file mode 100644
index 0000000000000..016dd7a4a4f7c
--- /dev/null
+++ b/Zend/tests/traits/gh17959.phpt
@@ -0,0 +1,18 @@
+--TEST--
+GH-17959: Missing trait error is recoverable
+--FILE--
+getMessage(), "\n";
+}
+
+?>
+===DONE===
+--EXPECT--
+Error: Trait "MissingTrait" not found
+===DONE===
diff --git a/Zend/tests/traits/gh18295.phpt b/Zend/tests/traits/gh18295.phpt
new file mode 100644
index 0000000000000..438431a5b4eec
--- /dev/null
+++ b/Zend/tests/traits/gh18295.phpt
@@ -0,0 +1,21 @@
+--TEST--
+GH-18295: Parent self is substitutable with child self through trait
+--FILE--
+
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/gh_17728.phpt b/Zend/tests/traits/gh_17728.phpt
similarity index 100%
rename from Zend/tests/gh_17728.phpt
rename to Zend/tests/traits/gh_17728.phpt
diff --git a/Zend/tests/traits/inheritance003.phpt b/Zend/tests/traits/inheritance003.phpt
index 1e630eef61d88..1826a084c5a01 100644
--- a/Zend/tests/traits/inheritance003.phpt
+++ b/Zend/tests/traits/inheritance003.phpt
@@ -35,4 +35,4 @@ $o->sayHello(array());
--EXPECTF--
World!
-Fatal error: Declaration of SayWorld::sayHello(Base $d) must be compatible with Base::sayHello(array $a) in %s on line %d
+Fatal error: Declaration of MyHelloWorld::sayHello(Base $d) must be compatible with Base::sayHello(array $a) in %s on line %d
diff --git a/Zend/tests/traits/interface_002.phpt b/Zend/tests/traits/interface_002.phpt
index bc56a6d68bf73..4d48706cc95b0 100644
--- a/Zend/tests/traits/interface_002.phpt
+++ b/Zend/tests/traits/interface_002.phpt
@@ -21,4 +21,4 @@ new bar;
?>
--EXPECTF--
-Fatal error: Class bar contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (baz::abc) in %s on line %d
+Fatal error: Class bar contains 1 abstract method and must therefore be declared abstract or implement the remaining method (baz::abc) in %s on line %d
diff --git a/Zend/tests/static_in_trait_insteadof_list.phpt b/Zend/tests/traits/static_in_trait_insteadof_list.phpt
similarity index 100%
rename from Zend/tests/static_in_trait_insteadof_list.phpt
rename to Zend/tests/traits/static_in_trait_insteadof_list.phpt
diff --git a/Zend/tests/static_in_trait_insteadof_reference.phpt b/Zend/tests/traits/static_in_trait_insteadof_reference.phpt
similarity index 100%
rename from Zend/tests/static_in_trait_insteadof_reference.phpt
rename to Zend/tests/traits/static_in_trait_insteadof_reference.phpt
diff --git a/Zend/tests/trait_exists_001.phpt b/Zend/tests/traits/trait_exists_001.phpt
similarity index 100%
rename from Zend/tests/trait_exists_001.phpt
rename to Zend/tests/traits/trait_exists_001.phpt
diff --git a/Zend/tests/trait_exists_002.phpt b/Zend/tests/traits/trait_exists_002.phpt
similarity index 100%
rename from Zend/tests/trait_exists_002.phpt
rename to Zend/tests/traits/trait_exists_002.phpt
diff --git a/Zend/tests/trait_exists_003.phpt b/Zend/tests/traits/trait_exists_003.phpt
similarity index 100%
rename from Zend/tests/trait_exists_003.phpt
rename to Zend/tests/traits/trait_exists_003.phpt
diff --git a/Zend/tests/trait_type_errors.phpt b/Zend/tests/traits/trait_type_errors.phpt
similarity index 100%
rename from Zend/tests/trait_type_errors.phpt
rename to Zend/tests/traits/trait_type_errors.phpt
diff --git a/Zend/tests/bug72188.phpt b/Zend/tests/try/bug72188.phpt
similarity index 100%
rename from Zend/tests/bug72188.phpt
rename to Zend/tests/try/bug72188.phpt
diff --git a/Zend/tests/bug72215.phpt b/Zend/tests/try/bug72215.phpt
similarity index 100%
rename from Zend/tests/bug72215.phpt
rename to Zend/tests/try/bug72215.phpt
diff --git a/Zend/tests/bug72215_1.phpt b/Zend/tests/try/bug72215_1.phpt
similarity index 100%
rename from Zend/tests/bug72215_1.phpt
rename to Zend/tests/try/bug72215_1.phpt
diff --git a/Zend/tests/bug72215_2.phpt b/Zend/tests/try/bug72215_2.phpt
similarity index 100%
rename from Zend/tests/bug72215_2.phpt
rename to Zend/tests/try/bug72215_2.phpt
diff --git a/Zend/tests/bug72215_3.phpt b/Zend/tests/try/bug72215_3.phpt
similarity index 100%
rename from Zend/tests/bug72215_3.phpt
rename to Zend/tests/try/bug72215_3.phpt
diff --git a/Zend/tests/bug72216.phpt b/Zend/tests/try/bug72216.phpt
similarity index 100%
rename from Zend/tests/bug72216.phpt
rename to Zend/tests/try/bug72216.phpt
diff --git a/Zend/tests/bug73337.phpt b/Zend/tests/try/bug73337.phpt
similarity index 100%
rename from Zend/tests/bug73337.phpt
rename to Zend/tests/try/bug73337.phpt
diff --git a/Zend/tests/cast_to_array.phpt b/Zend/tests/type_casts/cast_to_array.phpt
similarity index 100%
rename from Zend/tests/cast_to_array.phpt
rename to Zend/tests/type_casts/cast_to_array.phpt
diff --git a/Zend/tests/cast_to_bool.phpt b/Zend/tests/type_casts/cast_to_bool.phpt
similarity index 100%
rename from Zend/tests/cast_to_bool.phpt
rename to Zend/tests/type_casts/cast_to_bool.phpt
diff --git a/Zend/tests/cast_to_double.phpt b/Zend/tests/type_casts/cast_to_double.phpt
similarity index 100%
rename from Zend/tests/cast_to_double.phpt
rename to Zend/tests/type_casts/cast_to_double.phpt
diff --git a/Zend/tests/cast_to_int.phpt b/Zend/tests/type_casts/cast_to_int.phpt
similarity index 100%
rename from Zend/tests/cast_to_int.phpt
rename to Zend/tests/type_casts/cast_to_int.phpt
diff --git a/Zend/tests/cast_to_object.phpt b/Zend/tests/type_casts/cast_to_object.phpt
similarity index 100%
rename from Zend/tests/cast_to_object.phpt
rename to Zend/tests/type_casts/cast_to_object.phpt
diff --git a/Zend/tests/cast_to_string.phpt b/Zend/tests/type_casts/cast_to_string.phpt
similarity index 100%
rename from Zend/tests/cast_to_string.phpt
rename to Zend/tests/type_casts/cast_to_string.phpt
diff --git a/Zend/tests/type_casts/cast_to_void.phpt b/Zend/tests/type_casts/cast_to_void.phpt
new file mode 100644
index 0000000000000..cdbcffad519c9
--- /dev/null
+++ b/Zend/tests/type_casts/cast_to_void.phpt
@@ -0,0 +1,47 @@
+--TEST--
+casting different variables to void
+--FILE--
+
+--EXPECTF--
+Done
diff --git a/Zend/tests/type_casts/cast_to_void_ast.phpt b/Zend/tests/type_casts/cast_to_void_ast.phpt
new file mode 100644
index 0000000000000..26911bddb7ebc
--- /dev/null
+++ b/Zend/tests/type_casts/cast_to_void_ast.phpt
@@ -0,0 +1,18 @@
+--TEST--
+(void) is included in AST printing
+--FILE--
+getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+assert(false && function () {
+ (void)somefunc();
+})
diff --git a/Zend/tests/type_casts/cast_to_void_destructor.phpt b/Zend/tests/type_casts/cast_to_void_destructor.phpt
new file mode 100644
index 0000000000000..027e4b77e5dda
--- /dev/null
+++ b/Zend/tests/type_casts/cast_to_void_destructor.phpt
@@ -0,0 +1,30 @@
+--TEST--
+casting to void destroys the value.
+--FILE--
+
+--EXPECT--
+Before
+WithDestructor::__destruct
+After
diff --git a/Zend/tests/type_casts/cast_to_void_statement.phpt b/Zend/tests/type_casts/cast_to_void_statement.phpt
new file mode 100644
index 0000000000000..3262e1efd549c
--- /dev/null
+++ b/Zend/tests/type_casts/cast_to_void_statement.phpt
@@ -0,0 +1,11 @@
+--TEST--
+casting to void is a statement
+--FILE--
+
+--EXPECTF--
+Parse error: syntax error, unexpected token "(void)" in %s on line %d
diff --git a/Zend/tests/type_casts/gh18301_cast_to_void_for.phpt b/Zend/tests/type_casts/gh18301_cast_to_void_for.phpt
new file mode 100644
index 0000000000000..2ff7cfb0cb17b
--- /dev/null
+++ b/Zend/tests/type_casts/gh18301_cast_to_void_for.phpt
@@ -0,0 +1,46 @@
+--TEST--
+GH-18301: casting to void is allowed in for’s expression lists
+--FILE--
+
+--EXPECTF--
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+4
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+10
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+16
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+22
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+28
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
+
+Warning: The return value of function incCount() should either be used or intentionally ignored by casting it as (void) in %s on line %d
diff --git a/Zend/tests/type_casts/gh18301_cast_to_void_statement_for_condition.phpt b/Zend/tests/type_casts/gh18301_cast_to_void_statement_for_condition.phpt
new file mode 100644
index 0000000000000..6c90b2f7987c6
--- /dev/null
+++ b/Zend/tests/type_casts/gh18301_cast_to_void_statement_for_condition.phpt
@@ -0,0 +1,9 @@
+--TEST--
+GH-18301: casting to void is not allowed at the end of a for condition
+--FILE--
+
+--EXPECTF--
+Parse error: syntax error, unexpected token ";", expecting "," in %s on line %d
diff --git a/Zend/tests/real_cast.phpt b/Zend/tests/type_casts/real_cast.phpt
similarity index 100%
rename from Zend/tests/real_cast.phpt
rename to Zend/tests/type_casts/real_cast.phpt
diff --git a/Zend/tests/type_declarations/callable_001.phpt b/Zend/tests/type_declarations/callable/callable_001.phpt
similarity index 100%
rename from Zend/tests/type_declarations/callable_001.phpt
rename to Zend/tests/type_declarations/callable/callable_001.phpt
diff --git a/Zend/tests/type_declarations/callable_002.phpt b/Zend/tests/type_declarations/callable/callable_002.phpt
similarity index 100%
rename from Zend/tests/type_declarations/callable_002.phpt
rename to Zend/tests/type_declarations/callable/callable_002.phpt
diff --git a/Zend/tests/type_declarations/callable_003.phpt b/Zend/tests/type_declarations/callable/callable_003.phpt
similarity index 100%
rename from Zend/tests/type_declarations/callable_003.phpt
rename to Zend/tests/type_declarations/callable/callable_003.phpt
diff --git a/Zend/tests/type_declarations/callable/callable_004.phpt b/Zend/tests/type_declarations/callable/callable_004.phpt
new file mode 100644
index 0000000000000..5b05fea179a7a
--- /dev/null
+++ b/Zend/tests/type_declarations/callable/callable_004.phpt
@@ -0,0 +1,19 @@
+--TEST--
+callable type#004
+--FILE--
+
+===DONE===
+--EXPECT--
+===DONE===
diff --git a/Zend/tests/type_declarations/callable/callable_variance_closure.phpt b/Zend/tests/type_declarations/callable/callable_variance_closure.phpt
new file mode 100644
index 0000000000000..d3d0a77fd9a7c
--- /dev/null
+++ b/Zend/tests/type_declarations/callable/callable_variance_closure.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Closure should be covariant with callable
+--FILE--
+
+OK
+--EXPECT--
+OK
diff --git a/Zend/tests/gh9500.phpt b/Zend/tests/type_declarations/dnf_types/gh9500.phpt
similarity index 100%
rename from Zend/tests/gh9500.phpt
rename to Zend/tests/type_declarations/dnf_types/gh9500.phpt
diff --git a/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_allow_when_compile_time_resolve.phpt b/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_allow_when_compile_time_resolve.phpt
new file mode 100644
index 0000000000000..09deba14dc15b
--- /dev/null
+++ b/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_allow_when_compile_time_resolve.phpt
@@ -0,0 +1,15 @@
+--TEST--
+parent type can take part in an intersection type is resolvable at compile time
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_parent_type.phpt b/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_not_allow_if_not_compile_time_resolve.phpt
similarity index 62%
rename from Zend/tests/type_declarations/intersection_types/invalid_types/invalid_parent_type.phpt
rename to Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_not_allow_if_not_compile_time_resolve.phpt
index e3a86771a2189..9ff858deb0da7 100644
--- a/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_parent_type.phpt
+++ b/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_not_allow_if_not_compile_time_resolve.phpt
@@ -1,14 +1,13 @@
--TEST--
-parent type cannot take part in an intersection type
+parent type cannot take part in an intersection type if not resolvable at compile time
--FILE--
+DONE
--EXPECTF--
Fatal error: Type parent cannot be part of an intersection type in %s on line %d
diff --git a/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_not_allow_if_not_compile_time_resolve2.phpt b/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_not_allow_if_not_compile_time_resolve2.phpt
new file mode 100644
index 0000000000000..c511a1739ae1c
--- /dev/null
+++ b/Zend/tests/type_declarations/intersection_types/relative_types/relative_parent_type_is_not_allow_if_not_compile_time_resolve2.phpt
@@ -0,0 +1,13 @@
+--TEST--
+parent type cannot take part in an intersection type if not resolvable at compile time
+--FILE--
+
+DONE
+--EXPECTF--
+Fatal error: Type PARENT cannot be part of an intersection type in %s on line %d
diff --git a/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_allow_when_compile_time_resolve.phpt b/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_allow_when_compile_time_resolve.phpt
new file mode 100644
index 0000000000000..707acac144d2c
--- /dev/null
+++ b/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_allow_when_compile_time_resolve.phpt
@@ -0,0 +1,13 @@
+--TEST--
+self type can take part in an intersection type is resolvable at compile time
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_self_type.phpt b/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_not_allow_if_not_compile_time_resolve.phpt
similarity index 62%
rename from Zend/tests/type_declarations/intersection_types/invalid_types/invalid_self_type.phpt
rename to Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_not_allow_if_not_compile_time_resolve.phpt
index 66c7ec79325dc..69bd437a0743c 100644
--- a/Zend/tests/type_declarations/intersection_types/invalid_types/invalid_self_type.phpt
+++ b/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_not_allow_if_not_compile_time_resolve.phpt
@@ -1,12 +1,13 @@
--TEST--
-self type cannot take part in an intersection type
+self type cannot take part in an intersection type if not resolvable at compile time
--FILE--
+DONE
--EXPECTF--
Fatal error: Type self cannot be part of an intersection type in %s on line %d
diff --git a/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_not_allow_if_not_compile_time_resolve2.phpt b/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_not_allow_if_not_compile_time_resolve2.phpt
new file mode 100644
index 0000000000000..00a2127273f2a
--- /dev/null
+++ b/Zend/tests/type_declarations/intersection_types/relative_types/relative_self_type_is_not_allow_if_not_compile_time_resolve2.phpt
@@ -0,0 +1,13 @@
+--TEST--
+self type cannot take part in an intersection type if not resolvable at compile time
+--FILE--
+
+DONE
+--EXPECTF--
+Fatal error: Type SELF cannot be part of an intersection type in %s on line %d
diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt
index 138952b3df56b..0a63cc23df9fd 100644
--- a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt
+++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt
@@ -15,4 +15,4 @@ class Bar extends Foo
?>
--EXPECTF--
-Fatal error: Type of Bar::$property1 must not be defined (as in class Foo) in %s on line %d
+Fatal error: Type of Bar::$property1 must be omitted to match the parent definition in class Foo in %s on line %d
diff --git a/Zend/tests/type_declarations/relative_types/invalid_types/parent_global_function.phpt b/Zend/tests/type_declarations/relative_types/invalid_types/parent_global_function.phpt
new file mode 100644
index 0000000000000..38ca437dd3869
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/invalid_types/parent_global_function.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot use parent type outside a class/trait: global function
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use "parent" when no class scope is active in %s on line %d
diff --git a/Zend/tests/type_declarations/relative_types/invalid_types/parent_interface.phpt b/Zend/tests/type_declarations/relative_types/invalid_types/parent_interface.phpt
new file mode 100644
index 0000000000000..66e33a080903b
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/invalid_types/parent_interface.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Cannot use parent type outside a class/trait: interface
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use "parent" when current class scope has no parent in %s on line %d
diff --git a/Zend/tests/type_declarations/relative_types/invalid_types/self_global_function.phpt b/Zend/tests/type_declarations/relative_types/invalid_types/self_global_function.phpt
new file mode 100644
index 0000000000000..a9b1a075d0735
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/invalid_types/self_global_function.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot use self type outside a class/trait: global function
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use "self" when no class scope is active in %s on line %d
diff --git a/Zend/tests/type_declarations/relative_types/invalid_types/static_global_function.phpt b/Zend/tests/type_declarations/relative_types/invalid_types/static_global_function.phpt
new file mode 100644
index 0000000000000..4d287bbe9835f
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/invalid_types/static_global_function.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Cannot use static type outside a class/trait: global function
+--FILE--
+
+--EXPECTF--
+Fatal error: Cannot use "static" when no class scope is active in %s on line %d
diff --git a/Zend/tests/type_declarations/relative_types/relative_type_in_closures.phpt b/Zend/tests/type_declarations/relative_types/relative_type_in_closures.phpt
new file mode 100644
index 0000000000000..c729df12bf306
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/relative_type_in_closures.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Relative class types can be used for closures as it may be bound to a class
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/relative_types/relative_type_in_evaled_class_using_trait.phpt b/Zend/tests/type_declarations/relative_types/relative_type_in_evaled_class_using_trait.phpt
new file mode 100644
index 0000000000000..0c0e64cdec86e
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/relative_type_in_evaled_class_using_trait.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Eval Class definition should not leak memory when using compiled traits
+--FILE--
+bar();
+var_dump($a2);
+
+?>
+DONE
+--EXPECT--
+object(A)#2 (0) {
+}
+DONE
diff --git a/Zend/tests/type_declarations/relative_types/relative_type_in_evaled_trait.phpt b/Zend/tests/type_declarations/relative_types/relative_type_in_evaled_trait.phpt
new file mode 100644
index 0000000000000..74e71f6c324e7
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/relative_type_in_evaled_trait.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Eval code should not leak memory when using traits
+--FILE--
+bar();
+var_dump($a2);
+
+?>
+DONE
+--EXPECT--
+object(A)#2 (0) {
+}
+DONE
diff --git a/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent1.phpt b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent1.phpt
new file mode 100644
index 0000000000000..1959ab7f802cd
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent1.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Cannot use a trait which references parent as a type in a class with no parent, single type
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent2.phpt b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent2.phpt
new file mode 100644
index 0000000000000..0d5b9eefc4414
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent2.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Cannot use a trait which references parent as a type in a class with no parent, nullable type
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent3.phpt b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent3.phpt
new file mode 100644
index 0000000000000..eeb50a2abef46
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent3.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Cannot use a trait which references parent as a type in a class with no parent, union type
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent4.phpt b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent4.phpt
new file mode 100644
index 0000000000000..02605b28bdcc3
--- /dev/null
+++ b/Zend/tests/type_declarations/relative_types/traits/trait_parent_type_in_class_no_parent4.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Cannot use a trait which references parent as a type in a class with no parent, DNF type
+--FILE--
+
+DONE
+--EXPECT--
+DONE
diff --git a/Zend/tests/type_declarations/scalar_return_basic.phpt b/Zend/tests/type_declarations/scalar_return_basic.phpt
index 1fd3d26db6d8a..8f827600328ed 100644
--- a/Zend/tests/type_declarations/scalar_return_basic.phpt
+++ b/Zend/tests/type_declarations/scalar_return_basic.phpt
@@ -44,7 +44,7 @@ $values = [
[],
new StdClass,
new StringCapable,
- fopen("data:text/plain,foobar", "r")
+ STDERR,
];
foreach ($functions as $type => $function) {
@@ -92,13 +92,13 @@ int(0)
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type int, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type int, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type int, StringCapable returned in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type int, resource returned in %s on line %d
Testing 'float' type:
@@ -129,13 +129,13 @@ float(0)
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type float, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type float, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type float, StringCapable returned in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type float, resource returned in %s on line %d
Testing 'string' type:
@@ -166,13 +166,13 @@ string(0) ""
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type string, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type string, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
string(6) "foobar"
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type string, resource returned in %s on line %d
Testing 'bool' type:
@@ -203,13 +203,13 @@ bool(false)
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type bool, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type bool, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type bool, StringCapable returned in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type bool, resource returned in %s on line %d
Done
diff --git a/Zend/tests/type_declarations/scalar_return_basic_64bit.phpt b/Zend/tests/type_declarations/scalar_return_basic_64bit.phpt
index da930b98d9b65..0a32dd3f16f23 100644
--- a/Zend/tests/type_declarations/scalar_return_basic_64bit.phpt
+++ b/Zend/tests/type_declarations/scalar_return_basic_64bit.phpt
@@ -44,7 +44,7 @@ $values = [
[],
new StdClass,
new StringCapable,
- fopen("data:text/plain,foobar", "r")
+ STDERR,
];
foreach ($functions as $type => $function) {
@@ -92,13 +92,13 @@ int(0)
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type int, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type int, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type int, StringCapable returned in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type int, resource returned in %s on line %d
Testing 'float' type:
@@ -129,13 +129,13 @@ float(0)
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type float, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type float, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type float, StringCapable returned in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type float, resource returned in %s on line %d
Testing 'string' type:
@@ -166,13 +166,13 @@ string(0) ""
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type string, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type string, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
string(6) "foobar"
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type string, resource returned in %s on line %d
Testing 'bool' type:
@@ -203,13 +203,13 @@ bool(false)
*** Trying array(0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type bool, array returned in %s on line %d
-*** Trying object(stdClass)#6 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type bool, stdClass returned in %s on line %d
-*** Trying object(StringCapable)#7 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Return value must be of type bool, StringCapable returned in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Return value must be of type bool, resource returned in %s on line %d
Done
diff --git a/Zend/tests/type_declarations/scalar_strict.phpt b/Zend/tests/type_declarations/scalar_strict.phpt
index e89d18b2c86b3..626f322d8701f 100644
--- a/Zend/tests/type_declarations/scalar_strict.phpt
+++ b/Zend/tests/type_declarations/scalar_strict.phpt
@@ -96,15 +96,15 @@ int(2147483647)
}
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, resource given, called in %s on line %d
Testing 'float' type:
@@ -149,15 +149,15 @@ float(NAN)
}
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, resource given, called in %s on line %d
Testing 'string' type:
@@ -202,15 +202,15 @@ string(0) ""
}
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, resource given, called in %s on line %d
Testing 'bool' type:
@@ -255,15 +255,15 @@ bool(false)
}
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, resource given, called in %s on line %d
Done
diff --git a/Zend/tests/type_declarations/scalar_strict_64bit.phpt b/Zend/tests/type_declarations/scalar_strict_64bit.phpt
index 6335e2d1acee4..b7f2ce0de4396 100644
--- a/Zend/tests/type_declarations/scalar_strict_64bit.phpt
+++ b/Zend/tests/type_declarations/scalar_strict_64bit.phpt
@@ -35,7 +35,7 @@ $values = [
[],
new StdClass,
new StringCapable,
- fopen("data:text/plain,foobar", "r")
+ STDERR,
];
foreach ($functions as $type => $function) {
@@ -96,15 +96,15 @@ int(9223372036854775807)
}
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($i) must be of type int, resource given, called in %s on line %d
Testing 'float' type:
@@ -149,15 +149,15 @@ float(NAN)
}
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($f) must be of type float, resource given, called in %s on line %d
Testing 'string' type:
@@ -202,15 +202,15 @@ string(0) ""
}
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($s) must be of type string, resource given, called in %s on line %d
Testing 'bool' type:
@@ -255,15 +255,15 @@ bool(false)
}
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, array given, called in %s on line %d
-*** Trying object(stdClass)#5 (0) {
+*** Trying object(stdClass)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, stdClass given, called in %s on line %d
-*** Trying object(StringCapable)#6 (0) {
+*** Trying object(StringCapable)#%d (0) {
}
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, StringCapable given, called in %s on line %d
-*** Trying resource(5) of type (stream)
+*** Trying resource(%d) of type (stream)
*** Caught {closure:%s:%d}(): Argument #1 ($b) must be of type bool, resource given, called in %s on line %d
Done
diff --git a/Zend/tests/type_declarations/typed_properties_035.phpt b/Zend/tests/type_declarations/typed_properties_035.phpt
index 165479021a77b..ee7085d8837cc 100644
--- a/Zend/tests/type_declarations/typed_properties_035.phpt
+++ b/Zend/tests/type_declarations/typed_properties_035.phpt
@@ -11,4 +11,4 @@ class Baz extends Foo{
}
?>
--EXPECTF--
-Fatal error: Type of Baz::$bar must not be defined (as in class Foo) in %s on line 6
+Fatal error: Type of Baz::$bar must be omitted to match the parent definition in class Foo in %s on line 6
diff --git a/Zend/tests/type_declarations/typed_properties_095.phpt b/Zend/tests/type_declarations/typed_properties_095.phpt
index 321b07e34c73f..4d8e5168e15ea 100644
--- a/Zend/tests/type_declarations/typed_properties_095.phpt
+++ b/Zend/tests/type_declarations/typed_properties_095.phpt
@@ -75,6 +75,8 @@ object(_ZendTestClass)#1 (3) {
uninitialized(Traversable&Countable)
["readonlyProp"]=>
uninitialized(int)
+ ["finalProp"]=>
+ uninitialized(int)
["dnfProperty"]=>
uninitialized(Iterator|(Traversable&Countable))
}
@@ -93,6 +95,8 @@ object(Test)#4 (3) {
uninitialized(Traversable&Countable)
["readonlyProp"]=>
uninitialized(int)
+ ["finalProp"]=>
+ uninitialized(int)
["dnfProperty"]=>
uninitialized(Iterator|(Traversable&Countable))
}
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_case_sensitive_relative_class_parent_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_case_sensitive_relative_class_parent_type.phpt
new file mode 100644
index 0000000000000..3bcd9221f5e28
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_case_sensitive_relative_class_parent_type.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Duplicate parent type in different cases
+--FILE--
+
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_case_sensitive_relative_class_self_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_case_sensitive_relative_class_self_type.phpt
new file mode 100644
index 0000000000000..ce969e4dca9fc
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_case_sensitive_relative_class_self_type.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Duplicate self type in different cases
+--FILE--
+
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt
new file mode 100644
index 0000000000000..2312fed21e9e6
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_parent_type.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Duplicate parent type
+--FILE--
+
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt
new file mode 100644
index 0000000000000..fd3d073f97ba4
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_self_type.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Duplicate self type
+--FILE--
+
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt
new file mode 100644
index 0000000000000..9c6e37ca6e093
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_relative_class_static_type.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Duplicate static type
+--FILE--
+
+--EXPECTF--
+Fatal error: Duplicate type static is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt
new file mode 100644
index 0000000000000..46bdd1c04556b
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation1.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Relative class type self resolving to an existing entry (after variation)
+--FILE--
+
+DONE
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt
new file mode 100644
index 0000000000000..512f7d6463d93
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation2.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Relative class type self resolving to an existing entry (before variation)
+--FILE--
+
+DONE
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt
new file mode 100644
index 0000000000000..930b3116d756b
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation3.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Relative class type parent resolving to an existing entry (after variation)
+--FILE--
+
+DONE
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt
new file mode 100644
index 0000000000000..dcc3e428641d6
--- /dev/null
+++ b/Zend/tests/type_declarations/union_types/redundant_types/duplicate_resolved_relative_class_type_variation4.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Relative class type parent resolving to an existing entry (before variation)
+--FILE--
+
+DONE
+--EXPECTF--
+Fatal error: Duplicate type Foo is redundant in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self.phpt
new file mode 100644
index 0000000000000..db0b0a4136cc3
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self.phpt
@@ -0,0 +1,85 @@
+--TEST--
+Overriding static return types with self in final class
+--FILE--
+method1());
+var_dump($foo->method2());
+var_dump($foo->method3());
+
+$bar = new Bar();
+
+var_dump($bar->method1());
+var_dump($bar->method2());
+var_dump($bar->method3());
+?>
+--EXPECTF--
+object(Foo)#1 (0) {
+}
+object(Foo)#1 (0) {
+}
+object(Foo)#1 (0) {
+}
+object(Bar)#2 (0) {
+}
+object(Bar)#2 (0) {
+}
+object(Bar)#2 (0) {
+}
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self_in_non_final_class.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self_in_non_final_class.phpt
new file mode 100644
index 0000000000000..13b70f3f74ca0
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self_in_non_final_class.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Overriding static return types with self in non-final class
+--FILE--
+method1());
+?>
+--EXPECTF--
+Fatal error: Declaration of Foo::method1(): Foo must be compatible with A::method1(): static in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self_to_unions.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self_to_unions.phpt
new file mode 100644
index 0000000000000..1e9ca77a03831
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/static_to_self_to_unions.phpt
@@ -0,0 +1,64 @@
+--TEST--
+Overriding static return types with self in final class with union types
+--FILE--
+methodScalar());
+var_dump($b->methodIterable1());
+var_dump($b->methodIterable2());
+var_dump($b->methodObject1());
+var_dump($b->methodObject2());
+var_dump($b->methodObject3());
+var_dump($b->methodObject4());
+var_dump($b->methodNullable1());
+var_dump($b->methodNullable2());
+?>
+--EXPECTF--
+object(B)#1 (0) {
+}
+object(B)#1 (0) {
+}
+array(0) {
+}
+object(B)#1 (0) {
+}
+object(B)#1 (0) {
+}
+object(C)#2 (0) {
+}
+object(B)#1 (0) {
+}
+object(B)#1 (0) {
+}
+NULL
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_1.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_1.phpt
new file mode 100644
index 0000000000000..1a73ea5045cc5
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_1.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Overriding return type with type that is not in the interface in final class with union types
+--FILE--
+methodScalar1());
+?>
+--EXPECTF--
+Fatal error: Declaration of B::methodScalar1(): array must be compatible with A::methodScalar1(): static|bool in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_2.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_2.phpt
new file mode 100644
index 0000000000000..f3f85615944bd
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_2.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Overriding static with self and add a type that is not in the interface in final class
+--FILE--
+methodScalar1());
+?>
+--EXPECTF--
+Fatal error: Declaration of B::methodScalar1(): B|array must be compatible with A::methodScalar1(): static|bool in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_3.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_3.phpt
new file mode 100644
index 0000000000000..0f4e8ea2e189f
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_3.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Override static with another implementation of interface in final class
+--FILE--
+methodScalar1());
+?>
+--EXPECTF--
+Fatal error: Declaration of B::methodScalar1(): C must be compatible with A::methodScalar1(): static|bool in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_4.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_4.phpt
new file mode 100644
index 0000000000000..c4538181c0869
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_4.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Override static with another implementation of interface and add a type that is not in the interface in final class
+--FILE--
+methodScalar1());
+?>
+--EXPECTF--
+Fatal error: Declaration of B::methodScalar1(): C|array must be compatible with A::methodScalar1(): static|bool in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_5.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_5.phpt
new file mode 100644
index 0000000000000..65d7da1d0210c
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_5.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Override static with class that is even not an implementation of interface in final class
+--FILE--
+methodScalar1());
+?>
+--EXPECTF--
+Fatal error: Declaration of B::methodScalar1(): C must be compatible with A::methodScalar1(): static|bool in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_6.phpt b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_6.phpt
new file mode 100644
index 0000000000000..961027480522e
--- /dev/null
+++ b/Zend/tests/type_declarations/variance/override_static_with_self/union_to_union_6.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Override static with class that is even not an implementation of interface and add a type that is not in the interface in final class
+--FILE--
+methodScalar1());
+?>
+--EXPECTF--
+Fatal error: Declaration of B::methodScalar1(): C|array must be compatible with A::methodScalar1(): static|bool in %s on line %d
diff --git a/Zend/tests/type_declarations/variance/trait_error.phpt b/Zend/tests/type_declarations/variance/trait_error.phpt
index 667b177a5b931..dd4ac59d8e7e8 100644
--- a/Zend/tests/type_declarations/variance/trait_error.phpt
+++ b/Zend/tests/type_declarations/variance/trait_error.phpt
@@ -17,4 +17,4 @@ class U extends X {
?>
--EXPECTF--
-Fatal error: Could not check compatibility between T::method($r): B and X::method($a): A, because class B is not available in %s on line %d
+Fatal error: Could not check compatibility between U::method($r): B and X::method($a): A, because class B is not available in %s on line %d
diff --git a/Zend/tests/bug43332_1.phpt b/Zend/tests/typehints/bug43332_1.phpt
similarity index 100%
rename from Zend/tests/bug43332_1.phpt
rename to Zend/tests/typehints/bug43332_1.phpt
diff --git a/Zend/tests/bug43332_2.phpt b/Zend/tests/typehints/bug43332_2.phpt
similarity index 100%
rename from Zend/tests/bug43332_2.phpt
rename to Zend/tests/typehints/bug43332_2.phpt
diff --git a/Zend/tests/bug60573.phpt b/Zend/tests/typehints/bug60573.phpt
similarity index 100%
rename from Zend/tests/bug60573.phpt
rename to Zend/tests/typehints/bug60573.phpt
diff --git a/Zend/tests/bug60573_2.phpt b/Zend/tests/typehints/bug60573_2.phpt
similarity index 100%
rename from Zend/tests/bug60573_2.phpt
rename to Zend/tests/typehints/bug60573_2.phpt
diff --git a/Zend/tests/bug62441.phpt b/Zend/tests/typehints/bug62441.phpt
similarity index 100%
rename from Zend/tests/bug62441.phpt
rename to Zend/tests/typehints/bug62441.phpt
diff --git a/Zend/tests/bug24773.phpt b/Zend/tests/unset/bug24773.phpt
similarity index 100%
rename from Zend/tests/bug24773.phpt
rename to Zend/tests/unset/bug24773.phpt
diff --git a/Zend/tests/bug33512.phpt b/Zend/tests/unset/bug33512.phpt
similarity index 100%
rename from Zend/tests/bug33512.phpt
rename to Zend/tests/unset/bug33512.phpt
diff --git a/Zend/tests/bug34518.phpt b/Zend/tests/unset/bug34518.phpt
similarity index 100%
rename from Zend/tests/bug34518.phpt
rename to Zend/tests/unset/bug34518.phpt
diff --git a/Zend/tests/bug39346.phpt b/Zend/tests/unset/bug39346.phpt
similarity index 100%
rename from Zend/tests/bug39346.phpt
rename to Zend/tests/unset/bug39346.phpt
diff --git a/Zend/tests/bug40833.phpt b/Zend/tests/unset/bug40833.phpt
similarity index 100%
rename from Zend/tests/bug40833.phpt
rename to Zend/tests/unset/bug40833.phpt
diff --git a/Zend/tests/bug62653.phpt b/Zend/tests/unset/bug62653.phpt
similarity index 100%
rename from Zend/tests/bug62653.phpt
rename to Zend/tests/unset/bug62653.phpt
diff --git a/Zend/tests/bug68370.phpt b/Zend/tests/unset/bug68370.phpt
similarity index 100%
rename from Zend/tests/bug68370.phpt
rename to Zend/tests/unset/bug68370.phpt
diff --git a/Zend/tests/bug70240.phpt b/Zend/tests/unset/bug70240.phpt
similarity index 100%
rename from Zend/tests/bug70240.phpt
rename to Zend/tests/unset/bug70240.phpt
diff --git a/Zend/tests/bug81377.phpt b/Zend/tests/unset/bug81377.phpt
similarity index 100%
rename from Zend/tests/bug81377.phpt
rename to Zend/tests/unset/bug81377.phpt
diff --git a/Zend/tests/this_in_unset.phpt b/Zend/tests/unset/this_in_unset.phpt
similarity index 100%
rename from Zend/tests/this_in_unset.phpt
rename to Zend/tests/unset/this_in_unset.phpt
diff --git a/Zend/tests/unset.inc b/Zend/tests/unset/unset.inc
similarity index 100%
rename from Zend/tests/unset.inc
rename to Zend/tests/unset/unset.inc
diff --git a/Zend/tests/unset_cast_removed.phpt b/Zend/tests/unset/unset_cast_removed.phpt
similarity index 100%
rename from Zend/tests/unset_cast_removed.phpt
rename to Zend/tests/unset/unset_cast_removed.phpt
diff --git a/Zend/tests/unset_cv01.phpt b/Zend/tests/unset/unset_cv01.phpt
similarity index 100%
rename from Zend/tests/unset_cv01.phpt
rename to Zend/tests/unset/unset_cv01.phpt
diff --git a/Zend/tests/unset_cv02.phpt b/Zend/tests/unset/unset_cv02.phpt
similarity index 100%
rename from Zend/tests/unset_cv02.phpt
rename to Zend/tests/unset/unset_cv02.phpt
diff --git a/Zend/tests/unset_cv03.phpt b/Zend/tests/unset/unset_cv03.phpt
similarity index 100%
rename from Zend/tests/unset_cv03.phpt
rename to Zend/tests/unset/unset_cv03.phpt
diff --git a/Zend/tests/unset_cv04.phpt b/Zend/tests/unset/unset_cv04.phpt
similarity index 100%
rename from Zend/tests/unset_cv04.phpt
rename to Zend/tests/unset/unset_cv04.phpt
diff --git a/Zend/tests/unset_cv05.phpt b/Zend/tests/unset/unset_cv05.phpt
similarity index 100%
rename from Zend/tests/unset_cv05.phpt
rename to Zend/tests/unset/unset_cv05.phpt
diff --git a/Zend/tests/unset_cv06.phpt b/Zend/tests/unset/unset_cv06.phpt
similarity index 100%
rename from Zend/tests/unset_cv06.phpt
rename to Zend/tests/unset/unset_cv06.phpt
diff --git a/Zend/tests/unset_cv08.phpt b/Zend/tests/unset/unset_cv08.phpt
similarity index 100%
rename from Zend/tests/unset_cv08.phpt
rename to Zend/tests/unset/unset_cv08.phpt
diff --git a/Zend/tests/unset_cv10.phpt b/Zend/tests/unset/unset_cv10.phpt
similarity index 100%
rename from Zend/tests/unset_cv10.phpt
rename to Zend/tests/unset/unset_cv10.phpt
diff --git a/Zend/tests/unset_cv11.phpt b/Zend/tests/unset/unset_cv11.phpt
similarity index 100%
rename from Zend/tests/unset_cv11.phpt
rename to Zend/tests/unset/unset_cv11.phpt
diff --git a/Zend/tests/unset_cv12.phpt b/Zend/tests/unset/unset_cv12.phpt
similarity index 100%
rename from Zend/tests/unset_cv12.phpt
rename to Zend/tests/unset/unset_cv12.phpt
diff --git a/Zend/tests/unset_non_array.phpt b/Zend/tests/unset/unset_non_array.phpt
similarity index 100%
rename from Zend/tests/unset_non_array.phpt
rename to Zend/tests/unset/unset_non_array.phpt
diff --git a/Zend/tests/unset_prop_recursion.phpt b/Zend/tests/unset/unset_prop_recursion.phpt
similarity index 100%
rename from Zend/tests/unset_prop_recursion.phpt
rename to Zend/tests/unset/unset_prop_recursion.phpt
diff --git a/Zend/tests/bug67938.phpt b/Zend/tests/variadic/bug67938.phpt
similarity index 100%
rename from Zend/tests/bug67938.phpt
rename to Zend/tests/variadic/bug67938.phpt
diff --git a/Zend/tests/bug80049.phpt b/Zend/tests/variadic/bug80049.phpt
similarity index 100%
rename from Zend/tests/bug80049.phpt
rename to Zend/tests/variadic/bug80049.phpt
diff --git a/Zend/tests/weakrefs/gh17442_1.phpt b/Zend/tests/weakrefs/gh17442_1.phpt
new file mode 100644
index 0000000000000..fc7f60174ed9e
--- /dev/null
+++ b/Zend/tests/weakrefs/gh17442_1.phpt
@@ -0,0 +1,22 @@
+--TEST--
+GH-17442 (Engine UAF with reference assign and dtor) - untyped
+--CREDITS--
+YuanchengJiang
+--FILE--
+
+--EXPECTF--
+Fatal error: Uncaught Exception: Test in %s:%d
+Stack trace:
+#0 [internal function]: class@anonymous->__destruct()
+#1 %s(%d): headers_sent(NULL, 0)
+#2 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/weakrefs/gh17442_2.phpt b/Zend/tests/weakrefs/gh17442_2.phpt
new file mode 100644
index 0000000000000..296a23a651383
--- /dev/null
+++ b/Zend/tests/weakrefs/gh17442_2.phpt
@@ -0,0 +1,35 @@
+--TEST--
+GH-17442 (Engine UAF with reference assign and dtor) - typed
+--CREDITS--
+YuanchengJiang
+nielsdos
+--FILE--
+obj = new stdClass;
+
+$map[$test->obj] = new class {
+ function __destruct() {
+ global $test;
+ var_dump($test->obj);
+ throw new Exception("Test");
+ }
+};
+
+headers_sent($test->obj);
+?>
+--EXPECTF--
+string(0) ""
+
+Fatal error: Uncaught Exception: Test in %s:%d
+Stack trace:
+#0 [internal function]: class@anonymous->__destruct()
+#1 %s(%d): headers_sent('')
+#2 {main}
+ thrown in %s on line %d
diff --git a/Zend/zend.c b/Zend/zend.c
index b4a084b1f95c7..2d8a0f455f8b4 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -260,6 +260,7 @@ static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */
ZEND_INI_BEGIN()
ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
+ STD_ZEND_INI_BOOLEAN("fatal_error_backtraces", "1", ZEND_INI_ALL, OnUpdateBool, fatal_error_backtrace_on, zend_executor_globals, executor_globals)
STD_ZEND_INI_ENTRY("zend.assertions", "1", ZEND_INI_ALL, OnUpdateAssertions, assertions, zend_executor_globals, executor_globals)
ZEND_INI_ENTRY3_EX("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, NULL, NULL, NULL, zend_gc_enabled_displayer_cb)
STD_ZEND_INI_BOOLEAN("zend.multibyte", "0", ZEND_INI_PERDIR, OnUpdateBool, multibyte, zend_compiler_globals, compiler_globals)
@@ -1049,6 +1050,8 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
CG(map_ptr_last) = 0;
#endif /* ZTS */
EG(error_reporting) = E_ALL & ~E_NOTICE;
+ EG(fatal_error_backtrace_on) = false;
+ ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
zend_interned_strings_init();
zend_startup_builtin_functions();
@@ -1463,6 +1466,10 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
EG(errors)[EG(num_errors)-1] = info;
}
+ // Always clear the last backtrace.
+ zval_ptr_dtor(&EG(last_fatal_error_backtrace));
+ ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
+
/* Report about uncaught exception in case of fatal errors */
if (EG(exception)) {
zend_execute_data *ex;
@@ -1484,6 +1491,8 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
ex->opline = opline;
}
}
+ } else if (EG(fatal_error_backtrace_on) && (type & E_FATAL_ERRORS)) {
+ zend_fetch_debug_backtrace(&EG(last_fatal_error_backtrace), 0, EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0);
}
zend_observer_error_notify(type, error_filename, error_lineno, message);
@@ -2079,8 +2088,8 @@ ZEND_API void zend_alloc_ce_cache(zend_string *type_name)
return;
}
- if (zend_string_equals_literal_ci(type_name, "self")
- || zend_string_equals_literal_ci(type_name, "parent")) {
+ if (zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_SELF))
+ || zend_string_equals_ci(type_name, ZSTR_KNOWN(ZEND_STR_PARENT))) {
return;
}
diff --git a/Zend/zend.h b/Zend/zend.h
index 2e5fb5d0ef091..0cf1faeb653fe 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -20,7 +20,7 @@
#ifndef ZEND_H
#define ZEND_H
-#define ZEND_VERSION "4.4.7-dev"
+#define ZEND_VERSION "4.5.0-dev"
#define ZEND_ENGINE_3
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 6e254561d3745..e0006e7d7275f 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -2698,6 +2698,12 @@ static void zend_check_magic_method_arg_type(uint32_t arg_num, const zend_class_
static void zend_check_magic_method_return_type(const zend_class_entry *ce, const zend_function *fptr, int error_type, int return_type)
{
+ if (return_type == MAY_BE_VOID) {
+ if (fptr->common.fn_flags & ZEND_ACC_NODISCARD) {
+ zend_error_noreturn(error_type, "Method %s::%s cannot be #[\\NoDiscard]", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name));
+ }
+ }
+
if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
/* For backwards compatibility reasons, do not enforce the return type if it is not set. */
return;
@@ -2757,6 +2763,10 @@ static void zend_check_magic_method_no_return_type(
zend_error_noreturn(error_type, "Method %s::%s() cannot declare a return type",
ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name));
}
+
+ if (fptr->common.fn_flags & ZEND_ACC_NODISCARD) {
+ zend_error_noreturn(error_type, "Method %s::%s cannot be #[\\NoDiscard]", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name));
+ }
}
ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type) /* {{{ */
@@ -2904,14 +2914,14 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) {
ZEND_ASSERT(!ZEND_TYPE_CONTAINS_CODE(*type, IS_RESOURCE) && "resource is not allowed in a zend_type");
}
zend_type *current;
- ZEND_TYPE_FOREACH(*type, current) {
+ ZEND_TYPE_FOREACH_MUTABLE(*type, current) {
if (ZEND_TYPE_HAS_NAME(*current)) {
zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*current));
zend_alloc_ce_cache(name);
ZEND_TYPE_SET_PTR(*current, name);
} else if (ZEND_TYPE_HAS_LIST(*current)) {
zend_type *inner;
- ZEND_TYPE_FOREACH(*current, inner) {
+ ZEND_TYPE_FOREACH_MUTABLE(*current, inner) {
ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*inner) && !ZEND_TYPE_HAS_LIST(*inner));
if (ZEND_TYPE_HAS_NAME(*inner)) {
zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner));
@@ -3761,7 +3771,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(name), name_len);
*strict_class = 0;
- if (zend_string_equals_literal(lcname, "self")) {
+ 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");
} else {
@@ -3778,7 +3788,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
}
ret = 1;
}
- } else if (zend_string_equals_literal(lcname, "parent")) {
+ } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_PARENT))) {
if (!scope) {
if (error) *error = estrdup("cannot access \"parent\" when no class scope is active");
} else if (!scope->parent) {
@@ -4138,6 +4148,19 @@ 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);
+
+ if (ce == zend_ce_closure) {
+ const zend_function *fn = zend_get_closure_method_def(Z_OBJ_P(callable));
+
+ if (fn->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) {
+ if (fn->common.scope) {
+ return zend_create_member_string(fn->common.scope->name, fn->common.function_name);
+ } else {
+ return zend_string_copy(fn->common.function_name);
+ }
+ }
+ }
+
return zend_string_concat2(
ZSTR_VAL(ce->name), ZSTR_LEN(ce->name),
"::__invoke", sizeof("::__invoke") - 1);
@@ -4667,8 +4690,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val
zval_ptr_dtor(val);
return FAILURE;
} else {
- zval_ptr_dtor(&ref->val);
- ZVAL_COPY_VALUE(&ref->val, val);
+ zend_safe_assign_to_variable_noref(&ref->val, val);
return SUCCESS;
}
}
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index d91da91bf299e..a644de8e15134 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -690,8 +690,13 @@ ZEND_API zend_result _call_user_function_impl(zval *object, zval *function_name,
#define call_user_function_named(function_table, object, function_name, retval_ptr, param_count, params, named_params) \
_call_user_function_impl(object, function_name, retval_ptr, param_count, params, named_params)
-ZEND_API extern const zend_fcall_info empty_fcall_info;
-ZEND_API extern const zend_fcall_info_cache empty_fcall_info_cache;
+#ifndef __cplusplus
+# define empty_fcall_info (zend_fcall_info) {0}
+# define empty_fcall_info_cache (zend_fcall_info_cache) {0}
+#else
+# define empty_fcall_info zend_fcall_info {0}
+# define empty_fcall_info_cache zend_fcall_info_cache {0}
+#endif
/** Build zend_call_info/cache from a zval*
*
@@ -800,7 +805,7 @@ static zend_always_inline void zend_fcc_dtor(zend_fcall_info_cache *fcc)
if (fcc->closure) {
OBJ_RELEASE(fcc->closure);
}
- memcpy(fcc, &empty_fcall_info_cache, sizeof(zend_fcall_info_cache));
+ *fcc = empty_fcall_info_cache;
}
ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc, zval *callable);
@@ -1107,7 +1112,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_NULL(_zv); \
} while (0)
@@ -1129,7 +1134,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_FALSE(_zv); \
} while (0)
@@ -1151,7 +1156,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_TRUE(_zv); \
} while (0)
@@ -1173,7 +1178,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_BOOL(_zv, bval); \
} while (0)
@@ -1195,7 +1200,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_LONG(_zv, lval); \
} while (0)
@@ -1217,7 +1222,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_DOUBLE(_zv, dval); \
} while (0)
@@ -1239,7 +1244,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_EMPTY_STRING(_zv); \
} while (0)
@@ -1261,7 +1266,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_STR(_zv, str); \
} while (0)
@@ -1283,7 +1288,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_NEW_STR(_zv, str); \
} while (0)
@@ -1305,7 +1310,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_STRING(_zv, string); \
} while (0)
@@ -1327,7 +1332,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_STRINGL(_zv, string, len); \
} while (0)
@@ -1349,7 +1354,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_ARR(_zv, arr); \
} while (0)
@@ -1371,7 +1376,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_RES(_zv, res); \
} while (0)
@@ -1393,7 +1398,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_COPY_VALUE(_zv, other_zv); \
} while (0)
@@ -1415,7 +1420,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_COPY_VALUE(_zv, other_zv); \
} while (0)
@@ -1447,7 +1452,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
} \
_zv = &ref->val; \
} \
- zval_ptr_dtor(_zv); \
+ zval_ptr_safe_dtor(_zv); \
ZVAL_COPY_VALUE(_zv, other_zv); \
} while (0)
@@ -1485,10 +1490,7 @@ static zend_always_inline zval *zend_try_array_init_size(zval *zv, uint32_t size
}
zv = &ref->val;
}
- zval garbage;
- ZVAL_COPY_VALUE(&garbage, zv);
- ZVAL_NULL(zv);
- zval_ptr_dtor(&garbage);
+ zval_ptr_safe_dtor(zv);
ZVAL_ARR(zv, arr);
return zv;
}
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index 12e322d0347b3..b9f3bc015217c 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -305,7 +305,17 @@ struct _zend_mm_heap {
size_t (*_gc)(void);
void (*_shutdown)(bool full, bool silent);
} custom_heap;
- HashTable *tracked_allocs;
+ union {
+ HashTable *tracked_allocs;
+ struct {
+ bool poison_alloc;
+ uint8_t poison_alloc_value;
+ bool poison_free;
+ uint8_t poison_free_value;
+ uint8_t padding;
+ bool check_freelists_on_shutdown;
+ } debug;
+ };
#endif
pid_t pid;
zend_random_bytes_insecure_state rand_state;
@@ -2389,8 +2399,19 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)
#if ZEND_MM_CUSTOM
static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
static void tracked_free_all(zend_mm_heap *heap);
+static void *poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
#endif
+static void zend_mm_check_freelists(zend_mm_heap *heap)
+{
+ for (uint32_t bin_num = 0; bin_num < ZEND_MM_BINS; bin_num++) {
+ zend_mm_free_slot *slot = heap->free_slot[bin_num];
+ while (slot) {
+ slot = zend_mm_get_next_free_slot(heap, bin_num, slot);
+ }
+ }
+}
+
ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
{
zend_mm_chunk *p;
@@ -2555,8 +2576,9 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr
if (size_zv) {
return Z_LVAL_P(size_zv);
}
+ } else if (heap->custom_heap._malloc != poison_malloc) {
+ return 0;
}
- return 0;
}
#endif
return zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
@@ -3021,6 +3043,200 @@ static void tracked_free_all(zend_mm_heap *heap) {
}
#endif
+static void* poison_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
+{
+ zend_mm_heap *heap = AG(mm_heap);
+
+ if (SIZE_MAX - heap->debug.padding * 2 < size) {
+ zend_mm_panic("Integer overflow in memory allocation");
+ }
+ size += heap->debug.padding * 2;
+
+ void *ptr = zend_mm_alloc_heap(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+
+ if (EXPECTED(ptr)) {
+ if (heap->debug.poison_alloc) {
+ memset(ptr, heap->debug.poison_alloc_value, size);
+ }
+
+ ptr = (char*)ptr + heap->debug.padding;
+ }
+
+ return ptr;
+}
+
+static void poison_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
+{
+ zend_mm_heap *heap = AG(mm_heap);
+
+ if (EXPECTED(ptr)) {
+ /* zend_mm_shutdown() will try to free the heap when custom handlers
+ * are installed */
+ if (UNEXPECTED(ptr == heap)) {
+ return;
+ }
+
+ ptr = (char*)ptr - heap->debug.padding;
+
+ size_t size = zend_mm_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+
+ if (heap->debug.poison_free) {
+ memset(ptr, heap->debug.poison_free_value, size);
+ }
+ }
+
+ zend_mm_free_heap(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+}
+
+static void* poison_realloc(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
+{
+ zend_mm_heap *heap = AG(mm_heap);
+
+ void *new = poison_malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+
+ if (ptr) {
+ /* Determine the size of the old allocation from the unpadded pointer. */
+ size_t oldsize = zend_mm_size(heap, (char*)ptr - heap->debug.padding ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+
+ /* Remove the padding size to determine the size that is available to the user. */
+ oldsize -= (2 * heap->debug.padding);
+
+#if ZEND_DEBUG
+ oldsize -= sizeof(zend_mm_debug_info);
+#endif
+
+ memcpy(new, ptr, MIN(oldsize, size));
+ poison_free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
+ }
+
+ return new;
+}
+
+static size_t poison_gc(void)
+{
+ zend_mm_heap *heap = AG(mm_heap);
+
+ void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
+ void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
+ void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
+ size_t (*_gc)(void);
+ void (*_shutdown)(bool, bool);
+
+ zend_mm_get_custom_handlers_ex(heap, &_malloc, &_free, &_realloc, &_gc, &_shutdown);
+ zend_mm_set_custom_handlers_ex(heap, NULL, NULL, NULL, NULL, NULL);
+
+ size_t collected = zend_mm_gc(heap);
+
+ zend_mm_set_custom_handlers_ex(heap, _malloc, _free, _realloc, _gc, _shutdown);
+
+ return collected;
+}
+
+static void poison_shutdown(bool full, bool silent)
+{
+ zend_mm_heap *heap = AG(mm_heap);
+
+ void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
+ void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
+ void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
+ size_t (*_gc)(void);
+ void (*_shutdown)(bool, bool);
+
+ zend_mm_get_custom_handlers_ex(heap, &_malloc, &_free, &_realloc, &_gc, &_shutdown);
+ zend_mm_set_custom_handlers_ex(heap, NULL, NULL, NULL, NULL, NULL);
+
+ if (heap->debug.check_freelists_on_shutdown) {
+ zend_mm_check_freelists(heap);
+ }
+
+ zend_mm_shutdown(heap, full, silent);
+
+ if (!full) {
+ zend_mm_set_custom_handlers_ex(heap, _malloc, _free, _realloc, _gc, _shutdown);
+ }
+}
+
+static void poison_enable(zend_mm_heap *heap, char *parameters)
+{
+ char *tmp = parameters;
+ char *end = tmp + strlen(tmp);
+
+ /* Trim heading/trailing whitespaces */
+ while (*tmp == ' ' || *tmp == '\t' || *tmp == '\n') {
+ tmp++;
+ }
+ while (end != tmp && (*(end-1) == ' ' || *(end-1) == '\t' || *(end-1) == '\n')) {
+ end--;
+ }
+
+ if (tmp == end) {
+ return;
+ }
+
+ while (1) {
+ char *key = tmp;
+
+ tmp = memchr(tmp, '=', end - tmp);
+ if (!tmp) {
+ size_t key_len = end - key;
+ fprintf(stderr, "Unexpected EOF after ZEND_MM_DEBUG parameter '%.*s', expected '='\n",
+ (int)key_len, key);
+ return;
+ }
+
+ size_t key_len = tmp - key;
+ char *value = tmp + 1;
+
+ if (key_len == strlen("poison_alloc")
+ && !memcmp(key, "poison_alloc", key_len)) {
+
+ heap->debug.poison_alloc = true;
+ heap->debug.poison_alloc_value = (uint8_t) ZEND_STRTOUL(value, &tmp, 0);
+
+ } else if (key_len == strlen("poison_free")
+ && !memcmp(key, "poison_free", key_len)) {
+
+ heap->debug.poison_free = true;
+ heap->debug.poison_free_value = (uint8_t) ZEND_STRTOUL(value, &tmp, 0);
+
+ } else if (key_len == strlen("padding")
+ && !memcmp(key, "padding", key_len)) {
+
+ uint8_t padding = ZEND_STRTOUL(value, &tmp, 0);
+ if (ZEND_MM_ALIGNED_SIZE(padding) != padding) {
+ fprintf(stderr, "ZEND_MM_DEBUG padding must be a multiple of %u, %u given\n",
+ (unsigned int)ZEND_MM_ALIGNMENT,
+ (unsigned int)padding);
+ return;
+ }
+ heap->debug.padding = padding;
+
+ } else if (key_len == strlen("check_freelists_on_shutdown")
+ && !memcmp(key, "check_freelists_on_shutdown", key_len)) {
+
+ heap->debug.check_freelists_on_shutdown = (bool) ZEND_STRTOUL(value, &tmp, 0);
+
+ } else {
+ fprintf(stderr, "Unknown ZEND_MM_DEBUG parameter: '%.*s'\n",
+ (int)key_len, key);
+ return;
+ }
+
+ if (tmp == end) {
+ break;
+ }
+ if (*tmp != ',') {
+ fprintf(stderr, "Unexpected '%c' after value of ZEND_MM_DEBUG parameter '%.*s', expected ','\n",
+ *tmp, (int)key_len, key);
+ return;
+ }
+ tmp++;
+ }
+
+ zend_mm_set_custom_handlers_ex(heap, poison_malloc, poison_free,
+ poison_realloc, poison_gc, poison_shutdown);
+}
+
static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
{
char *tmp;
@@ -3057,6 +3273,14 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
zend_mm_use_huge_pages = true;
}
alloc_globals->mm_heap = zend_mm_init();
+
+#if ZEND_MM_CUSTOM
+ ZEND_ASSERT(!alloc_globals->mm_heap->tracked_allocs);
+ tmp = getenv("ZEND_MM_DEBUG");
+ if (tmp) {
+ poison_enable(alloc_globals->mm_heap, tmp);
+ }
+#endif
}
#ifdef ZTS
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 29b825024851d..5437208694d49 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -23,6 +23,7 @@
#include "zend_language_parser.h"
#include "zend_smart_str.h"
#include "zend_exceptions.h"
+#include "zend_closures.h"
#include "zend_constants.h"
#include "zend_enum.h"
@@ -53,6 +54,18 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_znode(znode *node) {
return (zend_ast *) ast;
}
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void) {
+ 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);
+ ZEND_MAP_PTR_INIT(ast->fptr, NULL);
+
+ return (zend_ast *) ast;
+}
+
static zend_always_inline zend_ast * zend_ast_create_zval_int(zval *zv, uint32_t attr, uint32_t lineno) {
zend_ast_zval *ast;
@@ -99,6 +112,18 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze
return (zend_ast *) ast;
}
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array) {
+ zend_ast_op_array *ast;
+
+ ast = zend_ast_alloc(sizeof(zend_ast_op_array));
+ ast->kind = ZEND_AST_OP_ARRAY;
+ ast->attr = 0;
+ ast->lineno = CG(zend_lineno);
+ ast->op_array = op_array;
+
+ return (zend_ast *) ast;
+}
+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name) {
zend_string *name_str = zend_ast_get_str(name);
if (zend_string_equals_ci(name_str, ZSTR_KNOWN(ZEND_STR_CLASS))) {
@@ -677,6 +702,41 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
}
zval_ptr_dtor_nogc(&op1);
break;
+ case ZEND_AST_CAST:
+ if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
+ ret = FAILURE;
+ break;
+ }
+ if (ast->attr == Z_TYPE(op1)) {
+ ZVAL_COPY_VALUE(result, &op1);
+ } else {
+ switch (ast->attr) {
+ case _IS_BOOL:
+ ZVAL_BOOL(result, zend_is_true(&op1));
+ break;
+ case IS_LONG:
+ ZVAL_LONG(result, zval_get_long_func(&op1, false));
+ break;
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(result, zval_get_double_func(&op1));
+ break;
+ case IS_STRING:
+ ZVAL_STR(result, zval_get_string_func(&op1));
+ break;
+ case IS_ARRAY:
+ zend_cast_zval_to_array(result, &op1, IS_VAR);
+ break;
+ case IS_OBJECT:
+ zend_cast_zval_to_object(result, &op1, IS_VAR);
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ zval_ptr_dtor_nogc(&op1);
+ if (UNEXPECTED(EG(exception))) {
+ ret = FAILURE;
+ }
+ }
+ break;
case ZEND_AST_OR:
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
ret = FAILURE;
@@ -989,6 +1049,118 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
}
return SUCCESS;
}
+ case ZEND_AST_CALL:
+ case ZEND_AST_STATIC_CALL:
+ {
+ zend_function *fptr;
+ 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) {
+ zend_string *function_name = zend_ast_get_str(ast->child[0]);
+ zend_string *function_name_lc = zend_string_tolower(function_name);
+ fptr = zend_fetch_function(function_name_lc);
+ if (!fptr && ast->child[0]->attr != ZEND_NAME_FQ) {
+ const char *backslash = zend_memrchr(ZSTR_VAL(function_name_lc), '\\', ZSTR_LEN(function_name_lc));
+ if (backslash) {
+ fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name_lc) - (backslash - ZSTR_VAL(function_name_lc) + 1));
+ }
+ }
+ zend_string_release(function_name_lc);
+ if (!fptr) {
+ zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function_name));
+ return FAILURE;
+ }
+
+ ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr);
+ }
+
+ 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];
+
+ fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
+
+ if (!fptr) {
+ zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
+ if (!ce) {
+ return FAILURE;
+ }
+ zend_string *method_name = zend_ast_get_str(ast->child[1]);
+ if (ce->get_static_method) {
+ fptr = ce->get_static_method(ce, method_name);
+ } else {
+ fptr = zend_hash_find_ptr_lc(&ce->function_table, method_name);
+ if (fptr) {
+ if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
+ if (UNEXPECTED(fptr->common.scope != scope)) {
+ if (
+ UNEXPECTED(fptr->op_array.fn_flags & ZEND_ACC_PRIVATE)
+ || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fptr), scope))
+ ) {
+ if (ce->__callstatic) {
+ zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
+ } else {
+ zend_bad_method_call(fptr, method_name, scope);
+ }
+
+ return FAILURE;
+ }
+ }
+ }
+ } else {
+ if (ce->__callstatic) {
+ zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
+ } else {
+ zend_undefined_method(ce, method_name);
+ }
+
+ return FAILURE;
+ }
+ }
+
+ 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,
+ "Calling static trait method %s::%s is deprecated, "
+ "it should only be called on a class using the trait",
+ ZSTR_VAL(fptr->common.scope->name), ZSTR_VAL(fptr->common.function_name));
+ if (EG(exception)) {
+ return FAILURE;
+ }
+ }
+
+ ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr);
+ }
+
+ break;
+ }
+ }
+
+ zend_create_fake_closure(result, fptr, scope, scope, NULL);
+
+ return SUCCESS;
+ }
+ case ZEND_AST_OP_ARRAY:
+ {
+ zend_function *func = (zend_function *)zend_ast_get_op_array(ast)->op_array;
+
+ zend_create_closure(result, func, scope, scope, NULL);
+ return SUCCESS;
+ }
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
{
@@ -1070,6 +1242,10 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
size = sizeof(zend_ast_zval);
+ } 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);
} else if (zend_ast_is_list(ast)) {
uint32_t i;
zend_ast_list *list = zend_ast_get_list(ast);
@@ -1080,6 +1256,9 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
size += zend_ast_tree_size(list->child[i]);
}
}
+ } else if (zend_ast_is_decl(ast)) {
+ /* Not implemented. */
+ ZEND_UNREACHABLE();
} else {
uint32_t i, children = zend_ast_get_num_children(ast);
@@ -1126,6 +1305,26 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
new->child[i] = NULL;
}
}
+ } else if (ast->kind == ZEND_AST_OP_ARRAY) {
+ zend_ast_op_array *old = zend_ast_get_op_array(ast);
+ zend_ast_op_array *new = (zend_ast_op_array*)buf;
+ new->kind = old->kind;
+ new->attr = old->attr;
+ new->lineno = old->lineno;
+ new->op_array = old->op_array;
+ function_add_ref((zend_function *)new->op_array);
+ buf = (void*)((char*)buf + sizeof(zend_ast_op_array));
+ } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
+ zend_ast_fcc *old = (zend_ast_fcc*)ast;
+ zend_ast_fcc *new = (zend_ast_fcc*)buf;
+ new->kind = old->kind;
+ new->attr = old->attr;
+ new->lineno = old->lineno;
+ ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr));
+ buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
+ } else if (zend_ast_is_decl(ast)) {
+ /* Not implemented. */
+ ZEND_UNREACHABLE();
} else {
uint32_t i, children = zend_ast_get_num_children(ast);
zend_ast *new = (zend_ast*)buf;
@@ -1189,7 +1388,9 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
}
} else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) {
zend_string_release_ex(zend_ast_get_constant_name(ast), 0);
- } else if (EXPECTED(ast->kind >= ZEND_AST_FUNC_DECL)) {
+ } else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) {
+ destroy_op_array(zend_ast_get_op_array(ast)->op_array);
+ } else if (EXPECTED(zend_ast_is_decl(ast))) {
zend_ast_decl *decl = (zend_ast_decl *) ast;
if (decl->name) {
@@ -1220,6 +1421,9 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *contex
for (i = 0; i < list->children; ++i) {
fn(&list->child[i], context);
}
+ } else if (zend_ast_is_decl(ast)) {
+ /* Not implemented. */
+ ZEND_UNREACHABLE();
} else {
uint32_t i, children = zend_ast_get_num_children(ast);
for (i = 0; i < children; ++i) {
@@ -1427,11 +1631,14 @@ static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int pri
smart_str_appendc(str, '}');
}
-static ZEND_COLD void zend_ast_export_list(smart_str *str, zend_ast_list *list, bool separator, int priority, int indent)
+/* Use zend_ast_export_list() unless fewer than `list->children` children should
+ * be exported. */
+static ZEND_COLD void zend_ast_export_list_ex(smart_str *str, zend_ast_list *list, bool separator, int priority, int indent, int children)
{
+ ZEND_ASSERT(children <= list->children);
uint32_t i = 0;
- while (i < list->children) {
+ while (i < children) {
if (i != 0 && separator) {
smart_str_appends(str, ", ");
}
@@ -1440,6 +1647,11 @@ static ZEND_COLD void zend_ast_export_list(smart_str *str, zend_ast_list *list,
}
}
+static ZEND_COLD void zend_ast_export_list(smart_str *str, zend_ast_list *list, bool separator, int priority, int indent)
+{
+ zend_ast_export_list_ex(str, list, separator, priority, indent, list->children);
+}
+
static ZEND_COLD void zend_ast_export_encaps_list(smart_str *str, char quote, zend_ast_list *list, int indent)
{
uint32_t i = 0;
@@ -1844,6 +2056,11 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appendl(str, ZSTR_VAL(name), ZSTR_LEN(name));
break;
}
+ case ZEND_AST_OP_ARRAY:
+ smart_str_appends(str, "Closure(");
+ smart_str_append(str, zend_ast_get_op_array(ast)->op_array->function_name);
+ smart_str_appends(str, ")");
+ break;
case ZEND_AST_CONSTANT_CLASS:
smart_str_appendl(str, "__CLASS__", sizeof("__CLASS__")-1);
break;
@@ -2007,9 +2224,26 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
goto simple_list;
}
- case ZEND_AST_CONST_DECL:
+ case ZEND_AST_CONST_DECL: {
+ zend_ast_list *ast_list = zend_ast_get_list(ast);
+ /* Attributes are stored at the end of the list if present. */
+ if (ast_list->child[ast_list->children - 1]->kind == ZEND_AST_ATTRIBUTE_LIST) {
+ zend_ast_export_attributes(
+ str,
+ ast_list->child[ast_list->children - 1],
+ indent,
+ 1
+ );
+ /* 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);
+ break;
+ }
smart_str_appends(str, "const ");
goto simple_list;
+ }
case ZEND_AST_CLASS_CONST_GROUP:
if (ast->child[1]) {
zend_ast_export_attributes(str, ast->child[1], indent, 1);
@@ -2088,6 +2322,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
EMPTY_SWITCH_DEFAULT_CASE();
}
break;
+ case ZEND_AST_CAST_VOID:
+ PREFIX_OP("(void)", 240, 241);
+ break;
case ZEND_AST_EMPTY:
FUNC_OP("empty");
case ZEND_AST_ISSET:
@@ -2109,13 +2346,6 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
break;
case ZEND_AST_CLONE:
PREFIX_OP("clone ", 270, 271);
- case ZEND_AST_EXIT:
- if (ast->child[0]) {
- FUNC_OP("exit");
- } else {
- APPEND_STR("exit");
- }
- break;
case ZEND_AST_PRINT:
PREFIX_OP("print ", 60, 61);
case ZEND_AST_INCLUDE_OR_EVAL:
@@ -2218,10 +2448,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
/* The const expr representation stores the fetch type instead. */
switch (ast->attr) {
case ZEND_FETCH_CLASS_SELF:
- smart_str_appends(str, "self");
+ smart_str_append(str, ZSTR_KNOWN(ZEND_STR_SELF));
break;
case ZEND_FETCH_CLASS_PARENT:
- smart_str_appends(str, "parent");
+ smart_str_append(str, ZSTR_KNOWN(ZEND_STR_PARENT));
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
@@ -2711,6 +2941,12 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr)
case ZEND_AST_CLASS_CONST_GROUP:
ast->child[1] = attr;
break;
+ case ZEND_AST_CONST_DECL:
+ /* Since constants are already stored in a list, just add the attributes
+ * to that list instead of storing them elsewhere;
+ * zend_compile_const_decl() checks the kind of the list elements. */
+ zend_ast_list_add(ast, attr);
+ break;
EMPTY_SWITCH_DEFAULT_CASE()
}
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index 8d1d93f2564a7..9348c35f6cc07 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -22,6 +22,7 @@
#define ZEND_AST_H
#include "zend_types.h"
+#include "zend_map_ptr.h"
#ifndef ZEND_AST_SPEC
# define ZEND_AST_SPEC 1
@@ -35,6 +36,7 @@ enum _zend_ast_kind {
/* special nodes */
ZEND_AST_ZVAL = 1 << ZEND_AST_SPECIAL_SHIFT,
ZEND_AST_CONSTANT,
+ ZEND_AST_OP_ARRAY,
ZEND_AST_ZNODE,
/* declaration nodes */
@@ -82,12 +84,12 @@ enum _zend_ast_kind {
ZEND_AST_UNARY_PLUS,
ZEND_AST_UNARY_MINUS,
ZEND_AST_CAST,
+ ZEND_AST_CAST_VOID,
ZEND_AST_EMPTY,
ZEND_AST_ISSET,
ZEND_AST_SILENCE,
ZEND_AST_SHELL_EXEC,
ZEND_AST_CLONE,
- ZEND_AST_EXIT,
ZEND_AST_PRINT,
ZEND_AST_INCLUDE_OR_EVAL,
ZEND_AST_UNARY_OP,
@@ -206,6 +208,15 @@ typedef struct _zend_ast_zval {
zval val;
} zend_ast_zval;
+typedef struct _zend_op_array zend_op_array;
+
+typedef struct _zend_ast_op_array {
+ zend_ast_kind kind;
+ zend_ast_attr attr;
+ uint32_t lineno;
+ zend_op_array *op_array;
+} zend_ast_op_array;
+
/* Separate structure for function and class declaration, as they need extra information. */
typedef struct _zend_ast_decl {
zend_ast_kind kind;
@@ -218,6 +229,13 @@ typedef struct _zend_ast_decl {
zend_ast *child[5];
} zend_ast_decl;
+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_MAP_PTR_DEF(zend_function *, fptr);
+} zend_ast_fcc;
+
typedef void (*zend_ast_process_t)(zend_ast *ast);
extern ZEND_API zend_ast_process_t zend_ast_process;
@@ -230,6 +248,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval)
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr);
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name);
+ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array);
+
#if ZEND_AST_SPEC
# define ZEND_AST_SPEC_CALL(name, ...) \
ZEND_EXPAND_VA(ZEND_AST_SPEC_CALL_(name, __VA_ARGS__, _n, _5, _4, _3, _2, _1, _0)(__VA_ARGS__))
@@ -307,6 +327,8 @@ ZEND_API zend_ast *zend_ast_create_decl(
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);
+
typedef struct {
bool had_side_effects;
} zend_ast_evaluate_ctx;
@@ -330,6 +352,10 @@ static zend_always_inline bool zend_ast_is_special(zend_ast *ast) {
return (ast->kind >> ZEND_AST_SPECIAL_SHIFT) & 1;
}
+static zend_always_inline bool zend_ast_is_decl(zend_ast *ast) {
+ return zend_ast_is_special(ast) && ast->kind >= ZEND_AST_FUNC_DECL;
+}
+
static zend_always_inline bool zend_ast_is_list(zend_ast *ast) {
return (ast->kind >> ZEND_AST_IS_LIST_SHIFT) & 1;
}
@@ -348,6 +374,11 @@ static zend_always_inline zend_string *zend_ast_get_str(zend_ast *ast) {
return Z_STR_P(zv);
}
+static zend_always_inline zend_ast_op_array *zend_ast_get_op_array(zend_ast *ast) {
+ ZEND_ASSERT(ast->kind == ZEND_AST_OP_ARRAY);
+ return (zend_ast_op_array *) ast;
+}
+
static zend_always_inline zend_string *zend_ast_get_constant_name(zend_ast *ast) {
ZEND_ASSERT(ast->kind == ZEND_AST_CONSTANT);
ZEND_ASSERT(Z_TYPE(((zend_ast_zval *) ast)->val) == IS_STRING);
@@ -356,6 +387,8 @@ static zend_always_inline zend_string *zend_ast_get_constant_name(zend_ast *ast)
static zend_always_inline uint32_t zend_ast_get_num_children(zend_ast *ast) {
ZEND_ASSERT(!zend_ast_is_list(ast));
+ ZEND_ASSERT(!zend_ast_is_special(ast));
+
return ast->kind >> ZEND_AST_NUM_CHILDREN_SHIFT;
}
static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) {
diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c
index d7bcb1f54e889..c3633801be83e 100644
--- a/Zend/zend_attributes.c
+++ b/Zend/zend_attributes.c
@@ -31,6 +31,7 @@ ZEND_API zend_class_entry *zend_ce_sensitive_parameter;
ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
ZEND_API zend_class_entry *zend_ce_override;
ZEND_API zend_class_entry *zend_ce_deprecated;
+ZEND_API zend_class_entry *zend_ce_nodiscard;
static zend_object_handlers attributes_object_handlers_sensitive_parameter_value;
@@ -193,6 +194,29 @@ ZEND_METHOD(Deprecated, __construct)
}
}
+ZEND_METHOD(NoDiscard, __construct)
+{
+ zend_string *message = NULL;
+ zval value;
+
+ ZEND_PARSE_PARAMETERS_START(0, 1)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_STR_OR_NULL(message)
+ ZEND_PARSE_PARAMETERS_END();
+
+ if (message) {
+ ZVAL_STR(&value, message);
+ } else {
+ ZVAL_NULL(&value);
+ }
+ zend_update_property_ex(zend_ce_nodiscard, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value);
+
+ /* The assignment might fail due to 'readonly'. */
+ if (UNEXPECTED(EG(exception))) {
+ RETURN_THROWS();
+ }
+}
+
static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
{
if (attributes) {
@@ -354,7 +378,8 @@ static const char *target_names[] = {
"method",
"property",
"class constant",
- "parameter"
+ "parameter",
+ "constant"
};
ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
@@ -520,6 +545,9 @@ void zend_register_attribute_ce(void)
zend_ce_deprecated = register_class_Deprecated();
attr = zend_mark_internal_attribute(zend_ce_deprecated);
+
+ zend_ce_nodiscard = register_class_NoDiscard();
+ attr = zend_mark_internal_attribute(zend_ce_nodiscard);
}
void zend_attributes_shutdown(void)
diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h
index 8a825247c00f8..eb464772c100c 100644
--- a/Zend/zend_attributes.h
+++ b/Zend/zend_attributes.h
@@ -28,9 +28,10 @@
#define ZEND_ATTRIBUTE_TARGET_PROPERTY (1<<3)
#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST (1<<4)
#define ZEND_ATTRIBUTE_TARGET_PARAMETER (1<<5)
-#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<6) - 1)
-#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<6)
-#define ZEND_ATTRIBUTE_FLAGS ((1<<7) - 1)
+#define ZEND_ATTRIBUTE_TARGET_CONST (1<<6)
+#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<7) - 1)
+#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<7)
+#define ZEND_ATTRIBUTE_FLAGS ((1<<8) - 1)
/* Flags for zend_attribute.flags */
#define ZEND_ATTRIBUTE_PERSISTENT (1<<0)
@@ -47,6 +48,7 @@ extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter;
extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
extern ZEND_API zend_class_entry *zend_ce_override;
extern ZEND_API zend_class_entry *zend_ce_deprecated;
+extern ZEND_API zend_class_entry *zend_ce_nodiscard;
typedef struct {
zend_string *name;
diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php
index 0a35b0c57cb44..fe70de83e4d21 100644
--- a/Zend/zend_attributes.stub.php
+++ b/Zend/zend_attributes.stub.php
@@ -17,6 +17,8 @@ final class Attribute
const int TARGET_CLASS_CONSTANT = UNKNOWN;
/** @cvalue ZEND_ATTRIBUTE_TARGET_PARAMETER */
const int TARGET_PARAMETER = UNKNOWN;
+ /** @cvalue ZEND_ATTRIBUTE_TARGET_CONST */
+ const int TARGET_CONSTANT = UNKNOWN;
/** @cvalue ZEND_ATTRIBUTE_TARGET_ALL */
const int TARGET_ALL = UNKNOWN;
/** @cvalue ZEND_ATTRIBUTE_IS_REPEATABLE */
@@ -75,7 +77,7 @@ public function __construct() {}
/**
* @strict-properties
*/
-#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT)]
+#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT)]
final class Deprecated
{
public readonly ?string $message;
@@ -84,3 +86,14 @@ final class Deprecated
public function __construct(?string $message = null, ?string $since = null) {}
}
+
+/**
+ * @strict-properties
+ */
+#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
+final class NoDiscard
+{
+ public readonly ?string $message;
+
+ public function __construct(?string $message = null) {}
+}
diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h
index 817dacbd44d50..14afe40c01adf 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: 2358a0d820edd06a1702c84104bfd545af08311c */
+ * Stub hash: 9aee3d8f2ced376f5929048444eaa2529ff90311 */
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")
@@ -29,6 +29,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Deprecated___construct, 0, 0, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, since, IS_STRING, 1, "null")
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NoDiscard___construct, 0, 0, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
ZEND_METHOD(Attribute, __construct);
ZEND_METHOD(ReturnTypeWillChange, __construct);
ZEND_METHOD(AllowDynamicProperties, __construct);
@@ -38,6 +42,7 @@ ZEND_METHOD(SensitiveParameterValue, getValue);
ZEND_METHOD(SensitiveParameterValue, __debugInfo);
ZEND_METHOD(Override, __construct);
ZEND_METHOD(Deprecated, __construct);
+ZEND_METHOD(NoDiscard, __construct);
static const zend_function_entry class_Attribute_methods[] = {
ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC)
@@ -76,6 +81,11 @@ static const zend_function_entry class_Deprecated_methods[] = {
ZEND_FE_END
};
+static const zend_function_entry class_NoDiscard_methods[] = {
+ ZEND_ME(NoDiscard, __construct, arginfo_class_NoDiscard___construct, ZEND_ACC_PUBLIC)
+ ZEND_FE_END
+};
+
static zend_class_entry *register_class_Attribute(void)
{
zend_class_entry ce, *class_entry;
@@ -119,6 +129,12 @@ static zend_class_entry *register_class_Attribute(void)
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);
+ 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_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);
+
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);
@@ -207,9 +223,7 @@ static zend_class_entry *register_class_SensitiveParameterValue(void)
zval property_value_default_value;
ZVAL_UNDEF(&property_value_default_value);
- zend_string *property_value_name = zend_string_init("value", sizeof("value") - 1, 1);
- zend_declare_typed_property(class_entry, property_value_name, &property_value_default_value, ZEND_ACC_PRIVATE|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY));
- zend_string_release(property_value_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_VALUE), &property_value_default_value, ZEND_ACC_PRIVATE|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY));
return class_entry;
}
@@ -240,22 +254,39 @@ static zend_class_entry *register_class_Deprecated(void)
zval property_message_default_value;
ZVAL_UNDEF(&property_message_default_value);
- zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1);
- zend_declare_typed_property(class_entry, property_message_name, &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_release(property_message_name);
+ 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));
zval property_since_default_value;
ZVAL_UNDEF(&property_since_default_value);
- zend_string *property_since_name = zend_string_init("since", sizeof("since") - 1, 1);
- zend_declare_typed_property(class_entry, property_since_name, &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_release(property_since_name);
+ 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_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);
zval attribute_Attribute_class_Deprecated_0_arg0;
- ZVAL_LONG(&attribute_Attribute_class_Deprecated_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
+ ZVAL_LONG(&attribute_Attribute_class_Deprecated_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST | ZEND_ATTRIBUTE_TARGET_CONST);
ZVAL_COPY_VALUE(&attribute_Attribute_class_Deprecated_0->args[0].value, &attribute_Attribute_class_Deprecated_0_arg0);
return class_entry;
}
+
+static zend_class_entry *register_class_NoDiscard(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "NoDiscard", class_NoDiscard_methods);
+ class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES);
+
+ zval property_message_default_value;
+ ZVAL_UNDEF(&property_message_default_value);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
+
+ zend_string *attribute_name_Attribute_class_NoDiscard_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1);
+ 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);
+ zval attribute_Attribute_class_NoDiscard_0_arg0;
+ ZVAL_LONG(&attribute_Attribute_class_NoDiscard_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION);
+ ZVAL_COPY_VALUE(&attribute_Attribute_class_NoDiscard_0->args[0].value, &attribute_Attribute_class_NoDiscard_0_arg0);
+
+ return class_entry;
+}
diff --git a/Zend/zend_bitset.h b/Zend/zend_bitset.h
index a42b35712f4e6..d990b6a2a871e 100644
--- a/Zend/zend_bitset.h
+++ b/Zend/zend_bitset.h
@@ -60,7 +60,6 @@ ZEND_ATTRIBUTE_CONST static zend_always_inline int zend_ulong_ntz(zend_ulong num
#else
if (!BitScanForward(&index, num)) {
#endif
- /* undefined behavior */
return SIZEOF_ZEND_LONG * 8;
}
@@ -98,7 +97,6 @@ ZEND_ATTRIBUTE_CONST static zend_always_inline int zend_ulong_nlz(zend_ulong num
#else
if (!BitScanReverse(&index, num)) {
#endif
- /* undefined behavior */
return SIZEOF_ZEND_LONG * 8;
}
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 8ed7939200c79..7a07ceadce2e2 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -1016,8 +1016,8 @@ static void _property_exists(zval *return_value, zval *object, zend_string *prop
RETURN_TRUE;
}
- if (Z_TYPE_P(object) == IS_OBJECT &&
- Z_OBJ_HANDLER_P(object, has_property)(Z_OBJ_P(object), property, 2, NULL)) {
+ 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;
@@ -1322,6 +1322,15 @@ ZEND_FUNCTION(restore_error_handler)
}
/* }}} */
+ZEND_FUNCTION(get_error_handler)
+{
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) {
+ RETURN_COPY(&EG(user_error_handler));
+ }
+}
+
/* {{{ Sets a user-defined exception handler function. Returns the previously defined exception handler, or false on error */
ZEND_FUNCTION(set_exception_handler)
{
@@ -1368,6 +1377,15 @@ ZEND_FUNCTION(restore_exception_handler)
}
/* }}} */
+ZEND_FUNCTION(get_exception_handler)
+{
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
+ RETURN_COPY(&EG(user_exception_handler));
+ }
+}
+
static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int flags) /* {{{ */
{
zend_string *key;
diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php
index f7009c4ffba6e..7f316835aea6b 100644
--- a/Zend/zend_builtin_functions.stub.php
+++ b/Zend/zend_builtin_functions.stub.php
@@ -117,11 +117,15 @@ function set_error_handler(?callable $callback, int $error_levels = E_ALL) {}
function restore_error_handler(): true {}
+function get_error_handler(): ?callable {}
+
/** @return callable|null */
function set_exception_handler(?callable $callback) {}
function restore_exception_handler(): true {}
+function get_exception_handler(): ?callable {}
+
/**
* @return array
* @refcount 1
diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h
index cf34b1c0012d5..9498b8292f892 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: 3dbc84896823c9aaa9ac8aeef8841266920c3e50 */
+ * Stub hash: a24761186f1ddf758e648b0a764826537cbd33b9 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_exit, 0, 0, IS_NEVER, 0)
ZEND_ARG_TYPE_MASK(0, status, MAY_BE_STRING|MAY_BE_LONG, "0")
@@ -148,12 +148,17 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_restore_error_handler, 0, 0, IS_TRUE, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_error_handler, 0, 0, IS_CALLABLE, 1)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_set_exception_handler, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 1)
ZEND_END_ARG_INFO()
#define arginfo_restore_exception_handler arginfo_restore_error_handler
+#define arginfo_get_exception_handler arginfo_get_error_handler
+
#define arginfo_get_declared_classes arginfo_func_get_args
#define arginfo_get_declared_traits arginfo_func_get_args
@@ -272,8 +277,10 @@ ZEND_FUNCTION(get_included_files);
ZEND_FUNCTION(trigger_error);
ZEND_FUNCTION(set_error_handler);
ZEND_FUNCTION(restore_error_handler);
+ZEND_FUNCTION(get_error_handler);
ZEND_FUNCTION(set_exception_handler);
ZEND_FUNCTION(restore_exception_handler);
+ZEND_FUNCTION(get_exception_handler);
ZEND_FUNCTION(get_declared_classes);
ZEND_FUNCTION(get_declared_traits);
ZEND_FUNCTION(get_declared_interfaces);
@@ -336,8 +343,10 @@ static const zend_function_entry ext_functions[] = {
ZEND_RAW_FENTRY("user_error", zif_trigger_error, arginfo_user_error, 0, NULL, NULL)
ZEND_FE(set_error_handler, arginfo_set_error_handler)
ZEND_FE(restore_error_handler, arginfo_restore_error_handler)
+ ZEND_FE(get_error_handler, arginfo_get_error_handler)
ZEND_FE(set_exception_handler, arginfo_set_exception_handler)
ZEND_FE(restore_exception_handler, arginfo_restore_exception_handler)
+ ZEND_FE(get_exception_handler, arginfo_get_exception_handler)
ZEND_FE(get_declared_classes, arginfo_get_declared_classes)
ZEND_FE(get_declared_traits, arginfo_get_declared_traits)
ZEND_FE(get_declared_interfaces, arginfo_get_declared_interfaces)
diff --git a/Zend/zend_call_stack.c b/Zend/zend_call_stack.c
index ac12b8b7d8716..ed86ecc74a238 100644
--- a/Zend/zend_call_stack.c
+++ b/Zend/zend_call_stack.c
@@ -380,7 +380,6 @@ static bool zend_call_stack_get_freebsd(zend_call_stack *stack)
static bool zend_call_stack_get_win32(zend_call_stack *stack)
{
ULONG_PTR low_limit, high_limit;
- ULONG size;
MEMORY_BASIC_INFORMATION guard_region = {0}, uncommitted_region = {0};
size_t result_size, page_size;
diff --git a/Zend/zend_call_stack.h b/Zend/zend_call_stack.h
index c8bc756426bf8..fee528c150f08 100644
--- a/Zend/zend_call_stack.h
+++ b/Zend/zend_call_stack.h
@@ -25,6 +25,10 @@
# include
#endif
+#ifdef _MSC_VER
+#include
+#endif
+
#ifdef ZEND_CHECK_STACK_LIMIT
typedef struct _zend_call_stack {
@@ -38,7 +42,7 @@ ZEND_API bool zend_call_stack_get(zend_call_stack *stack);
/** Returns an approximation of the current stack position */
static zend_always_inline void *zend_call_stack_position(void) {
-#ifdef ZEND_WIN32
+#ifdef _MSC_VER
return _AddressOfReturnAddress();
#elif defined(PHP_HAVE_BUILTIN_FRAME_ADDRESS)
return __builtin_frame_address(0);
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 6ad2f6ac40246..5777e1a34a2b8 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -40,6 +40,8 @@ typedef struct _zend_closure {
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);
+
ZEND_METHOD(Closure, __invoke) /* {{{ */
{
zend_function *func = EX(func);
@@ -51,9 +53,12 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args)
ZEND_PARSE_PARAMETERS_END();
- if (call_user_function_named(CG(function_table), NULL, ZEND_THIS, return_value, num_args, args, named_args) == FAILURE) {
- RETVAL_FALSE;
- }
+ zend_fcall_info_cache fcc = {
+ .closure = Z_OBJ_P(ZEND_THIS),
+ };
+ zend_closure_get_closure(Z_OBJ_P(ZEND_THIS), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false);
+ fcc.called_scope = fcc.calling_scope;
+ zend_call_known_fcc(&fcc, return_value, num_args, args, named_args);
/* destruct the function also, then - we have allocated it in get_method */
zend_string_release_ex(func->internal_function.function_name, 0);
@@ -523,7 +528,6 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */
/* We don't own the static variables of fake closures. */
if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
zend_destroy_static_vars(&closure->func.op_array);
- closure->func.op_array.static_variables = NULL;
}
destroy_op_array(&closure->func.op_array);
} else if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
@@ -755,16 +759,14 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
}
/* For fake closures, we want to reuse the static variables of the original function. */
+ HashTable *ht = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr);
if (!is_fake) {
- if (closure->func.op_array.static_variables) {
- closure->func.op_array.static_variables =
- zend_array_dup(closure->func.op_array.static_variables);
+ if (!ht) {
+ ht = closure->func.op_array.static_variables;
}
ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
- closure->func.op_array.static_variables);
+ ht ? zend_array_dup(ht) : NULL);
} else if (func->op_array.static_variables) {
- HashTable *ht = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr);
-
if (!ht) {
ht = zend_array_dup(func->op_array.static_variables);
ZEND_MAP_PTR_SET(func->op_array.static_variables_ptr, ht);
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 195c65d504374..0669d106f15e9 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -214,6 +214,10 @@ static const struct reserved_class_name reserved_class_names[] = {
{ZEND_STRL("iterable")},
{ZEND_STRL("object")},
{ZEND_STRL("mixed")},
+ /* These are not usable as class names because they're proper tokens,
+ * but they are here for class aliases. */
+ {ZEND_STRL("array")},
+ {ZEND_STRL("callable")},
{NULL, 0}
};
@@ -338,7 +342,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_arra
CG(context).brk_cont_array = NULL;
CG(context).labels = NULL;
CG(context).in_jmp_frameless_branch = false;
- CG(context).active_property_info = NULL;
+ CG(context).active_property_info_name = NULL;
CG(context).active_property_hook_kind = (zend_property_hook_kind)-1;
}
/* }}} */
@@ -843,7 +847,7 @@ static void zend_do_free(znode *op1) /* {{{ */
/* }}} */
-static char *zend_modifier_token_to_string(uint32_t token)
+static const char *zend_modifier_token_to_string(uint32_t token)
{
switch (token) {
case T_PUBLIC:
@@ -954,14 +958,22 @@ uint32_t zend_modifier_list_to_flags(zend_modifier_target target, zend_ast *modi
zend_ast_list *modifier_list = zend_ast_get_list(modifiers);
for (uint32_t i = 0; i < modifier_list->children; i++) {
- uint32_t new_flag = zend_modifier_token_to_flag(target, (uint32_t) Z_LVAL_P(zend_ast_get_zval(modifier_list->child[i])));
+ uint32_t token = (uint32_t) Z_LVAL_P(zend_ast_get_zval(modifier_list->child[i]));
+ uint32_t new_flag = zend_modifier_token_to_flag(target, token);
if (!new_flag) {
return 0;
}
+ /* Don't error immediately for duplicate flags, we want to prioritize the errors from zend_add_member_modifier(). */
+ bool duplicate_flag = (flags & new_flag);
flags = zend_add_member_modifier(flags, new_flag, target);
if (!flags) {
return 0;
}
+ if (duplicate_flag) {
+ zend_throw_exception_ex(zend_ce_compile_error, 0,
+ "Multiple %s modifiers are not allowed", zend_modifier_token_to_string(token));
+ return 0;
+ }
}
return flags;
@@ -1019,23 +1031,6 @@ uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag, zend_modifi
"Multiple access type modifiers are not allowed", 0);
return 0;
}
- if ((flags & ZEND_ACC_ABSTRACT) && (new_flag & ZEND_ACC_ABSTRACT)) {
- zend_throw_exception(zend_ce_compile_error, "Multiple abstract modifiers are not allowed", 0);
- return 0;
- }
- if ((flags & ZEND_ACC_STATIC) && (new_flag & ZEND_ACC_STATIC)) {
- zend_throw_exception(zend_ce_compile_error, "Multiple static modifiers are not allowed", 0);
- return 0;
- }
- if ((flags & ZEND_ACC_FINAL) && (new_flag & ZEND_ACC_FINAL)) {
- zend_throw_exception(zend_ce_compile_error, "Multiple final modifiers are not allowed", 0);
- return 0;
- }
- if ((flags & ZEND_ACC_READONLY) && (new_flag & ZEND_ACC_READONLY)) {
- zend_throw_exception(zend_ce_compile_error,
- "Multiple readonly modifiers are not allowed", 0);
- return 0;
- }
if ((new_flags & ZEND_ACC_ABSTRACT) && (new_flags & ZEND_ACC_FINAL)) {
if (target == ZEND_MODIFIER_TARGET_METHOD) {
zend_throw_exception(zend_ce_compile_error,
@@ -1394,9 +1389,9 @@ static zend_string *add_type_string(zend_string *type, zend_string *new_type, bo
static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scope) {
if (scope) {
- if (zend_string_equals_literal_ci(name, "self")) {
+ if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_SELF))) {
name = scope->name;
- } else if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
+ } else if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_PARENT)) && scope->parent) {
name = scope->parent->name;
}
}
@@ -1412,19 +1407,17 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop
}
static zend_string *add_intersection_type(zend_string *str,
- zend_type_list *intersection_type_list, zend_class_entry *scope,
+ const zend_type_list *intersection_type_list, zend_class_entry *scope,
bool is_bracketed)
{
- zend_type *single_type;
+ const zend_type *single_type;
zend_string *intersection_str = NULL;
ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) {
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type));
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type));
- zend_string *name = ZEND_TYPE_NAME(*single_type);
- zend_string *resolved = resolve_class_name(name, scope);
- intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true);
- zend_string_release(resolved);
+
+ intersection_str = add_type_string(intersection_str, ZEND_TYPE_NAME(*single_type), /* is_intersection */ true);
} ZEND_TYPE_LIST_FOREACH_END();
ZEND_ASSERT(intersection_str);
@@ -1439,7 +1432,7 @@ static zend_string *add_intersection_type(zend_string *str,
return str;
}
-zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) {
+zend_string *zend_type_to_string_resolved(const zend_type type, zend_class_entry *scope) {
zend_string *str = NULL;
/* Pure intersection type */
@@ -1448,7 +1441,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false);
} else if (ZEND_TYPE_HAS_LIST(type)) {
/* A union type might not be a list */
- zend_type *list_type;
+ 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);
@@ -1456,6 +1449,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
}
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));
+
zend_string *name = ZEND_TYPE_NAME(*list_type);
zend_string *resolved = resolve_class_name(name, scope);
str = add_type_string(str, resolved, /* is_intersection */ false);
@@ -1533,7 +1527,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type) {
return zend_type_to_string_resolved(type, NULL);
}
-static bool is_generator_compatible_class_type(zend_string *name) {
+static bool is_generator_compatible_class_type(const zend_string *name) {
return zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_TRAVERSABLE))
|| zend_string_equals_literal_ci(name, "Iterator")
|| zend_string_equals_literal_ci(name, "Generator");
@@ -1547,10 +1541,10 @@ static void zend_mark_function_as_generator(void) /* {{{ */
}
if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_type return_type = CG(active_op_array)->arg_info[-1].type;
+ const zend_type return_type = CG(active_op_array)->arg_info[-1].type;
bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_OBJECT) != 0;
if (!valid_type) {
- zend_type *single_type;
+ const zend_type *single_type;
ZEND_TYPE_FOREACH(return_type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)
&& is_generator_compatible_class_type(ZEND_TYPE_NAME(*single_type))) {
@@ -1743,9 +1737,9 @@ static inline bool class_name_refers_to_active_ce(zend_string *class_name, uint3
uint32_t zend_get_class_fetch_type(const zend_string *name) /* {{{ */
{
- if (zend_string_equals_literal_ci(name, "self")) {
+ if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_SELF))) {
return ZEND_FETCH_CLASS_SELF;
- } else if (zend_string_equals_literal_ci(name, "parent")) {
+ } else if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_PARENT))) {
return ZEND_FETCH_CLASS_PARENT;
} else if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_STATIC))) {
return ZEND_FETCH_CLASS_STATIC;
@@ -2626,33 +2620,6 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
}
/* }}} */
-/* Remember to update type_num_classes() in compact_literals.c when changing this function */
-static size_t zend_type_get_num_classes(zend_type type) {
- if (!ZEND_TYPE_IS_COMPLEX(type)) {
- return 0;
- }
- if (ZEND_TYPE_HAS_LIST(type)) {
- /* Intersection types cannot have nested list types */
- if (ZEND_TYPE_IS_INTERSECTION(type)) {
- return ZEND_TYPE_LIST(type)->num_types;
- }
- ZEND_ASSERT(ZEND_TYPE_IS_UNION(type));
- size_t count = 0;
- zend_type *list_type;
-
- ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
- if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
- count += ZEND_TYPE_LIST(*list_type)->num_types;
- } else {
- ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
- count += 1;
- }
- } ZEND_TYPE_LIST_FOREACH_END();
- return count;
- }
- return 1;
-}
-
static void zend_emit_return_type_check(
znode *expr, zend_arg_info *return_info, bool implicit) /* {{{ */
{
@@ -2714,8 +2681,6 @@ static void zend_emit_return_type_check(
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable();
}
-
- opline->op2.num = zend_alloc_cache_slots(zend_type_get_num_classes(return_info->type));
}
}
/* }}} */
@@ -3716,6 +3681,7 @@ static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) {
}
}
} 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);
@@ -3932,13 +3898,15 @@ static uint32_t zend_compile_args(
}
/* }}} */
-ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* {{{ */
+ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bool result_used) /* {{{ */
{
- if (fbc) {
+ uint32_t no_discard = result_used ? 0 : ZEND_ACC_NODISCARD;
+
+ if (fbc && init_op->opcode != ZEND_NEW) {
ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE));
if (fbc->type == ZEND_INTERNAL_FUNCTION && !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) {
if (init_op->opcode == ZEND_INIT_FCALL && !zend_execute_internal) {
- if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) {
+ if (!(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
return ZEND_DO_ICALL;
} else {
return ZEND_DO_FCALL_BY_NAME;
@@ -3946,7 +3914,7 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /*
}
} else if (!(CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS)){
if (zend_execute_ex == execute_ex) {
- if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) {
+ if (!(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
return ZEND_DO_UCALL;
} else {
return ZEND_DO_FCALL_BY_NAME;
@@ -3996,7 +3964,16 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun
opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc);
}
- opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
+ uint8_t call_op = zend_get_call_op(
+ opline,
+ fbc,
+ /* result_used: At this point we do not yet reliably
+ * know if the result is used. Deoptimize #[\NoDiscard]
+ * calls to be sure. The optimizer will fix this up.
+ */
+ false
+ );
+ opline = zend_emit_op(result, call_op, NULL, NULL);
if (may_have_extra_named_args) {
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
}
@@ -5112,13 +5089,13 @@ static bool zend_compile_parent_property_hook_call(znode *result, zend_ast *ast,
zend_property_hook_kind hook_kind = zend_get_property_hook_kind_from_name(hook_name);
ZEND_ASSERT(hook_kind != (uint32_t)-1);
- const zend_property_info *prop_info = CG(context).active_property_info;
- if (!prop_info) {
+ const zend_string *prop_info_name = CG(context).active_property_info_name;
+ if (!prop_info_name) {
zend_error_noreturn(E_COMPILE_ERROR, "Must not use parent::$%s::%s() outside a property hook",
ZSTR_VAL(property_name), ZSTR_VAL(hook_name));
}
- const char *unmangled_prop_name = zend_get_unmangled_property_name(prop_info->name);
+ const char *unmangled_prop_name = zend_get_unmangled_property_name(prop_info_name);
if (!zend_string_equals_cstr(property_name, unmangled_prop_name, strlen(unmangled_prop_name))) {
zend_error_noreturn(E_COMPILE_ERROR, "Must not use parent::$%s::%s() in a different property ($%s)",
ZSTR_VAL(property_name), ZSTR_VAL(hook_name), unmangled_prop_name);
@@ -5740,6 +5717,26 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
}
/* }}} */
+static void zend_compile_void_cast(znode *result, zend_ast *ast)
+{
+ zend_ast *expr_ast = ast->child[0];
+ znode expr_node;
+ zend_op *opline;
+
+ zend_compile_expr(&expr_node, expr_ast);
+
+ switch (expr_node.op_type) {
+ case IS_TMP_VAR:
+ case IS_VAR:
+ opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
+ opline->extended_value = ZEND_FREE_VOID_CAST;
+ break;
+ case IS_CONST:
+ zend_do_free(&expr_node);
+ break;
+ }
+}
+
static void zend_compile_echo(zend_ast *ast) /* {{{ */
{
zend_op *opline;
@@ -5990,7 +5987,7 @@ static void zend_compile_do_while(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_compile_expr_list(znode *result, zend_ast *ast) /* {{{ */
+static void zend_compile_for_expr_list(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast_list *list;
uint32_t i;
@@ -6007,7 +6004,13 @@ static void zend_compile_expr_list(znode *result, zend_ast *ast) /* {{{ */
zend_ast *expr_ast = list->child[i];
zend_do_free(result);
- zend_compile_expr(result, expr_ast);
+ if (expr_ast->kind == ZEND_AST_CAST_VOID) {
+ zend_compile_void_cast(NULL, expr_ast);
+ result->op_type = IS_CONST;
+ ZVAL_NULL(&result->u.constant);
+ } else {
+ zend_compile_expr(result, expr_ast);
+ }
}
}
/* }}} */
@@ -6022,7 +6025,7 @@ static void zend_compile_for(zend_ast *ast) /* {{{ */
znode result;
uint32_t opnum_start, opnum_jmp, opnum_loop;
- zend_compile_expr_list(&result, init_ast);
+ zend_compile_for_expr_list(&result, init_ast);
zend_do_free(&result);
opnum_jmp = zend_emit_jump(0);
@@ -6033,11 +6036,11 @@ static void zend_compile_for(zend_ast *ast) /* {{{ */
zend_compile_stmt(stmt_ast);
opnum_loop = get_next_op_number();
- zend_compile_expr_list(&result, loop_ast);
+ zend_compile_for_expr_list(&result, loop_ast);
zend_do_free(&result);
zend_update_jump_target_to_next(opnum_jmp);
- zend_compile_expr_list(&result, cond_ast);
+ zend_compile_for_expr_list(&result, cond_ast);
zend_do_extended_stmt();
zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start);
@@ -6968,14 +6971,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0);
} else {
- zend_string *class_name = zend_ast_get_str(ast);
- uint8_t type_code = zend_lookup_builtin_type_by_name(class_name);
+ zend_string *type_name = zend_ast_get_str(ast);
+ uint8_t type_code = zend_lookup_builtin_type_by_name(type_name);
if (type_code != 0) {
if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) {
zend_error_noreturn(E_COMPILE_ERROR,
"Type declaration '%s' must be unqualified",
- ZSTR_VAL(zend_string_tolower(class_name)));
+ ZSTR_VAL(zend_string_tolower(type_name)));
}
/* Transform iterable into a type union alias */
@@ -6989,43 +6992,60 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0);
} else {
const char *correct_name;
- zend_string *orig_name = zend_ast_get_str(ast);
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
+ zend_string *class_name = type_name;
+
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
class_name = zend_resolve_class_name_ast(ast);
zend_assert_valid_class_name(class_name, "a type name");
} else {
+ ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_SELF || fetch_type == ZEND_FETCH_CLASS_PARENT);
+
zend_ensure_valid_class_fetch_type(fetch_type);
+ if (fetch_type == ZEND_FETCH_CLASS_SELF) {
+ /* Scope might be unknown for unbound closures and traits */
+ if (zend_is_scope_known()) {
+ class_name = CG(active_class_entry)->name;
+ ZEND_ASSERT(class_name && "must know class name when resolving self type at compile time");
+ }
+ } else {
+ ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_PARENT);
+ /* Scope might be unknown for unbound closures and traits */
+ if (zend_is_scope_known()) {
+ class_name = CG(active_class_entry)->parent_name;
+ ZEND_ASSERT(class_name && "must know class name when resolving parent type at compile time");
+ }
+ }
zend_string_addref(class_name);
}
if (ast->attr == ZEND_NAME_NOT_FQ
- && zend_is_confusable_type(orig_name, &correct_name)
- && zend_is_not_imported(orig_name)) {
+ && zend_is_confusable_type(type_name, &correct_name)
+ && zend_is_not_imported(type_name)) {
const char *extra =
FC(current_namespace) ? " or import the class with \"use\"" : "";
if (correct_name) {
zend_error(E_COMPILE_WARNING,
"\"%s\" will be interpreted as a class name. Did you mean \"%s\"? "
"Write \"\\%s\"%s to suppress this warning",
- ZSTR_VAL(orig_name), correct_name, ZSTR_VAL(class_name), extra);
+ ZSTR_VAL(type_name), correct_name, ZSTR_VAL(class_name), extra);
} else {
zend_error(E_COMPILE_WARNING,
"\"%s\" is not a supported builtin type "
"and will be interpreted as a class name. "
"Write \"\\%s\"%s to suppress this warning",
- ZSTR_VAL(orig_name), ZSTR_VAL(class_name), extra);
+ ZSTR_VAL(type_name), ZSTR_VAL(class_name), extra);
}
}
class_name = zend_new_interned_string(class_name);
zend_alloc_ce_cache(class_name);
- return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0);
+ return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, 0);
}
}
}
-static void zend_are_intersection_types_redundant(zend_type left_type, zend_type right_type)
+static void zend_are_intersection_types_redundant(const zend_type left_type, const zend_type right_type)
{
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(left_type));
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(right_type));
@@ -7044,9 +7064,9 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type
}
unsigned int sum = 0;
- zend_type *outer_type;
+ const zend_type *outer_type;
ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type)
- zend_type *inner_type;
+ const zend_type *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++;
@@ -7075,12 +7095,12 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type
}
}
-static void zend_is_intersection_type_redundant_by_single_type(zend_type intersection_type, zend_type single_type)
+static void zend_is_intersection_type_redundant_by_single_type(const zend_type intersection_type, const zend_type single_type)
{
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(intersection_type));
ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(single_type));
- zend_type *single_intersection_type = NULL;
+ const zend_type *single_intersection_type = NULL;
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);
@@ -7092,7 +7112,7 @@ static void zend_is_intersection_type_redundant_by_single_type(zend_type interse
}
/* Used by both intersection and union types prior to transforming the type list to a full zend_type */
-static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list, zend_type type)
+static void zend_is_type_list_redundant_by_single_type(const zend_type_list *type_list, const zend_type type)
{
ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(type));
for (size_t i = 0; i < type_list->num_types - 1; i++) {
@@ -7143,6 +7163,7 @@ static zend_type zend_compile_typename_ex(
/* Switch from single name to name list. */
type_list->num_types = 1;
type_list->types[0] = type;
+ /* Clear MAY_BE_* type flags */
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
}
/* Mark type as list type */
@@ -7189,6 +7210,7 @@ static zend_type zend_compile_typename_ex(
"Type contains both true and false, bool must be used instead");
}
ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
+ /* Clear MAY_BE_* type flags */
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
if (ZEND_TYPE_IS_COMPLEX(single_type)) {
@@ -7201,6 +7223,7 @@ static zend_type zend_compile_typename_ex(
/* Switch from single name to name list. */
type_list->num_types = 1;
type_list->types[0] = type;
+ /* Clear MAY_BE_* type flags */
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
ZEND_TYPE_SET_LIST(type, type_list);
}
@@ -7264,8 +7287,10 @@ static zend_type zend_compile_typename_ex(
zend_string_release_ex(standard_type_str, false);
}
/* Check for "self" and "parent" too */
- if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self")
- || zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) {
+ if (
+ zend_string_equals_ci(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_SELF))
+ || zend_string_equals_ci(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_PARENT))
+ ) {
zend_error_noreturn(E_COMPILE_ERROR,
"Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type)));
}
@@ -7726,12 +7751,6 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
SET_NODE(opline->result, &var_node);
opline->op1.num = i + 1;
- if (type_ast) {
- /* Allocate cache slot to speed-up run-time class resolution */
- opline->extended_value =
- zend_alloc_cache_slots(zend_type_get_num_classes(arg_info->type));
- }
-
uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0)
| (is_promoted ? _ZEND_IS_PROMOTED_BIT : 0);
ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags;
@@ -8075,6 +8094,22 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes");
}
+ if ((fn_flags & ZEND_ACC_ABSTRACT)
+ && !(ce->ce_flags & (ZEND_ACC_EXPLICIT_ABSTRACT_CLASS|ZEND_ACC_TRAIT))) {
+ // Don't say that the class should be declared abstract if it is
+ // anonymous or an enum and can't be abstract
+ if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Anonymous class method %s() must not be abstract",
+ ZSTR_VAL(name));
+ } else if (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_INTERFACE)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "%s method %s::%s() must not be abstract",
+ zend_get_object_type_case(ce, true), ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ } else {
+ zend_error_noreturn(E_COMPILE_ERROR, "Class %s declares abstract method %s() and must therefore be declared abstract",
+ ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ }
+ }
+
if (in_interface) {
if (!(fn_flags & ZEND_ACC_PUBLIC)) {
zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "
@@ -8084,10 +8119,6 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
zend_error_noreturn(E_COMPILE_ERROR, "Interface method "
"%s::%s() must not be final", ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
- if (fn_flags & ZEND_ACC_ABSTRACT) {
- zend_error_noreturn(E_COMPILE_ERROR, "Interface method "
- "%s::%s() must not be abstract", ZSTR_VAL(ce->name), ZSTR_VAL(name));
- }
op_array->fn_flags |= ZEND_ACC_ABSTRACT;
}
@@ -8138,7 +8169,13 @@ static uint32_t zend_add_dynamic_func_def(zend_op_array *def) {
return def_offset;
}
-static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */
+enum func_decl_level {
+ FUNC_DECL_LEVEL_TOPLEVEL,
+ FUNC_DECL_LEVEL_NESTED,
+ 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) /* {{{ */
{
zend_string *unqualified_name, *name, *lcname;
zend_op *opline;
@@ -8208,26 +8245,33 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
}
zend_register_seen_symbol(lcname, ZEND_SYMBOL_FUNCTION);
- if (!toplevel) {
- uint32_t func_ref = zend_add_dynamic_func_def(op_array);
- if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
- opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
- opline->op2.num = func_ref;
- } else {
- opline = get_next_op();
- opline->opcode = ZEND_DECLARE_FUNCTION;
- opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, zend_string_copy(lcname));
- opline->op2.num = func_ref;
+ switch (level) {
+ case FUNC_DECL_LEVEL_NESTED: {
+ uint32_t func_ref = zend_add_dynamic_func_def(op_array);
+ if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
+ opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
+ opline->op2.num = func_ref;
+ } else {
+ opline = get_next_op();
+ opline->opcode = ZEND_DECLARE_FUNCTION;
+ opline->op1_type = IS_CONST;
+ LITERAL_STR(opline->op1, zend_string_copy(lcname));
+ opline->op2.num = func_ref;
+ }
+ break;
}
+ case FUNC_DECL_LEVEL_CONSTEXPR:
+ case FUNC_DECL_LEVEL_TOPLEVEL:
+ /* Nothing to do. */
+ break;
}
return lcname;
}
/* }}} */
static zend_op_array *zend_compile_func_decl_ex(
- znode *result, zend_ast *ast, bool toplevel,
- const zend_property_info *property_info,
+ znode *result, zend_ast *ast, enum func_decl_level level,
+ zend_string *property_info_name,
zend_property_hook_kind hook_kind
) {
zend_ast_decl *decl = (zend_ast_decl *) ast;
@@ -8272,7 +8316,7 @@ static zend_op_array *zend_compile_func_decl_ex(
bool has_body = stmt_ast != NULL;
lcname = zend_begin_method_decl(op_array, decl->name, has_body);
} else {
- lcname = zend_begin_func_decl(result, op_array, decl, toplevel);
+ lcname = zend_begin_func_decl(result, op_array, decl, level);
if (decl->kind == ZEND_AST_ARROW_FUNC) {
find_implicit_binds(&info, params_ast, stmt_ast);
compile_implicit_lexical_binds(&info, result, op_array);
@@ -8311,6 +8355,16 @@ static zend_op_array *zend_compile_func_decl_ex(
if (deprecated_attribute) {
op_array->fn_flags |= ZEND_ACC_DEPRECATED;
}
+
+ zend_attribute *nodiscard_attribute = zend_get_attribute_str(
+ op_array->attributes,
+ "nodiscard",
+ sizeof("nodiscard")-1
+ );
+
+ if (nodiscard_attribute) {
+ op_array->fn_flags |= ZEND_ACC_NODISCARD;
+ }
}
/* Do not leak the class scope into free standing functions, even if they are dynamically
@@ -8320,12 +8374,12 @@ static zend_op_array *zend_compile_func_decl_ex(
CG(active_class_entry) = NULL;
}
- if (toplevel) {
+ if (level == FUNC_DECL_LEVEL_TOPLEVEL) {
op_array->fn_flags |= ZEND_ACC_TOP_LEVEL;
}
zend_oparray_context_begin(&orig_oparray_context, op_array);
- CG(context).active_property_info = property_info;
+ CG(context).active_property_info_name = property_info_name;
CG(context).active_property_hook_kind = hook_kind;
{
@@ -8361,13 +8415,34 @@ static zend_op_array *zend_compile_func_decl_ex(
}
}
+ if (op_array->fn_flags & ZEND_ACC_NODISCARD) {
+ if (is_hook) {
+ zend_error_noreturn(E_COMPILE_ERROR, "#[\\NoDiscard] is not supported for property hooks");
+ }
+
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ 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",
+ CG(active_class_entry) != NULL ? "method" : "function");
+ }
+
+ if (ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_NEVER)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "A never returning %s does not return a value, but #[\\NoDiscard] requires a return value",
+ CG(active_class_entry) != NULL ? "method" : "function");
+ }
+ }
+ }
+
zend_compile_stmt(stmt_ast);
if (is_method) {
CG(zend_lineno) = decl->start_lineno;
zend_check_magic_method_implementation(
CG(active_class_entry), (zend_function *) op_array, lcname, E_COMPILE_ERROR);
- } else if (toplevel) {
+ } else if (level == FUNC_DECL_LEVEL_TOPLEVEL) {
/* Only register the function after a successful compile */
if (UNEXPECTED(zend_hash_add_ptr(CG(function_table), lcname, op_array) == NULL)) {
CG(zend_lineno) = decl->start_lineno;
@@ -8387,7 +8462,7 @@ static zend_op_array *zend_compile_func_decl_ex(
/* Pop the loop variable stack separator */
zend_stack_del_top(&CG(loop_var_stack));
- if (toplevel) {
+ if (level == FUNC_DECL_LEVEL_TOPLEVEL) {
zend_observer_function_declared_notify(op_array, lcname);
}
@@ -8401,9 +8476,9 @@ static zend_op_array *zend_compile_func_decl_ex(
return op_array;
}
-static zend_op_array *zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel)
+static zend_op_array *zend_compile_func_decl(znode *result, zend_ast *ast, enum func_decl_level level)
{
- return zend_compile_func_decl_ex(result, ast, toplevel, /* property_info */ NULL, (zend_property_hook_kind)-1);
+ 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) {
@@ -8542,7 +8617,7 @@ static void zend_compile_property_hooks(
hook->name = zend_strpprintf(0, "$%s::%s", ZSTR_VAL(prop_name), ZSTR_VAL(name));
zend_function *func = (zend_function *) zend_compile_func_decl_ex(
- NULL, (zend_ast *) hook, /* toplevel */ false, prop_info, hook_kind);
+ NULL, (zend_ast *) hook, FUNC_DECL_LEVEL_NESTED, prop_info->name, hook_kind);
func->common.prop_info = prop_info;
@@ -8610,10 +8685,6 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
zend_error_noreturn(E_COMPILE_ERROR, "Property cannot be both final and private");
}
- if ((flags & ZEND_ACC_STATIC) && (flags & ZEND_ACC_PPP_SET_MASK)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Static property may not have asymmetric visibility");
- }
-
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
if (flags & ZEND_ACC_FINAL) {
zend_error_noreturn(E_COMPILE_ERROR, "Property in interface cannot be final");
@@ -8642,12 +8713,8 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
zend_type type = ZEND_TYPE_INIT_NONE(0);
flags |= zend_property_is_virtual(ce, name, hooks_ast, flags) ? ZEND_ACC_VIRTUAL : 0;
- /* FIXME: This is a dirty fix to maintain ABI compatibility. We don't
- * have an actual property info yet, but we really only need the name
- * anyway. We should convert this to a zend_string. */
- const zend_property_info *old_active_property_info = CG(context).active_property_info;
- zend_property_info dummy_prop_info = { .name = name };
- CG(context).active_property_info = &dummy_prop_info;
+ zend_string *old_active_property_info_name = CG(context).active_property_info_name;
+ CG(context).active_property_info_name = name;
if (!hooks_ast) {
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
@@ -8742,7 +8809,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);
}
- CG(context).active_property_info = old_active_property_info;
+ CG(context).active_property_info_name = old_active_property_info_name;
}
}
/* }}} */
@@ -9435,8 +9502,16 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t i;
+ zend_ast *attributes_ast = NULL;
+ zend_op *last_op = NULL;
for (i = 0; i < list->children; ++i) {
zend_ast *const_ast = list->child[i];
+ if (const_ast->kind == ZEND_AST_ATTRIBUTE_LIST) {
+ ZEND_ASSERT(i == list->children - 1);
+ attributes_ast = const_ast;
+ continue;
+ }
+ ZEND_ASSERT(const_ast->kind == ZEND_AST_CONST_ELEM);
zend_ast *name_ast = const_ast->child[0];
zend_ast **value_ast_ptr = &const_ast->child[1];
zend_string *unqualified_name = zend_ast_get_str(name_ast);
@@ -9467,10 +9542,33 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
name_node.op_type = IS_CONST;
ZVAL_STR(&name_node.u.constant, name);
- zend_emit_op(NULL, ZEND_DECLARE_CONST, &name_node, &value_node);
+ last_op = zend_emit_op(NULL, ZEND_DECLARE_CONST, &name_node, &value_node);
zend_register_seen_symbol(name, ZEND_SYMBOL_CONST);
}
+ if (attributes_ast == NULL) {
+ return;
+ }
+ /* Validate: attributes can only be applied to one constant at a time
+ * Since we store the AST for the attributes in the list of children,
+ * there should be exactly 2 children. */
+ if (list->children > 2) {
+ zend_error_noreturn(
+ E_COMPILE_ERROR,
+ "Cannot apply attributes to multiple constants at once"
+ );
+ }
+
+ HashTable *attributes = NULL;
+ zend_compile_attributes(&attributes, list->child[1], 0, ZEND_ATTRIBUTE_TARGET_CONST, 0);
+
+ ZEND_ASSERT(last_op != NULL);
+ last_op->opcode = ZEND_DECLARE_ATTRIBUTED_CONST;
+ znode attribs_node;
+ attribs_node.op_type = IS_CONST;
+ ZVAL_PTR(&attribs_node.u.constant, attributes);
+ zend_emit_op_data(&attribs_node);
+ CG(active_op_array)->fn_flags |= ZEND_ACC_PTR_OPS;
}
/* }}}*/
@@ -9602,9 +9700,9 @@ static bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
}
break;
case T_PROPERTY_C: {
- const zend_property_info *prop_info = CG(context).active_property_info;
- if (prop_info) {
- ZVAL_STR(zv, zend_copy_unmangled_prop_name(prop_info->name));
+ zend_string *prop_info_name = CG(context).active_property_info_name;
+ if (prop_info_name) {
+ ZVAL_STR(zv, zend_copy_unmangled_prop_name(prop_info_name));
} else {
ZVAL_EMPTY_STRING(zv);
}
@@ -11060,6 +11158,7 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
|| kind == ZEND_AST_AND || kind == ZEND_AST_OR
|| kind == ZEND_AST_UNARY_OP
|| kind == ZEND_AST_UNARY_PLUS || kind == ZEND_AST_UNARY_MINUS
+ || kind == ZEND_AST_CAST
|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM
|| kind == ZEND_AST_UNPACK
@@ -11069,7 +11168,9 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
|| kind == ZEND_AST_CONST_ENUM_INIT
|| kind == ZEND_AST_NEW || kind == ZEND_AST_ARG_LIST
|| kind == ZEND_AST_NAMED_ARG
- || kind == ZEND_AST_PROP || kind == ZEND_AST_NULLSAFE_PROP;
+ || kind == ZEND_AST_PROP || kind == ZEND_AST_NULLSAFE_PROP
+ || kind == ZEND_AST_CLOSURE
+ || kind == ZEND_AST_CALL || kind == ZEND_AST_STATIC_CALL || kind == ZEND_AST_CALLABLE_CONVERT;
}
/* }}} */
@@ -11178,9 +11279,8 @@ static void zend_compile_const_expr_magic_const(zend_ast **ast_ptr) /* {{{ */
}
/* }}} */
-static void zend_compile_const_expr_new(zend_ast **ast_ptr)
+static void zend_compile_const_expr_class_reference(zend_ast *class_ast)
{
- zend_ast *class_ast = (*ast_ptr)->child[0];
if (class_ast->kind == ZEND_AST_CLASS) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use anonymous class in constant expression");
@@ -11203,6 +11303,84 @@ static void zend_compile_const_expr_new(zend_ast **ast_ptr)
class_ast->attr = fetch_type << ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT;
}
+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);
+}
+
+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];
+ if (!(closure_ast->flags & ZEND_ACC_STATIC)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Closures in constant expressions must be static");
+ }
+ if (uses_ast) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use(...) variables in constant expression");
+ }
+
+ znode node;
+ zend_op_array *op = zend_compile_func_decl(&node, (zend_ast*)closure_ast, FUNC_DECL_LEVEL_CONSTEXPR);
+
+ zend_ast_destroy(*ast_ptr);
+ *ast_ptr = zend_ast_create_op_array(op);
+}
+
+static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
+{
+ zend_ast **args_ast;
+ switch ((*ast_ptr)->kind) {
+ case ZEND_AST_CALL:
+ args_ast = &(*ast_ptr)->child[1];
+ break;
+ case ZEND_AST_STATIC_CALL:
+ args_ast = &(*ast_ptr)->child[2];
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+ 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);
+
+ switch ((*ast_ptr)->kind) {
+ case ZEND_AST_CALL: {
+ zend_ast *name_ast = (*ast_ptr)->child[0];
+ if (name_ast->kind != ZEND_AST_ZVAL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use dynamic function name in constant expression");
+ }
+ zval *name_ast_zv = zend_ast_get_zval(name_ast);
+ if (Z_TYPE_P(name_ast_zv) != IS_STRING) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Illegal function name");
+ }
+ bool is_fully_qualified;
+ zend_string *name = zend_resolve_function_name(Z_STR_P(name_ast_zv), name_ast->attr, &is_fully_qualified);
+ zval_ptr_dtor_nogc(name_ast_zv);
+ ZVAL_STR(name_ast_zv, name);
+ if (is_fully_qualified) {
+ name_ast->attr = ZEND_NAME_FQ;
+ }
+ break;
+ }
+ case ZEND_AST_STATIC_CALL: {
+ zend_ast *class_ast = (*ast_ptr)->child[0];
+ zend_compile_const_expr_class_reference(class_ast);
+ zend_ast *method_ast = (*ast_ptr)->child[1];
+ if (method_ast->kind != ZEND_AST_ZVAL) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use dynamic method name in constant expression");
+ }
+ if (Z_TYPE_P(zend_ast_get_zval(method_ast)) != IS_STRING) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Illegal method name");
+ }
+ break;
+ }
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+}
+
static void zend_compile_const_expr_args(zend_ast **ast_ptr)
{
zend_ast_list *list = zend_ast_get_list(*ast_ptr);
@@ -11255,6 +11433,12 @@ static void zend_compile_const_expr(zend_ast **ast_ptr, void *context) /* {{{ */
case ZEND_AST_MAGIC_CONST:
zend_compile_const_expr_magic_const(ast_ptr);
break;
+ case ZEND_AST_CAST:
+ if (ast->attr == IS_OBJECT && !ctx->allow_dynamic) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Object casts are not supported in this context");
+ }
+ break;
case ZEND_AST_NEW:
if (!ctx->allow_dynamic) {
zend_error_noreturn(E_COMPILE_ERROR,
@@ -11265,6 +11449,14 @@ static void zend_compile_const_expr(zend_ast **ast_ptr, void *context) /* {{{ */
case ZEND_AST_ARG_LIST:
zend_compile_const_expr_args(ast_ptr);
break;
+ case ZEND_AST_CLOSURE:
+ zend_compile_const_expr_closure(ast_ptr);
+ /* Return, because we do not want to traverse the children. */
+ return;
+ case ZEND_AST_CALL:
+ case ZEND_AST_STATIC_CALL:
+ zend_compile_const_expr_fcc(ast_ptr);
+ break;
}
zend_ast_apply(ast, zend_compile_const_expr, context);
@@ -11307,7 +11499,7 @@ void zend_compile_top_stmt(zend_ast *ast) /* {{{ */
if (ast->kind == ZEND_AST_FUNC_DECL) {
CG(zend_lineno) = ast->lineno;
- zend_compile_func_decl(NULL, ast, 1);
+ zend_compile_func_decl(NULL, ast, FUNC_DECL_LEVEL_TOPLEVEL);
CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
} else if (ast->kind == ZEND_AST_CLASS) {
CG(zend_lineno) = ast->lineno;
@@ -11389,7 +11581,7 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
break;
case ZEND_AST_FUNC_DECL:
case ZEND_AST_METHOD:
- zend_compile_func_decl(NULL, ast, 0);
+ zend_compile_func_decl(NULL, ast, FUNC_DECL_LEVEL_NESTED);
break;
case ZEND_AST_ENUM_CASE:
zend_compile_enum_case(ast);
@@ -11424,6 +11616,9 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
case ZEND_AST_THROW:
zend_compile_expr(NULL, ast);
break;
+ case ZEND_AST_CAST_VOID:
+ zend_compile_void_cast(NULL, ast);
+ break;
default:
{
znode result;
@@ -11566,7 +11761,7 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
return;
case ZEND_AST_CLOSURE:
case ZEND_AST_ARROW_FUNC:
- zend_compile_func_decl(result, ast, 0);
+ zend_compile_func_decl(result, ast, FUNC_DECL_LEVEL_NESTED);
return;
case ZEND_AST_THROW:
zend_compile_throw(result, ast);
@@ -11678,6 +11873,34 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
}
/* }}} */
+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));
+ return true;
+ case IS_LONG:
+ ZVAL_LONG(result, zval_get_long(op1));
+ return true;
+ case IS_DOUBLE:
+ ZVAL_DOUBLE(result, zval_get_double(op1));
+ return true;
+ case IS_STRING:
+ /* Conversion from double to string takes into account run-time
+ 'precision' setting and cannot be evaluated at compile-time */
+ if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
+ ZVAL_STR(result, zval_get_string(op1));
+ return true;
+ }
+ break;
+ case IS_ARRAY:
+ ZVAL_COPY(result, op1);
+ convert_to_array(result);
+ return true;
+ }
+ return false;
+}
+
static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
{
zend_ast *ast = *ast_ptr;
@@ -11963,6 +12186,13 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
zend_eval_const_expr(&ast->child[0]);
zend_eval_const_expr(&ast->child[1]);
return;
+ case ZEND_AST_CAST:
+ zend_eval_const_expr(&ast->child[0]);
+ if (ast->child[0]->kind == ZEND_AST_ZVAL
+ && zend_try_ct_eval_cast(&result, ast->attr, zend_ast_get_zval(ast->child[0]))) {
+ break;
+ }
+ return;
default:
return;
}
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index b2e7de4c343ae..62d0fbcded2ee 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -204,7 +204,7 @@ typedef struct _zend_oparray_context {
int last_brk_cont;
zend_brk_cont_element *brk_cont_array;
HashTable *labels;
- const zend_property_info *active_property_info;
+ zend_string *active_property_info_name;
zend_property_hook_kind active_property_hook_kind;
bool in_jmp_frameless_branch;
} zend_oparray_context;
@@ -333,7 +333,7 @@ typedef struct _zend_oparray_context {
/* Class cannot be serialized or unserialized | | | */
#define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */
/* | | | */
-/* Function Flags (unused: 29-30) | | | */
+/* Function Flags (unused: 30) | | | */
/* ============== | | | */
/* | | | */
/* deprecation flag | | | */
@@ -395,6 +395,14 @@ typedef struct _zend_oparray_context {
/* has #[\Override] attribute | | | */
#define ZEND_ACC_OVERRIDE (1 << 28) /* | X | | */
/* | | | */
+/* Has IS_PTR operands that needs special cleaning; same | | | */
+/* value as ZEND_ACC_OVERRIDE but override is for class | | | */
+/* methods and this is for the top level op array | | | */
+#define ZEND_ACC_PTR_OPS (1 << 28) /* | X | | */
+/* | | | */
+/* has #[\NoDiscard] attribute | | | */
+#define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */
+/* | | | */
/* op_array uses strict mode types | | | */
#define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */
@@ -983,7 +991,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);
+ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, 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);
@@ -1094,6 +1102,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define ZEND_FREE_ON_RETURN (1<<0)
#define ZEND_FREE_SWITCH (1<<1)
+#define ZEND_FREE_VOID_CAST (1<<2)
#define ZEND_SEND_BY_VAL 0u
#define ZEND_SEND_BY_REF 1u
@@ -1297,4 +1306,6 @@ ZEND_API bool zend_is_op_long_compatible(const zval *op);
ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, const zval *op1, const zval *op2);
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);
+
#endif /* ZEND_COMPILE_H */
diff --git a/Zend/zend_config.w32.h b/Zend/zend_config.w32.h
index ffd5f1736ee4a..a44a8b9f70d24 100644
--- a/Zend/zend_config.w32.h
+++ b/Zend/zend_config.w32.h
@@ -36,11 +36,9 @@
#include
#include
+#include
#include
-#if _MSC_VER < 1900
-#define snprintf _snprintf
-#endif
#define strcasecmp(s1, s2) _stricmp(s1, s2)
#define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index 2145cb1915354..28a4b7b048cf0 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -18,6 +18,7 @@
*/
#include "zend.h"
+#include "zend_attributes.h"
#include "zend_constants.h"
#include "zend_exceptions.h"
#include "zend_execute.h"
@@ -46,12 +47,24 @@ void free_zend_constant(zval *zv)
if (c->name) {
zend_string_release_ex(c->name, 0);
}
+ if (c->filename) {
+ zend_string_release_ex(c->filename, 0);
+ }
+ if (c->attributes) {
+ zend_hash_release(c->attributes);
+ }
efree(c);
} else {
zval_internal_ptr_dtor(&c->value);
if (c->name) {
zend_string_release_ex(c->name, 1);
}
+ if (c->filename) {
+ zend_string_release_ex(c->filename, 1);
+ }
+ if (c->attributes) {
+ zend_hash_release(c->attributes);
+ }
free(c);
}
}
@@ -68,6 +81,12 @@ static void copy_zend_constant(zval *zv)
c = Z_PTR_P(zv);
c->name = zend_string_copy(c->name);
+ if (c->filename != NULL) {
+ c->filename = zend_string_copy(c->filename);
+ }
+ if (c->attributes != NULL) {
+ c->attributes = zend_array_dup(c->attributes);
+ }
if (Z_TYPE(c->value) == IS_STRING) {
Z_STR(c->value) = zend_string_dup(Z_STR(c->value), 1);
}
@@ -303,13 +322,13 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
if (!ce) {
ce = zend_fetch_class(class_name, flags);
}
- } else if (zend_string_equals_literal_ci(class_name, "self")) {
+ } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_SELF))) {
if (UNEXPECTED(!scope)) {
zend_throw_error(NULL, "Cannot access \"self\" when no class scope is active");
goto failure;
}
ce = scope;
- } else if (zend_string_equals_literal_ci(class_name, "parent")) {
+ } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_PARENT))) {
if (UNEXPECTED(!scope)) {
zend_throw_error(NULL, "Cannot access \"parent\" when no class scope is active");
goto failure;
@@ -458,7 +477,11 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
}
if (!(flags & ZEND_FETCH_CLASS_SILENT) && (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)) {
- zend_error(E_DEPRECATED, "Constant %s is deprecated", name);
+ if (!CONST_IS_RECURSIVE(c)) {
+ CONST_PROTECT_RECURSION(c);
+ zend_deprecated_constant(c, c->name);
+ CONST_UNPROTECT_RECURSION(c);
+ }
}
return &c->value;
}
@@ -497,6 +520,16 @@ ZEND_API zend_result zend_register_constant(zend_constant *c)
name = c->name;
}
+ c->filename = NULL;
+ if (ZEND_CONSTANT_MODULE_NUMBER(c) == PHP_USER_CONSTANT) {
+ zend_string *filename = zend_get_executed_filename_ex();
+ if (filename) {
+ c->filename = zend_string_copy(filename);
+ }
+ }
+
+ c->attributes = NULL;
+
/* Check if the user is trying to define any special constant */
if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__")
|| (!persistent && zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name)))
@@ -504,6 +537,10 @@ ZEND_API zend_result zend_register_constant(zend_constant *c)
) {
zend_error(E_WARNING, "Constant %s already defined", ZSTR_VAL(name));
zend_string_release(c->name);
+ if (c->filename) {
+ zend_string_release(c->filename);
+ c->filename = NULL;
+ }
if (!persistent) {
zval_ptr_dtor_nogc(&c->value);
}
@@ -514,3 +551,22 @@ ZEND_API zend_result zend_register_constant(zend_constant *c)
}
return ret;
}
+
+void zend_constant_add_attributes(zend_constant *c, HashTable *attributes) {
+ GC_TRY_ADDREF(attributes);
+ c->attributes = attributes;
+
+ zend_attribute *deprecated_attribute = zend_get_attribute_str(
+ c->attributes,
+ "deprecated",
+ strlen("deprecated")
+ );
+
+ if (deprecated_attribute) {
+ ZEND_CONSTANT_SET_FLAGS(
+ c,
+ ZEND_CONSTANT_FLAGS(c) | CONST_DEPRECATED,
+ ZEND_CONSTANT_MODULE_NUMBER(c)
+ );
+ }
+}
diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h
index bd759c2891500..3912215d80775 100644
--- a/Zend/zend_constants.h
+++ b/Zend/zend_constants.h
@@ -44,6 +44,8 @@
typedef struct _zend_constant {
zval value;
zend_string *name;
+ zend_string *filename;
+ HashTable *attributes;
} zend_constant;
#define ZEND_CONSTANT_FLAGS(c) \
@@ -96,6 +98,7 @@ ZEND_API void zend_register_double_constant(const char *name, size_t name_len, d
ZEND_API void zend_register_string_constant(const char *name, size_t name_len, const char *strval, int flags, int module_number);
ZEND_API void zend_register_stringl_constant(const char *name, size_t name_len, const char *strval, size_t strlen, int flags, int module_number);
ZEND_API zend_result zend_register_constant(zend_constant *c);
+void zend_constant_add_attributes(zend_constant *c, HashTable *attributes);
#ifdef ZTS
void zend_copy_constants(HashTable *target, HashTable *source);
#endif
diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c
index ccafca48fe9b8..6baa34cc50125 100644
--- a/Zend/zend_enum.c
+++ b/Zend/zend_enum.c
@@ -610,8 +610,7 @@ ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zv
zend_string_release(name_str);
}
-ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name) {
- zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
+static zend_object *zend_enum_case_from_class_constant(zend_class_constant *c) {
ZEND_ASSERT(c && "Must be a valid enum case");
ZEND_ASSERT(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE);
@@ -624,9 +623,12 @@ ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name
return Z_OBJ(c->value);
}
+ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name) {
+ zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
+ return zend_enum_case_from_class_constant(c);
+}
+
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name) {
- zend_string *name_str = zend_string_init(name, strlen(name), 0);
- zend_object *result = zend_enum_get_case(ce, name_str);
- zend_string_release(name_str);
- return result;
+ zend_class_constant *c = zend_hash_str_find_ptr(CE_CONSTANTS_TABLE(ce), name, strlen(name));
+ return zend_enum_case_from_class_constant(c);
}
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index 207b781385452..f9d0ae8ea8173 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -528,7 +528,7 @@ static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */
}
/* }}} */
-static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /* {{{ */
+static void _build_trace_string(smart_str *str, const HashTable *ht, uint32_t num) /* {{{ */
{
zval *file, *tmp;
@@ -538,14 +538,14 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
file = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_FILE));
if (file) {
- if (Z_TYPE_P(file) != IS_STRING) {
+ if (UNEXPECTED(Z_TYPE_P(file) != IS_STRING)) {
zend_error(E_WARNING, "File name is not a string");
smart_str_appends(str, "[unknown file]: ");
} else{
zend_long line = 0;
tmp = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_LINE));
if (tmp) {
- if (Z_TYPE_P(tmp) == IS_LONG) {
+ if (EXPECTED(Z_TYPE_P(tmp) == IS_LONG)) {
line = Z_LVAL_P(tmp);
} else {
zend_error(E_WARNING, "Line is not an int");
@@ -565,7 +565,7 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
smart_str_appendc(str, '(');
tmp = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_ARGS));
if (tmp) {
- if (Z_TYPE_P(tmp) == IS_ARRAY) {
+ if (EXPECTED(Z_TYPE_P(tmp) == IS_ARRAY)) {
size_t last_len = ZSTR_LEN(str->s);
zend_string *name;
zval *arg;
@@ -589,14 +589,14 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
}
/* }}} */
-ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main) {
+ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_main) {
zend_ulong index;
zval *frame;
uint32_t num = 0;
smart_str str = {0};
ZEND_HASH_FOREACH_NUM_KEY_VAL(trace, index, frame) {
- if (Z_TYPE_P(frame) != IS_ARRAY) {
+ if (UNEXPECTED(Z_TYPE_P(frame) != IS_ARRAY)) {
zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
continue;
}
@@ -623,7 +623,7 @@ ZEND_METHOD(Exception, getTraceAsString)
zval *object = ZEND_THIS;
zend_class_entry *base_ce = i_get_exception_base(Z_OBJ_P(object));
zval rv;
- zval *trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
+ const zval *trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
if (EG(exception)) {
RETURN_THROWS();
}
@@ -858,14 +858,13 @@ ZEND_API ZEND_COLD zend_object *zend_throw_exception(zend_class_entry *exception
ZEND_API ZEND_COLD zend_object *zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format, ...) /* {{{ */
{
va_list arg;
- char *message;
zend_object *obj;
va_start(arg, format);
- zend_vspprintf(&message, 0, format, arg);
+ zend_string *msg_str = zend_vstrpprintf(0, format, arg);
va_end(arg);
- obj = zend_throw_exception(exception_ce, message, code);
- efree(message);
+ obj = zend_throw_exception_zstr(exception_ce, msg_str, code);
+ zend_string_release(msg_str);
return obj;
}
/* }}} */
@@ -904,6 +903,10 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit
ZVAL_OBJ(&exception, ex);
ce_exception = ex->ce;
EG(exception) = NULL;
+
+ zval_ptr_dtor(&EG(last_fatal_error_backtrace));
+ ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
+
if (ce_exception == zend_ce_parse_error || ce_exception == zend_ce_compile_error) {
zend_string *message = zval_get_string(GET_PROPERTY(&exception, ZEND_STR_MESSAGE));
zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE));
diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h
index b80c17d8f38f2..d0138021d1ea3 100644
--- a/Zend/zend_exceptions.h
+++ b/Zend/zend_exceptions.h
@@ -72,7 +72,7 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex);
/* show an exception using zend_error(severity,...), severity should be E_ERROR */
ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity);
ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2);
-ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main);
+ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_main);
ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void);
ZEND_API ZEND_COLD zend_object *zend_create_graceful_exit(void);
diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h
index 0519b61e3d978..cef37a1f0f0b9 100644
--- a/Zend/zend_exceptions_arginfo.h
+++ b/Zend/zend_exceptions_arginfo.h
@@ -163,46 +163,32 @@ static zend_class_entry *register_class_Exception(zend_class_entry *class_entry_
zval property_message_default_value;
ZVAL_EMPTY_STRING(&property_message_default_value);
- zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1);
- zend_declare_typed_property(class_entry, property_message_name, &property_message_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
- zend_string_release(property_message_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
zval property_string_default_value;
ZVAL_EMPTY_STRING(&property_string_default_value);
- zend_string *property_string_name = zend_string_init("string", sizeof("string") - 1, 1);
- zend_declare_typed_property(class_entry, property_string_name, &property_string_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_string_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_STRING), &property_string_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_code_default_value;
ZVAL_LONG(&property_code_default_value, 0);
- zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1);
- zend_declare_typed_property(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
- zend_string_release(property_code_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CODE), &property_code_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
zval property_file_default_value;
ZVAL_EMPTY_STRING(&property_file_default_value);
- zend_string *property_file_name = zend_string_init("file", sizeof("file") - 1, 1);
- zend_declare_typed_property(class_entry, property_file_name, &property_file_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_file_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_FILE), &property_file_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_line_default_value;
ZVAL_LONG(&property_line_default_value, 0);
- zend_string *property_line_name = zend_string_init("line", sizeof("line") - 1, 1);
- zend_declare_typed_property(class_entry, property_line_name, &property_line_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(property_line_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_LINE), &property_line_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
zval property_trace_default_value;
ZVAL_EMPTY_ARRAY(&property_trace_default_value);
- zend_string *property_trace_name = zend_string_init("trace", sizeof("trace") - 1, 1);
- zend_declare_typed_property(class_entry, property_trace_name, &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
- zend_string_release(property_trace_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_TRACE), &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
zval property_previous_default_value;
ZVAL_NULL(&property_previous_default_value);
- zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1);
zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1);
- zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL));
- zend_string_release(property_previous_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL));
return class_entry;
}
@@ -216,9 +202,7 @@ static zend_class_entry *register_class_ErrorException(zend_class_entry *class_e
zval property_severity_default_value;
ZVAL_LONG(&property_severity_default_value, E_ERROR);
- zend_string *property_severity_name = zend_string_init("severity", sizeof("severity") - 1, 1);
- zend_declare_typed_property(class_entry, property_severity_name, &property_severity_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(property_severity_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SEVERITY), &property_severity_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
return class_entry;
}
@@ -233,46 +217,32 @@ static zend_class_entry *register_class_Error(zend_class_entry *class_entry_Thro
zval property_message_default_value;
ZVAL_EMPTY_STRING(&property_message_default_value);
- zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1);
- zend_declare_typed_property(class_entry, property_message_name, &property_message_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
- zend_string_release(property_message_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
zval property_string_default_value;
ZVAL_EMPTY_STRING(&property_string_default_value);
- zend_string *property_string_name = zend_string_init("string", sizeof("string") - 1, 1);
- zend_declare_typed_property(class_entry, property_string_name, &property_string_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_string_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_STRING), &property_string_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_code_default_value;
ZVAL_LONG(&property_code_default_value, 0);
- zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1);
- zend_declare_typed_property(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
- zend_string_release(property_code_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CODE), &property_code_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
zval property_file_default_value;
ZVAL_EMPTY_STRING(&property_file_default_value);
- zend_string *property_file_name = zend_string_init("file", sizeof("file") - 1, 1);
- zend_declare_typed_property(class_entry, property_file_name, &property_file_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_file_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_FILE), &property_file_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_line_default_value;
ZVAL_UNDEF(&property_line_default_value);
- zend_string *property_line_name = zend_string_init("line", sizeof("line") - 1, 1);
- zend_declare_typed_property(class_entry, property_line_name, &property_line_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
- zend_string_release(property_line_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_LINE), &property_line_default_value, ZEND_ACC_PROTECTED, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
zval property_trace_default_value;
ZVAL_EMPTY_ARRAY(&property_trace_default_value);
- zend_string *property_trace_name = zend_string_init("trace", sizeof("trace") - 1, 1);
- zend_declare_typed_property(class_entry, property_trace_name, &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
- zend_string_release(property_trace_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_TRACE), &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
zval property_previous_default_value;
ZVAL_NULL(&property_previous_default_value);
- zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1);
zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1);
- zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL));
- zend_string_release(property_previous_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 0, MAY_BE_NULL));
return class_entry;
}
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index f5399ad28dcb2..0fbfdfa07ef04 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -609,7 +609,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg
zend_string_release(func_name);
}
-static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop) {
+static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(const zend_property_info *prop) {
zend_string *type_str = zend_type_to_string(prop->type);
zend_type_error(
"Cannot auto-initialize an array inside property %s::$%s of type %s",
@@ -619,7 +619,7 @@ static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_
zend_string_release(type_str);
}
-static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop) {
+static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(const zend_property_info *prop) {
zend_string *type_str = zend_type_to_string(prop->type);
zend_type_error(
"Cannot auto-initialize an array inside a reference held by property %s::$%s of type %s",
@@ -630,7 +630,7 @@ static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_p
}
static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error(
- zend_property_info *prop) {
+ const zend_property_info *prop) {
zend_throw_error(NULL,
"Cannot access uninitialized non-nullable property %s::$%s by reference",
ZSTR_VAL(prop->ce->name),
@@ -638,7 +638,7 @@ static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_err
}
/* this should modify object only if it's empty */
-static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_error(zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC)
+static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_error(const zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC)
{
zend_string *tmp_property_name;
zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name);
@@ -673,7 +673,7 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_erro
}
static ZEND_COLD void zend_verify_type_error_common(
- const zend_function *zf, const zend_arg_info *arg_info, zval *value,
+ const zend_function *zf, const zend_arg_info *arg_info, const zval *value,
const char **fname, const char **fsep, const char **fclass,
zend_string **need_msg, const char **given_kind)
{
@@ -696,9 +696,9 @@ static ZEND_COLD void zend_verify_type_error_common(
}
ZEND_API ZEND_COLD void zend_verify_arg_error(
- const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value)
+ const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, const zval *value)
{
- zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;
+ const zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;
const char *fname, *fsep, *fclass;
zend_string *need_msg;
const char *given_msg;
@@ -874,10 +874,12 @@ ZEND_COLD zend_never_inline void zend_magic_get_property_type_inconsistency_erro
ZEND_COLD void zend_match_unhandled_error(const zval *value)
{
+ zend_long max_len = EG(exception_string_param_max_len);
smart_str msg = {0};
if (
EG(exception_ignore_args)
- || smart_str_append_zval(&msg, value, EG(exception_string_param_max_len)) != SUCCESS
+ || (Z_TYPE_P(value) == IS_STRING && max_len == 0)
+ || smart_str_append_zval(&msg, value, max_len) != SUCCESS
) {
smart_str_appendl(&msg, "of type ", sizeof("of type ")-1);
smart_str_appends(&msg, zend_zval_type_name(value));
@@ -907,7 +909,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_indirect_modificati
ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name));
}
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(uint8_t type)
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(const uint8_t type)
{
zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(type));
}
@@ -949,9 +951,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modifi
}
static const zend_class_entry *resolve_single_class_type(zend_string *name, const zend_class_entry *self_ce) {
- if (zend_string_equals_literal_ci(name, "self")) {
+ if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_SELF))) {
return self_ce;
- } else if (zend_string_equals_literal_ci(name, "parent")) {
+ } else if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_PARENT))) {
return self_ce->parent;
} else {
return zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
@@ -973,9 +975,9 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type(
}
static bool zend_check_intersection_for_property_or_class_constant_class_type(
- const zend_class_entry *scope, zend_type_list *intersection_type_list, const zend_class_entry *value_ce)
+ const zend_class_entry *scope, const zend_type_list *intersection_type_list, const zend_class_entry *value_ce)
{
- zend_type *list_type;
+ const zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
@@ -988,13 +990,13 @@ static bool zend_check_intersection_for_property_or_class_constant_class_type(
}
static bool zend_check_and_resolve_property_or_class_constant_class_type(
- const zend_class_entry *scope, zend_type member_type, const zend_class_entry *value_ce) {
+ const zend_class_entry *scope, const zend_type member_type, const zend_class_entry *value_ce) {
if (ZEND_TYPE_HAS_LIST(member_type)) {
- zend_type *list_type;
if (ZEND_TYPE_IS_INTERSECTION(member_type)) {
return zend_check_intersection_for_property_or_class_constant_class_type(
scope, ZEND_TYPE_LIST(member_type), value_ce);
} else {
+ const zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(member_type), list_type) {
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
if (zend_check_intersection_for_property_or_class_constant_class_type(
@@ -1057,7 +1059,7 @@ ZEND_API bool zend_never_inline zend_verify_property_type(const zend_property_in
return i_zend_verify_property_type(info, property, strict);
}
-static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC)
+static zend_never_inline zval* zend_assign_to_typed_prop(const zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC)
{
zval tmp;
@@ -1085,7 +1087,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
return zend_assign_to_variable_ex(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES(), garbage_ptr);
}
-static zend_always_inline bool zend_value_instanceof_static(zval *zv) {
+static zend_always_inline bool zend_value_instanceof_static(const zval *zv) {
if (Z_TYPE_P(zv) != IS_OBJECT) {
return 0;
}
@@ -1097,23 +1099,9 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) {
return instanceof_function(Z_OBJCE_P(zv), called_scope);
}
-/* The cache_slot may only be NULL in debug builds, where arginfo verification of
- * internal functions is enabled. Avoid unnecessary checks in release builds. */
-#if ZEND_DEBUG
-# define HAVE_CACHE_SLOT (cache_slot != NULL)
-#else
-# define HAVE_CACHE_SLOT 1
-#endif
-
-#define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;}
-
-static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot(
- void **cache_slot, zend_type *type)
+static zend_always_inline zend_class_entry *zend_fetch_ce_from_type(
+ const zend_type *type)
{
- if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) {
- return (zend_class_entry *) *cache_slot;
- }
-
zend_string *name = ZEND_TYPE_NAME(*type);
zend_class_entry *ce;
if (ZSTR_HAS_CE_CACHE(name)) {
@@ -1132,68 +1120,54 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot(
return NULL;
}
}
- if (HAVE_CACHE_SLOT) {
- *cache_slot = (void *) ce;
- }
return ce;
}
-static bool zend_check_intersection_type_from_cache_slot(zend_type_list *intersection_type_list,
- zend_class_entry *arg_ce, void ***cache_slot_ptr)
+static bool zend_check_intersection_type_from_list(
+ const zend_type_list *intersection_type_list,
+ zend_class_entry *arg_ce)
{
- void **cache_slot = *cache_slot_ptr;
zend_class_entry *ce;
- zend_type *list_type;
- bool status = true;
+ const zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
- /* Only check classes if the type might be valid */
- if (status) {
- ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
- /* If type is not an instance of one of the types taking part in the
- * intersection it cannot be a valid instance of the whole intersection type. */
- if (!ce || !instanceof_function(arg_ce, ce)) {
- status = false;
- }
+ ce = zend_fetch_ce_from_type(list_type);
+ /* If type is not an instance of one of the types taking part in the
+ * intersection it cannot be a valid instance of the whole intersection type. */
+ if (!ce || !instanceof_function(arg_ce, ce)) {
+ return false;
}
- PROGRESS_CACHE_SLOT();
} ZEND_TYPE_LIST_FOREACH_END();
- if (HAVE_CACHE_SLOT) {
- *cache_slot_ptr = cache_slot;
- }
- return status;
+ return true;
}
static zend_always_inline bool zend_check_type_slow(
- zend_type *type, zval *arg, zend_reference *ref, void **cache_slot,
+ const zend_type *type, zval *arg, const zend_reference *ref,
bool is_return_type, bool is_internal)
{
- uint32_t type_mask;
if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
zend_class_entry *ce;
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
- zend_type *list_type;
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
- return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot);
+ return zend_check_intersection_type_from_list(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg));
} else {
+ const zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
- if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) {
+ if (zend_check_intersection_type_from_list(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg))) {
return true;
}
- /* The cache_slot is progressed in zend_check_intersection_type_from_cache_slot() */
} else {
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
- ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
+ ce = zend_fetch_ce_from_type(list_type);
/* Instance of a single type part of a union is sufficient to pass the type check */
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
return true;
}
- PROGRESS_CACHE_SLOT();
}
} ZEND_TYPE_LIST_FOREACH_END();
}
} else {
- ce = zend_fetch_ce_from_cache_slot(cache_slot, type);
+ ce = zend_fetch_ce_from_type(type);
/* If we have a CE we check if it satisfies the type constraint,
* otherwise it will check if a standard type satisfies it. */
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
@@ -1202,7 +1176,7 @@ static zend_always_inline bool zend_check_type_slow(
}
}
- type_mask = ZEND_TYPE_FULL_MASK(*type);
+ const uint32_t type_mask = ZEND_TYPE_FULL_MASK(*type);
if ((type_mask & MAY_BE_CALLABLE) &&
zend_is_callable(arg, is_internal ? IS_CALLABLE_SUPPRESS_DEPRECATIONS : 0, NULL)) {
return 1;
@@ -1230,10 +1204,10 @@ static zend_always_inline bool zend_check_type_slow(
}
static zend_always_inline bool zend_check_type(
- zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope,
+ const zend_type *type, zval *arg, zend_class_entry *scope,
bool is_return_type, bool is_internal)
{
- zend_reference *ref = NULL;
+ const zend_reference *ref = NULL;
ZEND_ASSERT(ZEND_TYPE_IS_SET(*type));
if (UNEXPECTED(Z_ISREF_P(arg))) {
@@ -1245,25 +1219,25 @@ static zend_always_inline bool zend_check_type(
return 1;
}
- return zend_check_type_slow(type, arg, ref, cache_slot, is_return_type, is_internal);
+ return zend_check_type_slow(type, arg, ref, is_return_type, is_internal);
}
ZEND_API bool zend_check_user_type_slow(
- zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type)
+ const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type)
{
return zend_check_type_slow(
- type, arg, ref, cache_slot, is_return_type, /* is_internal */ false);
+ type, arg, ref, is_return_type, /* is_internal */ false);
}
-static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
+static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg)
{
- zend_arg_info *cur_arg_info;
+ const zend_arg_info *cur_arg_info;
ZEND_ASSERT(arg_num <= zf->common.num_args);
cur_arg_info = &zf->common.arg_info[arg_num-1];
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
- && UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
+ && UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, zf->common.scope, 0, 0))) {
zend_verify_arg_error(zf, cur_arg_info, arg_num, arg);
return 0;
}
@@ -1272,10 +1246,10 @@ static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint
}
static zend_always_inline bool zend_verify_variadic_arg_type(
- zend_function *zf, zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot)
+ const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
- if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
+ if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, zf->common.scope, 0, 0))) {
zend_verify_arg_error(zf, arg_info, arg_num, arg);
return 0;
}
@@ -1283,7 +1257,7 @@ static zend_always_inline bool zend_verify_variadic_arg_type(
return 1;
}
-static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call)
+static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_types(const zend_function *fbc, zend_execute_data *call)
{
uint32_t i;
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
@@ -1300,7 +1274,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
}
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
- && UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, /* cache_slot */ NULL, fbc->common.scope, 0, /* is_internal */ 1))) {
+ && UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, fbc->common.scope, 0, /* is_internal */ 1))) {
return 0;
}
arg++;
@@ -1312,7 +1286,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
/* Determine whether an internal call should throw, because the passed arguments violate
* an arginfo constraint. This is only checked in debug builds. In release builds, we
* trust that arginfo matches what is enforced by zend_parse_parameters. */
-ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call)
+ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call)
{
if (fbc->internal_function.handler == ZEND_FN(pass) || (fbc->internal_function.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
/* Be lenient about the special pass function and about fake closures. */
@@ -1339,7 +1313,7 @@ ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_d
return 0;
}
-ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc)
+ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(const zend_function *fbc)
{
zend_error_noreturn(E_ERROR, "Arginfo / zpp mismatch during call of %s%s%s()",
fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "",
@@ -1351,10 +1325,10 @@ ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc)
# define ZEND_VERIFY_FUNC_INFO 0
#endif
-static void zend_verify_internal_func_info(zend_function *fn, zval *retval) {
+static void zend_verify_internal_func_info(const zend_function *fn, const zval *retval) {
#if ZEND_VERIFY_FUNC_INFO
zend_string *name = fn->common.function_name;
- uint32_t type_mask = zend_get_internal_func_info(fn, NULL, NULL);
+ const uint32_t type_mask = zend_get_internal_func_info(fn, NULL, NULL);
if (!type_mask) {
return;
}
@@ -1369,14 +1343,14 @@ static void zend_verify_internal_func_info(zend_function *fn, zval *retval) {
}
}
- uint32_t type = 1u << Z_TYPE_P(retval);
+ const uint32_t type = 1u << Z_TYPE_P(retval);
if (!(type_mask & type)) {
zend_error_noreturn(E_CORE_ERROR, "%s() missing type %s",
ZSTR_VAL(name), zend_get_type_by_const(Z_TYPE_P(retval)));
}
if (Z_TYPE_P(retval) == IS_ARRAY) {
- HashTable *ht = Z_ARRVAL_P(retval);
+ const HashTable *ht = Z_ARRVAL_P(retval);
uint32_t num_checked = 0;
zend_string *str;
zval *val;
@@ -1393,7 +1367,7 @@ static void zend_verify_internal_func_info(zend_function *fn, zval *retval) {
}
}
- uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
+ const uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
if (!(type_mask & array_type)) {
zend_error_noreturn(E_CORE_ERROR,
"%s() missing array element type %s",
@@ -1410,9 +1384,9 @@ static void zend_verify_internal_func_info(zend_function *fn, zval *retval) {
}
#endif
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data)
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data)
{
- zend_execute_data *ptr = EX(prev_execute_data);
+ const zend_execute_data *ptr = EX(prev_execute_data);
if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected",
@@ -1435,7 +1409,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *
}
}
-ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, zval *value)
+ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, const zval *value)
{
const zend_arg_info *arg_info = &zf->common.arg_info[-1];
const char *fname, *fsep, *fclass;
@@ -1462,7 +1436,7 @@ ZEND_API ZEND_COLD void zend_verify_never_error(const zend_function *zf)
}
#if ZEND_DEBUG
-static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, zval *value)
+static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const zval *value)
{
const zend_arg_info *arg_info = &zf->common.arg_info[-1];
const char *fname, *fsep, *fclass;
@@ -1494,9 +1468,9 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con
fclass, fsep, fname, returned_msg, returned_kind);
}
-ZEND_API bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
+ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret)
{
- zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
+ const zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) {
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
@@ -1506,7 +1480,7 @@ ZEND_API bool zend_verify_internal_return_type(zend_function *zf, zval *ret)
return 1;
}
- if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, /* cache_slot */ NULL, NULL, 1, /* is_internal */ 1))) {
+ if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, NULL, 1, /* is_internal */ 1))) {
zend_verify_internal_return_error(zf, ret);
return 0;
}
@@ -1521,7 +1495,7 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf)
zend_verify_return_error(zf, NULL);
}
-static zend_always_inline bool zend_check_class_constant_type(zend_class_constant *c, zval *constant)
+static zend_always_inline bool zend_check_class_constant_type(const zend_class_constant *c, zval *constant)
{
ZEND_ASSERT(!Z_ISREF_P(constant));
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(c->type, Z_TYPE_P(constant)))) {
@@ -1538,7 +1512,7 @@ static zend_always_inline bool zend_check_class_constant_type(zend_class_constan
return zend_verify_scalar_type_hint(type_mask, constant, true, false);
}
-ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant)
+ZEND_API bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, const zend_string *name, zval *constant)
{
if (!zend_check_class_constant_type(c, constant)) {
zend_verify_class_constant_type_error(c, name, constant);
@@ -1707,7 +1681,7 @@ static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *re
}
}
-static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_info *prop_info, zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC)
+static zend_never_inline void zend_binary_assign_op_typed_prop(const zend_property_info *prop_info, zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC)
{
zval z_copy;
@@ -1848,14 +1822,14 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute
z = zend_read_property_ex(zend_ce_deprecated, Z_OBJ_P(&obj), ZSTR_KNOWN(ZEND_STR_MESSAGE), false, NULL);
ZEND_ASSERT(z != &EG(uninitialized_zval));
if (Z_TYPE_P(z) == IS_STRING) {
- message = zend_string_copy(Z_STR_P(z));
+ message = Z_STR_P(z);
}
/* Extract the $since property. */
z = zend_read_property_ex(zend_ce_deprecated, Z_OBJ_P(&obj), ZSTR_KNOWN(ZEND_STR_SINCE), false, NULL);
ZEND_ASSERT(z != &EG(uninitialized_zval));
if (Z_TYPE_P(z) == IS_STRING) {
- since = zend_string_copy(Z_STR_P(z));
+ since = Z_STR_P(z);
}
/* Construct the suffix. */
@@ -1872,8 +1846,6 @@ ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute
out:
- zend_string_release(since);
- zend_string_release(message);
zval_ptr_dtor(&obj);
return result;
@@ -1905,6 +1877,87 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi
zend_string_release(message_suffix);
}
+ZEND_COLD static zend_result ZEND_FASTCALL get_nodiscard_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix)
+{
+ *message_suffix = ZSTR_EMPTY_ALLOC();
+
+ if (!attributes) {
+ return SUCCESS;
+ }
+
+ zend_attribute *nodiscard = zend_get_attribute_str(attributes, "nodiscard", sizeof("nodiscard")-1);
+
+ if (!nodiscard) {
+ return SUCCESS;
+ }
+
+ if (nodiscard->argc == 0) {
+ return SUCCESS;
+ }
+
+ zend_result result = FAILURE;
+
+ zend_string *message = ZSTR_EMPTY_ALLOC();
+
+ zval obj;
+ ZVAL_UNDEF(&obj);
+ zval *z;
+
+ /* Construct the NoDiscard object to correctly handle parameter processing. */
+ if (FAILURE == zend_get_attribute_object(&obj, zend_ce_nodiscard, nodiscard, scope, NULL)) {
+ goto out;
+ }
+
+ /* Extract the $message property. */
+ z = zend_read_property_ex(zend_ce_nodiscard, Z_OBJ_P(&obj), ZSTR_KNOWN(ZEND_STR_MESSAGE), false, NULL);
+ ZEND_ASSERT(z != &EG(uninitialized_zval));
+ if (Z_TYPE_P(z) == IS_STRING) {
+ message = Z_STR_P(z);
+ }
+
+ /* Construct the suffix. */
+ *message_suffix = zend_strpprintf_unchecked(
+ 0,
+ "%s%S",
+ ZSTR_LEN(message) > 0 ? ", " : "",
+ message
+ );
+
+ result = SUCCESS;
+
+ out:
+
+ zval_ptr_dtor(&obj);
+
+ return result;
+}
+
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc)
+{
+ zend_string *message_suffix = ZSTR_EMPTY_ALLOC();
+
+ if (get_nodiscard_suffix_from_attribute(fbc->common.attributes, fbc->common.scope, &message_suffix) == FAILURE) {
+ return;
+ }
+
+ int code = fbc->type == ZEND_INTERNAL_FUNCTION ? E_WARNING : E_USER_WARNING;
+
+ if (fbc->common.scope) {
+ zend_error_unchecked(code, "The return value of method %s::%s() should either be used or intentionally ignored by casting it as (void)%S",
+ ZSTR_VAL(fbc->common.scope->name),
+ ZSTR_VAL(fbc->common.function_name),
+ message_suffix
+ );
+ } else {
+ zend_error_unchecked(code, "The return value of function %s() should either be used or intentionally ignored by casting it as (void)%S",
+ ZSTR_VAL(fbc->common.function_name),
+ message_suffix
+ );
+ }
+
+ zend_string_release(message_suffix);
+}
+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name)
{
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();
@@ -1926,6 +1979,24 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_
zend_string_release(message_suffix);
}
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_constant *c, const zend_string *constant_name)
+{
+ zend_string *message_suffix = ZSTR_EMPTY_ALLOC();
+
+ if (get_deprecation_suffix_from_attribute(c->attributes, NULL, &message_suffix) == FAILURE) {
+ return;
+ }
+
+ int code = ZEND_CONSTANT_MODULE_NUMBER(c) == PHP_USER_CONSTANT ? E_USER_DEPRECATED : E_DEPRECATED;
+
+ zend_error_unchecked(code, "Constant %s is deprecated%S",
+ ZSTR_VAL(constant_name),
+ message_suffix
+ );
+
+ zend_string_release(message_suffix);
+}
+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void)
{
zend_error(E_DEPRECATED, "Automatic conversion of false to array is deprecated");
@@ -2446,7 +2517,7 @@ ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht,
return retval;
}
-static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method)
+ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method)
{
zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method));
}
@@ -2457,7 +2528,7 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_invalid_method_call(z
Z_STRVAL_P(function_name), zend_zval_value_name(object));
}
-static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc)
+ZEND_API void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc)
{
zend_throw_error(
zend_ce_error,
@@ -3538,7 +3609,8 @@ static zend_never_inline void zend_assign_to_property_reference_var_var(zval *co
OPLINE_CC EXECUTE_DATA_CC);
}
-static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval **retval, zend_property_info **prop_info, uint32_t cache_slot, int fetch_type OPLINE_DC EXECUTE_DATA_DC) {
+static zend_never_inline zval* zend_fetch_static_property_address_ex(zend_property_info **prop_info, uint32_t cache_slot, int fetch_type OPLINE_DC EXECUTE_DATA_DC) {
+ zval *result;
zend_string *name;
zend_class_entry *ce;
zend_property_info *property_info;
@@ -3554,7 +3626,7 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval
ce = zend_fetch_class_by_name(Z_STR_P(class_name), Z_STR_P(class_name + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
FREE_OP(op1_type, opline->op1.var);
- return FAILURE;
+ return NULL;
}
if (UNEXPECTED(op1_type != IS_CONST)) {
CACHE_PTR(cache_slot, ce);
@@ -3565,21 +3637,21 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval
ce = zend_fetch_class(NULL, opline->op2.num);
if (UNEXPECTED(ce == NULL)) {
FREE_OP(op1_type, opline->op1.var);
- return FAILURE;
+ return NULL;
}
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
if (EXPECTED(op1_type == IS_CONST) && EXPECTED(CACHED_PTR(cache_slot) == ce)) {
- *retval = CACHED_PTR(cache_slot + sizeof(void *));
+ result = CACHED_PTR(cache_slot + sizeof(void *));
*prop_info = CACHED_PTR(cache_slot + sizeof(void *) * 2);
- return SUCCESS;
+ return result;
}
}
if (EXPECTED(op1_type == IS_CONST)) {
name = Z_STR_P(RT_CONSTANT(opline, opline->op1));
- *retval = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
+ result = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
} else {
zend_string *tmp_name;
zval *varname = get_zval_ptr_undef(opline->op1_type, opline->op1, BP_VAR_R);
@@ -3592,30 +3664,31 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval
}
name = zval_get_tmp_string(varname, &tmp_name);
}
- *retval = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
+ result = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
zend_tmp_string_release(tmp_name);
FREE_OP(op1_type, opline->op1.var);
}
- if (UNEXPECTED(*retval == NULL)) {
- return FAILURE;
+ if (UNEXPECTED(result == NULL)) {
+ return NULL;
}
*prop_info = property_info;
if (EXPECTED(op1_type == IS_CONST)
&& EXPECTED(!(property_info->ce->ce_flags & ZEND_ACC_TRAIT))) {
- CACHE_POLYMORPHIC_PTR(cache_slot, ce, *retval);
+ CACHE_POLYMORPHIC_PTR(cache_slot, ce, result);
CACHE_PTR(cache_slot + sizeof(void *) * 2, property_info);
}
- return SUCCESS;
+ return result;
}
-static zend_always_inline zend_result zend_fetch_static_property_address(zval **retval, zend_property_info **prop_info, uint32_t cache_slot, int fetch_type, int flags OPLINE_DC EXECUTE_DATA_DC) {
+static zend_always_inline zval* zend_fetch_static_property_address(zend_property_info **prop_info, uint32_t cache_slot, int fetch_type, int flags OPLINE_DC EXECUTE_DATA_DC) {
+ zval *result;
zend_property_info *property_info;
if (opline->op1_type == IS_CONST
@@ -3623,36 +3696,77 @@ static zend_always_inline zend_result zend_fetch_static_property_address(zval **
|| (opline->op2_type == IS_UNUSED
&& ((opline->op2.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF
|| (opline->op2.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT)))
- && EXPECTED(CACHED_PTR(cache_slot) != NULL)) {
- *retval = CACHED_PTR(cache_slot + sizeof(void *));
+ && EXPECTED(CACHED_PTR(cache_slot + sizeof(void *)) != NULL)) {
+ result = CACHED_PTR(cache_slot + sizeof(void *));
property_info = CACHED_PTR(cache_slot + sizeof(void *) * 2);
if ((fetch_type == BP_VAR_R || fetch_type == BP_VAR_RW)
- && UNEXPECTED(Z_TYPE_P(*retval) == IS_UNDEF)
+ && UNEXPECTED(Z_TYPE_P(result) == IS_UNDEF)
&& ZEND_TYPE_IS_SET(property_info->type)) {
zend_throw_error(NULL, "Typed static property %s::$%s must not be accessed before initialization",
ZSTR_VAL(property_info->ce->name),
zend_get_unmangled_property_name(property_info->name));
- return FAILURE;
+ return NULL;
}
} else {
- zend_result success;
- success = zend_fetch_static_property_address_ex(retval, &property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC);
- if (UNEXPECTED(success != SUCCESS)) {
- return FAILURE;
+ result = zend_fetch_static_property_address_ex(&property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!result)) {
+ return NULL;
}
}
flags &= ZEND_FETCH_OBJ_FLAGS;
if (flags && ZEND_TYPE_IS_SET(property_info->type)) {
- zend_handle_fetch_obj_flags(NULL, *retval, NULL, property_info, flags);
+ zend_handle_fetch_obj_flags(NULL, result, NULL, property_info, flags);
}
if (prop_info) {
*prop_info = property_info;
}
- return SUCCESS;
+ return result;
+}
+
+ZEND_API zval* ZEND_FASTCALL zend_fetch_static_property(zend_execute_data *ex, int fetch_type) {
+ zval *result;
+ zend_property_info *property_info;
+#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
+ zend_execute_data *orig_execute_data = execute_data;
+#else
+ zend_execute_data *execute_data;
+#endif
+ execute_data = ex;
+#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
+ const zend_op *orig_opline = opline;
+#else
+ const zend_op *opline;
+#endif
+ opline = execute_data->opline;
+
+ uint32_t cache_slot = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
+ uint32_t flags = 0;
+
+ if (fetch_type == BP_VAR_W) {
+ flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
+ }
+ result = zend_fetch_static_property_address_ex(&property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC);
+ if (EXPECTED(result)) {
+ if (flags && ZEND_TYPE_IS_SET(property_info->type)) {
+ zend_handle_fetch_obj_flags(NULL, result, NULL, property_info, flags);
+ }
+ } else {
+ result = &EG(uninitialized_zval);
+ }
+
+#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
+ EX(opline) = opline;
+ opline = orig_opline;
+#endif
+#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
+ execute_data = orig_execute_data;
+#endif
+
+ return result;
}
ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv) {
@@ -5226,7 +5340,11 @@ static zend_always_inline zend_result _zend_quick_get_constant(
if (!check_defined_only) {
ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
- zend_error(E_DEPRECATED, "Constant %s is deprecated", ZSTR_VAL(c->name));
+ if (!CONST_IS_RECURSIVE(c)) {
+ CONST_PROTECT_RECURSION(c);
+ zend_deprecated_constant(c, c->name);
+ CONST_UNPROTECT_RECURSION(c);
+ }
return SUCCESS;
}
}
@@ -5267,6 +5385,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
}
}
} else {
+ ZEND_ASSERT(num_args == 0 || fbc->internal_function.arg_info);
for (uint32_t i = 0; i < num_args; i++) {
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
size_t len = strlen(arg_info->name);
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 1dd50ab8a69ef..cf15c9e3b2db5 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -25,6 +25,7 @@
#include "zend_hash.h"
#include "zend_operators.h"
#include "zend_variables.h"
+#include "zend_constants.h"
#include
@@ -60,9 +61,11 @@ ZEND_API zend_result zend_eval_stringl_ex(const char *str, size_t str_len, zval
/* export zend_pass_function to allow comparisons against it */
extern ZEND_API const zend_internal_function zend_pass_function;
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_constant *c, const zend_string *constant_name);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void);
ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_use_resource_as_offset(const zval *dim);
@@ -94,23 +97,23 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_pr
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_add_element(void);
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info);
-ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modification_error(const zend_property_info *info, const char *operation);
+ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modification_error(const zend_property_info *prop_info, const char *operation);
ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
ZEND_API ZEND_COLD void zend_verify_arg_error(
- const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value);
+ const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, const zval *value);
ZEND_API ZEND_COLD void zend_verify_return_error(
- const zend_function *zf, zval *value);
+ const zend_function *zf, const zval *value);
ZEND_API ZEND_COLD void zend_verify_never_error(
const zend_function *zf);
ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref);
ZEND_API bool zend_check_user_type_slow(
- zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type);
+ const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type);
#if ZEND_DEBUG
-ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call);
-ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc);
-ZEND_API bool zend_verify_internal_return_type(zend_function *zf, zval *ret);
+ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call);
+ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(const zend_function *fbc);
+ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret);
#endif
#define ZEND_REF_TYPE_SOURCES(ref) \
@@ -207,6 +210,71 @@ static zend_always_inline zval* zend_assign_to_variable_ex(zval *variable_ptr, z
return variable_ptr;
}
+static zend_always_inline void zend_safe_assign_to_variable_noref(zval *variable_ptr, zval *value) {
+ if (Z_REFCOUNTED_P(variable_ptr)) {
+ ZEND_ASSERT(Z_TYPE_P(variable_ptr) != IS_REFERENCE);
+ zend_refcounted *ref = Z_COUNTED_P(variable_ptr);
+ ZVAL_COPY_VALUE(variable_ptr, value);
+ GC_DTOR_NO_REF(ref);
+ } else {
+ ZVAL_COPY_VALUE(variable_ptr, value);
+ }
+}
+
+static zend_always_inline void zend_cast_zval_to_object(zval *result, zval *expr, uint8_t op1_type) {
+ HashTable *ht;
+
+ ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
+ if (Z_TYPE_P(expr) == IS_ARRAY) {
+ ht = zend_symtable_to_proptable(Z_ARR_P(expr));
+ if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
+ /* TODO: try not to duplicate immutable arrays as well ??? */
+ ht = zend_array_dup(ht);
+ }
+ Z_OBJ_P(result)->properties = ht;
+ } else if (Z_TYPE_P(expr) != IS_NULL) {
+ Z_OBJ_P(result)->properties = ht = zend_new_array(1);
+ expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
+ if (op1_type == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
+ } else {
+ if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ }
+ }
+}
+
+static zend_always_inline void zend_cast_zval_to_array(zval *result, zval *expr, uint8_t op1_type) {
+ extern zend_class_entry *zend_ce_closure;
+ if (op1_type == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
+ if (Z_TYPE_P(expr) != IS_NULL) {
+ ZVAL_ARR(result, zend_new_array(1));
+ expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
+ if (op1_type == IS_CONST) {
+ if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
+ } else {
+ if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
+ }
+ } else {
+ ZVAL_EMPTY_ARRAY(result);
+ }
+ } else if (ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(expr)) {
+ /* Optimized version without rebuilding properties HashTable */
+ ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
+ } else {
+ HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
+ if (obj_ht) {
+ /* fast copy */
+ ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
+ (Z_OBJCE_P(expr)->default_properties_count ||
+ Z_OBJ_P(expr)->handlers != &std_object_handlers ||
+ GC_IS_RECURSIVE(obj_ht))));
+ zend_release_properties(obj_ht);
+ } else {
+ ZVAL_EMPTY_ARRAY(result);
+ }
+ }
+}
+
ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp);
ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *pp, zend_class_entry *scope);
ZEND_API zend_result ZEND_FASTCALL zval_update_constant_with_ctx(zval *pp, zend_class_entry *scope, zend_ast_evaluate_ctx *ctx);
@@ -435,6 +503,9 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield);
+ZEND_API zval* ZEND_FASTCALL zend_fetch_static_property(zend_execute_data *ex, int fetch_type);
+ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method);
+ZEND_API void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc);
ZEND_API void zend_frameless_observed_call(zend_execute_data *execute_data);
@@ -503,11 +574,11 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal
} \
} while (0)
-#define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS)
-#define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS)
+#define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((bool)(ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS))
+#define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((bool)(ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS))
-ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant);
+ZEND_API bool zend_verify_class_constant_type(const zend_class_constant *c, const zend_string *name, zval *constant);
ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant);
ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict);
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 48e61c6839063..9a7803e44e66e 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -53,10 +53,6 @@ ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data);
ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
ZEND_API zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name);
-/* true globals */
-ZEND_API const zend_fcall_info empty_fcall_info = {0};
-ZEND_API const zend_fcall_info_cache empty_fcall_info_cache = {0};
-
#ifdef ZEND_WIN32
ZEND_TLS HANDLE tq_timer = NULL;
#endif
@@ -140,6 +136,8 @@ void init_executor(void) /* {{{ */
original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv);
#endif
+ ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
+
EG(symtable_cache_ptr) = EG(symtable_cache);
EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE;
EG(no_extensions) = 0;
@@ -299,11 +297,20 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
if (c->name) {
zend_string_release_ex(c->name, 0);
}
+ if (c->filename) {
+ zend_string_release_ex(c->filename, 0);
+ }
+ if (c->attributes) {
+ zend_hash_release(c->attributes);
+ }
efree(c);
zend_string_release_ex(key, 0);
} ZEND_HASH_MAP_FOREACH_END_DEL();
}
+ zval_ptr_dtor(&EG(last_fatal_error_backtrace));
+ ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
+
/* Release static properties and static variables prior to the final GC run,
* as they may hold GC roots. */
ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(function_table), zv) {
@@ -816,7 +823,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_
return SUCCESS; /* we would result in an unstable executor otherwise */
}
- ZEND_ASSERT(fci->size == sizeof(zend_fcall_info));
+ ZEND_ASSERT(ZEND_FCI_INITIALIZED(*fci));
if (!fci_cache || !fci_cache->function_handler) {
char *error = NULL;
diff --git a/Zend/zend_extensions.c b/Zend/zend_extensions.c
index fecea4fa0b5ea..a4e5a38f90d89 100644
--- a/Zend/zend_extensions.c
+++ b/Zend/zend_extensions.c
@@ -45,7 +45,7 @@ zend_result zend_load_extension(const char *path)
#ifdef ZEND_WIN32
char *err;
if (!php_win32_image_compatible(handle, &err)) {
- zend_error(E_CORE_WARNING, err);
+ zend_error(E_CORE_WARNING, "%s", err);
return FAILURE;
}
#endif
diff --git a/Zend/zend_extensions.h b/Zend/zend_extensions.h
index 83acea51900c0..becd53d7d08bd 100644
--- a/Zend/zend_extensions.h
+++ b/Zend/zend_extensions.h
@@ -44,7 +44,7 @@ You can use the following macro to check the extension API version for compatibi
/* The first number is the engine version and the rest is the date (YYYYMMDD).
* This way engine 2/3 API no. is always greater than engine 1 API no.. */
-#define ZEND_EXTENSION_API_NO 420240924
+#define ZEND_EXTENSION_API_NO 420240925
typedef struct _zend_extension_version_info {
int zend_extension_api_no;
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index f73fef50e16bb..8da55fcd48f66 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -818,7 +818,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
zval *entry = (zval*) Z_PTR_P(zv);
zval *weakmap = zv+1;
ZEND_ASSERT(Z_REFCOUNTED_P(weakmap));
- if (Z_OPT_REFCOUNTED_P(entry)) {
+ if (Z_OPT_COLLECTABLE_P(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
if (GC_REF_CHECK_COLOR(Z_COUNTED_P(weakmap), GC_GREY)) {
/* Weakmap was scanned in gc_mark_roots, we must
@@ -855,7 +855,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
ZEND_ASSERT(Z_TYPE_P(zv+1) == IS_PTR);
zval *key = zv;
zval *entry = (zval*) Z_PTR_P(zv+1);
- if (Z_OPT_REFCOUNTED_P(entry)) {
+ if (Z_OPT_COLLECTABLE_P(entry)) {
GC_UNSET_FROM_WEAKMAP(entry);
if (GC_REF_CHECK_COLOR(Z_COUNTED_P(key), GC_GREY)) {
/* Key was scanned in gc_mark_roots, we must
@@ -893,7 +893,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
if (!GC_REF_CHECK_COLOR(ht, GC_BLACK)) {
GC_REF_SET_BLACK(ht);
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -909,14 +909,14 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
handle_zvals:
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
GC_REF_SET_BLACK(ref);
zv++;
while (--n) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -948,7 +948,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -959,7 +959,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -975,7 +975,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack)
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
- if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
+ if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -1019,7 +1019,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
zval *entry = (zval*) Z_PTR_P(zv);
zval *weakmap = zv+1;
ZEND_ASSERT(Z_REFCOUNTED_P(weakmap));
- if (Z_REFCOUNTED_P(entry)) {
+ if (Z_COLLECTABLE_P(entry)) {
GC_SET_FROM_WEAKMAP_KEY(entry);
ref = Z_COUNTED_P(entry);
/* Only DELREF if the contribution from the weakmap has
@@ -1043,7 +1043,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
- if (Z_REFCOUNTED_P(entry)) {
+ if (Z_COLLECTABLE_P(entry)) {
GC_SET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
/* Only DELREF if the contribution from the weakmap key
@@ -1069,7 +1069,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
if (!GC_REF_CHECK_COLOR(ht, GC_GREY)) {
GC_REF_SET_COLOR(ht, GC_GREY);
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1084,14 +1084,14 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
}
handle_zvals:
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_GREY);
zv++;
while (--n) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1123,7 +1123,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1134,7 +1134,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1150,7 +1150,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack)
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
- if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
+ if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1263,7 +1263,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
- if (Z_OPT_REFCOUNTED_P(entry)) {
+ if (Z_OPT_COLLECTABLE_P(entry)) {
ref = Z_COUNTED_P(entry);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1282,7 +1282,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
GC_REF_SET_COLOR(ht, GC_WHITE);
GC_STACK_PUSH((zend_refcounted *) ht);
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1297,13 +1297,13 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
handle_zvals:
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
zv++;
while (--n) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1335,7 +1335,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1345,7 +1345,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1360,7 +1360,7 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack)
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
- if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
+ if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1473,7 +1473,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
- if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP_KEY(entry)) {
+ if (Z_COLLECTABLE_P(entry) && GC_FROM_WEAKMAP_KEY(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
GC_UNSET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
@@ -1494,7 +1494,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
- if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP(entry)) {
+ if (Z_COLLECTABLE_P(entry) && GC_FROM_WEAKMAP(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
GC_UNSET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
@@ -1517,7 +1517,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
if (GC_REF_CHECK_COLOR(ht, GC_WHITE)) {
GC_REF_SET_BLACK(ht);
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1533,14 +1533,14 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
handle_zvals:
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
GC_REF_SET_BLACK(ref);
zv++;
while (--n) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1576,7 +1576,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1587,7 +1587,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1603,7 +1603,7 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
- if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
+ if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1681,7 +1681,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
GC_REMOVE_FROM_BUFFER(ref);
count++;
} else if (GC_TYPE(ref) == IS_REFERENCE) {
- if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
+ if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
goto tail_call;
}
@@ -1704,7 +1704,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
- if (Z_OPT_REFCOUNTED_P(entry)) {
+ if (Z_OPT_COLLECTABLE_P(entry)) {
ref = Z_COUNTED_P(entry);
GC_STACK_PUSH(ref);
}
@@ -1717,7 +1717,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
zv = table;
if (UNEXPECTED(ht)) {
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_STACK_PUSH(ref);
}
@@ -1732,11 +1732,11 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
handle_zvals:
for (; n != 0; n--) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
zv++;
while (--n) {
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_STACK_PUSH(ref);
}
@@ -1763,7 +1763,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
p++;
while (--n) {
@@ -1771,7 +1771,7 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
- if (Z_REFCOUNTED_P(zv)) {
+ if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_STACK_PUSH(ref);
}
@@ -2175,7 +2175,7 @@ static void zend_gc_check_root_tmpvars(void) {
if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
zval *var = ZEND_CALL_VAR(ex, var_num);
- if (Z_REFCOUNTED_P(var)) {
+ if (Z_COLLECTABLE_P(var)) {
gc_check_possible_root(Z_COUNTED_P(var));
}
}
@@ -2205,7 +2205,7 @@ static void zend_gc_remove_root_tmpvars(void) {
if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
zval *var = ZEND_CALL_VAR(ex, var_num);
- if (Z_REFCOUNTED_P(var)) {
+ if (Z_COLLECTABLE_P(var)) {
GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(var));
}
}
diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h
index a52de1bfcfa14..06f550647bd79 100644
--- a/Zend/zend_gc.h
+++ b/Zend/zend_gc.h
@@ -134,6 +134,8 @@ static zend_always_inline void zend_get_gc_buffer_add_zval(
static zend_always_inline void zend_get_gc_buffer_add_obj(
zend_get_gc_buffer *gc_buffer, zend_object *obj) {
+ ZEND_ASSERT(obj != NULL);
+
if (UNEXPECTED(gc_buffer->cur == gc_buffer->end)) {
zend_get_gc_buffer_grow(gc_buffer);
}
@@ -141,6 +143,18 @@ static zend_always_inline void zend_get_gc_buffer_add_obj(
gc_buffer->cur++;
}
+static zend_always_inline void zend_get_gc_buffer_add_ht(
+ zend_get_gc_buffer *gc_buffer, HashTable *ht) {
+ if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
+ return;
+ }
+ if (UNEXPECTED(gc_buffer->cur == gc_buffer->end)) {
+ zend_get_gc_buffer_grow(gc_buffer);
+ }
+ ZVAL_ARR(gc_buffer->cur, ht);
+ gc_buffer->cur++;
+}
+
static zend_always_inline void zend_get_gc_buffer_add_ptr(
zend_get_gc_buffer *gc_buffer, void *ptr) {
if (UNEXPECTED(gc_buffer->cur == gc_buffer->end)) {
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index 62a97d753634a..079bfb99caccf 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -182,6 +182,10 @@ struct _zend_executor_globals {
JMP_BUF *bailout;
int error_reporting;
+
+ bool fatal_error_backtrace_on;
+ zval last_fatal_error_backtrace;
+
int exit_status;
HashTable *function_table; /* function symbol table */
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index a417290b82c49..66cfb250d1fec 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -30,7 +30,6 @@
#if defined(__AVX2__)
# include
#elif defined( __SSE2__)
-# include
# include
#endif
@@ -293,7 +292,7 @@ ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize)
return ht;
}
-ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2)
+ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(const zval *val1, const zval *val2)
{
zval *zv;
HashTable *ht = emalloc(sizeof(HashTable));
@@ -457,7 +456,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_discard(HashTable *ht, uint32_t nNumUsed)
}
}
-static uint32_t zend_array_recalc_elements(HashTable *ht)
+static uint32_t zend_array_recalc_elements(const HashTable *ht)
{
zval *val;
uint32_t num = ht->nNumOfElements;
@@ -671,10 +670,10 @@ ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx)
}
}
-static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(HashTable *ht)
+static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(const HashTable *ht)
{
HashTableIterator *iter = EG(ht_iterators);
- HashTableIterator *end = iter + EG(ht_iterators_used);
+ const HashTableIterator *end = iter + EG(ht_iterators_used);
while (iter != end) {
if (iter->ht == ht) {
@@ -684,17 +683,17 @@ static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(HashTabl
}
}
-static zend_always_inline void zend_hash_iterators_remove(HashTable *ht)
+static zend_always_inline void zend_hash_iterators_remove(const HashTable *ht)
{
if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
_zend_hash_iterators_remove(ht);
}
}
-ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start)
+ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(const HashTable *ht, HashPosition start)
{
- HashTableIterator *iter = EG(ht_iterators);
- HashTableIterator *end = iter + EG(ht_iterators_used);
+ const HashTableIterator *iter = EG(ht_iterators);
+ const HashTableIterator *end = iter + EG(ht_iterators_used);
HashPosition res = ht->nNumUsed;
while (iter != end) {
@@ -708,10 +707,10 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(HashTable *ht,
return res;
}
-ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to)
+ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(const HashTable *ht, HashPosition from, HashPosition to)
{
HashTableIterator *iter = EG(ht_iterators);
- HashTableIterator *end = iter + EG(ht_iterators_used);
+ const HashTableIterator *end = iter + EG(ht_iterators_used);
while (iter != end) {
if (iter->ht == ht && iter->pos == from) {
@@ -721,10 +720,10 @@ ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(HashTable *ht, HashPosit
}
}
-ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(HashTable *ht, HashPosition step)
+ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(const HashTable *ht, HashPosition step)
{
HashTableIterator *iter = EG(ht_iterators);
- HashTableIterator *end = iter + EG(ht_iterators_used);
+ const HashTableIterator *end = iter + EG(ht_iterators_used);
while (iter != end) {
if (iter->ht == ht) {
@@ -1429,11 +1428,11 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
}
}
-static zend_always_inline void zend_hash_iterators_clamp_max(HashTable *ht, uint32_t max)
+static zend_always_inline void zend_hash_iterators_clamp_max(const HashTable *ht, uint32_t max)
{
if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
HashTableIterator *iter = EG(ht_iterators);
- HashTableIterator *end = iter + EG(ht_iterators_used);
+ const HashTableIterator *end = iter + EG(ht_iterators_used);
while (iter != end) {
if (iter->ht == ht) {
iter->pos = MIN(iter->pos, max);
@@ -2237,7 +2236,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t
}
-ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor)
+ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor)
{
uint32_t idx;
zval *new_entry, *data;
@@ -2284,7 +2283,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source,
}
-static zend_always_inline bool zend_array_dup_value(HashTable *source, HashTable *target, zval *data, zval *dest, bool packed, bool with_holes)
+static zend_always_inline bool zend_array_dup_value(const HashTable *source, zval *data, zval *dest, bool packed, bool with_holes)
{
if (with_holes) {
if (!packed && Z_TYPE_INFO_P(data) == IS_INDIRECT) {
@@ -2321,9 +2320,9 @@ static zend_always_inline bool zend_array_dup_value(HashTable *source, HashTable
return 1;
}
-static zend_always_inline bool zend_array_dup_element(HashTable *source, HashTable *target, uint32_t idx, Bucket *p, Bucket *q, bool packed, bool static_keys, bool with_holes)
+static zend_always_inline bool zend_array_dup_element(const HashTable *source, HashTable *target, uint32_t idx, Bucket *p, Bucket *q, bool packed, bool static_keys, bool with_holes)
{
- if (!zend_array_dup_value(source, target, &p->val, &q->val, packed, with_holes)) {
+ if (!zend_array_dup_value(source, &p->val, &q->val, packed, with_holes)) {
return 0;
}
@@ -2344,7 +2343,7 @@ static zend_always_inline bool zend_array_dup_element(HashTable *source, HashTab
}
// We need to duplicate iterators to be able to search through all copy-on-write copies to find the actually iterated HashTable and position back
-static void zend_array_dup_ht_iterators(HashTable *source, HashTable *target) {
+static void zend_array_dup_ht_iterators(const HashTable *source, HashTable *target) {
uint32_t iter_index = 0;
uint32_t end_index = EG(ht_iterators_used);
@@ -2362,14 +2361,14 @@ static void zend_array_dup_ht_iterators(HashTable *source, HashTable *target) {
}
}
-static zend_always_inline void zend_array_dup_packed_elements(HashTable *source, HashTable *target, bool with_holes)
+static zend_always_inline void zend_array_dup_packed_elements(const HashTable *source, HashTable *target, bool with_holes)
{
zval *p = source->arPacked;
zval *q = target->arPacked;
- zval *end = p + source->nNumUsed;
+ const zval *end = p + source->nNumUsed;
do {
- if (!zend_array_dup_value(source, target, p, q, 1, with_holes)) {
+ if (!zend_array_dup_value(source, p, q, 1, with_holes)) {
if (with_holes) {
ZVAL_UNDEF(q);
}
@@ -2382,12 +2381,12 @@ static zend_always_inline void zend_array_dup_packed_elements(HashTable *source,
}
}
-static zend_always_inline uint32_t zend_array_dup_elements(HashTable *source, HashTable *target, bool static_keys, bool with_holes)
+static zend_always_inline uint32_t zend_array_dup_elements(const HashTable *source, HashTable *target, bool static_keys, bool with_holes)
{
uint32_t idx = 0;
Bucket *p = source->arData;
Bucket *q = target->arData;
- Bucket *end = p + source->nNumUsed;
+ const Bucket *end = p + source->nNumUsed;
if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
zend_array_dup_ht_iterators(source, target);
@@ -2435,7 +2434,7 @@ static zend_always_inline uint32_t zend_array_dup_elements(HashTable *source, Ha
return idx;
}
-ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
+ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(const HashTable *source)
{
uint32_t idx;
HashTable *target;
@@ -2523,7 +2522,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
return target;
}
-ZEND_API HashTable* zend_array_to_list(HashTable *source)
+ZEND_API HashTable* zend_array_to_list(const HashTable *source)
{
HashTable *result = _zend_new_array(zend_hash_num_elements(source));
zend_hash_real_init_packed(result);
@@ -2544,7 +2543,7 @@ ZEND_API HashTable* zend_array_to_list(HashTable *source)
}
-ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite)
+ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite)
{
uint32_t idx;
Bucket *p;
@@ -2640,7 +2639,7 @@ static bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, z
}
-ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam)
+ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam)
{
uint32_t idx;
Bucket *p;
@@ -2730,7 +2729,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulo
return p ? &p->val : NULL;
}
-ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
+ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(const HashTable *ht, HashPosition *pos)
{
IS_CONSISTENT(ht);
HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
@@ -2741,7 +2740,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, H
/* This function will be extremely optimized by remembering
* the end of the list
*/
-ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
+ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(const HashTable *ht, HashPosition *pos)
{
uint32_t idx;
@@ -2770,7 +2769,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Has
}
-ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(const HashTable *ht, HashPosition *pos)
{
uint32_t idx;
@@ -2809,7 +2808,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, Hash
}
}
-ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *ht, HashPosition *pos)
{
uint32_t idx = *pos;
@@ -2890,7 +2889,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *h
}
}
-ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
+ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos)
{
uint32_t idx;
Bucket *p;
@@ -2912,7 +2911,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, Hash
}
-ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos)
+ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(const HashTable *ht, const HashPosition *pos)
{
uint32_t idx;
Bucket *p;
@@ -3071,7 +3070,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, b
zend_hash_sort_internal(ht, sort, compar, renumber);
}
-void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
+ZEND_API void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
{
HT_ASSERT_RC1(ht);
@@ -3092,7 +3091,7 @@ void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort, bucket_co
}
}
-static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered) {
+static zend_always_inline int zend_hash_compare_impl(const HashTable *ht1, const HashTable *ht2, compare_func_t compar, bool ordered) {
uint32_t idx1, idx2;
zend_string *key1, *key2;
zend_ulong h1, h2;
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index b0cf18184553c..b2aaecce0d27c 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -30,12 +30,13 @@
#define HASH_KEY_IS_LONG 2
#define HASH_KEY_NON_EXISTENT 3
-#define HASH_UPDATE (1<<0)
-#define HASH_ADD (1<<1)
-#define HASH_UPDATE_INDIRECT (1<<2)
-#define HASH_ADD_NEW (1<<3)
-#define HASH_ADD_NEXT (1<<4)
-#define HASH_LOOKUP (1<<5)
+#define HASH_UPDATE (1<<0) /* Create new entry, or update the existing one. */
+#define HASH_ADD (1<<1) /* Create new entry, or fail if it exists. */
+#define HASH_UPDATE_INDIRECT (1<<2) /* If the given ht entry is an indirect zval, unwrap it before writing to it. \
+ * When used with HASH_ADD, writing is allowed if the target zval is IS_UNDEF. */
+#define HASH_ADD_NEW (1<<3) /* Used when the offset is known not to exist. */
+#define HASH_ADD_NEXT (1<<4) /* Append to an array. (e.g. $array[] = 42;) */
+#define HASH_LOOKUP (1<<5) /* Look up an existing entry, or create one with a NULL value. */
#define HASH_FLAG_CONSISTENCY ((1<<0) | (1<<1))
#define HASH_FLAG_PACKED (1<<2)
@@ -248,19 +249,19 @@ static zend_always_inline bool zend_hash_index_exists(const HashTable *ht, zend_
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos_ex(const HashTable *ht, HashPosition pos);
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht);
-ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos);
-ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos);
+ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(const HashTable *ht, HashPosition *pos);
+ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *ht, HashPosition *pos);
ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos);
ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, const HashPosition *pos);
-ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos);
-ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos);
-ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos);
-ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos);
+ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos);
+ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(const HashTable *ht, const HashPosition *pos);
+ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(const HashTable *ht, HashPosition *pos);
+ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(const HashTable *ht, HashPosition *pos);
-static zend_always_inline zend_result zend_hash_has_more_elements_ex(HashTable *ht, HashPosition *pos) {
+static zend_always_inline zend_result zend_hash_has_more_elements_ex(const HashTable *ht, const HashPosition *pos) {
return (zend_hash_get_current_key_type_ex(ht, pos) == HASH_KEY_NON_EXISTENT ? FAILURE : SUCCESS);
}
-static zend_always_inline zend_result zend_hash_has_more_elements(HashTable *ht) {
+static zend_always_inline zend_result zend_hash_has_more_elements(const HashTable *ht) {
return zend_hash_has_more_elements_ex(ht, &ht->nInternalPointer);
}
static zend_always_inline zend_result zend_hash_move_forward(HashTable *ht) {
@@ -275,10 +276,10 @@ static zend_always_inline int zend_hash_get_current_key(const HashTable *ht, zen
static zend_always_inline void zend_hash_get_current_key_zval(const HashTable *ht, zval *key) {
zend_hash_get_current_key_zval_ex(ht, key, &ht->nInternalPointer);
}
-static zend_always_inline int zend_hash_get_current_key_type(HashTable *ht) {
+static zend_always_inline int zend_hash_get_current_key_type(const HashTable *ht) {
return zend_hash_get_current_key_type_ex(ht, &ht->nInternalPointer);
}
-static zend_always_inline zval* zend_hash_get_current_data(HashTable *ht) {
+static zend_always_inline zval* zend_hash_get_current_data(const HashTable *ht) {
return zend_hash_get_current_data_ex(ht, &ht->nInternalPointer);
}
static zend_always_inline void zend_hash_internal_pointer_reset(HashTable *ht) {
@@ -289,9 +290,9 @@ static zend_always_inline void zend_hash_internal_pointer_end(HashTable *ht) {
}
/* Copying, merging and sorting */
-ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor);
-ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite);
-ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam);
+ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor);
+ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite);
+ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam);
ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q);
ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q);
ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q);
@@ -299,7 +300,7 @@ ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q);
typedef int (*bucket_compare_func_t)(Bucket *a, Bucket *b);
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered);
ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber);
-void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber);
+ZEND_API void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber);
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag);
static zend_always_inline void ZEND_FASTCALL zend_hash_sort(HashTable *ht, bucket_compare_func_t compare_func, bool renumber) {
@@ -341,11 +342,11 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht);
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void);
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t size);
-ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2);
+ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(const zval *val1, const zval *val2);
ZEND_API uint32_t zend_array_count(HashTable *ht);
-ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source);
+ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(const HashTable *source);
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht);
-ZEND_API HashTable* zend_array_to_list(HashTable *source);
+ZEND_API HashTable* zend_array_to_list(const HashTable *source);
ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht);
ZEND_API HashTable* ZEND_FASTCALL zend_symtable_to_proptable(HashTable *ht);
ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate);
@@ -356,11 +357,11 @@ ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPo
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTable *ht);
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval *array);
ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx);
-ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start);
-ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to);
-ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(HashTable *ht, HashPosition step);
+ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(const HashTable *ht, HashPosition start);
+ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(const HashTable *ht, HashPosition from, HashPosition to);
+ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(const HashTable *ht, HashPosition step);
-static zend_always_inline void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to)
+static zend_always_inline void zend_hash_iterators_update(const HashTable *ht, HashPosition from, HashPosition to)
{
if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
_zend_hash_iterators_update(ht, from, to);
@@ -553,7 +554,7 @@ static zend_always_inline zval *zend_symtable_find_ind(const HashTable *ht, zend
}
-static zend_always_inline bool zend_symtable_exists(HashTable *ht, zend_string *key)
+static zend_always_inline bool zend_symtable_exists(const HashTable *ht, zend_string *key)
{
zend_ulong idx;
@@ -565,7 +566,7 @@ static zend_always_inline bool zend_symtable_exists(HashTable *ht, zend_string *
}
-static zend_always_inline bool zend_symtable_exists_ind(HashTable *ht, zend_string *key)
+static zend_always_inline bool zend_symtable_exists_ind(const HashTable *ht, zend_string *key)
{
zend_ulong idx;
@@ -936,7 +937,7 @@ static zend_always_inline void *zend_hash_index_find_ptr(const HashTable *ht, ze
}
}
-static zend_always_inline zval *zend_hash_index_find_deref(HashTable *ht, zend_ulong h)
+static zend_always_inline zval *zend_hash_index_find_deref(const HashTable *ht, zend_ulong h)
{
zval *zv = zend_hash_index_find(ht, h);
if (zv) {
@@ -945,7 +946,7 @@ static zend_always_inline zval *zend_hash_index_find_deref(HashTable *ht, zend_u
return zv;
}
-static zend_always_inline zval *zend_hash_find_deref(HashTable *ht, zend_string *str)
+static zend_always_inline zval *zend_hash_find_deref(const HashTable *ht, zend_string *str)
{
zval *zv = zend_hash_find(ht, str);
if (zv) {
@@ -954,7 +955,7 @@ static zend_always_inline zval *zend_hash_find_deref(HashTable *ht, zend_string
return zv;
}
-static zend_always_inline zval *zend_hash_str_find_deref(HashTable *ht, const char *str, size_t len)
+static zend_always_inline zval *zend_hash_str_find_deref(const HashTable *ht, const char *str, size_t len)
{
zval *zv = zend_hash_str_find(ht, str, len);
if (zv) {
@@ -963,7 +964,7 @@ static zend_always_inline zval *zend_hash_str_find_deref(HashTable *ht, const ch
return zv;
}
-static zend_always_inline void *zend_symtable_str_find_ptr(HashTable *ht, const char *str, size_t len)
+static zend_always_inline void *zend_symtable_str_find_ptr(const HashTable *ht, const char *str, size_t len)
{
zend_ulong idx;
@@ -974,7 +975,7 @@ static zend_always_inline void *zend_symtable_str_find_ptr(HashTable *ht, const
}
}
-static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, HashPosition *pos)
+static zend_always_inline void *zend_hash_get_current_data_ptr_ex(const HashTable *ht, const HashPosition *pos)
{
zval *zv;
@@ -1584,7 +1585,7 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht,
} while (0)
/* Check if an array is a list */
-static zend_always_inline bool zend_array_is_list(zend_array *array)
+static zend_always_inline bool zend_array_is_list(const zend_array *array)
{
zend_ulong expected_idx = 0;
zend_ulong num_idx;
diff --git a/Zend/zend_hrtime.c b/Zend/zend_hrtime.c
index bcf11964f1cea..7fa36b1b654f4 100644
--- a/Zend/zend_hrtime.c
+++ b/Zend/zend_hrtime.c
@@ -33,7 +33,7 @@
ZEND_API double zend_hrtime_timer_scale = .0;
-#elif ZEND_HRTIME_PLATFORM_APPLE
+#elif ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE
# include
# include
@@ -62,7 +62,7 @@ void zend_startup_hrtime(void)
zend_hrtime_timer_scale = (double)ZEND_NANO_IN_SEC / (zend_hrtime_t)tf.QuadPart;
}
-#elif ZEND_HRTIME_PLATFORM_APPLE
+#elif ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE
mach_timebase_info(&zend_hrtime_timerlib_info);
diff --git a/Zend/zend_hrtime.h b/Zend/zend_hrtime.h
index 1449c4e443cf2..994dd6da169ed 100644
--- a/Zend/zend_hrtime.h
+++ b/Zend/zend_hrtime.h
@@ -33,7 +33,8 @@
#define ZEND_HRTIME_PLATFORM_POSIX 0
#define ZEND_HRTIME_PLATFORM_WINDOWS 0
-#define ZEND_HRTIME_PLATFORM_APPLE 0
+#define ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE 0
+#define ZEND_HRTIME_PLATFORM_APPLE_GETTIME_NSEC 0
#define ZEND_HRTIME_PLATFORM_HPUX 0
#define ZEND_HRTIME_PLATFORM_AIX 0
@@ -43,9 +44,12 @@
#elif defined(_WIN32) || defined(_WIN64)
# undef ZEND_HRTIME_PLATFORM_WINDOWS
# define ZEND_HRTIME_PLATFORM_WINDOWS 1
+#elif HAVE_CLOCK_GETTIME_NSEC_NP
+# undef ZEND_HRTIME_PLATFORM_APPLE_GETTIME_NSEC
+# define ZEND_HRTIME_PLATFORM_APPLE_GETTIME_NSEC 1
#elif defined(__APPLE__)
-# undef ZEND_HRTIME_PLATFORM_APPLE
-# define ZEND_HRTIME_PLATFORM_APPLE 1
+# undef ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE
+# define ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE 1
#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__)))
# undef ZEND_HRTIME_PLATFORM_HPUX
# define ZEND_HRTIME_PLATFORM_HPUX 1
@@ -54,7 +58,7 @@
# define ZEND_HRTIME_PLATFORM_AIX 1
#endif
-#define ZEND_HRTIME_AVAILABLE (ZEND_HRTIME_PLATFORM_POSIX || ZEND_HRTIME_PLATFORM_WINDOWS || ZEND_HRTIME_PLATFORM_APPLE || ZEND_HRTIME_PLATFORM_HPUX || ZEND_HRTIME_PLATFORM_AIX)
+#define ZEND_HRTIME_AVAILABLE (ZEND_HRTIME_PLATFORM_POSIX || ZEND_HRTIME_PLATFORM_WINDOWS || ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE || ZEND_HRTIME_PLATFORM_APPLE_GETTIME_NSEC || ZEND_HRTIME_PLATFORM_HPUX || ZEND_HRTIME_PLATFORM_AIX)
BEGIN_EXTERN_C()
@@ -62,7 +66,7 @@ BEGIN_EXTERN_C()
ZEND_API extern double zend_hrtime_timer_scale;
-#elif ZEND_HRTIME_PLATFORM_APPLE
+#elif ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE
# include
# include
@@ -79,10 +83,12 @@ void zend_startup_hrtime(void);
static zend_always_inline zend_hrtime_t zend_hrtime(void)
{
#if ZEND_HRTIME_PLATFORM_WINDOWS
- LARGE_INTEGER lt = {0};
+ LARGE_INTEGER lt = {{0}};
QueryPerformanceCounter(<);
return (zend_hrtime_t)((zend_hrtime_t)lt.QuadPart * zend_hrtime_timer_scale);
-#elif ZEND_HRTIME_PLATFORM_APPLE
+#elif ZEND_HRTIME_PLATFORM_APPLE_GETTIME_NSEC
+ return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
+#elif ZEND_HRTIME_PLATFORM_APPLE_MACH_ABSOLUTE
return (zend_hrtime_t)mach_absolute_time() * zend_hrtime_timerlib_info.numer / zend_hrtime_timerlib_info.denom;
#elif ZEND_HRTIME_PLATFORM_POSIX
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index b1ed8e4b61675..e718eb5684e65 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -23,6 +23,7 @@
#include "zend_execute.h"
#include "zend_inheritance.h"
#include "zend_interfaces.h"
+#include "zend_closures.h"
#include "zend_smart_str.h"
#include "zend_operators.h"
#include "zend_exceptions.h"
@@ -82,7 +83,7 @@ static void zend_type_list_copy_ctor(
}
zend_type *list_type;
- ZEND_TYPE_LIST_FOREACH(new_list, list_type) {
+ ZEND_TYPE_LIST_FOREACH_MUTABLE(new_list, list_type) {
zend_type_copy_ctor(list_type, use_arena, persistent);
} ZEND_TYPE_LIST_FOREACH_END();
}
@@ -95,7 +96,7 @@ static void zend_type_copy_ctor(zend_type *const type, bool use_arena, bool pers
}
}
-static zend_function *zend_duplicate_internal_function(zend_function *func, const zend_class_entry *ce) /* {{{ */
+static zend_function *zend_duplicate_internal_function(const zend_function *func, const zend_class_entry *ce) /* {{{ */
{
zend_function *new_function;
@@ -224,15 +225,15 @@ static const char *zend_asymmetric_visibility_string(uint32_t fn_flags) /* {{{ *
}
}
-static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) {
+static zend_string *resolve_class_name(const zend_class_entry *scope, zend_string *name) {
ZEND_ASSERT(scope);
- if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
+ if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_PARENT)) && scope->parent) {
if (scope->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
return scope->parent->name;
} else {
return scope->parent_name;
}
- } else if (zend_string_equals_literal_ci(name, "self")) {
+ } else if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_SELF))) {
return scope->name;
} else {
return name;
@@ -309,7 +310,7 @@ static zend_class_entry *lookup_class(zend_class_entry *scope, zend_string *name
}
/* Instanceof that's safe to use on unlinked classes. */
-static bool unlinked_instanceof(zend_class_entry *ce1, const zend_class_entry *ce2) {
+static bool unlinked_instanceof(const zend_class_entry *ce1, const zend_class_entry *ce2) {
if (ce1 == ce2) {
return 1;
}
@@ -346,7 +347,7 @@ static bool unlinked_instanceof(zend_class_entry *ce1, const zend_class_entry *c
}
} else {
for (i = 0; i < ce1->num_interfaces; i++) {
- zend_class_entry *ce = zend_lookup_class_ex(
+ const zend_class_entry *ce = zend_lookup_class_ex(
ce1->interface_names[i].name, ce1->interface_names[i].lc_name,
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
/* Avoid recursing if class implements itself. */
@@ -361,7 +362,7 @@ static bool unlinked_instanceof(zend_class_entry *ce1, const zend_class_entry *c
}
static bool zend_type_permits_self(
- zend_type type, zend_class_entry *scope, zend_class_entry *self) {
+ const zend_type type, const zend_class_entry *scope, zend_class_entry *self) {
if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_OBJECT) {
return 1;
}
@@ -369,11 +370,11 @@ static bool zend_type_permits_self(
/* Any types that may satisfy self must have already been loaded at this point
* (as a parent or interface), so we never need to register delayed variance obligations
* for this case. */
- zend_type *single_type;
+ const zend_type *single_type;
ZEND_TYPE_FOREACH(type, single_type) {
if (ZEND_TYPE_HAS_NAME(*single_type)) {
zend_string *name = resolve_class_name(scope, ZEND_TYPE_NAME(*single_type));
- zend_class_entry *ce = lookup_class(self, name);
+ const zend_class_entry *ce = lookup_class(self, name);
if (ce && unlinked_instanceof(self, ce)) {
return 1;
}
@@ -389,8 +390,8 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name
ZEND_ASSERT(class_name);
if (!CG(current_linking_class) || ce == CG(current_linking_class)) {
return;
- } else if (zend_string_equals_literal_ci(class_name, "self")
- || zend_string_equals_literal_ci(class_name, "parent")) {
+ } else if (zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_SELF))
+ || zend_string_equals_ci(class_name, ZSTR_KNOWN(ZEND_STR_PARENT))) {
return;
}
@@ -427,12 +428,12 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name
/* Check whether any type in the fe_type intersection type is a subtype of the proto class. */
static inheritance_status zend_is_intersection_subtype_of_class(
- zend_class_entry *fe_scope, zend_type fe_type,
+ zend_class_entry *fe_scope, const zend_type fe_type,
zend_class_entry *proto_scope, zend_string *proto_class_name, zend_class_entry *proto_ce)
{
ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(fe_type));
bool have_unresolved = false;
- zend_type *single_type;
+ const zend_type *single_type;
/* Traverse the list of child types and check that at least one is
* a subtype of the parent type being checked */
@@ -472,7 +473,7 @@ static inheritance_status zend_is_intersection_subtype_of_class(
/* Check whether a single class proto type is a subtype of a potentially complex fe_type. */
static inheritance_status zend_is_class_subtype_of_type(
zend_class_entry *fe_scope, zend_string *fe_class_name,
- zend_class_entry *proto_scope, zend_type proto_type) {
+ zend_class_entry *proto_scope, const zend_type proto_type) {
zend_class_entry *fe_ce = NULL;
bool have_unresolved = 0;
@@ -490,7 +491,29 @@ static inheritance_status zend_is_class_subtype_of_type(
}
}
- zend_type *single_type;
+ /* If the parent has 'callable' as a return type, then Closure satisfies the co-variant check */
+ if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_CALLABLE) {
+ if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name);
+ if (!fe_ce) {
+ have_unresolved = 1;
+ } else if (fe_ce == zend_ce_closure) {
+ track_class_dependency(fe_ce, fe_class_name);
+ return INHERITANCE_SUCCESS;
+ }
+ }
+
+ /* If the parent has 'static' as a return type, then final classes could replace it with self */
+ if ((ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_STATIC) && (fe_scope->ce_flags & ZEND_ACC_FINAL)) {
+ if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name);
+ if (!fe_ce) {
+ have_unresolved = 1;
+ } else if (fe_ce == fe_scope) {
+ track_class_dependency(fe_ce, fe_class_name);
+ return INHERITANCE_SUCCESS;
+ }
+ }
+
+ const zend_type *single_type;
/* Traverse the list of parent types and check if the current child (FE)
* class is the subtype of at least one of them (union) or all of them (intersection). */
@@ -561,15 +584,15 @@ static inheritance_status zend_is_class_subtype_of_type(
return is_intersection ? INHERITANCE_SUCCESS : INHERITANCE_ERROR;
}
-static zend_string *get_class_from_type(zend_class_entry *scope, zend_type single_type) {
+static zend_string *get_class_from_type(const zend_class_entry *scope, const zend_type single_type) {
if (ZEND_TYPE_HAS_NAME(single_type)) {
return resolve_class_name(scope, ZEND_TYPE_NAME(single_type));
}
return NULL;
}
-static void register_unresolved_classes(zend_class_entry *scope, zend_type type) {
- zend_type *single_type;
+static void register_unresolved_classes(zend_class_entry *scope, const zend_type type) {
+ const zend_type *single_type;
ZEND_TYPE_FOREACH(type, single_type) {
if (ZEND_TYPE_HAS_LIST(*single_type)) {
register_unresolved_classes(scope, *single_type);
@@ -583,11 +606,11 @@ static void register_unresolved_classes(zend_class_entry *scope, zend_type type)
}
static inheritance_status zend_is_intersection_subtype_of_type(
- zend_class_entry *fe_scope, zend_type fe_type,
- zend_class_entry *proto_scope, zend_type proto_type)
+ zend_class_entry *fe_scope, const zend_type fe_type,
+ zend_class_entry *proto_scope, const zend_type proto_type)
{
bool have_unresolved = false;
- zend_type *single_type;
+ const zend_type *single_type;
uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK(proto_type);
/* Currently, for object type any class name would be allowed here.
@@ -649,8 +672,8 @@ static inheritance_status zend_is_intersection_subtype_of_type(
}
ZEND_API inheritance_status zend_perform_covariant_type_check(
- zend_class_entry *fe_scope, zend_type fe_type,
- zend_class_entry *proto_scope, zend_type proto_type)
+ zend_class_entry *fe_scope, const zend_type fe_type,
+ zend_class_entry *proto_scope, const zend_type proto_type)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type));
@@ -683,7 +706,6 @@ ZEND_API inheritance_status zend_perform_covariant_type_check(
}
}
- zend_type *single_type;
inheritance_status early_exit_status;
bool have_unresolved = false;
@@ -705,6 +727,7 @@ ZEND_API inheritance_status zend_perform_covariant_type_check(
* We need to iterate over fe_type (U_i) first and the logic is independent of
* whether proto_type is a union or intersection (only the inner check differs). */
early_exit_status = INHERITANCE_ERROR;
+ const zend_type *single_type;
ZEND_TYPE_FOREACH(fe_type, single_type) {
inheritance_status status;
/* Union has an intersection type as it's member */
@@ -767,7 +790,7 @@ static inheritance_status zend_do_perform_implementation_check(
const zend_function *fe, zend_class_entry *fe_scope,
const zend_function *proto, zend_class_entry *proto_scope) /* {{{ */
{
- uint32_t i, num_args, proto_num_args, fe_num_args;
+ uint32_t num_args, proto_num_args, fe_num_args;
inheritance_status status, local_status;
bool proto_is_variadic, fe_is_variadic;
@@ -808,7 +831,7 @@ static inheritance_status zend_do_perform_implementation_check(
num_args = MAX(proto_num_args, fe_num_args);
status = INHERITANCE_SUCCESS;
- for (i = 0; i < num_args; i++) {
+ for (uint32_t i = 0; i < num_args; i++) {
zend_arg_info *proto_arg_info =
i < proto_num_args ? &proto->common.arg_info[i] :
proto_is_variadic ? &proto->common.arg_info[proto_num_args - 1] : NULL;
@@ -910,7 +933,7 @@ static ZEND_COLD zend_string *zend_get_function_declaration(
smart_str_appendc(&str, '(');
if (fptr->common.arg_info) {
- uint32_t i, num_args, required;
+ uint32_t num_args, required;
zend_arg_info *arg_info = fptr->common.arg_info;
required = fptr->common.required_num_args;
@@ -918,7 +941,7 @@ static ZEND_COLD zend_string *zend_get_function_declaration(
if (fptr->common.fn_flags & ZEND_ACC_VARIADIC) {
num_args++;
}
- for (i = 0; i < num_args;) {
+ for (uint32_t i = 0; i < num_args;) {
zend_append_type_hint(&str, scope, arg_info, 0);
if (ZEND_ARG_SEND_MODE(arg_info)) {
@@ -950,7 +973,7 @@ static ZEND_COLD zend_string *zend_get_function_declaration(
{
uint32_t idx = i;
zend_op *op = fptr->op_array.opcodes;
- zend_op *end = op + fptr->op_array.last;
+ const zend_op *end = op + fptr->op_array.last;
++idx;
while (op < end) {
@@ -1112,7 +1135,12 @@ static inheritance_status do_inheritance_check_on_method(
#define SEPARATE_METHOD() do { \
if ((flags & ZEND_INHERITANCE_LAZY_CHILD_CLONE) \
- && child_scope != ce && child->type == ZEND_USER_FUNCTION) { \
+ && child_scope != ce \
+ /* Trait methods have already been separated at this point. However, their */ \
+ /* scope isn't fixed until after inheritance checks to preserve the scope */ \
+ /* in error messages. Skip them here explicitly. */ \
+ && !(child_scope->ce_flags & ZEND_ACC_TRAIT) \
+ && child->type == ZEND_USER_FUNCTION) { \
/* op_array wasn't duplicated yet */ \
zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); \
memcpy(new_function, child, sizeof(zend_op_array)); \
@@ -1338,7 +1366,7 @@ static inheritance_status verify_property_type_compatibility(
return INHERITANCE_SUCCESS;
}
-static bool property_has_operation(zend_property_info *prop_info, zend_property_hook_kind kind)
+static bool property_has_operation(const zend_property_info *prop_info, zend_property_hook_kind kind)
{
return (!(prop_info->flags & ZEND_ACC_VIRTUAL)
&& (kind == ZEND_PROPERTY_HOOK_GET || !(prop_info->flags & ZEND_ACC_READONLY)))
@@ -1423,10 +1451,9 @@ static prop_variance prop_get_variance(const zend_property_info *prop_info) {
static void do_inherit_property(zend_property_info *parent_info, zend_string *key, zend_class_entry *ce) /* {{{ */
{
zval *child = zend_hash_find_known_hash(&ce->properties_info, key);
- zend_property_info *child_info;
if (UNEXPECTED(child)) {
- child_info = Z_PTR_P(child);
+ zend_property_info *child_info = Z_PTR_P(child);
if (parent_info->flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED)) {
child_info->flags |= ZEND_ACC_CHANGED;
}
@@ -1534,7 +1561,7 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
}
} else if (UNEXPECTED(ZEND_TYPE_IS_SET(child_info->type) && !ZEND_TYPE_IS_SET(parent_info->type))) {
zend_error_noreturn(E_COMPILE_ERROR,
- "Type of %s::$%s must not be defined (as in class %s)",
+ "Type of %s::$%s must be omitted to match the parent definition in class %s",
ZSTR_VAL(ce->name),
ZSTR_VAL(key),
ZSTR_VAL(parent_info->ce->name));
@@ -1568,7 +1595,6 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
{
/* expects interface to be contained in ce's interface list already */
uint32_t i, ce_num, if_num = iface->num_interfaces;
- zend_class_entry *entry;
ce_num = ce->num_interfaces;
@@ -1580,7 +1606,7 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
/* Inherit the interfaces, only if they're not already inherited by the class */
while (if_num--) {
- entry = iface->interfaces[if_num];
+ zend_class_entry *entry = iface->interfaces[if_num];
for (i = 0; i < ce_num; i++) {
if (ce->interfaces[i] == entry) {
break;
@@ -1623,7 +1649,7 @@ static inheritance_status class_constant_types_compatible(const zend_class_const
}
static bool do_inherit_constant_check(
- zend_class_entry *ce, zend_class_constant *parent_constant, zend_string *name);
+ zend_class_entry *ce, const zend_class_constant *parent_constant, zend_string *name);
static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */
{
@@ -1696,7 +1722,7 @@ void zend_build_properties_info_table(zend_class_entry *ce)
} ZEND_HASH_FOREACH_END();
}
-ZEND_API void zend_verify_hooked_property(zend_class_entry *ce, zend_property_info *prop_info, zend_string *prop_name)
+ZEND_API void zend_verify_hooked_property(const zend_class_entry *ce, zend_property_info *prop_info, zend_string *prop_name)
{
if (!prop_info->hooks) {
return;
@@ -1720,7 +1746,7 @@ ZEND_API void zend_verify_hooked_property(zend_class_entry *ce, zend_property_in
ZVAL_NULL(&ce->default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]);
}
for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
- zend_function *func = prop_info->hooks[i];
+ const zend_function *func = prop_info->hooks[i];
if (func) {
if ((zend_property_hook_kind)i == ZEND_PROPERTY_HOOK_GET
&& (func->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
@@ -1802,7 +1828,6 @@ static void zend_link_hooked_object_iter(zend_class_entry *ce) {
ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */
{
zend_property_info *property_info;
- zend_function *func;
zend_string *key;
if (UNEXPECTED(ce->ce_flags & ZEND_ACC_INTERFACE)) {
@@ -1810,7 +1835,12 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
if (UNEXPECTED(!(parent_ce->ce_flags & ZEND_ACC_INTERFACE))) {
zend_error_noreturn(E_COMPILE_ERROR, "Interface %s cannot extend class %s", ZSTR_VAL(ce->name), ZSTR_VAL(parent_ce->name));
}
- } else if (UNEXPECTED(parent_ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_FINAL))) {
+ } else if (UNEXPECTED(parent_ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_FINAL|ZEND_ACC_ENUM))) {
+ /* Class must not extend an enum (GH-16315); check enums first since
+ * enums are implemented as final classes */
+ if (parent_ce->ce_flags & ZEND_ACC_ENUM) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot extend enum %s", ZSTR_VAL(ce->name), ZSTR_VAL(parent_ce->name));
+ }
/* Class must not extend a final class */
if (parent_ce->ce_flags & ZEND_ACC_FINAL) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot extend final class %s", ZSTR_VAL(ce->name), ZSTR_VAL(parent_ce->name));
@@ -1988,6 +2018,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
if (!checked) {
flags |= ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY;
}
+ zend_function *func;
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
do_inherit_method(key, func, ce, 0, flags);
} ZEND_HASH_FOREACH_END();
@@ -2046,7 +2077,7 @@ static zend_always_inline bool check_trait_property_or_constant_value_compatibil
/** @return bool Returns true if the class constant should be inherited, i.e. whether it doesn't already exist. */
static bool do_inherit_constant_check(
- zend_class_entry *ce, zend_class_constant *parent_constant, zend_string *name
+ zend_class_entry *ce, const zend_class_constant *parent_constant, zend_string *name
) {
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
if (zv == NULL) {
@@ -2093,7 +2124,7 @@ static bool do_inherit_constant_check(
}
/* }}} */
-static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, zend_class_entry *iface) /* {{{ */
+static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_entry *ce, const zend_class_entry *iface) /* {{{ */
{
if (do_inherit_constant_check(ce, c, name)) {
zend_class_constant *ct;
@@ -2163,27 +2194,27 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface) /* {{{ */
{
- uint32_t i, ignore = 0;
+ bool ignore = false;
uint32_t current_iface_num = ce->num_interfaces;
uint32_t parent_iface_num = ce->parent ? ce->parent->num_interfaces : 0;
- zend_string *key;
- zend_class_constant *c;
ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED);
- for (i = 0; i < ce->num_interfaces; i++) {
+ for (uint32_t i = 0; i < ce->num_interfaces; i++) {
if (ce->interfaces[i] == NULL) {
memmove(ce->interfaces + i, ce->interfaces + i + 1, sizeof(zend_class_entry*) * (--ce->num_interfaces - i));
i--;
} else if (ce->interfaces[i] == iface) {
if (EXPECTED(i < parent_iface_num)) {
- ignore = 1;
+ ignore = true;
} else {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot implement previously implemented interface %s", ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
}
}
}
if (ignore) {
+ zend_string *key;
+ zend_class_constant *c;
/* Check for attempt to redeclare interface constants */
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) {
do_inherit_constant_check(ce, c, key);
@@ -2205,15 +2236,14 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry **interfaces) /* {{{ */
{
- zend_class_entry *iface;
uint32_t num_parent_interfaces = ce->parent ? ce->parent->num_interfaces : 0;
uint32_t num_interfaces = num_parent_interfaces;
zend_string *key;
zend_class_constant *c;
- uint32_t i, j;
+ uint32_t i;
for (i = 0; i < ce->num_interfaces; i++) {
- iface = interfaces[num_parent_interfaces + i];
+ zend_class_entry *iface = interfaces[num_parent_interfaces + i];
if (!(iface->ce_flags & ZEND_ACC_LINKED)) {
add_dependency_obligation(ce, iface);
}
@@ -2222,7 +2252,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
zend_error_noreturn(E_ERROR, "%s cannot implement %s - it is not an interface", ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
return;
}
- for (j = 0; j < num_interfaces; j++) {
+ for (uint32_t j = 0; j < num_interfaces; j++) {
if (interfaces[j] == iface) {
if (j >= num_parent_interfaces) {
efree(interfaces);
@@ -2271,7 +2301,7 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry
/* }}} */
-void zend_inheritance_check_override(zend_class_entry *ce)
+void zend_inheritance_check_override(const zend_class_entry *ce)
{
zend_function *f;
@@ -2322,14 +2352,13 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
{
zend_function *existing_fn = NULL;
zend_function *new_fn;
- bool check_inheritance = false;
if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) {
/* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless
* of where it is coming from there is no conflict and we do not need to add it again */
if (existing_fn->op_array.opcodes == fn->op_array.opcodes &&
(existing_fn->common.fn_flags & ZEND_ACC_PPP_MASK) == (fn->common.fn_flags & ZEND_ACC_PPP_MASK) &&
- (existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
+ (existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT)) {
return;
}
@@ -2356,8 +2385,6 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
ZSTR_VAL(fn->common.scope->name), ZSTR_VAL(fn->common.function_name),
ZSTR_VAL(ce->name), ZSTR_VAL(name),
ZSTR_VAL(existing_fn->common.scope->name), ZSTR_VAL(existing_fn->common.function_name));
- } else {
- check_inheritance = true;
}
}
@@ -2377,25 +2404,12 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_
function_add_ref(new_fn);
fn = zend_hash_update_ptr(&ce->function_table, key, new_fn);
zend_add_magic_method(ce, fn, key);
-
- if (check_inheritance) {
- /* Inherited members are overridden by members inserted by traits.
- * Check whether the trait method fulfills the inheritance requirements. */
- uint32_t flags = ZEND_INHERITANCE_CHECK_PROTO | ZEND_INHERITANCE_CHECK_VISIBILITY;
- if (!(existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT)) {
- flags |= ZEND_INHERITANCE_SET_CHILD_CHANGED |ZEND_INHERITANCE_SET_CHILD_PROTO |
- ZEND_INHERITANCE_RESET_CHILD_OVERRIDE;
- }
- do_inheritance_check_on_method(
- fn, fixup_trait_scope(fn, ce), existing_fn, fixup_trait_scope(existing_fn, ce),
- ce, NULL, flags);
- }
}
/* }}} */
static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /* {{{ */
{
- if ((fn->common.scope->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
+ if (fn->common.scope->ce_flags & ZEND_ACC_TRAIT) {
fn->common.scope = ce;
@@ -2409,7 +2423,7 @@ static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /*
}
/* }}} */
-static void zend_traits_check_private_final_inheritance(uint32_t original_fn_flags, zend_function *fn_copy, zend_string *name)
+static void zend_traits_check_private_final_inheritance(uint32_t original_fn_flags, const zend_function *fn_copy, const zend_string *name)
{
/* If the function was originally already private+final, then it will have
* already been warned about. Only emit this error when the used trait method
@@ -2425,7 +2439,6 @@ static void zend_traits_check_private_final_inheritance(uint32_t original_fn_fla
static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable *exclude_table, zend_class_entry **aliases) /* {{{ */
{
zend_trait_alias *alias, **alias_ptr;
- zend_string *lcname;
zend_function fn_copy;
int i;
@@ -2449,7 +2462,7 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, alias->alias);
- lcname = zend_string_tolower(alias->alias);
+ zend_string *lcname = zend_string_tolower(alias->alias);
zend_add_trait_method(ce, alias->alias, lcname, &fn_copy);
zend_string_release_ex(lcname, 0);
}
@@ -2493,16 +2506,14 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z
}
/* }}} */
-static uint32_t zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *trait, zend_class_entry **traits) /* {{{ */
+static uint32_t zend_check_trait_usage(const zend_class_entry *ce, const zend_class_entry *trait, zend_class_entry **traits) /* {{{ */
{
- uint32_t i;
-
if (UNEXPECTED((trait->ce_flags & ZEND_ACC_TRAIT) != ZEND_ACC_TRAIT)) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements", ZSTR_VAL(trait->name));
return 0;
}
- for (i = 0; i < ce->num_traits; i++) {
+ for (uint32_t i = 0; i < ce->num_traits; i++) {
if (traits[i] == trait) {
return i;
}
@@ -2515,7 +2526,6 @@ static uint32_t zend_check_trait_usage(zend_class_entry *ce, zend_class_entry *t
static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_entry **traits, HashTable ***exclude_tables_ptr, zend_class_entry ***aliases_ptr) /* {{{ */
{
size_t i, j = 0;
- zend_trait_precedence **precedences;
zend_trait_precedence *cur_precedence;
zend_trait_method_reference *cur_method_ref;
zend_string *lc_trait_name;
@@ -2528,7 +2538,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
if (ce->trait_precedences) {
exclude_tables = ecalloc(ce->num_traits, sizeof(HashTable*));
i = 0;
- precedences = ce->trait_precedences;
+ zend_trait_precedence **precedences = ce->trait_precedences;
ce->trait_precedences = NULL;
while ((cur_precedence = precedences[i])) {
/** Resolve classes for all precedence operations. */
@@ -2602,7 +2612,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
aliases = ecalloc(i, sizeof(zend_class_entry*));
i = 0;
while (ce->trait_aliases[i]) {
- zend_trait_alias *cur_alias = ce->trait_aliases[i];
+ const zend_trait_alias *cur_alias = ce->trait_aliases[i];
cur_method_ref = &ce->trait_aliases[i]->trait_method;
lcname = zend_string_tolower(cur_method_ref->method_name);
if (cur_method_ref->class_name) {
@@ -2667,7 +2677,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
}
/* }}} */
-static void zend_do_traits_method_binding(zend_class_entry *ce, zend_class_entry **traits, HashTable **exclude_tables, zend_class_entry **aliases) /* {{{ */
+static void zend_do_traits_method_binding(zend_class_entry *ce, zend_class_entry **traits, HashTable **exclude_tables, zend_class_entry **aliases, bool verify_abstract, bool *contains_abstract_methods) /* {{{ */
{
uint32_t i;
zend_string *key;
@@ -2678,6 +2688,11 @@ static void zend_do_traits_method_binding(zend_class_entry *ce, zend_class_entry
if (traits[i]) {
/* copies functions, applies defined aliasing, and excludes unused trait methods */
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&traits[i]->function_table, key, fn) {
+ bool is_abstract = (bool) (fn->common.fn_flags & ZEND_ACC_ABSTRACT);
+ *contains_abstract_methods |= is_abstract;
+ if (verify_abstract != is_abstract) {
+ continue;
+ }
zend_traits_copy_functions(key, fn, ce, exclude_tables[i], aliases);
} ZEND_HASH_FOREACH_END();
@@ -2692,15 +2707,16 @@ static void zend_do_traits_method_binding(zend_class_entry *ce, zend_class_entry
for (i = 0; i < ce->num_traits; i++) {
if (traits[i]) {
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&traits[i]->function_table, key, fn) {
+ bool is_abstract = (bool) (fn->common.fn_flags & ZEND_ACC_ABSTRACT);
+ *contains_abstract_methods |= is_abstract;
+ if (verify_abstract != is_abstract) {
+ continue;
+ }
zend_traits_copy_functions(key, fn, ce, NULL, aliases);
} ZEND_HASH_FOREACH_END();
}
}
}
-
- ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
- zend_fixup_trait_method(fn, ce);
- } ZEND_HASH_FOREACH_END();
}
/* }}} */
@@ -2713,10 +2729,8 @@ static const zend_class_entry* find_first_constant_definition(const zend_class_e
* process like this is needed to find the location of the first definition
* of the constant from traits.
*/
- size_t i;
-
if (colliding_ce == ce) {
- for (i = 0; i < current_trait; i++) {
+ for (size_t i = 0; i < current_trait; i++) {
if (traits[i]
&& zend_hash_exists(&traits[i]->constants_table, constant_name)) {
return traits[i];
@@ -2783,9 +2797,7 @@ static bool do_trait_constant_check(
static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
{
- size_t i;
-
- for (i = 0; i < ce->num_traits; i++) {
+ for (uint32_t i = 0; i < ce->num_traits; i++) {
zend_string *constant_name;
zend_class_constant *constant;
@@ -2827,10 +2839,8 @@ static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_ent
static const zend_class_entry* find_first_property_definition(const zend_class_entry *ce, zend_class_entry **traits, size_t current_trait, zend_string *prop_name, const zend_class_entry *colliding_ce) /* {{{ */
{
- size_t i;
-
if (colliding_ce == ce) {
- for (i = 0; i < current_trait; i++) {
+ for (size_t i = 0; i < current_trait; i++) {
if (traits[i]
&& zend_hash_exists(&traits[i]->properties_info, prop_name)) {
return traits[i];
@@ -2844,20 +2854,17 @@ static const zend_class_entry* find_first_property_definition(const zend_class_e
static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
{
- size_t i;
zend_property_info *property_info;
const zend_property_info *colliding_prop;
- zend_property_info *new_prop;
zend_string* prop_name;
zval* prop_value;
- zend_string *doc_comment;
/* In the following steps the properties are inserted into the property table
* for that, a very strict approach is applied:
* - check for compatibility, if not compatible with any property in class -> fatal
* - if compatible, then strict notice
*/
- for (i = 0; i < ce->num_traits; i++) {
+ for (uint32_t i = 0; i < ce->num_traits; i++) {
if (!traits[i]) {
continue;
}
@@ -2937,12 +2944,13 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
prop_value = &tmp_prop_value;
ZVAL_UNDEF(&tmp_prop_value);
}
- doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL;
+
+ zend_string *doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL;
zend_type type = property_info->type;
/* Assumption: only userland classes can use traits, as such the type must be arena allocated */
zend_type_copy_ctor(&type, /* use arena */ true, /* persistent */ false);
- new_prop = zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, type);
+ zend_property_info *new_prop = zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, type);
if (property_info->attributes) {
new_prop->attributes = property_info->attributes;
@@ -2955,9 +2963,9 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
zend_function **hooks = new_prop->hooks =
zend_arena_alloc(&CG(arena), ZEND_PROPERTY_HOOK_STRUCT_SIZE);
memcpy(hooks, property_info->hooks, ZEND_PROPERTY_HOOK_STRUCT_SIZE);
- for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
- if (hooks[i]) {
- zend_function *old_fn = hooks[i];
+ for (uint32_t j = 0; j < ZEND_PROPERTY_HOOK_COUNT; j++) {
+ if (hooks[j]) {
+ zend_function *old_fn = hooks[j];
/* Hooks are not yet supported for internal properties. */
ZEND_ASSERT(ZEND_USER_CODE(old_fn->type));
@@ -2972,7 +2980,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
zend_fixup_trait_method(new_fn, ce);
- hooks[i] = new_fn;
+ hooks[j] = new_fn;
}
}
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
@@ -2982,33 +2990,6 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
}
/* }}} */
-static void zend_do_bind_traits(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
-{
- HashTable **exclude_tables;
- zend_class_entry **aliases;
-
- ZEND_ASSERT(ce->num_traits > 0);
-
- /* complete initialization of trait structures in ce */
- zend_traits_init_trait_structures(ce, traits, &exclude_tables, &aliases);
-
- /* first care about all methods to be flattened into the class */
- zend_do_traits_method_binding(ce, traits, exclude_tables, aliases);
-
- if (aliases) {
- efree(aliases);
- }
-
- if (exclude_tables) {
- efree(exclude_tables);
- }
-
- /* then flatten the constants and properties into it, to, mostly to notify developer about problems */
- zend_do_traits_constant_binding(ce, traits);
- zend_do_traits_property_binding(ce, traits);
-}
-/* }}} */
-
#define MAX_ABSTRACT_INFO_CNT 3
#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s"
#define DISPLAY_ABSTRACT_FN(idx) \
@@ -3036,7 +3017,7 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
const zend_function *func;
zend_abstract_info ai;
bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
- bool can_be_abstract = (ce->ce_flags & ZEND_ACC_ENUM) == 0;
+ bool can_be_abstract = (ce->ce_flags & (ZEND_ACC_ENUM|ZEND_ACC_ANON_CLASS)) == 0;
memset(&ai, 0, sizeof(ai));
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, func) {
@@ -3064,16 +3045,28 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */
}
if (ai.cnt) {
- zend_error_noreturn(E_ERROR, !is_explicit_abstract && can_be_abstract
- ? "%s %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")"
- : "%s %s must implement %d abstract private method%s (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")",
- zend_get_object_type_uc(ce),
- ZSTR_VAL(ce->name), ai.cnt,
- ai.cnt > 1 ? "s" : "",
- DISPLAY_ABSTRACT_FN(0),
- DISPLAY_ABSTRACT_FN(1),
- DISPLAY_ABSTRACT_FN(2)
+ if (!is_explicit_abstract && can_be_abstract) {
+ zend_error_noreturn(E_ERROR,
+ "%s %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining method%s (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")",
+ zend_get_object_type_uc(ce),
+ ZSTR_VAL(ce->name), ai.cnt,
+ ai.cnt > 1 ? "s" : "",
+ ai.cnt > 1 ? "s" : "",
+ DISPLAY_ABSTRACT_FN(0),
+ DISPLAY_ABSTRACT_FN(1),
+ DISPLAY_ABSTRACT_FN(2)
);
+ } else {
+ zend_error_noreturn(E_ERROR,
+ "%s %s must implement %d abstract method%s (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")",
+ zend_get_object_type_uc(ce),
+ ZSTR_VAL(ce->name), ai.cnt,
+ ai.cnt > 1 ? "s" : "",
+ DISPLAY_ABSTRACT_FN(0),
+ DISPLAY_ABSTRACT_FN(1),
+ DISPLAY_ABSTRACT_FN(2)
+ );
+ }
} else {
/* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */
ce->ce_flags &= ~ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
@@ -3213,7 +3206,7 @@ static void add_property_hook_obligation(
static void resolve_delayed_variance_obligations(zend_class_entry *ce);
-static void check_variance_obligation(variance_obligation *obligation) {
+static void check_variance_obligation(const variance_obligation *obligation) {
if (obligation->type == OBLIGATION_DEPENDENCY) {
zend_class_entry *dependency_ce = obligation->dependency_ce;
if (dependency_ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
@@ -3252,7 +3245,7 @@ static void check_variance_obligation(variance_obligation *obligation) {
}
}
-static void load_delayed_classes(zend_class_entry *ce) {
+static void load_delayed_classes(const zend_class_entry *ce) {
HashTable *delayed_autoloads = CG(delayed_autoloads);
if (!delayed_autoloads) {
return;
@@ -3281,11 +3274,11 @@ static void load_delayed_classes(zend_class_entry *ce) {
}
static void resolve_delayed_variance_obligations(zend_class_entry *ce) {
- HashTable *all_obligations = CG(delayed_variance_obligations), *obligations;
+ HashTable *all_obligations = CG(delayed_variance_obligations);
zend_ulong num_key = (zend_ulong) (uintptr_t) ce;
ZEND_ASSERT(all_obligations != NULL);
- obligations = zend_hash_index_find_ptr(all_obligations, num_key);
+ const HashTable *obligations = zend_hash_index_find_ptr(all_obligations, num_key);
ZEND_ASSERT(obligations != NULL);
variance_obligation *obligation;
@@ -3319,7 +3312,7 @@ static void check_unrecoverable_load_failure(const zend_class_entry *ce) {
} while (0)
static zend_op_array *zend_lazy_method_load(
- zend_op_array *op_array, zend_class_entry *ce, const zend_class_entry *pce) {
+ const zend_op_array *op_array, zend_class_entry *ce, const zend_class_entry *pce) {
ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
ZEND_ASSERT(op_array->scope == pce);
ZEND_ASSERT(op_array->prototype == NULL);
@@ -3333,12 +3326,10 @@ static zend_op_array *zend_lazy_method_load(
return new_op_array;
}
-static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
+static zend_class_entry *zend_lazy_class_load(const zend_class_entry *pce)
{
- zend_class_entry *ce;
- Bucket *p, *end;
+ zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
- ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
memcpy(ce, pce, sizeof(zend_class_entry));
ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
ce->refcount = 1;
@@ -3353,7 +3344,7 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
if (ce->default_properties_table) {
zval *dst = emalloc(sizeof(zval) * ce->default_properties_count);
zval *src = ce->default_properties_table;
- zval *end = src + ce->default_properties_count;
+ const zval *end = src + ce->default_properties_count;
ce->default_properties_table = dst;
for (; src != end; src++, dst++) {
@@ -3364,11 +3355,11 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
/* methods */
ce->function_table.pDestructor = ZEND_FUNCTION_DTOR;
if (!(HT_FLAGS(&ce->function_table) & HASH_FLAG_UNINITIALIZED)) {
- p = emalloc(HT_SIZE(&ce->function_table));
+ Bucket *p = emalloc(HT_SIZE(&ce->function_table));
memcpy(p, HT_GET_DATA_ADDR(&ce->function_table), HT_USED_SIZE(&ce->function_table));
HT_SET_DATA_ADDR(&ce->function_table, p);
p = ce->function_table.arData;
- end = p + ce->function_table.nNumUsed;
+ const Bucket *end = p + ce->function_table.nNumUsed;
for (; p != end; p++) {
zend_op_array *op_array = Z_PTR(p->val);
zend_op_array *new_op_array = Z_PTR(p->val) = zend_lazy_method_load(op_array, ce, pce);
@@ -3393,7 +3384,7 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
if (ce->default_static_members_table) {
zval *dst = emalloc(sizeof(zval) * ce->default_static_members_count);
zval *src = ce->default_static_members_table;
- zval *end = src + ce->default_static_members_count;
+ const zval *end = src + ce->default_static_members_count;
ce->default_static_members_table = dst;
for (; src != end; src++, dst++) {
@@ -3404,15 +3395,15 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
/* properties_info */
if (!(HT_FLAGS(&ce->properties_info) & HASH_FLAG_UNINITIALIZED)) {
- p = emalloc(HT_SIZE(&ce->properties_info));
+ Bucket *p = emalloc(HT_SIZE(&ce->properties_info));
memcpy(p, HT_GET_DATA_ADDR(&ce->properties_info), HT_USED_SIZE(&ce->properties_info));
HT_SET_DATA_ADDR(&ce->properties_info, p);
p = ce->properties_info.arData;
- end = p + ce->properties_info.nNumUsed;
+ const Bucket *end = p + ce->properties_info.nNumUsed;
for (; p != end; p++) {
- zend_property_info *prop_info, *new_prop_info;
+ zend_property_info *new_prop_info;
- prop_info = Z_PTR(p->val);
+ const zend_property_info *prop_info = Z_PTR(p->val);
ZEND_ASSERT(prop_info->ce == pce);
ZEND_ASSERT(prop_info->prototype == prop_info);
new_prop_info= zend_arena_alloc(&CG(arena), sizeof(zend_property_info));
@@ -3440,15 +3431,15 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
/* constants table */
if (!(HT_FLAGS(&ce->constants_table) & HASH_FLAG_UNINITIALIZED)) {
- p = emalloc(HT_SIZE(&ce->constants_table));
+ Bucket *p = emalloc(HT_SIZE(&ce->constants_table));
memcpy(p, HT_GET_DATA_ADDR(&ce->constants_table), HT_USED_SIZE(&ce->constants_table));
HT_SET_DATA_ADDR(&ce->constants_table, p);
p = ce->constants_table.arData;
- end = p + ce->constants_table.nNumUsed;
+ const Bucket *end = p + ce->constants_table.nNumUsed;
for (; p != end; p++) {
- zend_class_constant *c, *new_c;
+ zend_class_constant *new_c;
- c = Z_PTR(p->val);
+ const zend_class_constant *c = Z_PTR(p->val);
ZEND_ASSERT(c->ce == pce);
new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
Z_PTR(p->val) = new_c;
@@ -3473,7 +3464,7 @@ static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce)
} while (0)
#endif
-ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key) /* {{{ */
+ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, const zend_string *key) /* {{{ */
{
/* Load parent/interface dependencies first, so we can still gracefully abort linking
* with an exception and remove the class from the class table. This is only possible
@@ -3506,13 +3497,13 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
for (i = 0; i < ce->num_traits; i++) {
zend_class_entry *trait = zend_fetch_class_by_name(ce->trait_names[i].name,
- ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT);
+ ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(trait == NULL)) {
free_alloca(traits_and_interfaces, use_heap);
return NULL;
}
if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) {
- zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name));
+ zend_throw_error(NULL, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name));
free_alloca(traits_and_interfaces, use_heap);
return NULL;
}
@@ -3609,6 +3600,20 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
zend_link_hooked_object_iter(ce);
#endif
+ HashTable **trait_exclude_tables;
+ zend_class_entry **trait_aliases;
+ bool trait_contains_abstract_methods = false;
+ if (ce->num_traits) {
+ zend_traits_init_trait_structures(ce, traits_and_interfaces, &trait_exclude_tables, &trait_aliases);
+ zend_do_traits_method_binding(ce, traits_and_interfaces, trait_exclude_tables, trait_aliases, false, &trait_contains_abstract_methods);
+ zend_do_traits_constant_binding(ce, traits_and_interfaces);
+ zend_do_traits_property_binding(ce, traits_and_interfaces);
+
+ zend_function *fn;
+ ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
+ zend_fixup_trait_method(fn, ce);
+ } ZEND_HASH_FOREACH_END();
+ }
if (parent) {
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
add_dependency_obligation(ce, parent);
@@ -3616,7 +3621,31 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
zend_do_inheritance(ce, parent);
}
if (ce->num_traits) {
- zend_do_bind_traits(ce, traits_and_interfaces);
+ if (trait_contains_abstract_methods) {
+ zend_do_traits_method_binding(ce, traits_and_interfaces, trait_exclude_tables, trait_aliases, true, &trait_contains_abstract_methods);
+
+ /* New abstract methods may have been added, make sure to add
+ * ZEND_ACC_IMPLICIT_ABSTRACT_CLASS to ce. */
+ zend_function *fn;
+ ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
+ zend_fixup_trait_method(fn, ce);
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ if (trait_exclude_tables) {
+ for (i = 0; i < ce->num_traits; i++) {
+ if (traits_and_interfaces[i]) {
+ if (trait_exclude_tables[i]) {
+ zend_hash_destroy(trait_exclude_tables[i]);
+ FREE_HASHTABLE(trait_exclude_tables[i]);
+ }
+ }
+ }
+ efree(trait_exclude_tables);
+ }
+ if (trait_aliases) {
+ efree(trait_aliases);
+ }
}
if (ce->num_interfaces) {
/* Also copy the parent interfaces here, so we don't need to reallocate later. */
@@ -3745,7 +3774,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
/* }}} */
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
-static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
+static inheritance_status zend_can_early_bind(zend_class_entry *ce, const zend_class_entry *parent_ce) /* {{{ */
{
zend_string *key;
zend_function *parent_func;
diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h
index 477874181e416..7171a9385f3ba 100644
--- a/Zend/zend_inheritance.h
+++ b/Zend/zend_inheritance.h
@@ -32,13 +32,13 @@ static zend_always_inline void zend_do_inheritance(zend_class_entry *ce, zend_cl
zend_do_inheritance_ex(ce, parent_ce, 0);
}
-ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key);
+ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, const zend_string *key);
void zend_verify_abstract_class(zend_class_entry *ce);
void zend_build_properties_info_table(zend_class_entry *ce);
ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding);
-void zend_inheritance_check_override(zend_class_entry *ce);
+void zend_inheritance_check_override(const zend_class_entry *ce);
ZEND_API extern zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces);
ZEND_API extern zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies);
@@ -53,7 +53,7 @@ typedef enum {
ZEND_API zend_inheritance_status zend_verify_property_hook_variance(const zend_property_info *prop_info, const zend_function *func);
ZEND_API ZEND_COLD ZEND_NORETURN void zend_hooked_property_variance_error(const zend_property_info *prop_info);
ZEND_API ZEND_COLD ZEND_NORETURN void zend_hooked_property_variance_error_ex(zend_string *value_param_name, zend_string *class_name, zend_string *prop_name);
-ZEND_API void zend_verify_hooked_property(zend_class_entry *ce, zend_property_info *prop_info, zend_string *prop_name);
+ZEND_API void zend_verify_hooked_property(const zend_class_entry *ce, zend_property_info *prop_info, zend_string *prop_name);
END_EXTERN_C()
diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c
index 28bccceaee20a..199ebfb2e9b45 100644
--- a/Zend/zend_ini.c
+++ b/Zend/zend_ini.c
@@ -225,6 +225,7 @@ ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_
while (ini_entry->name) {
p = pemalloc(sizeof(zend_ini_entry), 1);
+ p->def = ini_entry;
p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, 1);
p->on_modify = ini_entry->on_modify;
p->mh_arg1 = ini_entry->mh_arg1;
@@ -480,40 +481,17 @@ ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig)
ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
{
- zend_ini_entry *ini_entry;
+ zend_string *str = zend_ini_str_ex(name, name_length, orig, exists);
- ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
- if (ini_entry) {
- if (exists) {
- *exists = 1;
- }
-
- if (orig && ini_entry->modified) {
- return ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : NULL;
- } else {
- return ini_entry->value ? ZSTR_VAL(ini_entry->value) : NULL;
- }
- } else {
- if (exists) {
- *exists = 0;
- }
- return NULL;
- }
+ return str ? ZSTR_VAL(str) : NULL;
}
/* }}} */
ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
{
- bool exists = 1;
- char *return_value;
+ zend_string *str = zend_ini_str(name, name_length, orig);
- return_value = zend_ini_string_ex(name, name_length, orig, &exists);
- if (!exists) {
- return NULL;
- } else if (!return_value) {
- return_value = "";
- }
- return return_value;
+ return str ? ZSTR_VAL(str) : NULL;
}
/* }}} */
diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h
index 1939d7b89e3ff..5a7377f1181d8 100644
--- a/Zend/zend_ini.h
+++ b/Zend/zend_ini.h
@@ -60,6 +60,7 @@ struct _zend_ini_entry {
uint8_t orig_modifiable;
uint8_t modified;
+ const zend_ini_entry_def *def;
};
BEGIN_EXTERN_C()
diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c
index c0127feabbf68..ce9cc00fdfb95 100644
--- a/Zend/zend_interfaces.c
+++ b/Zend/zend_interfaces.c
@@ -412,13 +412,12 @@ ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *b
zend_call_method_with_0_params(
Z_OBJ_P(object), Z_OBJCE_P(object), NULL, "serialize", &retval);
- if (Z_TYPE(retval) == IS_UNDEF || EG(exception)) {
+ if (Z_TYPE(retval) == IS_UNDEF) {
result = FAILURE;
} else {
switch(Z_TYPE(retval)) {
case IS_NULL:
/* we could also make this '*buf_len = 0' but this allows to skip variables */
- zval_ptr_dtor(&retval);
return FAILURE;
case IS_STRING:
*buffer = (unsigned char*)estrndup(Z_STRVAL(retval), Z_STRLEN(retval));
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index d2a29e670d8bf..0c5bb36501e72 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -217,6 +217,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token T_OBJECT_CAST "'(object)'"
%token T_BOOL_CAST "'(bool)'"
%token T_UNSET_CAST "'(unset)'"
+%token T_VOID_CAST "'(void)'"
%token T_OBJECT_OPERATOR "'->'"
%token T_NULLSAFE_OBJECT_OPERATOR "'?->'"
%token T_DOUBLE_ARROW "'=>'"
@@ -267,7 +268,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type callable_expr callable_variable static_member new_variable
%type encaps_var encaps_var_offset isset_variables
%type top_statement_list use_declarations const_list inner_statement_list if_stmt
-%type alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
+%type alt_if_stmt for_cond_exprs for_exprs switch_case_list global_var_list static_var_list
%type echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
%type implements_list case_list if_stmt_without_else
%type non_empty_parameter_list argument_list non_empty_argument_list property_list
@@ -390,6 +391,7 @@ attributed_statement:
| trait_declaration_statement { $$ = $1; }
| interface_declaration_statement { $$ = $1; }
| enum_declaration_statement { $$ = $1; }
+ | T_CONST const_list ';' { $$ = $2; }
;
top_statement:
@@ -413,7 +415,6 @@ top_statement:
| T_USE use_type group_use_declaration ';' { $$ = $3; $$->attr = $2; }
| T_USE use_declarations ';' { $$ = $2; $$->attr = ZEND_SYMBOL_CLASS; }
| T_USE use_type use_declarations ';' { $$ = $3; $$->attr = $2; }
- | T_CONST const_list ';' { $$ = $2; }
;
use_type:
@@ -507,7 +508,7 @@ statement:
{ $$ = zend_ast_create(ZEND_AST_WHILE, $3, $5); }
| T_DO statement T_WHILE '(' expr ')' ';'
{ $$ = zend_ast_create(ZEND_AST_DO_WHILE, $2, $5); }
- | T_FOR '(' for_exprs ';' for_exprs ';' for_exprs ')' for_statement
+ | T_FOR '(' for_exprs ';' for_cond_exprs ';' for_exprs ')' for_statement
{ $$ = zend_ast_create(ZEND_AST_FOR, $3, $5, $7, $9); }
| T_SWITCH '(' expr ')' switch_case_list
{ $$ = zend_ast_create(ZEND_AST_SWITCH, $3, $5); }
@@ -534,6 +535,7 @@ statement:
{ $$ = zend_ast_create(ZEND_AST_TRY, $3, $5, $6); }
| T_GOTO T_STRING ';' { $$ = zend_ast_create(ZEND_AST_GOTO, $2); }
| T_STRING ':' { $$ = zend_ast_create(ZEND_AST_LABEL, $1); }
+ | T_VOID_CAST expr ';' { $$ = zend_ast_create(ZEND_AST_CAST_VOID, $2); }
;
catch_list:
@@ -896,7 +898,7 @@ return_type:
argument_list:
'(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); }
| '(' non_empty_argument_list possible_comma ')' { $$ = $2; }
- | '(' T_ELLIPSIS ')' { $$ = zend_ast_create(ZEND_AST_CALLABLE_CONVERT); }
+ | '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); }
;
non_empty_argument_list:
@@ -1167,6 +1169,12 @@ echo_expr:
expr { $$ = zend_ast_create(ZEND_AST_ECHO, $1); }
;
+for_cond_exprs:
+ %empty { $$ = NULL; }
+ | non_empty_for_exprs ',' expr { $$ = zend_ast_list_add($1, $3); }
+ | expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
+;
+
for_exprs:
%empty { $$ = NULL; }
| non_empty_for_exprs { $$ = $1; }
@@ -1174,6 +1182,8 @@ for_exprs:
non_empty_for_exprs:
non_empty_for_exprs ',' expr { $$ = zend_ast_list_add($1, $3); }
+ | non_empty_for_exprs ',' T_VOID_CAST expr { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CAST_VOID, $4)); }
+ | T_VOID_CAST expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, zend_ast_create(ZEND_AST_CAST_VOID, $2)); }
| expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
;
@@ -1819,6 +1829,14 @@ static YYSIZE_T zend_yytnamerr(char *yyres, const char *yystr)
return sizeof("\"\\\"")-1;
}
+ /* We used "amp" as a dummy label to avoid a duplicate token literal warning. */
+ if (strcmp(toktype, "\"amp\"") == 0) {
+ if (yyres) {
+ yystpcpy(yyres, "token \"&\"");
+ }
+ return sizeof("token \"&\"")-1;
+ }
+
/* Strip off the outer quote marks */
if (toktype_len >= 2 && *toktype == '"') {
toktype++;
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index 7ae73875926eb..4c883b81c5f7d 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -1657,6 +1657,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
RETURN_TOKEN(T_UNSET_CAST);
}
+"("{TABS_AND_SPACES}("void"){TABS_AND_SPACES}")" {
+ RETURN_TOKEN(T_VOID_CAST);
+}
+
"eval" {
RETURN_TOKEN_WITH_IDENT(T_EVAL);
}
diff --git a/Zend/zend_max_execution_timer.h b/Zend/zend_max_execution_timer.h
index 789f50e6b7dcd..6557d6c918455 100644
--- a/Zend/zend_max_execution_timer.h
+++ b/Zend/zend_max_execution_timer.h
@@ -21,8 +21,10 @@
#include "zend_long.h"
+BEGIN_EXTERN_C()
/* Must be called after calls to fork() */
ZEND_API void zend_max_execution_timer_init(void);
+END_EXTERN_C()
void zend_max_execution_timer_settime(zend_long seconds);
void zend_max_execution_timer_shutdown(void);
diff --git a/Zend/zend_modules.h b/Zend/zend_modules.h
index 03998c36e5c0e..efc04a63bb363 100644
--- a/Zend/zend_modules.h
+++ b/Zend/zend_modules.h
@@ -31,7 +31,7 @@
#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module
#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module
-#define ZEND_MODULE_API_NO 20240924
+#define ZEND_MODULE_API_NO 20240925
#ifdef ZTS
#define USING_ZTS 1
#else
diff --git a/Zend/zend_multiply.h b/Zend/zend_multiply.h
index a64cd612148fe..bdeb435d44319 100644
--- a/Zend/zend_multiply.h
+++ b/Zend/zend_multiply.h
@@ -79,46 +79,25 @@
else (lval) = __tmpvar; \
} while (0)
-#elif defined(ZEND_WIN32)
-
-# ifdef _M_X64
-# pragma intrinsic(_mul128)
-# define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
- __int64 __high; \
- __int64 __low = _mul128((a), (b), &__high); \
- if ((__low >> 63I64) == __high) { \
- (usedval) = 0; \
- (lval) = __low; \
- } else { \
- (usedval) = 1; \
- (dval) = (double)(a) * (double)(b); \
- } \
-} while (0)
-# elif defined(_M_ARM64)
-# pragma intrinsic(__mulh)
-# define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
- __int64 __high = __mulh((a), (b)); \
- __int64 __low = (a) * (b); \
- if ((__low >> 63I64) == __high) { \
- (usedval) = 0; \
- (lval) = __low; \
- } else { \
- (usedval) = 1; \
- (dval) = (double)(a) * (double)(b); \
- } \
+#elif defined(ZEND_WIN32) && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long long __tmpvar; \
+ if (((usedval) = FAILED(LongLongMult((a), (b), &__tmpvar)))) { \
+ (dval) = (double) (a) * (double) (b); \
+ } \
+ else (lval) = __tmpvar; \
} while (0)
-# else
-# define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
- zend_long __lres = (a) * (b); \
- long double __dres = (long double)(a) * (long double)(b); \
- long double __delta = (long double) __lres - __dres; \
- if ( ((usedval) = (( __dres + __delta ) != __dres))) { \
- (dval) = __dres; \
- } else { \
- (lval) = __lres; \
+
+#elif defined(ZEND_WIN32) && SIZEOF_LONG == SIZEOF_ZEND_LONG
+
+#define ZEND_SIGNED_MULTIPLY_LONG(a, b, lval, dval, usedval) do { \
+ long __tmpvar; \
+ if (((usedval) = FAILED(LongMult((a), (b), &__tmpvar)))) { \
+ (dval) = (double) (a) * (double) (b); \
} \
+ else (lval) = __tmpvar; \
} while (0)
-# endif
#elif defined(__powerpc64__) && defined(__GNUC__)
@@ -311,6 +290,19 @@ static zend_always_inline size_t zend_safe_address(size_t nmemb, size_t size, si
return (size_t) res;
}
+#elif defined(_MSC_VER)
+
+static zend_always_inline size_t zend_safe_address(size_t nmemb, size_t size, size_t offset, bool *overflow)
+{
+ size_t res;
+ if (UNEXPECTED(FAILED(ULongLongMult(nmemb, size, &res)) || FAILED(ULongLongAdd(res, offset, &res)))) {
+ *overflow = 1;
+ return 0;
+ }
+ *overflow = 0;
+ return res;
+}
+
#else
static zend_always_inline size_t zend_safe_address(size_t nmemb, size_t size, size_t offset, bool *overflow)
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 4aea3501cf269..a31c7d2afdeee 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -877,7 +877,6 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
if (!((*guard) & IN_ISSET)) {
GC_ADDREF(zobj);
- ZVAL_UNDEF(&tmp_result);
*guard |= IN_ISSET;
zend_std_call_issetter(zobj, name, &tmp_result);
@@ -1680,7 +1679,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce
func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE
| ZEND_ACC_PUBLIC
| ZEND_ACC_VARIADIC
- | (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED));
+ | (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD));
/* Attributes outlive the trampoline because they are created by the compiler. */
func->attributes = fbc->common.attributes;
if (is_static) {
@@ -1775,6 +1774,9 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
func = (zend_function *)(uintptr_t)ecalloc(1, sizeof(zend_internal_function));
}
func->type = ZEND_INTERNAL_FUNCTION;
+ /* This trampoline does not use the call_trampoline_op, so it won't reuse the call frame,
+ * which means we don't even need to reserve a temporary for observers. */
+ func->common.T = 0;
func->common.arg_flags[0] = 0;
func->common.arg_flags[1] = 0;
func->common.arg_flags[2] = 0;
@@ -1807,7 +1809,7 @@ static zend_always_inline zend_function *zend_get_user_call_function(zend_class_
}
/* }}} */
-static ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc, zend_string *method_name, zend_class_entry *scope) /* {{{ */
+ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc, zend_string *method_name, zend_class_entry *scope) /* {{{ */
{
zend_throw_error(NULL, "Call to %s method %s::%s() from %s%s",
zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name),
@@ -1817,7 +1819,7 @@ static ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc,
}
/* }}} */
-static ZEND_COLD zend_never_inline void zend_abstract_method_call(zend_function *fbc) /* {{{ */
+ZEND_API ZEND_COLD zend_never_inline void zend_abstract_method_call(zend_function *fbc) /* {{{ */
{
zend_throw_error(NULL, "Cannot call abstract method %s::%s()",
ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
@@ -2136,8 +2138,9 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
object_lhs = false;
}
ZEND_ASSERT(Z_TYPE_P(value) != IS_OBJECT);
- uint8_t target_type = (Z_TYPE_P(value) == IS_FALSE || Z_TYPE_P(value) == IS_TRUE)
- ? _IS_BOOL : Z_TYPE_P(value);
+ uint8_t target_type = Z_TYPE_P(value);
+ /* Should be handled in zend_compare(). */
+ ZEND_ASSERT(target_type != IS_FALSE && target_type != IS_TRUE);
if (Z_OBJ_HT_P(object)->cast_object(Z_OBJ_P(object), &casted, target_type) == FAILURE) {
// TODO: Less crazy.
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
@@ -2448,17 +2451,9 @@ ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **c
return FAILURE;
}
*fptr_ptr = Z_FUNC_P(func);
-
*ce_ptr = ce;
- if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
- if (obj_ptr) {
- *obj_ptr = NULL;
- }
- } else {
- if (obj_ptr) {
- *obj_ptr = obj;
- }
- }
+ *obj_ptr = obj;
+
return SUCCESS;
}
/* }}} */
diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h
index c7348a7424558..7e7d3df37a6ad 100644
--- a/Zend/zend_object_handlers.h
+++ b/Zend/zend_object_handlers.h
@@ -272,6 +272,8 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2);
ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only);
/* Use zend_std_get_properties_ex() */
ZEND_API HashTable *rebuild_object_properties_internal(zend_object *zobj);
+ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc, zend_string *method_name, zend_class_entry *scope);
+ZEND_API ZEND_COLD zend_never_inline void zend_abstract_method_call(zend_function *fbc);
static zend_always_inline HashTable *zend_std_get_properties_ex(zend_object *object)
{
@@ -333,8 +335,8 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info);
#define zend_release_properties(ht) do { \
- if ((ht) && !(GC_FLAGS(ht) & GC_IMMUTABLE) && !GC_DELREF(ht)) { \
- zend_array_destroy(ht); \
+ if (ht) { \
+ zend_array_release(ht); \
} \
} while (0)
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index f32ae13e06793..6e7d31e15a40f 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -112,7 +112,7 @@ ZEND_API void destroy_zend_function(zend_function *function)
ZEND_API void zend_type_release(zend_type type, bool persistent) {
if (ZEND_TYPE_HAS_LIST(type)) {
zend_type *list_type;
- ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
+ ZEND_TYPE_LIST_FOREACH_MUTABLE(ZEND_TYPE_LIST(type), list_type) {
zend_type_release(*list_type, persistent);
} ZEND_TYPE_LIST_FOREACH_END();
if (!ZEND_TYPE_USES_ARENA(type)) {
@@ -580,6 +580,18 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
efree(op_array->vars);
}
+ /* ZEND_ACC_PTR_OPS and ZEND_ACC_OVERRIDE use the same value */
+ if ((op_array->fn_flags & ZEND_ACC_PTR_OPS) && !op_array->function_name) {
+ zend_op *op = op_array->opcodes;
+ zend_op *end = op + op_array->last;
+ while (op < end) {
+ if (op->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) {
+ HashTable *attributes = Z_PTR_P(RT_CONSTANT(op+1, (op+1)->op1));
+ zend_hash_release(attributes);
+ }
+ op++;
+ }
+ }
if (op_array->literals) {
zval *literal = op_array->literals;
zval *end = literal + op_array->last_literal;
@@ -636,13 +648,6 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
}
if (op_array->num_dynamic_func_defs) {
for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
- /* Closures overwrite static_variables in their copy.
- * Make sure to destroy them when the prototype function is destroyed. */
- if (op_array->dynamic_func_defs[i]->static_variables
- && (op_array->dynamic_func_defs[i]->fn_flags & ZEND_ACC_CLOSURE)) {
- zend_array_destroy(op_array->dynamic_func_defs[i]->static_variables);
- op_array->dynamic_func_defs[i]->static_variables = NULL;
- }
destroy_op_array(op_array->dynamic_func_defs[i]);
}
efree(op_array->dynamic_func_defs);
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c
index ae75e95a71c1d..879141d1a139e 100644
--- a/Zend/zend_operators.c
+++ b/Zend/zend_operators.c
@@ -1389,10 +1389,13 @@ ZEND_API zend_result ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *o
}
/* }}} */
-/* Returns SUCCESS/TYPES_NOT_HANDLED/DIV_BY_ZERO */
-#define TYPES_NOT_HANDLED 1
-#define DIV_BY_ZERO 2
-static int ZEND_FASTCALL div_function_base(zval *result, zval *op1, zval *op2) /* {{{ */
+typedef enum {
+ DIV_SUCCESS,
+ DIV_BY_ZERO,
+ DIV_TYPES_NOT_HANDLED
+} zend_div_status;
+
+static zend_div_status ZEND_FASTCALL div_function_base(zval *result, const zval *op1, const zval *op2) /* {{{ */
{
uint8_t type_pair = TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2));
@@ -1402,34 +1405,34 @@ static int ZEND_FASTCALL div_function_base(zval *result, zval *op1, zval *op2) /
} else if (Z_LVAL_P(op2) == -1 && Z_LVAL_P(op1) == ZEND_LONG_MIN) {
/* Prevent overflow error/crash */
ZVAL_DOUBLE(result, (double) ZEND_LONG_MIN / -1);
- return SUCCESS;
+ return DIV_SUCCESS;
}
if (Z_LVAL_P(op1) % Z_LVAL_P(op2) == 0) { /* integer */
ZVAL_LONG(result, Z_LVAL_P(op1) / Z_LVAL_P(op2));
} else {
ZVAL_DOUBLE(result, ((double) Z_LVAL_P(op1)) / Z_LVAL_P(op2));
}
- return SUCCESS;
+ return DIV_SUCCESS;
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_DOUBLE))) {
if (Z_DVAL_P(op2) == 0) {
return DIV_BY_ZERO;
}
ZVAL_DOUBLE(result, Z_DVAL_P(op1) / Z_DVAL_P(op2));
- return SUCCESS;
+ return DIV_SUCCESS;
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_DOUBLE, IS_LONG))) {
if (Z_LVAL_P(op2) == 0) {
return DIV_BY_ZERO;
}
ZVAL_DOUBLE(result, Z_DVAL_P(op1) / (double)Z_LVAL_P(op2));
- return SUCCESS;
+ return DIV_SUCCESS;
} else if (EXPECTED(type_pair == TYPE_PAIR(IS_LONG, IS_DOUBLE))) {
if (Z_DVAL_P(op2) == 0) {
return DIV_BY_ZERO;
}
ZVAL_DOUBLE(result, (double)Z_LVAL_P(op1) / Z_DVAL_P(op2));
- return SUCCESS;
+ return DIV_SUCCESS;
} else {
- return TYPES_NOT_HANDLED;
+ return DIV_TYPES_NOT_HANDLED;
}
}
/* }}} */
@@ -1439,8 +1442,8 @@ ZEND_API zend_result ZEND_FASTCALL div_function(zval *result, zval *op1, zval *o
ZVAL_DEREF(op1);
ZVAL_DEREF(op2);
- int retval = div_function_base(result, op1, op2);
- if (EXPECTED(retval == SUCCESS)) {
+ zend_div_status retval = div_function_base(result, op1, op2);
+ if (EXPECTED(retval == DIV_SUCCESS)) {
return SUCCESS;
}
@@ -1461,7 +1464,7 @@ ZEND_API zend_result ZEND_FASTCALL div_function(zval *result, zval *op1, zval *o
}
retval = div_function_base(&result_copy, &op1_copy, &op2_copy);
- if (retval == SUCCESS) {
+ if (retval == DIV_SUCCESS) {
if (result == op1) {
zval_ptr_dtor(result);
}
@@ -1470,7 +1473,7 @@ ZEND_API zend_result ZEND_FASTCALL div_function(zval *result, zval *op1, zval *o
}
div_by_zero:
- ZEND_ASSERT(retval == DIV_BY_ZERO && "TYPES_NOT_HANDLED should not occur here");
+ ZEND_ASSERT(retval == DIV_BY_ZERO && "DIV_TYPES_NOT_HANDLED should not occur here");
if (result != op1) {
ZVAL_UNDEF(result);
}
@@ -2330,13 +2333,29 @@ ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2) /* {{{ */
}
if (Z_TYPE_P(op1) == IS_OBJECT
- && Z_TYPE_P(op2) == IS_OBJECT
- && Z_OBJ_P(op1) == Z_OBJ_P(op2)) {
- return 0;
- } else if (Z_TYPE_P(op1) == IS_OBJECT) {
- return Z_OBJ_HANDLER_P(op1, compare)(op1, op2);
- } else if (Z_TYPE_P(op2) == IS_OBJECT) {
- return Z_OBJ_HANDLER_P(op2, compare)(op1, op2);
+ || Z_TYPE_P(op2) == IS_OBJECT) {
+ zval *object, *other;
+ if (Z_TYPE_P(op1) == IS_OBJECT) {
+ object = op1;
+ other = op2;
+ } else {
+ object = op2;
+ other = op1;
+ }
+ if (EXPECTED(Z_TYPE_P(other) == IS_OBJECT)) {
+ if (Z_OBJ_P(object) == Z_OBJ_P(other)) {
+ return 0;
+ }
+ } else if (Z_TYPE_P(other) == IS_TRUE || Z_TYPE_P(other) == IS_FALSE) {
+ zval casted;
+ if (Z_OBJ_HANDLER_P(object, cast_object)(Z_OBJ_P(object), &casted, _IS_BOOL) == FAILURE) {
+ return object == op1 ? 1 : -1;
+ }
+ int ret = object == op1 ? zend_compare(&casted, other) : zend_compare(other, &casted);
+ ZEND_ASSERT(!Z_REFCOUNTED_P(&casted));
+ return ret;
+ }
+ return Z_OBJ_HANDLER_P(object, compare)(op1, op2);
}
if (!converted) {
diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
index 6a3d54a9dbf2e..db86345b69968 100644
--- a/Zend/zend_operators.h
+++ b/Zend/zend_operators.h
@@ -579,6 +579,22 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL
} else {
Z_LVAL_P(op1) = llresult;
}
+#elif defined(ZEND_WIN32) && SIZEOF_LONG == SIZEOF_ZEND_LONG
+ long lresult;
+ if (UNEXPECTED(FAILED(LongAdd(Z_LVAL_P(op1), 1, &lresult)))) {
+ /* switch to double */
+ ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
+ } else {
+ Z_LVAL_P(op1) = lresult;
+ }
+#elif defined(ZEND_WIN32) && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
+ long long llresult;
+ if (UNEXPECTED(FAILED(LongLongAdd(Z_LVAL_P(op1), 1, &llresult)))) {
+ /* switch to double */
+ ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
+ } else {
+ Z_LVAL_P(op1) = llresult;
+ }
#else
if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MAX)) {
/* switch to double */
@@ -642,6 +658,22 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL
} else {
Z_LVAL_P(op1) = llresult;
}
+#elif defined(ZEND_WIN32) && SIZEOF_LONG == SIZEOF_ZEND_LONG
+ long lresult;
+ if (UNEXPECTED(FAILED(LongSub(Z_LVAL_P(op1), 1, &lresult)))) {
+ /* switch to double */
+ ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
+ } else {
+ Z_LVAL_P(op1) = lresult;
+ }
+#elif defined(ZEND_WIN32) && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
+ long long llresult;
+ if (UNEXPECTED(FAILED(LongLongSub(Z_LVAL_P(op1), 1, &llresult)))) {
+ /* switch to double */
+ ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
+ } else {
+ Z_LVAL_P(op1) = llresult;
+ }
#else
if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MIN)) {
/* switch to double */
@@ -724,6 +756,20 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL
} else {
ZVAL_LONG(result, llresult);
}
+#elif defined(ZEND_WIN32) && SIZEOF_LONG == SIZEOF_ZEND_LONG
+ long lresult;
+ if (UNEXPECTED(FAILED(LongAdd(Z_LVAL_P(op1), Z_LVAL_P(op2), &lresult)))) {
+ ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
+ } else {
+ ZVAL_LONG(result, lresult);
+ }
+#elif defined(ZEND_WIN32) && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
+ long long llresult;
+ if (UNEXPECTED(FAILED(LongLongAdd(Z_LVAL_P(op1), Z_LVAL_P(op2), &llresult)))) {
+ ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
+ } else {
+ ZVAL_LONG(result, llresult);
+ }
#else
/*
* 'result' may alias with op1 or op2, so we need to
@@ -814,6 +860,20 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL
} else {
ZVAL_LONG(result, llresult);
}
+#elif defined(ZEND_WIN32) && SIZEOF_LONG == SIZEOF_ZEND_LONG
+ long lresult;
+ if (UNEXPECTED(FAILED(LongSub(Z_LVAL_P(op1), Z_LVAL_P(op2), &lresult)))) {
+ ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
+ } else {
+ ZVAL_LONG(result, lresult);
+ }
+#elif defined(ZEND_WIN32) && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
+ long long llresult;
+ if (UNEXPECTED(FAILED(LongLongSub(Z_LVAL_P(op1), Z_LVAL_P(op2), &llresult)))) {
+ ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
+ } else {
+ ZVAL_LONG(result, llresult);
+ }
#else
/*
* 'result' may alias with op1 or op2, so we need to
diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h
index e4195402fc294..3b2278d545637 100644
--- a/Zend/zend_portability.h
+++ b/Zend/zend_portability.h
@@ -47,6 +47,9 @@
#include "../TSRM/TSRM.h"
#include
+#if ZEND_DEBUG && defined(NDEBUG)
+# error "NDEBUG must not be defined when ZEND_DEBUG is enabled"
+#endif
#include
#include
@@ -304,9 +307,7 @@ char *alloca();
#if defined(__GNUC__) && ZEND_GCC_VERSION >= 3004 && defined(__i386__)
# define ZEND_FASTCALL __attribute__((fastcall))
-#elif defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER == 1700
-# define ZEND_FASTCALL __fastcall
-#elif defined(_MSC_VER) && _MSC_VER >= 1800
+#elif defined(_MSC_VER)
# define ZEND_FASTCALL __vectorcall
#else
# define ZEND_FASTCALL
@@ -337,9 +338,7 @@ char *alloca();
# define HAVE_BUILTIN_CONSTANT_P
#endif
-#if __has_attribute(element_count)
-#define ZEND_ELEMENT_COUNT(m) __attribute__((element_count(m)))
-#elif __has_attribute(counted_by)
+#if __has_attribute(counted_by)
#define ZEND_ELEMENT_COUNT(m) __attribute__((counted_by(m)))
#else
#define ZEND_ELEMENT_COUNT(m)
@@ -444,14 +443,6 @@ char *alloca();
# define ZTS_V 0
#endif
-#ifndef LONG_MAX
-# define LONG_MAX 2147483647L
-#endif
-
-#ifndef LONG_MIN
-# define LONG_MIN (- LONG_MAX - 1)
-#endif
-
#define MAX_LENGTH_OF_DOUBLE 32
#undef MIN
@@ -520,6 +511,13 @@ extern "C++" {
# undef HAVE_FUNC_ATTRIBUTE_IFUNC
#endif
+#if __has_feature(memory_sanitizer)
+# include
+# define MSAN_UNPOISON(value) __msan_unpoison(&(value), sizeof(value))
+#else
+# define MSAN_UNPOISON(value)
+#endif
+
/* Only use ifunc resolvers if we have __builtin_cpu_supports() and __builtin_cpu_init(),
* otherwise the use of zend_cpu_supports() may not be safe inside ifunc resolvers. */
#if defined(HAVE_FUNC_ATTRIBUTE_IFUNC) && defined(HAVE_FUNC_ATTRIBUTE_TARGET) && \
@@ -791,13 +789,9 @@ extern "C++" {
# define ZEND_STATIC_ASSERT(c, m)
#endif
-#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) /* C11 */
+#if ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) /* C11 */ \
+ || (defined(__cplusplus) && __cplusplus >= 201103L) /* C++11 */) && !defined(ZEND_WIN32)
typedef max_align_t zend_max_align_t;
-#elif (defined(__cplusplus) && __cplusplus >= 201103L) /* C++11 */
-extern "C++" {
-# include
-}
-typedef std::max_align_t zend_max_align_t;
#else
typedef union {
char c;
diff --git a/Zend/zend_smart_str.c b/Zend/zend_smart_str.c
index ade137a4bb6c3..501f6e6176c8b 100644
--- a/Zend/zend_smart_str.c
+++ b/Zend/zend_smart_str.c
@@ -131,6 +131,13 @@ ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...)
va_end(arg);
}
+ZEND_API void smart_string_append_printf(smart_string *dest, const char *format, ...) {
+ va_list arg;
+ va_start(arg, format);
+ zend_printf_to_smart_string(dest, format, arg);
+ va_end(arg);
+}
+
#define SMART_STRING_OVERHEAD (ZEND_MM_OVERHEAD + 1)
#define SMART_STRING_START_SIZE 256
#define SMART_STRING_START_LEN (SMART_STRING_START_SIZE - SMART_STRING_OVERHEAD)
diff --git a/Zend/zend_smart_string.h b/Zend/zend_smart_string.h
index 8149b29fb3330..9f04e1a340ad2 100644
--- a/Zend/zend_smart_string.h
+++ b/Zend/zend_smart_string.h
@@ -48,6 +48,9 @@
#define smart_string_append_unsigned(str, val) \
smart_string_append_unsigned_ex((str), (val), 0)
+ZEND_API void smart_string_append_printf(smart_string *dest, const char *format, ...)
+ ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
+
ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len);
ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len);
diff --git a/Zend/zend_string.h b/Zend/zend_string.h
index 93b9207a80059..e9e2b947a6c91 100644
--- a/Zend/zend_string.h
+++ b/Zend/zend_string.h
@@ -560,6 +560,8 @@ EMPTY_SWITCH_DEFAULT_CASE()
#endif
}
+// When adding a new string here, please also update build/gen_stub.php to the
+// known strings to be used in property registration; see gh-15751
#define ZEND_KNOWN_STRINGS(_) \
_(ZEND_STR_FILE, "file") \
_(ZEND_STR_LINE, "line") \
@@ -622,6 +624,8 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_NULL_LOWERCASE, "null") \
_(ZEND_STR_MIXED, "mixed") \
_(ZEND_STR_TRAVERSABLE, "Traversable") \
+ _(ZEND_STR_SELF, "self") \
+ _(ZEND_STR_PARENT, "parent") \
_(ZEND_STR_SLEEP, "__sleep") \
_(ZEND_STR_WAKEUP, "__wakeup") \
_(ZEND_STR_CASES, "cases") \
diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c
index a20b4dda93985..88b905ffeab40 100644
--- a/Zend/zend_strtod.c
+++ b/Zend/zend_strtod.c
@@ -292,10 +292,6 @@ static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
#define DBL_MAX 1.7014118346046923e+38
#endif
-#ifndef LONG_MAX
-#define LONG_MAX 2147483647
-#endif
-
#else /* ifndef Bad_float_h */
#include "float.h"
#endif /* Bad_float_h */
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 8f012868ddab4..4a6d00b9d73ea 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -28,7 +28,6 @@
#include
#ifdef __SSE2__
-# include
# include
#endif
#if defined(__AVX2__)
@@ -209,8 +208,14 @@ typedef struct {
/* This iterates over a zend_type_list. */
#define ZEND_TYPE_LIST_FOREACH(list, type_ptr) do { \
+ const zend_type *_list = (list)->types; \
+ const zend_type *_end = _list + (list)->num_types; \
+ for (; _list < _end; _list++) { \
+ type_ptr = _list;
+
+#define ZEND_TYPE_LIST_FOREACH_MUTABLE(list, type_ptr) do { \
zend_type *_list = (list)->types; \
- zend_type *_end = _list + (list)->num_types; \
+ const zend_type *_end = _list + (list)->num_types; \
for (; _list < _end; _list++) { \
type_ptr = _list;
@@ -221,7 +226,22 @@ typedef struct {
/* This iterates over any zend_type. If it's a type list, all list elements will
* be visited. If it's a single type, only the single type is visited. */
#define ZEND_TYPE_FOREACH(type, type_ptr) do { \
- zend_type *_cur, *_end; \
+ const zend_type *_cur, *_end; \
+ if (ZEND_TYPE_HAS_LIST(type)) { \
+ zend_type_list *_list = ZEND_TYPE_LIST(type); \
+ _cur = _list->types; \
+ _end = _cur + _list->num_types; \
+ } else { \
+ _cur = &(type); \
+ _end = _cur + 1; \
+ } \
+ do { \
+ type_ptr = _cur;
+
+
+#define ZEND_TYPE_FOREACH_MUTABLE(type, type_ptr) do { \
+ zend_type *_cur; \
+ const zend_type *_end; \
if (ZEND_TYPE_HAS_LIST(type)) { \
zend_type_list *_list = ZEND_TYPE_LIST(type); \
_cur = _list->types; \
@@ -808,7 +828,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#define IS_ARRAY_EX (IS_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT) | (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT))
#define IS_OBJECT_EX (IS_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT) | (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT))
#define IS_RESOURCE_EX (IS_RESOURCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
-#define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
+#define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT) | (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT))
#define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
@@ -943,6 +963,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#define Z_OPT_REFCOUNTED(zval) Z_TYPE_INFO_REFCOUNTED(Z_TYPE_INFO(zval))
#define Z_OPT_REFCOUNTED_P(zval_p) Z_OPT_REFCOUNTED(*(zval_p))
+#define Z_OPT_COLLECTABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT)) != 0)
+#define Z_OPT_COLLECTABLE_P(zval_p) Z_OPT_COLLECTABLE(*(zval_p))
+
/* deprecated: (COPYABLE is the same as IS_ARRAY) */
#define Z_OPT_COPYABLE(zval) (Z_OPT_TYPE(zval) == IS_ARRAY)
#define Z_OPT_COPYABLE_P(zval_p) Z_OPT_COPYABLE(*(zval_p))
@@ -1273,14 +1296,16 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#define Z_DELREF(z) Z_DELREF_P(&(z))
#define Z_TRY_ADDREF_P(pz) do { \
- if (Z_REFCOUNTED_P((pz))) { \
- Z_ADDREF_P((pz)); \
+ zval *_pz = (pz); \
+ if (Z_REFCOUNTED_P(_pz)) { \
+ Z_ADDREF_P(_pz); \
} \
} while (0)
#define Z_TRY_DELREF_P(pz) do { \
- if (Z_REFCOUNTED_P((pz))) { \
- Z_DELREF_P((pz)); \
+ zval *_pz = (pz); \
+ if (Z_REFCOUNTED_P(_pz)) { \
+ Z_DELREF_P(_pz); \
} \
} while (0)
diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c
index 27e09d7db22b1..00f10b08f80ab 100644
--- a/Zend/zend_variables.c
+++ b/Zend/zend_variables.c
@@ -85,6 +85,20 @@ ZEND_API void zval_ptr_dtor(zval *zval_ptr) /* {{{ */
}
/* }}} */
+ZEND_API void zval_ptr_safe_dtor(zval *zval_ptr)
+{
+ if (Z_REFCOUNTED_P(zval_ptr)) {
+ zend_refcounted *ref = Z_COUNTED_P(zval_ptr);
+
+ if (GC_DELREF(ref) == 0) {
+ ZVAL_NULL(zval_ptr);
+ rc_dtor_func(ref);
+ } else {
+ gc_check_possible_root(ref);
+ }
+ }
+}
+
ZEND_API void zval_internal_ptr_dtor(zval *zval_ptr) /* {{{ */
{
if (Z_REFCOUNTED_P(zval_ptr)) {
diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h
index d504b0f0f5795..1cb745ca1b1dc 100644
--- a/Zend/zend_variables.h
+++ b/Zend/zend_variables.h
@@ -78,6 +78,7 @@ static zend_always_inline void zval_ptr_dtor_str(zval *zval_ptr)
}
ZEND_API void zval_ptr_dtor(zval *zval_ptr);
+ZEND_API void zval_ptr_safe_dtor(zval *zval_ptr);
ZEND_API void zval_internal_ptr_dtor(zval *zvalue);
/* Kept for compatibility */
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 37e0e0fb43d95..9c2ba0038b4a4 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -1109,7 +1109,16 @@ ZEND_VM_HANDLER(29, ZEND_ASSIGN_STATIC_PROP_OP, ANY, ANY, OP)
SAVE_OPLINE();
- if (UNEXPECTED(zend_fetch_static_property_address(&prop, &prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
+ prop = zend_fetch_static_property_address(&prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ UNDEF_RESULT();
+ FREE_OP_DATA();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
UNDEF_RESULT();
FREE_OP_DATA();
HANDLE_EXCEPTION();
@@ -1423,7 +1432,15 @@ ZEND_VM_HANDLER(38, ZEND_PRE_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT)
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ UNDEF_RESULT();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -1449,7 +1466,15 @@ ZEND_VM_HANDLER(40, ZEND_POST_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT)
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ UNDEF_RESULT();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -1829,19 +1854,33 @@ ZEND_VM_HANDLER(89, ZEND_FETCH_IS, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
-ZEND_VM_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type)
+ZEND_VM_INLINE_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type)
{
USE_OPLINE
zval *prop;
+ zend_property_info *prop_info;
SAVE_OPLINE();
- if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
+ prop = zend_fetch_static_property_address(
+ &prop_info, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
+ type == BP_VAR_W ? opline->extended_value : 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS));
prop = &EG(uninitialized_zval);
+ } else if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ if (Z_TYPE_P(prop) == IS_OBJECT) {
+ ZEND_VM_C_GOTO(copy_deref);
+ } else if (type != BP_VAR_UNSET || Z_TYPE_P(prop) != IS_UNDEF) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
+ }
+ prop = &EG(uninitialized_zval);
}
if (type == BP_VAR_R || type == BP_VAR_IS) {
+ZEND_VM_C_LABEL(copy_deref):
ZVAL_COPY_DEREF(EX_VAR(opline->result.var), prop);
} else {
ZVAL_INDIRECT(EX_VAR(opline->result.var), prop);
@@ -1870,10 +1909,11 @@ ZEND_VM_HANDLER(175, ZEND_FETCH_STATIC_PROP_RW, ANY, CLASS_FETCH, CACHE_SLOT)
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
ZEND_VM_HANDLER(177, ZEND_FETCH_STATIC_PROP_FUNC_ARG, ANY, CLASS_FETCH, FETCH_REF|CACHE_SLOT)
{
- int fetch_type =
- (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
- BP_VAR_W : BP_VAR_R;
- ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, fetch_type);
+ if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) {
+ ZEND_VM_DISPATCH_TO_HANDLER(ZEND_FETCH_STATIC_PROP_W);
+ } else {
+ ZEND_VM_DISPATCH_TO_HANDLER(ZEND_FETCH_STATIC_PROP_R);
+ }
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
@@ -2584,7 +2624,8 @@ ZEND_VM_HANDLER(25, ZEND_ASSIGN_STATIC_PROP, ANY, ANY, CACHE_SLOT, SPEC(OP_DATA=
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
FREE_OP_DATA();
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -2878,7 +2919,16 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_STATIC_PROP_REF, ANY, ANY, CACHE_SLOT|SRC)
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ FREE_OP_DATA();
+ UNDEF_RESULT();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
FREE_OP_DATA();
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -3198,7 +3248,7 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
USE_OPLINE
SAVE_OPLINE();
- zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -4147,8 +4197,15 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER))
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!RETURN_VALUE_USED(opline)) {
@@ -4251,8 +4308,15 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
@@ -4359,7 +4423,7 @@ ZEND_VM_C_LABEL(fcall_end):
ZEND_VM_CONTINUE();
}
-ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED|CACHE_SLOT)
+ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
{
if (OP1_TYPE == IS_UNUSED) {
SAVE_OPLINE();
@@ -4401,7 +4465,6 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}
zend_reference *ref = NULL;
- void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
@@ -4418,7 +4481,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -5593,11 +5656,10 @@ ZEND_VM_HOT_HANDLER(199, ZEND_CHECK_UNDEF_ARGS, UNUSED, UNUSED)
ZEND_VM_COLD_HELPER(zend_missing_arg_helper, ANY, ANY)
{
-#ifdef ZEND_VM_IP_GLOBAL_REG
USE_OPLINE
SAVE_OPLINE();
-#endif
+
zend_missing_arg_error(execute_data);
HANDLE_EXCEPTION();
}
@@ -5607,14 +5669,14 @@ ZEND_VM_HELPER(zend_verify_recv_arg_type_helper, ANY, ANY, zval *op_1)
USE_OPLINE
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1))) {
HANDLE_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED, CACHE_SLOT)
+ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED)
{
USE_OPLINE
uint32_t arg_num = opline->op1.num;
@@ -5633,7 +5695,7 @@ ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED, CACHE_SLOT)
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_RECV, op->op2.num == MAY_BE_ANY, ZEND_RECV_NOTYPE, NUM, NUM, CACHE_SLOT)
+ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_RECV, op->op2.num == MAY_BE_ANY, ZEND_RECV_NOTYPE, NUM, NUM)
{
USE_OPLINE
uint32_t arg_num = opline->op1.num;
@@ -5645,7 +5707,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_RECV, op->op2.num == MAY_BE_ANY, ZEND_RECV_NO
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST, CACHE_SLOT)
+ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
{
USE_OPLINE
uint32_t arg_num;
@@ -5685,7 +5747,7 @@ ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST, CACHE_SLOT)
ZEND_VM_C_LABEL(recv_init_check_type):
if ((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param))) {
HANDLE_EXCEPTION();
}
}
@@ -5695,7 +5757,7 @@ ZEND_VM_C_LABEL(recv_init_check_type):
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED, CACHE_SLOT)
+ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
{
USE_OPLINE
uint32_t arg_num = opline->op1.num;
@@ -5718,7 +5780,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED, CACHE_SLOT)
if (ZEND_TYPE_IS_SET(arg_info->type)) {
ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS);
do {
- if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
ZEND_HASH_FILL_FINISH();
HANDLE_EXCEPTION();
}
@@ -5746,7 +5808,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED, CACHE_SLOT)
if (ZEND_TYPE_IS_SET(arg_info->type)) {
SEPARATE_ARRAY(params);
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, param) {
- if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
HANDLE_EXCEPTION();
}
Z_TRY_ADDREF_P(param);
@@ -6396,7 +6458,6 @@ ZEND_VM_COLD_CONST_HANDLER(51, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE)
USE_OPLINE
zval *expr;
zval *result = EX_VAR(opline->result.var);
- HashTable *ht;
SAVE_OPLINE();
expr = GET_OP1_ZVAL_PTR(BP_VAR_R);
@@ -6430,53 +6491,10 @@ ZEND_VM_COLD_CONST_HANDLER(51, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE)
}
if (opline->extended_value == IS_ARRAY) {
- if (OP1_TYPE == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
- if (Z_TYPE_P(expr) != IS_NULL) {
- ZVAL_ARR(result, zend_new_array(1));
- expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
- if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- } else if (ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(expr)) {
- /* Optimized version without rebuilding properties HashTable */
- ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
- } else {
- HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
- if (obj_ht) {
- /* fast copy */
- ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
- (Z_OBJCE_P(expr)->default_properties_count ||
- Z_OBJ_P(expr)->handlers != &std_object_handlers ||
- GC_IS_RECURSIVE(obj_ht))));
- zend_release_properties(obj_ht);
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- }
+ zend_cast_zval_to_array(result, expr, OP1_TYPE);
} else {
ZEND_ASSERT(opline->extended_value == IS_OBJECT);
- ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
- if (Z_TYPE_P(expr) == IS_ARRAY) {
- ht = zend_symtable_to_proptable(Z_ARR_P(expr));
- if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
- /* TODO: try not to duplicate immutable arrays as well ??? */
- ht = zend_array_dup(ht);
- }
- Z_OBJ_P(result)->properties = ht;
- } else if (Z_TYPE_P(expr) != IS_NULL) {
- Z_OBJ_P(result)->properties = ht = zend_new_array(1);
- expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
- if (OP1_TYPE == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- }
+ zend_cast_zval_to_object(result, expr, OP1_TYPE);
}
}
@@ -7459,18 +7477,17 @@ ZEND_VM_HANDLER(180, ZEND_ISSET_ISEMPTY_STATIC_PROP, ANY, CLASS_FETCH, ISSET|CAC
{
USE_OPLINE
zval *value;
- zend_result fetch_result;
bool result;
SAVE_OPLINE();
- fetch_result = zend_fetch_static_property_address(&value, NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
+ value = zend_fetch_static_property_address(NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
if (!(opline->extended_value & ZEND_ISEMPTY)) {
- result = fetch_result == SUCCESS && Z_TYPE_P(value) > IS_NULL &&
+ result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
} else {
- result = fetch_result != SUCCESS || !i_zend_is_true(value);
+ result = value == NULL || !i_zend_is_true(value);
}
ZEND_VM_SMART_BRANCH(result, 1);
@@ -8244,6 +8261,49 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST)
+{
+ USE_OPLINE
+ zval *name;
+ zval *val;
+ zend_constant c;
+
+ SAVE_OPLINE();
+ name = GET_OP1_ZVAL_PTR(BP_VAR_R);
+ val = GET_OP2_ZVAL_PTR(BP_VAR_R);
+
+ ZVAL_COPY(&c.value, val);
+ if (Z_OPT_CONSTANT(c.value)) {
+ if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) {
+ zval_ptr_dtor_nogc(&c.value);
+ FREE_OP1();
+ FREE_OP2();
+ HANDLE_EXCEPTION();
+ }
+ }
+ /* non persistent, case sensitive */
+ ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT);
+ c.name = zend_string_copy(Z_STR_P(name));
+
+ if (zend_register_constant(&c) == FAILURE) {
+ FREE_OP1();
+ FREE_OP2();
+ /* two opcodes used, second one is the data with attributes */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+ }
+
+ HashTable *attributes = Z_PTR_P(GET_OP_DATA_ZVAL_PTR(BP_VAR_R));
+ zend_constant *registered = zend_get_constant_ptr(c.name);
+ ZEND_ASSERT(attributes != NULL);
+ ZEND_ASSERT(registered != NULL);
+ zend_constant_add_attributes(registered, attributes);
+
+ FREE_OP1();
+ FREE_OP2();
+ /* two opcodes used, second one is the data with attributes */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
{
USE_OPLINE
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index dadc57d13f278..226e0446abbd1 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -306,6 +306,8 @@ static uint8_t zend_user_opcodes[256] = {0,
241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};
+#include "Zend/zend_vm_opcodes.h"
+
#define SPEC_START_MASK 0x0000ffff
#define SPEC_EXTRA_MASK 0xfffc0000
#define SPEC_RULE_OP1 0x00010000
@@ -367,13 +369,13 @@ static const void *zend_vm_get_opcode_handler_func(uint8_t opcode, const zend_op
#ifdef ZEND_VM_FP_GLOBAL_REG
# define ZEND_OPCODE_HANDLER_ARGS void
# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU
-# define ZEND_OPCODE_HANDLER_ARGS_DC
-# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC
+# define ZEND_OPCODE_HANDLER_ARGS_EX
+# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX
#else
-# define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data
-# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU execute_data
-# define ZEND_OPCODE_HANDLER_ARGS_DC , ZEND_OPCODE_HANDLER_ARGS
-# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC , ZEND_OPCODE_HANDLER_ARGS_PASSTHRU
+# define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data, const zend_op *opline
+# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU execute_data, opline
+# define ZEND_OPCODE_HANDLER_ARGS_EX ZEND_OPCODE_HANDLER_ARGS,
+# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX ZEND_OPCODE_HANDLER_ARGS_PASSTHRU,
#endif
#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)
@@ -394,18 +396,18 @@ static const void *zend_vm_get_opcode_handler_func(uint8_t opcode, const zend_op
# define ZEND_VM_COLD ZEND_COLD ZEND_OPT_SIZE
# endif
#else
-# define ZEND_OPCODE_HANDLER_RET int
+# define ZEND_OPCODE_HANDLER_RET const zend_op *
# define ZEND_VM_TAIL_CALL(call) return call
-# define ZEND_VM_CONTINUE() return 0
-# define ZEND_VM_RETURN() return -1
+# define ZEND_VM_CONTINUE() return opline
+# define ZEND_VM_RETURN() return (const zend_op*)ZEND_VM_ENTER_BIT
# define ZEND_VM_HOT
# define ZEND_VM_COLD ZEND_COLD ZEND_OPT_SIZE
#endif
typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);
-#define DCL_OPLINE
#ifdef ZEND_VM_IP_GLOBAL_REG
+# define DCL_OPLINE
# define OPLINE opline
# define USE_OPLINE
# define LOAD_OPLINE() opline = EX(opline)
@@ -414,12 +416,13 @@ typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_H
# define SAVE_OPLINE() EX(opline) = opline
# define SAVE_OPLINE_EX() SAVE_OPLINE()
#else
-# define OPLINE EX(opline)
-# define USE_OPLINE const zend_op *opline = EX(opline);
-# define LOAD_OPLINE()
-# define LOAD_OPLINE_EX()
-# define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()
-# define SAVE_OPLINE()
+# define DCL_OPLINE const zend_op *opline;
+# define OPLINE opline
+# define USE_OPLINE
+# define LOAD_OPLINE() opline = EX(opline)
+# define LOAD_OPLINE_EX() opline = EX(opline)
+# define LOAD_NEXT_OPLINE() opline = EX(opline) + 1
+# define SAVE_OPLINE() EX(opline) = opline
# define SAVE_OPLINE_EX()
#endif
#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_CONTINUE()
@@ -433,9 +436,10 @@ typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_H
# define ZEND_VM_ENTER() opline = EG(current_execute_data)->opline; ZEND_VM_ENTER_EX()
# define ZEND_VM_LEAVE() return 2
#else
-# define ZEND_VM_ENTER_EX() return 1
-# define ZEND_VM_ENTER() return 1
-# define ZEND_VM_LEAVE() return 2
+# define ZEND_VM_ENTER_BIT 1ULL
+# define ZEND_VM_ENTER_EX() return (zend_op*)((uintptr_t)opline | ZEND_VM_ENTER_BIT)
+# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()
+# define ZEND_VM_LEAVE() return (zend_op*)((uintptr_t)opline | ZEND_VM_ENTER_BIT)
#endif
#define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
#define ZEND_VM_LOOP_INTERRUPT() zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -444,7 +448,7 @@ typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_H
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS);
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_add_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_add_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -465,7 +469,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_add_helper_S
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_sub_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_sub_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -486,7 +490,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_sub_helper_S
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_mul_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_mul_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -517,7 +521,7 @@ static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_mo
HANDLE_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_mod_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_mod_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -538,7 +542,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_mod_helper_S
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_shift_left_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_shift_left_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -559,7 +563,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_shift_left_h
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_shift_right_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_shift_right_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -580,7 +584,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_shift_right_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_equal_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
int ret;
USE_OPLINE
@@ -602,7 +606,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_equal_hel
ZEND_VM_SMART_BRANCH(ret == 0, 1);
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_not_equal_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
int ret;
USE_OPLINE
@@ -624,7 +628,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_not_equal
ZEND_VM_SMART_BRANCH(ret != 0, 1);
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_smaller_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
int ret;
USE_OPLINE
@@ -646,7 +650,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_smaller_h
ZEND_VM_SMART_BRANCH(ret < 0, 1);
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_smaller_or_equal_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
int ret;
USE_OPLINE
@@ -668,7 +672,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_is_smaller_o
ZEND_VM_SMART_BRANCH(ret <= 0, 1);
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_or_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_or_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -689,7 +693,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_or_helper
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_and_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_and_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -710,7 +714,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_and_helpe
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_xor_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_xor_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
USE_OPLINE
@@ -731,7 +735,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_xor_helpe
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_not_helper_SPEC(zval *op_1 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_bw_not_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1)
{
USE_OPLINE
@@ -776,7 +780,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_OP_SPEC_HAN
SAVE_OPLINE();
- if (UNEXPECTED(zend_fetch_static_property_address(&prop, &prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
+ prop = zend_fetch_static_property_address(&prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ UNDEF_RESULT();
+ FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
UNDEF_RESULT();
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
HANDLE_EXCEPTION();
@@ -819,7 +832,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_STATIC_PROP_SPEC_HANDL
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ UNDEF_RESULT();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -839,7 +860,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ UNDEF_RESULT();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -851,19 +880,33 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_always_inline ZEND_OPCODE_HANDLER_RET zend_fetch_static_prop_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX int type)
{
USE_OPLINE
zval *prop;
+ zend_property_info *prop_info;
SAVE_OPLINE();
- if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
+ prop = zend_fetch_static_property_address(
+ &prop_info, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
+ type == BP_VAR_W ? opline->extended_value : 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS));
prop = &EG(uninitialized_zval);
+ } else if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ if (Z_TYPE_P(prop) == IS_OBJECT) {
+ goto copy_deref;
+ } else if (type != BP_VAR_UNSET || Z_TYPE_P(prop) != IS_UNDEF) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
+ }
+ prop = &EG(uninitialized_zval);
}
if (type == BP_VAR_R || type == BP_VAR_IS) {
+copy_deref:
ZVAL_COPY_DEREF(EX_VAR(opline->result.var), prop);
} else {
ZVAL_INDIRECT(EX_VAR(opline->result.var), prop);
@@ -874,40 +917,41 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_R_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_R));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_W_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_W));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_RW));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- int fetch_type =
- (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
- BP_VAR_W : BP_VAR_R;
- ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(fetch_type ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) {
+ ZEND_VM_TAIL_CALL(ZEND_FETCH_STATIC_PROP_W_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ } else {
+ ZEND_VM_TAIL_CALL(ZEND_FETCH_STATIC_PROP_R_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
+ }
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_UNSET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_UNSET));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_IS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_IS));
}
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_use_tmp_in_write_context_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
@@ -943,7 +987,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -979,7 +1024,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1015,7 +1061,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1051,7 +1098,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1087,7 +1135,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_REF_SPEC_HA
SAVE_OPLINE();
- if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
+ prop = zend_fetch_static_property_address(&prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
+ if (UNEXPECTED(!prop)) {
+ FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
+ UNDEF_RESULT();
+ HANDLE_EXCEPTION();
+ }
+
+ if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
+ && UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
+ zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1540,8 +1597,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = 0 ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!0) {
@@ -1642,8 +1706,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = 1 ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!1) {
@@ -1744,8 +1815,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
UNDEF_RESULT();
if (!RETURN_VALUE_USED(opline)) {
@@ -1848,8 +1926,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = 0 ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
@@ -1966,8 +2051,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = 1 ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
@@ -2084,8 +2176,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
- if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
- zend_deprecated_function(fbc);
+ const uint32_t no_discard = RETURN_VALUE_USED(opline) ? 0 : ZEND_ACC_NODISCARD;
+
+ if (UNEXPECTED(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard))) {
+ if (fbc->common.fn_flags & ZEND_ACC_DEPRECATED) {
+ zend_deprecated_function(fbc);
+ }
+ if ((fbc->common.fn_flags & no_discard) && EG(exception) == NULL) {
+ zend_nodiscard_function(fbc);
+ }
if (UNEXPECTED(EG(exception) != NULL)) {
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
@@ -2270,7 +2369,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_CREATE_SPEC_HANDLER(
}
}
-static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_cannot_pass_by_ref_helper_SPEC(uint32_t _arg_num, zval *_arg ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX uint32_t _arg_num, zval *_arg)
{
USE_OPLINE
@@ -2648,21 +2747,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_missing_arg_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
{
-#ifdef ZEND_VM_IP_GLOBAL_REG
USE_OPLINE
SAVE_OPLINE();
-#endif
+
zend_missing_arg_error(execute_data);
HANDLE_EXCEPTION();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_verify_recv_arg_type_helper_SPEC(zval *op_1 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_verify_recv_arg_type_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1)
{
USE_OPLINE
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1))) {
HANDLE_EXCEPTION();
}
@@ -2681,7 +2779,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_NOTYPE_SPEC_H
ZEND_VM_NEXT_OPCODE();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_case_helper_SPEC(zval *op_1, zval *op_2 ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_case_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2)
{
int ret;
USE_OPLINE
@@ -3008,18 +3106,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC
{
USE_OPLINE
zval *value;
- zend_result fetch_result;
bool result;
SAVE_OPLINE();
- fetch_result = zend_fetch_static_property_address(&value, NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
+ value = zend_fetch_static_property_address(NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
if (!(opline->extended_value & ZEND_ISEMPTY)) {
- result = fetch_result == SUCCESS && Z_TYPE_P(value) > IS_NULL &&
+ result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
} else {
- result = fetch_result != SUCCESS || !i_zend_is_true(value);
+ result = value == NULL || !i_zend_is_true(value);
}
ZEND_VM_SMART_BRANCH(result, 1);
@@ -3162,7 +3259,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NOP_SPEC_HANDLER(Z
ZEND_VM_NEXT_OPCODE();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_helper_SPEC(uint32_t try_catch_offset, uint32_t op_num ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_EX uint32_t try_catch_offset, uint32_t op_num)
{
/* May be NULL during generator closing (only finally blocks are executed) */
zend_object *ex = EG(exception);
@@ -3245,7 +3342,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
/* Exception was thrown before executing any op */
if (UNEXPECTED(!throw_op)) {
- ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(-1, 0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0));
}
uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes;
@@ -3309,7 +3406,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
}
}
- ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, throw_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX current_try_catch_offset, throw_op_num));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -3408,7 +3505,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
Z_OBJ_P(fast_call) = NULL;
current_try_catch_offset = opline->op2.num;
current_op_num = opline - EX(func)->op_array.opcodes;
- ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, current_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX current_try_catch_offset, current_op_num));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -4096,7 +4193,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CON
recv_init_check_type:
if ((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
SAVE_OPLINE();
- if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param))) {
HANDLE_EXCEPTION();
}
}
@@ -4172,7 +4269,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_SPEC_UNUSED_H
param = EX_VAR(opline->result.var);
if (UNEXPECTED(!(opline->op2.num & (1u << Z_TYPE_P(param))))) {
- ZEND_VM_TAIL_CALL(zend_verify_recv_arg_type_helper_SPEC(param ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_verify_recv_arg_type_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX param));
}
ZEND_VM_NEXT_OPCODE();
@@ -4201,7 +4298,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_VARIADIC_SPEC_UNUSED_HAND
if (ZEND_TYPE_IS_SET(arg_info->type)) {
ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS);
do {
- if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
ZEND_HASH_FILL_FINISH();
HANDLE_EXCEPTION();
}
@@ -4229,7 +4326,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_VARIADIC_SPEC_UNUSED_HAND
if (ZEND_TYPE_IS_SET(arg_info->type)) {
SEPARATE_ARRAY(params);
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, param) {
- if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
+ if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
HANDLE_EXCEPTION();
}
Z_TRY_ADDREF_P(param);
@@ -4366,7 +4463,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_NOT_SPEC_CONST
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_not_helper_SPEC(op1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_not_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_NOT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -5139,7 +5236,6 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H
USE_OPLINE
zval *expr;
zval *result = EX_VAR(opline->result.var);
- HashTable *ht;
SAVE_OPLINE();
expr = RT_CONSTANT(opline, opline->op1);
@@ -5172,53 +5268,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H
}
if (opline->extended_value == IS_ARRAY) {
- if (IS_CONST == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
- if (Z_TYPE_P(expr) != IS_NULL) {
- ZVAL_ARR(result, zend_new_array(1));
- expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
- if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- } else if (ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(expr)) {
- /* Optimized version without rebuilding properties HashTable */
- ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
- } else {
- HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
- if (obj_ht) {
- /* fast copy */
- ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
- (Z_OBJCE_P(expr)->default_properties_count ||
- Z_OBJ_P(expr)->handlers != &std_object_handlers ||
- GC_IS_RECURSIVE(obj_ht))));
- zend_release_properties(obj_ht);
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- }
+ zend_cast_zval_to_array(result, expr, IS_CONST);
} else {
ZEND_ASSERT(opline->extended_value == IS_OBJECT);
- ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
- if (Z_TYPE_P(expr) == IS_ARRAY) {
- ht = zend_symtable_to_proptable(Z_ARR_P(expr));
- if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
- /* TODO: try not to duplicate immutable arrays as well ??? */
- ht = zend_array_dup(ht);
- }
- Z_OBJ_P(result)->properties = ht;
- } else if (Z_TYPE_P(expr) != IS_NULL) {
- Z_OBJ_P(result)->properties = ht = zend_new_array(1);
- expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
- if (IS_CONST == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- }
+ zend_cast_zval_to_object(result, expr, IS_CONST);
}
}
@@ -6062,7 +6115,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SIMPLE
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
if (QUICK_ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = RT_CONSTANT(opline, opline->op1);
ZVAL_COPY_VALUE(arg, value);
@@ -6104,7 +6157,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CO
}
}
- ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6142,7 +6195,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_CONST_CO
}
}
- ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6183,7 +6236,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_SPEC_CONST_CO
}
}
- ZEND_VM_TAIL_CALL(zend_mul_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mul_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6224,7 +6277,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_CONST_CO
}
}
- ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6245,7 +6298,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_CON
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6264,7 +6317,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_CON
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6366,7 +6419,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CON
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6424,7 +6477,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6467,7 +6520,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_C
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6514,7 +6567,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQU
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6546,7 +6599,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CONST_
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_or_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_or_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6564,7 +6617,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CONST
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_and_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_and_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6582,7 +6635,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CONST
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_xor_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_xor_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -7521,7 +7574,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_CONST_CONST_H
}
} else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
send_val_by_ref:
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = RT_CONSTANT(opline, opline->op1);
ZVAL_COPY_VALUE(arg, value);
@@ -7989,6 +8042,48 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *name;
+ zval *val;
+ zend_constant c;
+
+ SAVE_OPLINE();
+ name = RT_CONSTANT(opline, opline->op1);
+ val = RT_CONSTANT(opline, opline->op2);
+
+ ZVAL_COPY(&c.value, val);
+ if (Z_OPT_CONSTANT(c.value)) {
+ if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) {
+ zval_ptr_dtor_nogc(&c.value);
+
+
+ HANDLE_EXCEPTION();
+ }
+ }
+ /* non persistent, case sensitive */
+ ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT);
+ c.name = zend_string_copy(Z_STR_P(name));
+
+ if (zend_register_constant(&c) == FAILURE) {
+
+
+ /* two opcodes used, second one is the data with attributes */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+ }
+
+ HashTable *attributes = Z_PTR_P(get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1));
+ zend_constant *registered = zend_get_constant_ptr(c.name);
+ ZEND_ASSERT(attributes != NULL);
+ ZEND_ASSERT(registered != NULL);
+ zend_constant_add_attributes(registered, attributes);
+
+
+ /* two opcodes used, second one is the data with attributes */
+ ZEND_VM_NEXT_OPCODE_EX(1, 2);
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -8317,7 +8412,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CONST_TMP
}
}
- ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8355,7 +8450,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_CONST_TMP
}
}
- ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8382,7 +8477,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_CONST_TMPVARCV_HANDLE
}
}
- ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8403,7 +8498,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_TMPVARCV_HANDLER
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8422,7 +8517,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_TMPVARCV_HANDLER
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8465,7 +8560,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_TMPVARCV
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8508,7 +8603,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CO
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8551,7 +8646,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CO
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8598,7 +8693,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8645,7 +8740,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -8692,7 +8787,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -10446,7 +10541,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMPVAR_HANDLE
ZEND_VM_RETURN();
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_UNUSED(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_EX int type)
{
USE_OPLINE
zval *varname;
@@ -10549,17 +10644,17 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_R));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_W));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_RW));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -10567,17 +10662,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CONST_UNUS
int fetch_type =
(UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
BP_VAR_W : BP_VAR_R;
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(fetch_type ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX fetch_type));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_UNSET));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_IS));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
@@ -10775,7 +10870,6 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
}
zend_reference *ref = NULL;
- void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
@@ -10792,7 +10886,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -10854,7 +10948,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_CONST_UNUSED_
}
} else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
send_val_by_ref:
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = RT_CONSTANT(opline, opline->op1);
ZVAL_COPY_VALUE(arg, value);
@@ -10891,7 +10985,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_C
}
} else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
send_val_by_ref:
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = RT_CONSTANT(opline, opline->op1);
ZVAL_COPY_VALUE(arg, value);
@@ -12946,7 +13040,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_NOT_SPEC_TMPVAR
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_not_helper_SPEC(op1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_not_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13014,7 +13108,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_TMPVARCV_
}
}
- ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13052,7 +13146,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_TMPVARCV_
}
}
- ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13093,7 +13187,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_SPEC_TMPVARCV_CONST_HANDLE
}
}
- ZEND_VM_TAIL_CALL(zend_mul_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mul_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13120,7 +13214,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_TMPVARCV_CONST_HANDLE
}
}
- ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13141,7 +13235,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVARCV_CONST_HANDLER
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13160,7 +13254,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVARCV_CONST_HANDLER
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13203,7 +13297,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_CONST
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13246,7 +13340,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TM
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13289,7 +13383,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TM
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13336,7 +13430,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVARCV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13383,7 +13477,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13430,7 +13524,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13448,7 +13542,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVARC
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_or_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_or_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13466,7 +13560,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVAR
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_and_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_and_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -13484,7 +13578,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVAR
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_xor_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_xor_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_R_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14034,7 +14128,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_TMPVARCV_
}
}
- ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_add_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14072,7 +14166,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SUB_SPEC_TMPVARCV_
}
}
- ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_sub_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14113,7 +14207,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MUL_SPEC_TMPVARCV_TMPVARCV_HAN
}
}
- ZEND_VM_TAIL_CALL(zend_mul_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mul_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14140,7 +14234,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MOD_SPEC_TMPVARCV_TMPVARCV_HAN
}
}
- ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_mod_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14161,7 +14255,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVARCV_TMPVARCV_HAND
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_left_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14180,7 +14274,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVARCV_TMPVARCV_HAND
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_shift_right_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14223,7 +14317,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_TMPVA
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14266,7 +14360,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TM
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14309,7 +14403,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TM
goto is_smaller_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14356,7 +14450,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVARCV_TMPVARCV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14403,7 +14497,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVARCV_TMPVARCV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14450,7 +14544,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUA
goto is_smaller_or_equal_double;
}
}
- ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_smaller_or_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14468,7 +14562,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVARC
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_or_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_or_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14486,7 +14580,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVAR
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_and_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_and_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14504,7 +14598,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVAR
ZEND_VM_NEXT_OPCODE();
}
- ZEND_VM_TAIL_CALL(zend_bw_xor_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_bw_xor_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -15799,7 +15893,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HAN
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -15857,7 +15951,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_JMP
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -15915,7 +16009,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_JMP
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -15973,7 +16067,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -16031,7 +16125,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -16089,7 +16183,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -16813,7 +16907,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER
}
}
}
- ZEND_VM_TAIL_CALL(zend_case_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_case_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -17292,7 +17386,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HA
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -17350,7 +17444,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_JM
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -17408,7 +17502,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_JM
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -17466,7 +17560,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVA
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -17524,7 +17618,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVA
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVAR_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -17582,7 +17676,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVA
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -18278,7 +18372,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLE
}
}
}
- ZEND_VM_TAIL_CALL(zend_case_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_case_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -18488,7 +18582,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HAN
ZEND_VM_SMART_BRANCH(result, 1);
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_EX int type)
{
USE_OPLINE
zval *varname;
@@ -18591,17 +18685,17 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_R));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_W));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_RW));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -18609,17 +18703,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_TMPVAR_UNU
int fetch_type =
(UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
BP_VAR_W : BP_VAR_R;
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(fetch_type ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX fetch_type));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_UNSET));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_TMPVAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_TMPVAR_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_IS));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
@@ -19685,7 +19779,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CV_HANDLER(ZE
}
}
}
- ZEND_VM_TAIL_CALL(zend_case_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_case_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -20061,7 +20155,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
USE_OPLINE
zval *expr;
zval *result = EX_VAR(opline->result.var);
- HashTable *ht;
SAVE_OPLINE();
expr = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
@@ -20094,53 +20187,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
}
if (opline->extended_value == IS_ARRAY) {
- if (IS_TMP_VAR == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
- if (Z_TYPE_P(expr) != IS_NULL) {
- ZVAL_ARR(result, zend_new_array(1));
- expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
- if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- } else if (ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(expr)) {
- /* Optimized version without rebuilding properties HashTable */
- ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
- } else {
- HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
- if (obj_ht) {
- /* fast copy */
- ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
- (Z_OBJCE_P(expr)->default_properties_count ||
- Z_OBJ_P(expr)->handlers != &std_object_handlers ||
- GC_IS_RECURSIVE(obj_ht))));
- zend_release_properties(obj_ht);
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- }
+ zend_cast_zval_to_array(result, expr, IS_TMP_VAR);
} else {
ZEND_ASSERT(opline->extended_value == IS_OBJECT);
- ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
- if (Z_TYPE_P(expr) == IS_ARRAY) {
- ht = zend_symtable_to_proptable(Z_ARR_P(expr));
- if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
- /* TODO: try not to duplicate immutable arrays as well ??? */
- ht = zend_array_dup(ht);
- }
- Z_OBJ_P(result)->properties = ht;
- } else if (Z_TYPE_P(expr) != IS_NULL) {
- Z_OBJ_P(result)->properties = ht = zend_new_array(1);
- expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
- if (IS_TMP_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- }
+ zend_cast_zval_to_object(result, expr, IS_TMP_VAR);
}
}
@@ -20703,7 +20753,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_TMP_CONST_HAN
}
} else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
send_val_by_ref:
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
ZVAL_COPY_VALUE(arg, value);
@@ -21512,7 +21562,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
}
zend_reference *ref = NULL;
- void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
@@ -21529,7 +21578,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -21563,7 +21612,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_TMP_UNUSED_HA
}
} else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
send_val_by_ref:
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
ZVAL_COPY_VALUE(arg, value);
@@ -21600,7 +21649,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_EX_SPEC_T
}
} else if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
send_val_by_ref:
- ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(arg_num, arg ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_cannot_pass_by_ref_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX arg_num, arg));
}
value = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
ZVAL_COPY_VALUE(arg, value);
@@ -22728,7 +22777,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
USE_OPLINE
zval *expr;
zval *result = EX_VAR(opline->result.var);
- HashTable *ht;
SAVE_OPLINE();
expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
@@ -22762,53 +22810,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
}
if (opline->extended_value == IS_ARRAY) {
- if (IS_VAR == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
- if (Z_TYPE_P(expr) != IS_NULL) {
- ZVAL_ARR(result, zend_new_array(1));
- expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
- if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- } else if (ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(expr)) {
- /* Optimized version without rebuilding properties HashTable */
- ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
- } else {
- HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
- if (obj_ht) {
- /* fast copy */
- ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
- (Z_OBJCE_P(expr)->default_properties_count ||
- Z_OBJ_P(expr)->handlers != &std_object_handlers ||
- GC_IS_RECURSIVE(obj_ht))));
- zend_release_properties(obj_ht);
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- }
+ zend_cast_zval_to_array(result, expr, IS_VAR);
} else {
ZEND_ASSERT(opline->extended_value == IS_OBJECT);
- ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
- if (Z_TYPE_P(expr) == IS_ARRAY) {
- ht = zend_symtable_to_proptable(Z_ARR_P(expr));
- if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
- /* TODO: try not to duplicate immutable arrays as well ??? */
- ht = zend_array_dup(ht);
- }
- Z_OBJ_P(result)->properties = ht;
- } else if (Z_TYPE_P(expr) != IS_NULL) {
- Z_OBJ_P(result)->properties = ht = zend_new_array(1);
- expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
- if (IS_VAR == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- }
+ zend_cast_zval_to_object(result, expr, IS_VAR);
}
}
@@ -29992,7 +29997,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
}
zend_reference *ref = NULL;
- void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
@@ -30009,7 +30013,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -37786,7 +37790,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
}
zend_reference *ref = NULL;
- void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
@@ -37803,7 +37806,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -40971,7 +40974,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
USE_OPLINE
zval *expr;
zval *result = EX_VAR(opline->result.var);
- HashTable *ht;
SAVE_OPLINE();
expr = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
@@ -41004,53 +41006,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
}
if (opline->extended_value == IS_ARRAY) {
- if (IS_CV == IS_CONST || Z_TYPE_P(expr) != IS_OBJECT || Z_OBJCE_P(expr) == zend_ce_closure) {
- if (Z_TYPE_P(expr) != IS_NULL) {
- ZVAL_ARR(result, zend_new_array(1));
- expr = zend_hash_index_add_new(Z_ARRVAL_P(result), 0, expr);
- if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- } else if (ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(expr)) {
- /* Optimized version without rebuilding properties HashTable */
- ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ_P(expr)));
- } else {
- HashTable *obj_ht = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_ARRAY_CAST);
- if (obj_ht) {
- /* fast copy */
- ZVAL_ARR(result, zend_proptable_to_symtable(obj_ht,
- (Z_OBJCE_P(expr)->default_properties_count ||
- Z_OBJ_P(expr)->handlers != &std_object_handlers ||
- GC_IS_RECURSIVE(obj_ht))));
- zend_release_properties(obj_ht);
- } else {
- ZVAL_EMPTY_ARRAY(result);
- }
- }
+ zend_cast_zval_to_array(result, expr, IS_CV);
} else {
ZEND_ASSERT(opline->extended_value == IS_OBJECT);
- ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
- if (Z_TYPE_P(expr) == IS_ARRAY) {
- ht = zend_symtable_to_proptable(Z_ARR_P(expr));
- if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
- /* TODO: try not to duplicate immutable arrays as well ??? */
- ht = zend_array_dup(ht);
- }
- Z_OBJ_P(result)->properties = ht;
- } else if (Z_TYPE_P(expr) != IS_NULL) {
- Z_OBJ_P(result)->properties = ht = zend_new_array(1);
- expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr);
- if (IS_CV == IS_CONST) {
- if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr);
- } else {
- if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr);
- }
- }
+ zend_cast_zval_to_object(result, expr, IS_CV);
}
}
@@ -42177,7 +42136,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_HANDLER
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42235,7 +42194,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_JMPZ_HA
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42293,7 +42252,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_JMPNZ_H
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42351,7 +42310,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HAN
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42409,7 +42368,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_JMP
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -42467,7 +42426,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_JMP
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -46130,7 +46089,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLE
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -46188,7 +46147,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_JMPZ_H
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -46246,7 +46205,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_JMPNZ_
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -46304,7 +46263,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HA
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -46362,7 +46321,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_JM
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -46420,7 +46379,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_JM
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -49751,7 +49710,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_UNUSED(int type ZEND_OPCODE_HANDLER_ARGS_DC)
+static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_EX int type)
{
USE_OPLINE
zval *varname;
@@ -49854,17 +49813,17 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_R_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(BP_VAR_R ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_R));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_W_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(BP_VAR_W ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_W));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_RW_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(BP_VAR_RW ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_RW));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -49872,17 +49831,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_FUNC_ARG_SPEC_CV_UNUSED_
int fetch_type =
(UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
BP_VAR_W : BP_VAR_R;
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(fetch_type ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX fetch_type));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_UNSET_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(BP_VAR_UNSET ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_UNSET));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_IS_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
- ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(BP_VAR_IS ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_fetch_var_address_helper_SPEC_CV_UNUSED(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX BP_VAR_IS));
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
@@ -50587,7 +50546,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
}
zend_reference *ref = NULL;
- void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
@@ -50604,7 +50562,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
}
SAVE_OPLINE();
- if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
+ if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
@@ -51633,7 +51591,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER(ZE
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -51691,7 +51649,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_JMPZ_HANDL
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -51749,7 +51707,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_JMPNZ_HAND
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -51807,7 +51765,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLE
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_JMPZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -51865,7 +51823,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_JMPZ_H
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_JMPNZ_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -51923,7 +51881,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_JMPNZ_
}
}
}
- ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(op1, op2 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
+ ZEND_VM_TAIL_CALL(zend_is_not_equal_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX op1, op2));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -57653,6 +57611,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER_LABEL,
(void*)&&ZEND_JMP_FRAMELESS_SPEC_CONST_LABEL,
(void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL,
+ (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL,
(void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL,
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
(void*)&&ZEND_NULL_LABEL,
@@ -58592,19 +58551,17 @@ ZEND_API void execute_ex(zend_execute_data *ex)
#endif /* ZEND_CHECK_STACK_LIMIT */
while (1) {
-#if !defined(ZEND_VM_FP_GLOBAL_REG) || !defined(ZEND_VM_IP_GLOBAL_REG)
- int ret;
-#endif
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
HYBRID_SWITCH() {
-#else
+#else /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */
#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)
((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
if (UNEXPECTED(!OPLINE)) {
#else
- if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0)) {
-#endif
+ opline = ((opcode_handler_t)opline->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+ if (UNEXPECTED(((uintptr_t)opline & ZEND_VM_ENTER_BIT))) {
#endif
+#endif /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
HYBRID_CASE(ZEND_ASSIGN_STATIC_PROP_OP_SPEC):
VM_TRACE(ZEND_ASSIGN_STATIC_PROP_OP_SPEC)
@@ -59629,6 +59586,11 @@ ZEND_API void execute_ex(zend_execute_data *ex)
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_DECLARE_CONST_SPEC_CONST_CONST)
HYBRID_BREAK();
+ HYBRID_CASE(ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST):
+ VM_TRACE(ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST)
+ ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+ VM_TRACE_OP_END(ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST)
+ HYBRID_BREAK();
HYBRID_CASE(ZEND_YIELD_SPEC_CONST_CONST):
VM_TRACE(ZEND_YIELD_SPEC_CONST_CONST)
ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -64199,7 +64161,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_NULL)
HYBRID_BREAK(); /* Never reached */
-#else
+#else /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */
#ifdef ZEND_VM_FP_GLOBAL_REG
execute_data = vm_stack_data.orig_execute_data;
# ifdef ZEND_VM_IP_GLOBAL_REG
@@ -64207,7 +64169,8 @@ ZEND_API void execute_ex(zend_execute_data *ex)
# endif
return;
#else
- if (EXPECTED(ret > 0)) {
+ opline = (const zend_op*)((uintptr_t)opline & ~ZEND_VM_ENTER_BIT);
+ if (EXPECTED(opline != NULL)) {
execute_data = EG(current_execute_data);
ZEND_VM_LOOP_INTERRUPT_CHECK();
} else {
@@ -64217,7 +64180,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
return;
}
#endif
-#endif
+#endif /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */
}
}
@@ -66840,6 +66803,7 @@ void zend_vm_init(void)
ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER_HANDLER,
ZEND_JMP_FRAMELESS_SPEC_CONST_HANDLER,
ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER,
+ ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER,
ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER,
ZEND_RECV_NOTYPE_SPEC_HANDLER,
ZEND_NULL_HANDLER,
@@ -67797,7 +67761,7 @@ void zend_vm_init(void)
1255,
1256 | SPEC_RULE_OP1,
1261 | SPEC_RULE_OP1,
- 3486,
+ 3487,
1266 | SPEC_RULE_OP1,
1271 | SPEC_RULE_OP1,
1276 | SPEC_RULE_OP2,
@@ -67831,7 +67795,7 @@ void zend_vm_init(void)
1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
1584 | SPEC_RULE_OP1,
1589,
- 3486,
+ 3487,
1590 | SPEC_RULE_OP1,
1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
@@ -67962,52 +67926,52 @@ void zend_vm_init(void)
2573 | SPEC_RULE_OBSERVER,
2575,
2576,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
- 3486,
+ 2577,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
+ 3487,
};
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
zend_opcode_handler_funcs = labels;
@@ -68180,7 +68144,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2585 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2586 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@@ -68188,7 +68152,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2610 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2611 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@@ -68196,7 +68160,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2635 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2636 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@@ -68207,17 +68171,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2660 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 2661 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2685 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 2686 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2710 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 2711 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
@@ -68228,17 +68192,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2735 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2736 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2760 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2761 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2785 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 2786 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_IDENTICAL:
@@ -68249,14 +68213,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2810 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2811 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2885 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2886 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
- spec = 3110 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 3111 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_IDENTICAL:
@@ -68267,14 +68231,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2960 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2961 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3035 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 3036 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
- spec = 3115 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
+ spec = 3116 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_EQUAL:
@@ -68285,12 +68249,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2810 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2811 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2885 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2886 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_EQUAL:
@@ -68301,12 +68265,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 2960 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 2961 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3035 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
+ spec = 3036 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_SMALLER:
@@ -68314,12 +68278,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3120 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3121 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3195 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3196 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
@@ -68327,79 +68291,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3270 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3271 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3345 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
+ spec = 3346 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_LONG) {
- spec = 3432 | SPEC_RULE_OP1;
+ spec = 3433 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_DOUBLE) {
- spec = 3437 | SPEC_RULE_OP1;
+ spec = 3438 | SPEC_RULE_OP1;
} else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
- spec = 3442 | SPEC_RULE_OP1;
+ spec = 3443 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3420 | SPEC_RULE_RETVAL;
+ spec = 3421 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
- spec = 3422 | SPEC_RULE_RETVAL;
+ spec = 3423 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3424 | SPEC_RULE_RETVAL;
+ spec = 3425 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
- spec = 3426 | SPEC_RULE_RETVAL;
+ spec = 3427 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3428;
- } else if (op1_info == MAY_BE_LONG) {
spec = 3429;
+ } else if (op1_info == MAY_BE_LONG) {
+ spec = 3430;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
- spec = 3430;
- } else if (op1_info == MAY_BE_LONG) {
spec = 3431;
+ } else if (op1_info == MAY_BE_LONG) {
+ spec = 3432;
}
break;
case ZEND_JMP:
if (OP_JMP_ADDR(op, op->op1) > op) {
- spec = 2584;
+ spec = 2585;
}
break;
case ZEND_INIT_FCALL:
if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) {
- spec = 2577;
+ spec = 2578;
}
break;
case ZEND_RECV:
if (op->op2.num == MAY_BE_ANY) {
- spec = 2578;
+ spec = 2579;
}
break;
case ZEND_SEND_VAL:
if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
- spec = 3482;
+ spec = 3483;
}
break;
case ZEND_SEND_VAR_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
- spec = 3477 | SPEC_RULE_OP1;
+ spec = 3478 | SPEC_RULE_OP1;
}
break;
case ZEND_FE_FETCH_R:
if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) {
- spec = 3484 | SPEC_RULE_RETVAL;
+ spec = 3485 | SPEC_RULE_RETVAL;
}
break;
case ZEND_FETCH_DIM_R:
@@ -68407,22 +68371,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
- spec = 3447 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
+ spec = 3448 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAL_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
- spec = 3483;
+ spec = 3484;
}
break;
case ZEND_SEND_VAR:
if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
- spec = 3472 | SPEC_RULE_OP1;
+ spec = 3473 | SPEC_RULE_OP1;
}
break;
case ZEND_COUNT:
if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) {
- spec = 2579 | SPEC_RULE_OP1;
+ spec = 2580 | SPEC_RULE_OP1;
}
break;
case ZEND_BW_OR:
@@ -68451,6 +68415,7 @@ ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex)
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
opcode_handler_t handler;
#endif
+ DCL_OPLINE;
int ret;
#ifdef ZEND_VM_IP_GLOBAL_REG
const zend_op *orig_opline = opline;
@@ -68478,8 +68443,23 @@ ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex)
ret = -1;
}
#else
- ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
- SAVE_OPLINE();
+ opline = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+if (UNEXPECTED(((uintptr_t)opline & ZEND_VM_ENTER_BIT))) {
+ opline = (const zend_op*)((uintptr_t)opline & ~ZEND_VM_ENTER_BIT);
+ if (EXPECTED(opline)) {
+ /* ZEND_VM_ENTER() or ZEND_VM_LEAVE() */
+ ret = EG(current_execute_data) != ex ? (int)(EG(current_execute_data)->prev_execute_data != ex) + 1 : 0;
+ execute_data = EG(current_execute_data);
+ SAVE_OPLINE();
+ } else {
+ /* ZEND_VM_RETURN() */
+ ret = -1;
+ }
+ } else {
+ /* ZEND_VM_CONTINUE() */
+ SAVE_OPLINE();
+ ret = 0;
+ }
#endif
#ifdef ZEND_VM_FP_GLOBAL_REG
execute_data = orig_execute_data;
diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl
index 5b4799cd67c2a..6fecd39346a70 100644
--- a/Zend/zend_vm_execute.skl
+++ b/Zend/zend_vm_execute.skl
@@ -1,3 +1,5 @@
+#include "Zend/zend_vm_opcodes.h"
+
{%DEFINES%}
#if (ZEND_VM_KIND != ZEND_VM_KIND_CALL) && (ZEND_GCC_VERSION >= 4000) && !defined(__clang__)
diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php
index c94cd6b616e43..5a4a31b60b8d3 100755
--- a/Zend/zend_vm_gen.php
+++ b/Zend/zend_vm_gen.php
@@ -818,7 +818,7 @@ function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec) {
if (isset($matches[2])) {
// extra args
$args = substr(preg_replace("/,\s*[A-Za-z0-9_]*\s*,\s*([^,)\s]*)\s*/", ", $1", $matches[2]), 2);
- return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(" . $args. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC))";
+ return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX " . $args . "))";
}
return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
}
@@ -852,7 +852,7 @@ function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec, $name) {
if (isset($matches[2])) {
// extra args
$args = substr(preg_replace("/,\s*[A-Za-z0-9_]*\s*,\s*([^,)\s]*)\s*/", ", $1", $matches[2]), 2);
- return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(" . $args. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC))";
+ return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX " . $args . "))";
}
return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
}
@@ -1164,7 +1164,7 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno,
out($f, "static$zend_attributes ZEND_OPCODE_HANDLER_RET$zend_fastcall $spec_name(ZEND_OPCODE_HANDLER_ARGS)\n");
} else {
// Helper with parameter
- out($f, "static$zend_attributes ZEND_OPCODE_HANDLER_RET$zend_fastcall $spec_name($param ZEND_OPCODE_HANDLER_ARGS_DC)\n");
+ out($f, "static$zend_attributes ZEND_OPCODE_HANDLER_RET$zend_fastcall $spec_name(ZEND_OPCODE_HANDLER_ARGS_EX $param)\n");
}
break;
case ZEND_VM_KIND_SWITCH:
@@ -1870,13 +1870,13 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#ifdef ZEND_VM_FP_GLOBAL_REG\n");
out($f,"# define ZEND_OPCODE_HANDLER_ARGS void\n");
out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU\n");
- out($f,"# define ZEND_OPCODE_HANDLER_ARGS_DC\n");
- out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC\n");
+ out($f,"# define ZEND_OPCODE_HANDLER_ARGS_EX\n");
+ out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX\n");
out($f,"#else\n");
- out($f,"# define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data\n");
- out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU execute_data\n");
- out($f,"# define ZEND_OPCODE_HANDLER_ARGS_DC , ZEND_OPCODE_HANDLER_ARGS\n");
- out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC , ZEND_OPCODE_HANDLER_ARGS_PASSTHRU\n");
+ out($f,"# define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data, const zend_op *opline\n");
+ out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU execute_data, opline\n");
+ out($f,"# define ZEND_OPCODE_HANDLER_ARGS_EX ZEND_OPCODE_HANDLER_ARGS, \n");
+ out($f,"# define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX ZEND_OPCODE_HANDLER_ARGS_PASSTHRU, \n");
out($f,"#endif\n");
out($f,"\n");
out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
@@ -1902,10 +1902,10 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"# define ZEND_VM_COLD ZEND_COLD ZEND_OPT_SIZE\n");
}
out($f,"#else\n");
- out($f,"# define ZEND_OPCODE_HANDLER_RET int\n");
+ out($f,"# define ZEND_OPCODE_HANDLER_RET const zend_op *\n");
out($f,"# define ZEND_VM_TAIL_CALL(call) return call\n");
- out($f,"# define ZEND_VM_CONTINUE() return 0\n");
- out($f,"# define ZEND_VM_RETURN() return -1\n");
+ out($f,"# define ZEND_VM_CONTINUE() return opline\n");
+ out($f,"# define ZEND_VM_RETURN() return (const zend_op*)ZEND_VM_ENTER_BIT\n");
if ($kind == ZEND_VM_KIND_HYBRID) {
out($f,"# define ZEND_VM_HOT\n");
}
@@ -1914,8 +1914,8 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"\n");
out($f,"typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);\n");
out($f,"\n");
- out($f,"#define DCL_OPLINE\n");
out($f,"#ifdef ZEND_VM_IP_GLOBAL_REG\n");
+ out($f,"# define DCL_OPLINE\n");
out($f,"# define OPLINE opline\n");
out($f,"# define USE_OPLINE\n");
out($f,"# define LOAD_OPLINE() opline = EX(opline)\n");
@@ -1924,12 +1924,13 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"# define SAVE_OPLINE() EX(opline) = opline\n");
out($f,"# define SAVE_OPLINE_EX() SAVE_OPLINE()\n");
out($f,"#else\n");
- out($f,"# define OPLINE EX(opline)\n");
- out($f,"# define USE_OPLINE const zend_op *opline = EX(opline);\n");
- out($f,"# define LOAD_OPLINE()\n");
- out($f,"# define LOAD_OPLINE_EX()\n");
- out($f,"# define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()\n");
- out($f,"# define SAVE_OPLINE()\n");
+ out($f,"# define DCL_OPLINE const zend_op *opline;\n");
+ out($f,"# define OPLINE opline\n");
+ out($f,"# define USE_OPLINE\n");
+ out($f,"# define LOAD_OPLINE() opline = EX(opline)\n");
+ out($f,"# define LOAD_OPLINE_EX() opline = EX(opline)\n");
+ out($f,"# define LOAD_NEXT_OPLINE() opline = EX(opline) + 1\n");
+ out($f,"# define SAVE_OPLINE() EX(opline) = opline\n");
out($f,"# define SAVE_OPLINE_EX()\n");
out($f,"#endif\n");
out($f,"#define HANDLE_EXCEPTION() ZEND_ASSERT(EG(exception)); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
@@ -1943,9 +1944,10 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"# define ZEND_VM_ENTER() opline = EG(current_execute_data)->opline; ZEND_VM_ENTER_EX()\n");
out($f,"# define ZEND_VM_LEAVE() return 2\n");
out($f,"#else\n");
- out($f,"# define ZEND_VM_ENTER_EX() return 1\n");
- out($f,"# define ZEND_VM_ENTER() return 1\n");
- out($f,"# define ZEND_VM_LEAVE() return 2\n");
+ out($f,"# define ZEND_VM_ENTER_BIT 1ULL\n");
+ out($f,"# define ZEND_VM_ENTER_EX() return (zend_op*)((uintptr_t)opline | ZEND_VM_ENTER_BIT)\n");
+ out($f,"# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_ENTER_EX()\n");
+ out($f,"# define ZEND_VM_LEAVE() return (zend_op*)((uintptr_t)opline | ZEND_VM_ENTER_BIT)\n");
out($f,"#endif\n");
out($f,"#define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n");
out($f,"#define ZEND_VM_LOOP_INTERRUPT() zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
@@ -2119,12 +2121,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
}
break;
case "ZEND_VM_CONTINUE_LABEL":
- if ($kind == ZEND_VM_KIND_CALL || $kind == ZEND_VM_KIND_HYBRID) {
- // Only SWITCH dispatch method use it
- out($f,"#if !defined(ZEND_VM_FP_GLOBAL_REG) || !defined(ZEND_VM_IP_GLOBAL_REG)\n");
- out($f,$m[1]."\tint ret;".$m[3]."\n");
- out($f,"#endif\n");
- } else if ($kind == ZEND_VM_KIND_SWITCH) {
+ if ($kind == ZEND_VM_KIND_SWITCH) {
// Only SWITCH dispatch method use it
out($f,"zend_vm_continue:".$m[3]."\n");
} else {
@@ -2143,16 +2140,17 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
case ZEND_VM_KIND_HYBRID:
out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
out($f, $m[1]."HYBRID_SWITCH()".$m[3]."\n");
- out($f,"#else\n");
+ out($f,"#else /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */\n");
case ZEND_VM_KIND_CALL:
out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
out($f, $m[1]."((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
out($f, $m[1]."if (UNEXPECTED(!OPLINE))".$m[3]."\n");
out($f,"#else\n");
- out($f, $m[1]."if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0))".$m[3]."\n");
+ out($f, $m[1]."opline = ((opcode_handler_t)opline->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
+ out($f, $m[1]."if (UNEXPECTED(((uintptr_t)opline & ZEND_VM_ENTER_BIT)))".$m[3]."\n");
out($f,"#endif\n");
if ($kind == ZEND_VM_KIND_HYBRID) {
- out($f,"#endif\n");
+ out($f,"#endif /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */\n");
}
break;
}
@@ -2168,7 +2166,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
if ($kind == ZEND_VM_KIND_CALL || $kind == ZEND_VM_KIND_HYBRID) {
// Executor is defined as a set of functions
if ($kind == ZEND_VM_KIND_HYBRID) {
- out($f,"#else\n");
+ out($f,"#else /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */\n");
}
out($f,
"#ifdef ZEND_VM_FP_GLOBAL_REG\n" .
@@ -2178,18 +2176,19 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
"# endif\n" .
$m[1]."return;\n" .
"#else\n" .
- $m[1]."if (EXPECTED(ret > 0)) {\n" .
+ $m[1]."opline = (const zend_op*)((uintptr_t)opline & ~ZEND_VM_ENTER_BIT);\n".
+ $m[1]."if (EXPECTED(opline != NULL)) {\n" .
$m[1]."\texecute_data = EG(current_execute_data);\n".
$m[1]."\tZEND_VM_LOOP_INTERRUPT_CHECK();\n".
$m[1]."} else {\n" .
"# ifdef ZEND_VM_IP_GLOBAL_REG\n" .
$m[1]."\topline = vm_stack_data.orig_opline;\n" .
- "# endif\n".
+ "# endif\n" .
$m[1]."\treturn;\n".
$m[1]."}\n".
"#endif\n");
if ($kind == ZEND_VM_KIND_HYBRID) {
- out($f,"#endif\n");
+ out($f,"#endif /* ZEND_VM_KIND != ZEND_VM_KIND_HYBRID */\n");
}
}
break;
@@ -2335,6 +2334,8 @@ function gen_vm_opcodes_header(
): string {
$str = HEADER_TEXT;
$str .= "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n";
+ $str .= "#include \"Zend/zend_portability.h\"\n";
+ $str .= "\n";
$str .= "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n";
$str .= "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n";
$str .= "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n";
@@ -2968,6 +2969,7 @@ function gen_vm($def, $skel) {
out($f, "\topcode_handler_t handler;\n");
out($f,"#endif\n");
}
+ out($f, "\tDCL_OPLINE;\n");
out($f, "\tint ret;\n");
out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
out($f, "\tconst zend_op *orig_opline = opline;\n");
@@ -3001,8 +3003,24 @@ function gen_vm($def, $skel) {
out($f, "\t\tret = -1;\n");
out($f, "\t}\n");
out($f, "#else\n");
- out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
- out($f, "\tSAVE_OPLINE();\n");
+ out($f, "\topline = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
+
+ out($f, "if (UNEXPECTED(((uintptr_t)opline & ZEND_VM_ENTER_BIT))) {\n");
+ out($f, "\t\topline = (const zend_op*)((uintptr_t)opline & ~ZEND_VM_ENTER_BIT);\n");
+ out($f, "\t\tif (EXPECTED(opline)) {\n");
+ out($f, "\t\t\t/* ZEND_VM_ENTER() or ZEND_VM_LEAVE() */\n");
+ out($f, "\t\t\tret = EG(current_execute_data) != ex ? (int)(EG(current_execute_data)->prev_execute_data != ex) + 1 : 0;\n");
+ out($f, "\t\t\texecute_data = EG(current_execute_data);\n");
+ out($f, "\t\t\tSAVE_OPLINE();\n");
+ out($f, "\t\t} else {\n");
+ out($f, "\t\t\t/* ZEND_VM_RETURN() */\n");
+ out($f, "\t\t\tret = -1;\n");
+ out($f, "\t\t}\n");
+ out($f, "\t} else {\n");
+ out($f, "\t\t/* ZEND_VM_CONTINUE() */\n");
+ out($f, "\t\tSAVE_OPLINE();\n");
+ out($f, "\t\tret = 0;\n");
+ out($f, "\t}\n");
out($f, "#endif\n");
out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
out($f, "\texecute_data = orig_execute_data;\n");
diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h
index 7f3a3cb5de260..cbbc6a4772788 100644
--- a/Zend/zend_vm_handlers.h
+++ b/Zend/zend_vm_handlers.h
@@ -1372,502 +1372,503 @@
_(2574, ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER) \
_(2575, ZEND_JMP_FRAMELESS_SPEC_CONST) \
_(2576, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \
- _(2577, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \
- _(2578, ZEND_RECV_NOTYPE_SPEC) \
- _(2580, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \
+ _(2577, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \
+ _(2578, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \
+ _(2579, ZEND_RECV_NOTYPE_SPEC) \
_(2581, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \
- _(2583, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \
- _(2584, ZEND_JMP_FORWARD_SPEC) \
- _(2590, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2582, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \
+ _(2584, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \
+ _(2585, ZEND_JMP_FORWARD_SPEC) \
+ _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2592, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2594, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2595, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2593, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2595, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2597, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2599, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2605, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2606, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2600, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2606, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2607, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2609, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2615, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
- _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2608, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2610, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
_(2617, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2619, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2620, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
- _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2618, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2620, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
_(2622, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2624, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2630, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
- _(2631, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2625, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2631, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \
_(2632, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2634, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2640, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2633, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2635, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2642, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2644, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2645, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2643, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2645, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2647, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2649, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2655, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2656, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2650, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2656, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2657, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2659, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2661, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
+ _(2658, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2660, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
_(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
- _(2664, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
- _(2665, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
+ _(2665, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \
+ _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2667, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2669, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2670, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2668, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2670, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2674, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2680, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2681, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2675, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2681, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2682, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2684, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2686, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
+ _(2683, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2685, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
_(2687, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
- _(2689, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
- _(2690, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
- _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2688, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
+ _(2690, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \
+ _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
_(2692, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2694, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2695, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
- _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2693, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2695, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
_(2697, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2699, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2705, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
- _(2706, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2700, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2706, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \
_(2707, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2709, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2711, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(2708, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2710, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \
_(2712, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(2714, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(2715, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2713, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(2715, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2717, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2719, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2720, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2718, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2720, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2722, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2724, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2730, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2731, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2725, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2731, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2732, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2734, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2740, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2733, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2735, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2742, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2744, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2745, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2743, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2745, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2747, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2749, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2755, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
- _(2756, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2750, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2756, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \
_(2757, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2759, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
- _(2765, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
- _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2758, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2760, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \
+ _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
_(2767, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2769, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2770, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
- _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2768, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2770, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
_(2772, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2774, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2780, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
- _(2781, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2775, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2781, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \
_(2782, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2784, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2790, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2783, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2785, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2792, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2794, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2795, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2793, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2795, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2797, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2799, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2805, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2806, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2800, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2806, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \
_(2807, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2809, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2870, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2871, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2872, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2873, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2877, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2878, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2882, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2883, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2884, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2907, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2908, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2945, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(2946, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2947, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2948, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2952, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2953, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2957, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(2958, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2959, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2982, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2983, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3020, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3021, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3022, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3023, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3027, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3028, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3032, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3033, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3034, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3057, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3058, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3095, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3096, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3097, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3098, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3102, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3103, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3107, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3108, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3109, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3110, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
- _(3114, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \
- _(3115, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
- _(3119, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \
- _(3123, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
- _(3124, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3125, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3126, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
- _(3127, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3128, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3132, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
- _(3133, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3134, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
- _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
- _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3154, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3162, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3163, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3164, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
- _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3183, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3184, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3185, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3186, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3187, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3188, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3192, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3193, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3194, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3207, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3208, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3229, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3237, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3238, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3239, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3258, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3259, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3260, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3261, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3262, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3263, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3267, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3268, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3269, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
- _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
- _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3282, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
- _(3283, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3284, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3304, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3312, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3313, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3314, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
- _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3333, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3334, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3335, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3336, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3337, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3338, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3342, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
- _(3343, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3344, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3357, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
- _(3358, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
- _(3359, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
- _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3379, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3387, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3388, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3389, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
- _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
- _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
- _(3408, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3409, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3410, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3411, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3412, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3413, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3417, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
- _(3418, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
- _(3419, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
- _(3420, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
- _(3421, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
- _(3422, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \
- _(3423, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \
- _(3424, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
- _(3425, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
- _(3426, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \
- _(3427, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \
- _(3428, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \
- _(3429, ZEND_POST_INC_LONG_SPEC_CV) \
- _(3430, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \
- _(3431, ZEND_POST_DEC_LONG_SPEC_CV) \
- _(3432, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \
- _(3433, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
+ _(2808, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2810, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2849, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2871, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2872, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2873, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2877, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2878, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2879, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2883, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2884, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2885, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2907, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2908, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2924, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2946, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(2947, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2948, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2952, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2953, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2954, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2958, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(2959, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2960, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2982, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2983, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(2999, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3021, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3022, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3023, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3027, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3028, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3029, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3033, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3034, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3035, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3057, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3058, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3074, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3096, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3097, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3098, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3102, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3103, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3104, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3108, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3109, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3110, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3111, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
+ _(3115, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \
+ _(3116, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \
+ _(3120, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \
+ _(3124, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
+ _(3125, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3126, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3127, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
+ _(3128, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3129, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3133, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \
+ _(3134, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3135, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
+ _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
+ _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3154, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3163, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3164, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3165, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \
+ _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3183, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3184, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3185, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3186, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3187, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3188, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3189, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3193, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3194, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3195, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3208, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3229, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3238, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3239, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3240, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3258, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3259, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3260, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3261, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3262, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3263, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3264, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3268, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3269, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3270, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
+ _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
+ _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3283, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \
+ _(3284, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3304, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3313, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3314, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3315, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \
+ _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3333, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3334, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3335, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3336, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3337, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3338, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3339, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3343, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \
+ _(3344, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3345, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3358, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \
+ _(3359, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \
+ _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \
+ _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3379, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3388, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3389, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3390, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \
+ _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \
+ _(3408, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \
+ _(3409, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3410, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3411, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3412, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3413, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3414, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3418, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \
+ _(3419, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \
+ _(3420, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \
+ _(3421, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
+ _(3422, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
+ _(3423, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \
+ _(3424, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \
+ _(3425, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \
+ _(3426, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \
+ _(3427, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \
+ _(3428, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \
+ _(3429, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \
+ _(3430, ZEND_POST_INC_LONG_SPEC_CV) \
+ _(3431, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \
+ _(3432, ZEND_POST_DEC_LONG_SPEC_CV) \
+ _(3433, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \
_(3434, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
- _(3436, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
- _(3437, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \
- _(3438, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
+ _(3435, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
+ _(3437, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \
+ _(3438, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \
_(3439, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
- _(3441, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
- _(3442, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \
- _(3443, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
+ _(3440, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
+ _(3442, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \
+ _(3443, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \
_(3444, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
- _(3446, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
- _(3448, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
+ _(3445, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
+ _(3447, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \
_(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
- _(3451, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
- _(3452, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
- _(3453, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3450, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
+ _(3452, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \
+ _(3453, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
_(3454, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
- _(3458, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3458, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \
_(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3461, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
- _(3467, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \
- _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
+ _(3460, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3462, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \
+ _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \
_(3469, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
- _(3471, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
- _(3474, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \
- _(3476, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \
- _(3479, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
- _(3481, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
- _(3482, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
- _(3483, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
- _(3484, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
- _(3485, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
- _(3485+1, ZEND_NULL)
+ _(3470, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
+ _(3472, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \
+ _(3475, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \
+ _(3477, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \
+ _(3480, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \
+ _(3482, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \
+ _(3483, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \
+ _(3484, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \
+ _(3485, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \
+ _(3486, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \
+ _(3486+1, ZEND_NULL)
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index 202dfd3f734f3..00ad38baaafeb 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -22,7 +22,7 @@
#include
#include
-static const char *zend_vm_opcodes_names[210] = {
+static const char *zend_vm_opcodes_names[211] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@@ -233,9 +233,10 @@ static const char *zend_vm_opcodes_names[210] = {
"ZEND_FRAMELESS_ICALL_3",
"ZEND_JMP_FRAMELESS",
"ZEND_INIT_PARENT_PROPERTY_HOOK_CALL",
+ "ZEND_DECLARE_ATTRIBUTED_CONST",
};
-static uint32_t zend_vm_opcodes_flags[210] = {
+static uint32_t zend_vm_opcodes_flags[211] = {
0x00000000,
0x00000b0b,
0x00000b0b,
@@ -299,8 +300,8 @@ static uint32_t zend_vm_opcodes_flags[210] = {
0x00000000,
0x01040310,
0x00000003,
- 0x00040110,
- 0x00040310,
+ 0x00000110,
+ 0x00000310,
0x00001307,
0x00001301,
0x00001301,
@@ -360,7 +361,7 @@ static uint32_t zend_vm_opcodes_flags[210] = {
0x00000007,
0x00040003,
0x09000007,
- 0x0000a103,
+ 0x00000103,
0x00002003,
0x03000001,
0x00000005,
@@ -400,7 +401,7 @@ static uint32_t zend_vm_opcodes_flags[210] = {
0x00000003,
0x00000020,
0x00003000,
- 0x00040110,
+ 0x00000110,
0x00000000,
0x00000007,
0x00000105,
@@ -446,6 +447,7 @@ static uint32_t zend_vm_opcodes_flags[210] = {
0x00000000,
0x01042003,
0x01001103,
+ 0x00000303,
};
ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(uint8_t opcode) {
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index d472b5b9660f5..29469bb5f7dca 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -21,6 +21,8 @@
#ifndef ZEND_VM_OPCODES_H
#define ZEND_VM_OPCODES_H
+#include "Zend/zend_portability.h"
+
#define ZEND_VM_SPEC 1
#define ZEND_VM_LINES 0
#define ZEND_VM_KIND_CALL 1
@@ -291,7 +293,8 @@ END_EXTERN_C()
#define ZEND_FRAMELESS_ICALL_3 207
#define ZEND_JMP_FRAMELESS 208
#define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209
+#define ZEND_DECLARE_ATTRIBUTED_CONST 210
-#define ZEND_VM_LAST_OPCODE 209
+#define ZEND_VM_LAST_OPCODE 210
#endif
diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c
index b613b3f4ac1c7..cf363cd12595c 100644
--- a/Zend/zend_weakrefs.c
+++ b/Zend/zend_weakrefs.c
@@ -183,6 +183,13 @@ ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) {
return FAILURE;
}
+ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) {
+ zend_ulong obj_key;
+ ZEND_HASH_FOREACH_NUM_KEY(ht, obj_key) {
+ zend_weakrefs_hash_del(ht, zend_weakref_key_to_object(obj_key));
+ } ZEND_HASH_FOREACH_END();
+}
+
void zend_weakrefs_init(void) {
zend_hash_init(&EG(weakrefs), 8, NULL, NULL, 0);
}
diff --git a/Zend/zend_weakrefs.h b/Zend/zend_weakrefs.h
index 5b1c8ea1c325d..00d4fbcc6bc4c 100644
--- a/Zend/zend_weakrefs.h
+++ b/Zend/zend_weakrefs.h
@@ -41,6 +41,12 @@ static zend_always_inline void *zend_weakrefs_hash_add_ptr(HashTable *ht, zend_o
return NULL;
}
}
+ZEND_API void zend_weakrefs_hash_clean(HashTable *ht);
+static zend_always_inline void zend_weakrefs_hash_destroy(HashTable *ht) {
+ zend_weakrefs_hash_clean(ht);
+ ZEND_ASSERT(zend_hash_num_elements(ht) == 0);
+ zend_hash_destroy(ht);
+}
/* Because php uses the raw numbers as a hash function, raw pointers will lead to hash collisions.
* We have a guarantee that the lowest ZEND_MM_ALIGNED_OFFSET_LOG2 bits of a pointer are zero.
diff --git a/build/Makefile.global b/build/Makefile.global
index 52a629c048a22..32605a203e188 100644
--- a/build/Makefile.global
+++ b/build/Makefile.global
@@ -121,7 +121,7 @@ clean:
find . -name \*.la -o -name \*.a | xargs rm -f
find . -name \*.so | xargs rm -f
find . -name .libs -a -type d|xargs rm -rf
- rm -f libphp.la $(SAPI_CLI_PATH) $(SAPI_CGI_PATH) $(SAPI_LITESPEED_PATH) $(SAPI_FPM_PATH) $(OVERALL_TARGET) modules/* libs/*
+ rm -f libphp.la $(SAPI_CLI_PATH) $(SAPI_CGI_PATH) $(SAPI_LITESPEED_PATH) $(SAPI_FPM_PATH) $(SAPI_PHPDBG_PATH) $(SAPI_PHPDBG_SHARED_PATH) $(OVERALL_TARGET) modules/* libs/*
rm -f ext/opcache/jit/ir/gen_ir_fold_hash
rm -f ext/opcache/jit/ir/minilua
rm -f ext/opcache/jit/ir/ir_fold_hash.h
@@ -147,7 +147,7 @@ prof-clean:
find . -name \*.lo -o -name \*.o | xargs rm -f
find . -name \*.la -o -name \*.a | xargs rm -f
find . -name \*.so | xargs rm -f
- rm -f libphp.la $(SAPI_CLI_PATH) $(SAPI_CGI_PATH) $(SAPI_LITESPEED_PATH) $(SAPI_FPM_PATH) $(OVERALL_TARGET) modules/* libs/*
+ rm -f libphp.la $(SAPI_CLI_PATH) $(SAPI_CGI_PATH) $(SAPI_LITESPEED_PATH) $(SAPI_FPM_PATH) $(SAPI_PHPDBG_PATH) $(SAPI_PHPDBG_SHARED_PATH) $(OVERALL_TARGET) modules/* libs/*
prof-use:
CCACHE_DISABLE=1 $(MAKE) PROF_FLAGS=-fprofile-use all
diff --git a/build/gen_stub.php b/build/gen_stub.php
index 45641f4061b9c..5e06a53172e07 100755
--- a/build/gen_stub.php
+++ b/build/gen_stub.php
@@ -25,6 +25,7 @@
const PHP_82_VERSION_ID = 80200;
const PHP_83_VERSION_ID = 80300;
const PHP_84_VERSION_ID = 80400;
+const PHP_85_VERSION_ID = 80500;
const ALL_PHP_VERSION_IDS = [
PHP_70_VERSION_ID,
PHP_80_VERSION_ID,
@@ -32,8 +33,16 @@
PHP_82_VERSION_ID,
PHP_83_VERSION_ID,
PHP_84_VERSION_ID,
+ PHP_85_VERSION_ID,
];
+// file_put_contents() but with a success message printed after saving
+function reportFilePutContents(string $filename, string $content): void {
+ if (file_put_contents($filename, $content)) {
+ echo "Saved $filename\n";
+ }
+}
+
/**
* @return FileInfo[]
*/
@@ -83,15 +92,10 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
}
}
- /* Because exit() and die() are proper token/keywords we need to hack-around */
- $hasSpecialExitAsFunctionHandling = str_ends_with($stubFile, 'zend_builtin_functions.stub.php');
if (!$fileInfo = $context->parsedFiles[$stubFile] ?? null) {
initPhpParser();
$stubContent = $stubCode ?? file_get_contents($stubFile);
- if ($hasSpecialExitAsFunctionHandling) {
- $stubContent = str_replace(['exit', 'die'], ['exit_dummy', 'die_dummy'], $stubContent);
- }
- $fileInfo = parseStubFile($stubContent);
+ $fileInfo = FileInfo::parseStubFile($stubContent);
$context->parsedFiles[$stubFile] = $fileInfo;
foreach ($fileInfo->dependencies as $dependency) {
@@ -124,27 +128,12 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
$context->allConstInfos,
$stubHash
);
- if ($hasSpecialExitAsFunctionHandling) {
- $arginfoCode = str_replace(['exit_dummy', 'die_dummy'], ['exit', 'die'], $arginfoCode);
- }
- if (($context->forceRegeneration || $stubHash !== $oldStubHash) && file_put_contents($arginfoFile, $arginfoCode)) {
- echo "Saved $arginfoFile\n";
+ if ($context->forceRegeneration || $stubHash !== $oldStubHash) {
+ reportFilePutContents($arginfoFile, $arginfoCode);
}
if ($fileInfo->shouldGenerateLegacyArginfo()) {
- $legacyFileInfo = clone $fileInfo;
- $legacyFileInfo->legacyArginfoGeneration = true;
- $phpVersionIdMinimumCompatibility = $legacyFileInfo->getMinimumPhpVersionIdCompatibility();
-
- foreach ($legacyFileInfo->getAllFuncInfos() as $funcInfo) {
- $funcInfo->discardInfoForOldPhpVersions($phpVersionIdMinimumCompatibility);
- }
- foreach ($legacyFileInfo->getAllClassInfos() as $classInfo) {
- $classInfo->discardInfoForOldPhpVersions($phpVersionIdMinimumCompatibility);
- }
- foreach ($legacyFileInfo->getAllConstInfos() as $constInfo) {
- $constInfo->discardInfoForOldPhpVersions($phpVersionIdMinimumCompatibility);
- }
+ $legacyFileInfo = $fileInfo->getLegacyVersion();
$arginfoCode = generateArgInfoCode(
basename($stubFilenameWithoutExtension),
@@ -152,8 +141,8 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly =
$context->allConstInfos,
$stubHash
);
- if (($context->forceRegeneration || $stubHash !== $oldStubHash) && file_put_contents($legacyFile, $arginfoCode)) {
- echo "Saved $legacyFile\n";
+ if ($context->forceRegeneration || $stubHash !== $oldStubHash) {
+ reportFilePutContents($legacyFile, $arginfoCode);
}
}
@@ -191,8 +180,8 @@ class Context {
}
class ArrayType extends SimpleType {
- public Type $keyType;
- public Type $valueType;
+ private /* readonly */ Type $keyType;
+ private /* readonly */ Type $valueType;
public static function createGenericArray(): self
{
@@ -230,8 +219,8 @@ public function equals(SimpleType $other): bool {
}
class SimpleType {
- public string $name;
- public bool $isBuiltin;
+ public /* readonly */ string $name;
+ public /* readonly */ bool $isBuiltin;
public static function fromNode(Node $node): SimpleType {
if ($node instanceof Node\Name) {
@@ -314,17 +303,17 @@ public static function fromValue($value): SimpleType
case "NULL":
return SimpleType::null();
case "boolean":
- return SimpleType::bool();
+ return new SimpleType("bool", true);
case "integer":
- return SimpleType::int();
+ return new SimpleType("int", true);
case "double":
- return SimpleType::float();
+ return new SimpleType("float", true);
case "string":
- return SimpleType::string();
+ return new SimpleType("string", true);
case "array":
- return SimpleType::array();
+ return new SimpleType("array", true);
case "object":
- return SimpleType::object();
+ return new SimpleType("object", true);
default:
throw new Exception("Type \"" . gettype($value) . "\" cannot be inferred based on value");
}
@@ -335,41 +324,6 @@ public static function null(): SimpleType
return new SimpleType("null", true);
}
- public static function bool(): SimpleType
- {
- return new SimpleType("bool", true);
- }
-
- public static function int(): SimpleType
- {
- return new SimpleType("int", true);
- }
-
- public static function float(): SimpleType
- {
- return new SimpleType("float", true);
- }
-
- public static function string(): SimpleType
- {
- return new SimpleType("string", true);
- }
-
- public static function array(): SimpleType
- {
- return new SimpleType("array", true);
- }
-
- public static function object(): SimpleType
- {
- return new SimpleType("object", true);
- }
-
- public static function void(): SimpleType
- {
- return new SimpleType("void", true);
- }
-
protected function __construct(string $name, bool $isBuiltin) {
$this->name = $name;
$this->isBuiltin = $isBuiltin;
@@ -565,10 +519,12 @@ public function equals(SimpleType $other): bool {
}
}
+// Instances of Type are immutable and do not need to be cloned
+// when held by an object that is cloned
class Type {
/** @var SimpleType[] */
- public array $types;
- public bool $isIntersection;
+ public /* readonly */ array $types;
+ public /* readonly */ bool $isIntersection;
public static function fromNode(Node $node): Type {
if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) {
@@ -669,28 +625,19 @@ public function isNullable(): bool {
return false;
}
- public function getWithoutNull(): Type {
- return new Type(
- array_values(
- array_filter(
- $this->types,
- function(SimpleType $type) {
- return !$type->isNull();
- }
- )
- ),
- false
- );
- }
-
public function tryToSimpleType(): ?SimpleType {
- $withoutNull = $this->getWithoutNull();
+ $nonNullTypes = array_filter(
+ $this->types,
+ function(SimpleType $type) {
+ return !$type->isNull();
+ }
+ );
/* type has only null */
- if (count($withoutNull->types) === 0) {
+ if (count($nonNullTypes) === 0) {
return $this->types[0];
}
- if (count($withoutNull->types) === 1) {
- return $withoutNull->types[0];
+ if (count($nonNullTypes) === 1) {
+ return reset($nonNullTypes);
}
return null;
}
@@ -751,9 +698,7 @@ public function getTypeForDoc(DOMDocument $doc): DOMElement {
}
} else {
$type = $this->types[0];
- $name = $type->name;
-
- $typeElement = $doc->createElement('type', $name);
+ $typeElement = $doc->createElement('type', $type->name);
}
return $typeElement;
@@ -778,10 +723,6 @@ public static function equals(?Type $a, ?Type $b): bool {
}
public function __toString() {
- if ($this->types === null) {
- return 'mixed';
- }
-
$char = $this->isIntersection ? '&' : '|';
return implode($char, array_map(
function ($type) { return $type->name; },
@@ -792,9 +733,9 @@ function ($type) { return $type->name; },
class ArginfoType {
/** @var SimpleType[] $classTypes */
- public array $classTypes;
+ public /* readonly */ array $classTypes;
/** @var SimpleType[] $builtinTypes */
- private array $builtinTypes;
+ private /* readonly */ array $builtinTypes;
/**
* @param SimpleType[] $classTypes
@@ -826,15 +767,15 @@ public function toTypeMask(): string {
}
class ArgInfo {
- const SEND_BY_VAL = 0;
- const SEND_BY_REF = 1;
- const SEND_PREFER_REF = 2;
+ public const SEND_BY_VAL = "0";
+ public const SEND_BY_REF = "1";
+ public const SEND_PREFER_REF = "ZEND_SEND_PREFER_REF";
- public string $name;
- public int $sendBy;
- public bool $isVariadic;
+ public /* readonly */ string $name;
+ public /* readonly */ string $sendBy;
+ public /* readonly */ bool $isVariadic;
public ?Type $type;
- public ?Type $phpDocType;
+ private /* readonly */ ?Type $phpDocType;
public ?string $defaultValue;
/** @var AttributeInfo[] */
public array $attributes;
@@ -844,7 +785,7 @@ class ArgInfo {
*/
public function __construct(
string $name,
- int $sendBy,
+ string $sendBy,
bool $isVariadic,
?Type $type,
?Type $phpDocType,
@@ -854,7 +795,8 @@ public function __construct(
$this->name = $name;
$this->sendBy = $sendBy;
$this->isVariadic = $isVariadic;
- $this->setTypes($type, $phpDocType);
+ $this->type = $type;
+ $this->phpDocType = $phpDocType;
$this->defaultValue = $defaultValue;
$this->attributes = $attributes;
}
@@ -867,18 +809,6 @@ public function equals(ArgInfo $other): bool {
&& $this->defaultValue === $other->defaultValue;
}
- public function getSendByString(): string {
- switch ($this->sendBy) {
- case self::SEND_BY_VAL:
- return "0";
- case self::SEND_BY_REF:
- return "1";
- case self::SEND_PREFER_REF:
- return "ZEND_SEND_PREFER_REF";
- }
- throw new Exception("Invalid sendBy value");
- }
-
public function getMethodSynopsisType(): Type {
if ($this->type) {
return $this->type;
@@ -895,7 +825,7 @@ public function hasProperDefaultValue(): bool {
return $this->defaultValue !== null && $this->defaultValue !== "UNKNOWN";
}
- public function getDefaultValueAsArginfoString(): string {
+ private function getDefaultValueAsArginfoString(): string {
if ($this->hasProperDefaultValue()) {
return '"' . addslashes($this->defaultValue) . '"';
}
@@ -904,10 +834,6 @@ public function getDefaultValueAsArginfoString(): string {
}
public function getDefaultValueAsMethodSynopsisString(): ?string {
- if ($this->defaultValue === null) {
- return null;
- }
-
switch ($this->defaultValue) {
case 'UNKNOWN':
return null;
@@ -920,10 +846,48 @@ public function getDefaultValueAsMethodSynopsisString(): ?string {
return $this->defaultValue;
}
- private function setTypes(?Type $type, ?Type $phpDocType): void
- {
- $this->type = $type;
- $this->phpDocType = $phpDocType;
+ public function toZendInfo(): string {
+ $argKind = $this->isVariadic ? "ARG_VARIADIC" : "ARG";
+ $argDefaultKind = $this->hasProperDefaultValue() ? "_WITH_DEFAULT_VALUE" : "";
+ $argType = $this->type;
+ if ($argType !== null) {
+ if (null !== $simpleArgType = $argType->tryToSimpleType()) {
+ if ($simpleArgType->isBuiltin) {
+ return sprintf(
+ "\tZEND_%s_TYPE_INFO%s(%s, %s, %s, %d%s)\n",
+ $argKind, $argDefaultKind, $this->sendBy, $this->name,
+ $simpleArgType->toTypeCode(), $argType->isNullable(),
+ $this->hasProperDefaultValue() ? ", " . $this->getDefaultValueAsArginfoString() : ""
+ );
+ }
+ return sprintf(
+ "\tZEND_%s_OBJ_INFO%s(%s, %s, %s, %d%s)\n",
+ $argKind, $argDefaultKind, $this->sendBy, $this->name,
+ $simpleArgType->toEscapedName(), $argType->isNullable(),
+ $this->hasProperDefaultValue() ? ", " . $this->getDefaultValueAsArginfoString() : ""
+ );
+ }
+ $arginfoType = $argType->toArginfoType();
+ if ($arginfoType->hasClassType()) {
+ return sprintf(
+ "\tZEND_%s_OBJ_TYPE_MASK(%s, %s, %s, %s%s)\n",
+ $argKind, $this->sendBy, $this->name,
+ $arginfoType->toClassTypeString(), $arginfoType->toTypeMask(),
+ !$this->isVariadic ? ", " . $this->getDefaultValueAsArginfoString() : ""
+ );
+ }
+ return sprintf(
+ "\tZEND_%s_TYPE_MASK(%s, %s, %s, %s)\n",
+ $argKind, $this->sendBy, $this->name,
+ $arginfoType->toTypeMask(),
+ $this->getDefaultValueAsArginfoString()
+ );
+ }
+ return sprintf(
+ "\tZEND_%s_INFO%s(%s, %s%s)\n",
+ $argKind, $argDefaultKind, $this->sendBy, $this->name,
+ $this->hasProperDefaultValue() ? ", " . $this->getDefaultValueAsArginfoString() : ""
+ );
}
}
@@ -932,19 +896,15 @@ public function __toString(): string;
public function getDeclarationName(): string;
}
-interface ConstOrClassConstName extends VariableLikeName {
- public function equals(ConstOrClassConstName $const): bool;
- public function isClassConst(): bool;
- public function isUnknown(): bool;
-}
-
-abstract class AbstractConstName implements ConstOrClassConstName
+abstract class AbstractConstName implements VariableLikeName
{
- public function equals(ConstOrClassConstName $const): bool
+ public function equals(AbstractConstName $const): bool
{
return $this->__toString() === $const->__toString();
}
+ abstract public function isClassConst(): bool;
+
public function isUnknown(): bool
{
return strtolower($this->__toString()) === "unknown";
@@ -952,7 +912,7 @@ public function isUnknown(): bool
}
class ConstName extends AbstractConstName {
- public string $const;
+ private /* readonly */ string $const;
public function __construct(?Name $namespace, string $const)
{
@@ -988,8 +948,8 @@ public function getDeclarationName(): string
}
class ClassConstName extends AbstractConstName {
- public Name $class;
- public string $const;
+ public /* readonly */ Name $class;
+ private /* readonly */ string $const;
public function __construct(Name $class, string $const)
{
@@ -1014,8 +974,8 @@ public function getDeclarationName(): string
}
class PropertyName implements VariableLikeName {
- public Name $class;
- public string $property;
+ public /* readonly */ Name $class;
+ private /* readonly */ string $property;
public function __construct(Name $class, string $property)
{
@@ -1030,7 +990,7 @@ public function __toString(): string
public function getDeclarationName(): string
{
- return $this->property;
+ return $this->property;
}
}
@@ -1046,7 +1006,7 @@ public function isDestructor(): bool;
}
class FunctionName implements FunctionOrMethodName {
- private Name $name;
+ private /* readonly */ Name $name;
public function __construct(Name $name) {
$this->name = $name;
@@ -1114,8 +1074,8 @@ public function isDestructor(): bool {
}
class MethodName implements FunctionOrMethodName {
- public Name $className;
- public string $methodName;
+ public /* readonly */ Name $className;
+ public /* readonly */ string $methodName;
public function __construct(Name $className, string $methodName) {
$this->className = $className;
@@ -1163,25 +1123,27 @@ public function isDestructor(): bool {
}
class ReturnInfo {
- const REFCOUNT_0 = "0";
- const REFCOUNT_1 = "1";
- const REFCOUNT_N = "N";
+ public const REFCOUNT_0 = "0";
+ public const REFCOUNT_1 = "1";
+ public const REFCOUNT_N = "N";
- const REFCOUNTS = [
- self::REFCOUNT_0,
+ public const REFCOUNTS_NONSCALAR = [
self::REFCOUNT_1,
self::REFCOUNT_N,
];
- public bool $byRef;
+ private /* readonly */ bool $byRef;
+ // NOT readonly - gets removed when discarding info for older PHP versions
public ?Type $type;
- public ?Type $phpDocType;
- public bool $tentativeReturnType;
- public string $refcount;
+ public /* readonly */ ?Type $phpDocType;
+ public /* readonly */ bool $tentativeReturnType;
+ public /* readonly */ string $refcount;
public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType, bool $tentativeReturnType, ?string $refcount) {
$this->byRef = $byRef;
- $this->setTypes($type, $phpDocType, $tentativeReturnType);
+ $this->type = $type;
+ $this->phpDocType = $phpDocType;
+ $this->tentativeReturnType = $tentativeReturnType;
$this->setRefcount($refcount);
}
@@ -1195,13 +1157,6 @@ public function getMethodSynopsisType(): ?Type {
return $this->type ?? $this->phpDocType;
}
- private function setTypes(?Type $type, ?Type $phpDocType, bool $tentativeReturnType): void
- {
- $this->type = $type;
- $this->phpDocType = $phpDocType;
- $this->tentativeReturnType = $tentativeReturnType;
- }
-
private function setRefcount(?string $refcount): void
{
$type = $this->phpDocType ?? $this->type;
@@ -1212,47 +1167,108 @@ private function setRefcount(?string $refcount): void
return;
}
- if (!in_array($refcount, ReturnInfo::REFCOUNTS, true)) {
- throw new Exception("@refcount must have one of the following values: \"0\", \"1\", \"N\", $refcount given");
+ if ($isScalarType) {
+ throw new Exception(
+ "@refcount on functions returning scalar values is redundant and not permitted"
+ );
}
- if ($isScalarType && $refcount !== self::REFCOUNT_0) {
- throw new Exception('A scalar return type of "' . $type->__toString() . '" must have a refcount of "' . self::REFCOUNT_0 . '"');
+ if (!in_array($refcount, ReturnInfo::REFCOUNTS_NONSCALAR, true)) {
+ throw new Exception("@refcount must have one of the following values: \"1\", \"N\", $refcount given");
}
- if (!$isScalarType && $refcount === self::REFCOUNT_0) {
- throw new Exception('A non-scalar return type of "' . $type->__toString() . '" cannot have a refcount of "' . self::REFCOUNT_0 . '"');
+ $this->refcount = $refcount;
+ }
+
+ public function beginArgInfo(string $funcInfoName, int $minArgs, bool $php81MinimumCompatibility): string {
+ $code = $this->beginArgInfoCompatible($funcInfoName, $minArgs);
+ if ($this->type !== null && $this->tentativeReturnType && !$php81MinimumCompatibility) {
+ $realCode = "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
+ $realCode .= $code;
+ $realCode .= sprintf(
+ "#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
+ $funcInfoName, $this->byRef, $minArgs
+ );
+ return $realCode;
}
+ return $code;
+ }
- $this->refcount = $refcount;
+ /**
+ * Assumes PHP 8.1 compatibility, if that is not the case the caller is
+ * responsible for making the use of a tentative return type conditional
+ * based on the PHP version. Separate to allow using early returns
+ */
+ private function beginArgInfoCompatible(string $funcInfoName, int $minArgs): string {
+ if ($this->type !== null) {
+ if (null !== $simpleReturnType = $this->type->tryToSimpleType()) {
+ if ($simpleReturnType->isBuiltin) {
+ return sprintf(
+ "%s(%s, %d, %d, %s, %d)\n",
+ $this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
+ $funcInfoName, $this->byRef,
+ $minArgs,
+ $simpleReturnType->toTypeCode(), $this->type->isNullable()
+ );
+ }
+ return sprintf(
+ "%s(%s, %d, %d, %s, %d)\n",
+ $this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
+ $funcInfoName, $this->byRef,
+ $minArgs,
+ $simpleReturnType->toEscapedName(), $this->type->isNullable()
+ );
+ }
+ $arginfoType = $this->type->toArginfoType();
+ if ($arginfoType->hasClassType()) {
+ return sprintf(
+ "%s(%s, %d, %d, %s, %s)\n",
+ $this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
+ $funcInfoName, $this->byRef,
+ $minArgs,
+ $arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
+ );
+ }
+ return sprintf(
+ "%s(%s, %d, %d, %s)\n",
+ $this->tentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
+ $funcInfoName, $this->byRef,
+ $minArgs,
+ $arginfoType->toTypeMask()
+ );
+ }
+ return sprintf(
+ "ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
+ $funcInfoName, $this->byRef, $minArgs
+ );
}
}
class FuncInfo {
- public FunctionOrMethodName $name;
- public int $classFlags;
+ public /* readonly */ FunctionOrMethodName $name;
+ private /* readonly */ int $classFlags;
public int $flags;
- public ?string $aliasType;
+ public /* readonly */ ?string $aliasType;
public ?FunctionOrMethodName $alias;
- public bool $isDeprecated;
- public bool $supportsCompileTimeEval;
- public bool $verify;
+ private /* readonly */ bool $isDeprecated;
+ private bool $supportsCompileTimeEval;
+ public /* readonly */ bool $verify;
/** @var ArgInfo[] */
- public array $args;
- public ReturnInfo $return;
- public int $numRequiredArgs;
- public ?string $cond;
+ public /* readonly */ array $args;
+ public /* readonly */ ReturnInfo $return;
+ private /* readonly */ int $numRequiredArgs;
+ public /* readonly */ ?string $cond;
public bool $isUndocumentable;
- public ?int $minimumPhpVersionIdCompatibility;
+ private ?int $minimumPhpVersionIdCompatibility;
/** @var AttributeInfo[] */
public array $attributes;
/** @var FramelessFunctionInfo[] */
- public array $framelessFunctionInfos;
- public ?ExposedDocComment $exposedDocComment;
+ private array $framelessFunctionInfos;
+ private ?ExposedDocComment $exposedDocComment;
/**
* @param ArgInfo[] $args
- * @param AttributeInfo[] $attribute
+ * @param AttributeInfo[] $attributes
* @param FramelessFunctionInfo[] $framelessFunctionInfos
*/
public function __construct(
@@ -1291,6 +1307,9 @@ public function __construct(
$this->attributes = $attributes;
$this->framelessFunctionInfos = $framelessFunctionInfos;
$this->exposedDocComment = $exposedDocComment;
+ if ($return->tentativeReturnType && $this->isFinalMethod()) {
+ throw new Exception("Tentative return inapplicable for final method");
+ }
}
public function isMethod(): bool
@@ -1338,7 +1357,7 @@ public function getModifierNames(): array
return $result;
}
- public function hasParamWithUnknownDefaultValue(): bool
+ private function hasParamWithUnknownDefaultValue(): bool
{
foreach ($this->args as $arg) {
if ($arg->defaultValue && !$arg->hasProperDefaultValue()) {
@@ -1387,7 +1406,7 @@ public function getDeclaration(): ?string
return $name->getDeclaration();
}
- public function getFramelessDeclaration(FuncInfo $funcInfo): ?string {
+ public function getFramelessDeclaration(): ?string {
if (empty($this->framelessFunctionInfos)) {
return null;
}
@@ -1418,7 +1437,7 @@ public function getFramelessDeclaration(FuncInfo $funcInfo): ?string {
return $code;
}
- public function getFramelessFunctionInfosName(): string {
+ private function getFramelessFunctionInfosName(): string {
return $this->name->getFramelessFunctionInfosName();
}
@@ -1460,7 +1479,13 @@ public function getFunctionEntry(): string {
$name = "zim_" . $this->name->getDeclarationClassName() . "_" . $this->name->methodName;
if ($isVanillaEntry) {
- $functionEntryCode = "\tZEND_ME(" . $this->name->getDeclarationClassName() . ", " . $this->name->methodName . ", $argInfoName, " . implode("|", reset($flagsByPhpVersions)) . ")";
+ $template = "\tZEND_ME(" . $this->name->getDeclarationClassName() . ", " . $this->name->methodName . ", $argInfoName, %s)\n";
+ $flagsCode = generateVersionDependentFlagCode(
+ $template,
+ $flagsByPhpVersions,
+ $this->minimumPhpVersionIdCompatibility
+ );
+ $functionEntryCode = rtrim($flagsCode);
}
}
}
@@ -1476,7 +1501,15 @@ public function getFunctionEntry(): string {
$zendName = '"' . $functionName . '"';
$name = "zif_$declarationName";
- if ($isVanillaEntry && reset($flagsByPhpVersions) === ["0"]) {
+ // Can only use ZEND_FE() if we have no flags for *all* versions
+ $hasFlags = false;
+ foreach ($flagsByPhpVersions as $flags) {
+ if ($flags !== ['0']) {
+ $hasFlags = true;
+ break;
+ }
+ }
+ if ($isVanillaEntry && !$hasFlags) {
$functionEntryCode = "\tZEND_FE($declarationName, $argInfoName)";
}
}
@@ -1495,30 +1528,22 @@ public function getFunctionEntry(): string {
$docComment = $this->exposedDocComment ? '"' . $this->exposedDocComment->escape() . '"' : "NULL";
$framelessFuncInfosName = !empty($this->framelessFunctionInfos) ? $this->getFramelessFunctionInfosName() : "NULL";
- $template = "\tZEND_RAW_FENTRY($zendName, $name, $argInfoName, %s, $framelessFuncInfosName, $docComment)\n";
- $flagsCode = generateVersionDependentFlagCode(
- $template,
+ $code .= generateVersionDependentFlagCode(
+ "\tZEND_RAW_FENTRY($zendName, $name, $argInfoName, %s, $framelessFuncInfosName, $docComment)\n",
$php84AndAboveFlags,
PHP_84_VERSION_ID
);
- $code .= implode("", $flagsCode);
if (!$php84MinimumCompatibility) {
$code .= "#else\n";
- }
- if (!$php84MinimumCompatibility) {
$flags = array_slice($flagsByPhpVersions, 0, 4, true);
- $template = "\tZEND_RAW_FENTRY($zendName, $name, $argInfoName, %s)\n";
- $flagsCode = generateVersionDependentFlagCode(
- $template,
+ $code .= generateVersionDependentFlagCode(
+ "\tZEND_RAW_FENTRY($zendName, $name, $argInfoName, %s)\n",
$flags,
$this->minimumPhpVersionIdCompatibility
);
- $code .= implode("", $flagsCode);
- }
- if (!$php84MinimumCompatibility) {
$code .= "#endif\n";
}
}
@@ -1593,9 +1618,10 @@ private function getArginfoFlagsByPhpVersions(): array
}
foreach ($this->attributes as $attr) {
- if ($attr->class === "Deprecated") {
- $flags[] = "ZEND_ACC_DEPRECATED";
- break;
+ switch ($attr->class) {
+ case "Deprecated":
+ $flags[] = "ZEND_ACC_DEPRECATED";
+ break;
}
}
@@ -1604,12 +1630,24 @@ private function getArginfoFlagsByPhpVersions(): array
$php82AndAboveFlags[] = "ZEND_ACC_COMPILE_TIME_EVAL";
}
+ $php85AndAboveFlags = $php82AndAboveFlags;
+ foreach ($this->attributes as $attr) {
+ switch ($attr->class) {
+ case "NoDiscard":
+ $php85AndAboveFlags[] = "ZEND_ACC_NODISCARD";
+ break;
+ }
+ }
+
if (empty($flags)) {
$flags[] = "0";
}
if (empty($php82AndAboveFlags)) {
$php82AndAboveFlags[] = "0";
}
+ if (empty($php85AndAboveFlags)) {
+ $php85AndAboveFlags[] = "0";
+ }
return [
PHP_70_VERSION_ID => $flags,
@@ -1618,6 +1656,7 @@ private function getArginfoFlagsByPhpVersions(): array
PHP_82_VERSION_ID => $php82AndAboveFlags,
PHP_83_VERSION_ID => $php82AndAboveFlags,
PHP_84_VERSION_ID => $php82AndAboveFlags,
+ PHP_85_VERSION_ID => $php85AndAboveFlags,
];
}
@@ -1632,12 +1671,7 @@ private function generateRefSect1(DOMDocument $doc, string $role): DOMElement {
return $refSec;
}
- /**
- * @param array $funcMap
- * @param array $aliasMap
- * @throws Exception
- */
- public function getMethodSynopsisDocument(array $funcMap, array $aliasMap): ?string {
+ public function getMethodSynopsisDocument(): ?string {
$REFSEC1_SEPERATOR = "\n\n ";
$doc = new DOMDocument("1.0", "utf-8");
@@ -1682,7 +1716,7 @@ public function getMethodSynopsisDocument(array $funcMap, array $aliasMap): ?str
/* Creation of */
$descriptionRefSec = $this->generateRefSect1($doc, 'description');
- $methodSynopsis = $this->getMethodSynopsisElement($funcMap, $aliasMap, $doc);
+ $methodSynopsis = $this->getMethodSynopsisElement($doc);
if (!$methodSynopsis) {
return null;
}
@@ -1691,7 +1725,7 @@ public function getMethodSynopsisDocument(array $funcMap, array $aliasMap): ?str
$undocumentedEntity = $doc->createEntityReference('warn.undocumented.func');
$descriptionRefSec->appendChild($undocumentedEntity);
$descriptionRefSec->appendChild(new DOMText("\n "));
- $returnDescriptionPara = $doc->createElement('para');
+ $returnDescriptionPara = $doc->createElement('simpara');
$returnDescriptionPara->appendChild(new DOMText("\n Description.\n "));
$descriptionRefSec->appendChild($returnDescriptionPara);
@@ -1714,7 +1748,7 @@ public function getMethodSynopsisDocument(array $funcMap, array $aliasMap): ?str
$errorsDescriptionParaConstantTag->append('E_*');
$errorsDescriptionParaExceptionTag = $doc->createElement('exceptionname');
$errorsDescriptionParaExceptionTag->append('Exception');
- $errorsDescriptionPara = $doc->createElement('para');
+ $errorsDescriptionPara = $doc->createElement('simpara');
$errorsDescriptionPara->append(
"\n When does this function issue ",
$errorsDescriptionParaConstantTag,
@@ -1818,20 +1852,19 @@ private function getParameterSection(DOMDocument $doc): DOMElement {
$parametersRefSec->appendChild($noParamEntity);
return $parametersRefSec;
} else {
- $parametersPara = $doc->createElement('para');
- $parametersRefSec->appendChild($parametersPara);
+ $parametersContainer = $doc->createDocumentFragment();
- $parametersPara->appendChild(new DOMText("\n "));
+ $parametersContainer->appendChild(new DOMText("\n "));
$parametersList = $doc->createElement('variablelist');
- $parametersPara->appendChild($parametersList);
+ $parametersContainer->appendChild($parametersList);
/*
name
-
+
Description.
-
+
*/
@@ -1840,35 +1873,36 @@ private function getParameterSection(DOMDocument $doc): DOMElement {
$parameterTerm = $doc->createElement('term');
$parameterTerm->appendChild($parameter);
- $listItemPara = $doc->createElement('para');
+ $listItemPara = $doc->createElement('simpara');
$listItemPara->append(
- "\n ",
- "Description.",
"\n ",
+ "Description.",
+ "\n ",
);
$parameterEntryListItem = $doc->createElement('listitem');
$parameterEntryListItem->append(
- "\n ",
- $listItemPara,
"\n ",
+ $listItemPara,
+ "\n ",
);
$parameterEntry = $doc->createElement('varlistentry');
$parameterEntry->append(
- "\n ",
+ "\n ",
$parameterTerm,
- "\n ",
- $parameterEntryListItem,
"\n ",
+ $parameterEntryListItem,
+ "\n ",
);
- $parametersList->appendChild(new DOMText("\n "));
+ $parametersList->appendChild(new DOMText("\n "));
$parametersList->appendChild($parameterEntry);
}
- $parametersList->appendChild(new DOMText("\n "));
+ $parametersList->appendChild(new DOMText("\n "));
}
- $parametersPara->appendChild(new DOMText("\n "));
+ $parametersContainer->appendChild(new DOMText("\n "));
+ $parametersRefSec->appendChild($parametersContainer);
$parametersRefSec->appendChild(new DOMText("\n "));
return $parametersRefSec;
}
@@ -1876,7 +1910,7 @@ private function getParameterSection(DOMDocument $doc): DOMElement {
private function getReturnValueSection(DOMDocument $doc): DOMElement {
$returnRefSec = $this->generateRefSect1($doc, 'returnvalues');
- $returnDescriptionPara = $doc->createElement('para');
+ $returnDescriptionPara = $doc->createElement('simpara');
$returnDescriptionPara->appendChild(new DOMText("\n "));
$returnType = $this->return->getMethodSynopsisType();
@@ -1884,9 +1918,8 @@ private function getReturnValueSection(DOMDocument $doc): DOMElement {
$returnDescriptionPara->appendChild(new DOMText("Description."));
} else if (count($returnType->types) === 1) {
$type = $returnType->types[0];
- $name = $type->name;
- switch ($name) {
+ switch ($type->name) {
case 'void':
$descriptionNode = $doc->createEntityReference('return.void');
break;
@@ -2015,18 +2048,22 @@ private function getExampleSection(DOMDocument $doc, string $id): DOMElement {
$example->append("\n ", $title);
- $para = $doc->createElement('para');
+ $para = $doc->createElement('simpara');
$para->append("\n ", "Description.", "\n ");
$example->append("\n ", $para);
$prog = $doc->createElement('programlisting');
$prog->setAttribute('role', 'php');
+ // So that GitHub syntax highlighting doesn't treat the closing tag
+ // in the DOMCdataSection as indication that it should stop syntax
+ // highlighting, break it up
+ $empty = '';
$code = new DOMCdataSection(
<<
+?$empty>
CODE_EXAMPLE
);
@@ -2062,12 +2099,7 @@ private function getExampleSection(DOMDocument $doc, string $id): DOMElement {
return $refSec;
}
- /**
- * @param array $funcMap
- * @param array $aliasMap
- * @throws Exception
- */
- public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDocument $doc): ?DOMElement {
+ public function getMethodSynopsisElement(DOMDocument $doc): ?DOMElement {
if ($this->hasParamWithUnknownDefaultValue()) {
return null;
}
@@ -2091,6 +2123,13 @@ public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDoc
$methodSynopsis->appendChild(new DOMText("\n "));
+ foreach ($this->attributes as $attribute) {
+ $modifier = $doc->createElement("modifier", "#[\\" . $attribute->class . "]");
+ $modifier->setAttribute("role", "attribute");
+ $methodSynopsis->appendChild($modifier);
+ $methodSynopsis->appendChild(new DOMText("\n "));
+ }
+
foreach ($this->getModifierNames() as $modifierString) {
$modifierElement = $doc->createElement('modifier', $modifierString);
$methodSynopsis->appendChild($modifierElement);
@@ -2154,28 +2193,43 @@ public function getMethodSynopsisElement(array $funcMap, array $aliasMap, DOMDoc
return $methodSynopsis;
}
+ /** @param FuncInfo[] $generatedFuncInfos */
+ public function findEquivalent(array $generatedFuncInfos): ?FuncInfo {
+ foreach ($generatedFuncInfos as $generatedFuncInfo) {
+ if ($generatedFuncInfo->equalsApartFromNameAndRefcount($this)) {
+ return $generatedFuncInfo;
+ }
+ }
+ return null;
+ }
+
+ public function toArgInfoCode(?int $minPHPCompatability): string {
+ $code = $this->return->beginArgInfo(
+ $this->getArgInfoName(),
+ $this->numRequiredArgs,
+ $minPHPCompatability === null || $minPHPCompatability >= PHP_81_VERSION_ID
+ );
+
+ foreach ($this->args as $argInfo) {
+ $code .= $argInfo->toZendInfo();
+ }
+
+ $code .= "ZEND_END_ARG_INFO()";
+ return $code . "\n";
+ }
+
public function __clone()
{
foreach ($this->args as $key => $argInfo) {
$this->args[$key] = clone $argInfo;
}
$this->return = clone $this->return;
- foreach ($this->attributes as $key => $attribute) {
- $this->attributes[$key] = clone $attribute;
- }
- foreach ($this->framelessFunctionInfos as $key => $framelessFunctionInfo) {
- $this->framelessFunctionInfos[$key] = clone $framelessFunctionInfo;
- }
- if ($this->exposedDocComment) {
- $this->exposedDocComment = clone $this->exposedDocComment;
- }
}
}
class EvaluatedValue
{
- /** @var mixed */
- public $value;
+ public /* readonly */ mixed $value;
public SimpleType $type;
public Expr $expr;
public bool $isUnknownConstValue;
@@ -2236,7 +2290,7 @@ public function enterNode(Node $expr)
static function (Expr $expr) use ($allConstInfos, &$isUnknownConstValue) {
// $expr is a ConstFetch with a name of a C macro here
if (!$expr instanceof Expr\ConstFetch) {
- throw new Exception($this->getVariableTypeName() . " " . $this->name->__toString() . " has an unsupported value");
+ throw new Exception("Expression at line " . $expr->getStartLine() . " must be a global, non-magic constant");
}
$constName = $expr->name->__toString();
@@ -2356,17 +2410,17 @@ public function getCExpr(): ?string
abstract class VariableLike
{
- public int $flags;
+ protected int $flags;
public ?Type $type;
- public ?Type $phpDocType;
- public ?string $link;
- public ?int $phpVersionIdMinimumCompatibility;
+ public /* readonly */ ?Type $phpDocType;
+ private /* readonly */ ?string $link;
+ protected ?int $phpVersionIdMinimumCompatibility;
/** @var AttributeInfo[] */
public array $attributes;
- public ?ExposedDocComment $exposedDocComment;
+ protected /* readonly */ ?ExposedDocComment $exposedDocComment;
/**
- * @var AttributeInfo[] $attributes
+ * @param AttributeInfo[] $attributes
*/
public function __construct(
int $flags,
@@ -2386,8 +2440,6 @@ public function __construct(
$this->exposedDocComment = $exposedDocComment;
}
- abstract protected function getVariableTypeCode(): string;
-
abstract protected function getVariableTypeName(): string;
abstract protected function getFieldSynopsisDefaultLinkend(): string;
@@ -2399,16 +2451,6 @@ abstract protected function getFieldSynopsisValueString(array $allConstInfos): ?
abstract public function discardInfoForOldPhpVersions(?int $minimumPhpVersionIdCompatibility): void;
- protected function addTypeToFieldSynopsis(DOMDocument $doc, DOMElement $fieldsynopsisElement): void
- {
- $type = $this->phpDocType ?? $this->type;
-
- if ($type) {
- $fieldsynopsisElement->appendChild(new DOMText("\n "));
- $fieldsynopsisElement->appendChild($type->getTypeForDoc($doc));
- }
- }
-
/**
* @return array
*/
@@ -2428,6 +2470,7 @@ protected function getFlagsByPhpVersion(): array
PHP_82_VERSION_ID => [$flags],
PHP_83_VERSION_ID => [$flags],
PHP_84_VERSION_ID => [$flags],
+ PHP_85_VERSION_ID => [$flags],
];
}
@@ -2487,7 +2530,11 @@ public function getFieldSynopsisElement(DOMDocument $doc, array $allConstInfos):
$this->addModifiersToFieldSynopsis($doc, $fieldsynopsisElement);
- $this->addTypeToFieldSynopsis($doc, $fieldsynopsisElement);
+ $type = $this->phpDocType ?? $this->type;
+ if ($type) {
+ $fieldsynopsisElement->appendChild(new DOMText("\n "));
+ $fieldsynopsisElement->appendChild($type->getTypeForDoc($doc));
+ }
$varnameElement = $doc->createElement("varname", $this->getFieldSynopsisName());
if ($this->link) {
@@ -2546,20 +2593,20 @@ protected function addFlagForVersionsAbove(array $flags, string $flag, int $mini
class ConstInfo extends VariableLike
{
- public ConstOrClassConstName $name;
- public Expr $value;
- public bool $isDeprecated;
- public ?string $valueString;
- public ?string $cond;
- public ?string $cValue;
- public bool $isUndocumentable;
- public bool $isFileCacheAllowed;
+ public /* readonly */ AbstractConstName $name;
+ public /* readonly */ Expr $value;
+ private bool $isDeprecated;
+ private /* readonly */ ?string $valueString;
+ public /* readonly */ ?string $cond;
+ public /* readonly */ ?string $cValue;
+ public /* readonly */ bool $isUndocumentable;
+ private /* readonly */ bool $isFileCacheAllowed;
/**
- * @var AttributeInfo[] $attributes
+ * @param AttributeInfo[] $attributes
*/
public function __construct(
- ConstOrClassConstName $name,
+ AbstractConstName $name,
int $flags,
Expr $value,
?string $valueString,
@@ -2602,16 +2649,11 @@ protected function getVariableTypeName(): string
return "constant";
}
- protected function getVariableTypeCode(): string
- {
- return "const";
- }
-
protected function getFieldSynopsisDefaultLinkend(): string
{
$className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString());
- return "$className.constants." . strtolower(str_replace(["__", "_"], ["", "-"], $this->name->getDeclarationName()));
+ return "$className.constants." . strtolower(str_replace("_", "-", trim($this->name->getDeclarationName(), "_")));
}
protected function getFieldSynopsisName(): string
@@ -2655,7 +2697,7 @@ public function getPredefinedConstantTerm(DOMDocument $doc, int $indentationLeve
return $termElement;
}
- public function getPredefinedConstantEntry(DOMDocument $doc, int $indentationLevel): DOMElement {
+ public function getPredefinedConstantEntry(DOMDocument $doc, int $indentationLevel): DOMElement {
$indentation = str_repeat(" ", $indentationLevel);
$entryElement = $doc->createElement("entry");
@@ -2709,7 +2751,7 @@ public function getDeclaration(array $allConstInfos): string
if ($this->name->isClassConst()) {
$code .= $this->getClassConstDeclaration($value, $allConstInfos);
} else {
- $code .= $this->getGlobalConstDeclaration($value, $allConstInfos);
+ $code .= $this->getGlobalConstDeclaration($value);
}
$code .= $this->getValueAssertion($value);
@@ -2720,8 +2762,7 @@ public function getDeclaration(array $allConstInfos): string
return $code;
}
- /** @param array $allConstInfos */
- private function getGlobalConstDeclaration(EvaluatedValue $value, array $allConstInfos): string
+ private function getGlobalConstDeclaration(EvaluatedValue $value): string
{
$constName = str_replace('\\', '\\\\', $this->name->__toString());
$constValue = $value->value;
@@ -2775,9 +2816,8 @@ private function getClassConstDeclaration(EvaluatedValue $value, array $allConst
if ($this->exposedDocComment) {
$commentCode = "const_{$constName}_comment";
- $escapedComment = $this->exposedDocComment->escape();
- $escapedCommentLength = $this->exposedDocComment->getLength();
- $code .= "\tzend_string *$commentCode = zend_string_init_interned(\"$escapedComment\", $escapedCommentLength, 1);\n";
+ $escapedCommentInit = $this->exposedDocComment->getInitCode();
+ $code .= "\tzend_string *$commentCode = $escapedCommentInit\n";
} else {
$commentCode = "NULL";
}
@@ -2798,12 +2838,11 @@ private function getClassConstDeclaration(EvaluatedValue $value, array $allConst
}
$template .= "zend_declare_typed_class_constant(class_entry, $nameCode, &const_{$constName}_value, %s, $commentCode, $typeCode);\n";
- $flagsCode = generateVersionDependentFlagCode(
+ $code .= generateVersionDependentFlagCode(
$template,
$this->getFlagsByPhpVersion(),
$this->phpVersionIdMinimumCompatibility
);
- $code .= implode("", $flagsCode);
}
if ($this->type && !$php83MinimumCompatibility) {
@@ -2817,12 +2856,11 @@ private function getClassConstDeclaration(EvaluatedValue $value, array $allConst
$template = "\t";
}
$template .= "zend_declare_class_constant_ex(class_entry, $nameCode, &const_{$constName}_value, %s, $commentCode);\n";
- $flagsCode = generateVersionDependentFlagCode(
+ $code .= generateVersionDependentFlagCode(
$template,
$this->getFlagsByPhpVersion(),
$this->phpVersionIdMinimumCompatibility
);
- $code .= implode("", $flagsCode);
}
if ($this->type && !$php83MinimumCompatibility) {
@@ -2911,15 +2949,115 @@ protected function addModifiersToFieldSynopsis(DOMDocument $doc, DOMElement $fie
class PropertyInfo extends VariableLike
{
- public int $classFlags;
- public PropertyName $name;
- public ?Expr $defaultValue;
- public ?string $defaultValueString;
- public bool $isDocReadonly;
- public bool $isVirtual;
+ private /* readonly */ int $classFlags;
+ public /* readonly */ PropertyName $name;
+ private /* readonly */ ?Expr $defaultValue;
+ private /* readonly */ ?string $defaultValueString;
+ private /* readonly */ bool $isDocReadonly;
+ private /* readonly */ bool $isVirtual;
+
+ // Map possible variable names to the known string constant, see
+ // ZEND_KNOWN_STRINGS
+ private const PHP_80_KNOWN = [
+ "file" => "ZEND_STR_FILE",
+ "line" => "ZEND_STR_LINE",
+ "function" => "ZEND_STR_FUNCTION",
+ "class" => "ZEND_STR_CLASS",
+ "object" => "ZEND_STR_OBJECT",
+ "type" => "ZEND_STR_TYPE",
+ // ZEND_STR_OBJECT_OPERATOR and ZEND_STR_PAAMAYIM_NEKUDOTAYIM are
+ // not valid variable names
+ "args" => "ZEND_STR_ARGS",
+ "unknown" => "ZEND_STR_UNKNOWN",
+ "eval" => "ZEND_STR_EVAL",
+ "include" => "ZEND_STR_INCLUDE",
+ "require" => "ZEND_STR_REQUIRE",
+ "include_once" => "ZEND_STR_INCLUDE_ONCE",
+ "require_once" => "ZEND_STR_REQUIRE_ONCE",
+ "scalar" => "ZEND_STR_SCALAR",
+ "error_reporting" => "ZEND_STR_ERROR_REPORTING",
+ "static" => "ZEND_STR_STATIC",
+ // ZEND_STR_THIS cannot be used since $this cannot be reassigned
+ "value" => "ZEND_STR_VALUE",
+ "key" => "ZEND_STR_KEY",
+ "__invoke" => "ZEND_STR_MAGIC_INVOKE",
+ "previous" => "ZEND_STR_PREVIOUS",
+ "code" => "ZEND_STR_CODE",
+ "message" => "ZEND_STR_MESSAGE",
+ "severity" => "ZEND_STR_SEVERITY",
+ "string" => "ZEND_STR_STRING",
+ "trace" => "ZEND_STR_TRACE",
+ "scheme" => "ZEND_STR_SCHEME",
+ "host" => "ZEND_STR_HOST",
+ "port" => "ZEND_STR_PORT",
+ "user" => "ZEND_STR_USER",
+ "pass" => "ZEND_STR_PASS",
+ "path" => "ZEND_STR_PATH",
+ "query" => "ZEND_STR_QUERY",
+ "fragment" => "ZEND_STR_FRAGMENT",
+ "NULL" => "ZEND_STR_NULL",
+ "boolean" => "ZEND_STR_BOOLEAN",
+ "integer" => "ZEND_STR_INTEGER",
+ "double" => "ZEND_STR_DOUBLE",
+ "array" => "ZEND_STR_ARRAY",
+ "resource" => "ZEND_STR_RESOURCE",
+ // ZEND_STR_CLOSED_RESOURCE has a space in it
+ "name" => "ZEND_STR_NAME",
+ // ZEND_STR_ARGV and ZEND_STR_ARGC are superglobals that wouldn't be
+ // variable names
+ "Array" => "ZEND_STR_ARRAY_CAPITALIZED",
+ "bool" => "ZEND_STR_BOOL",
+ "int" => "ZEND_STR_INT",
+ "float" => "ZEND_STR_FLOAT",
+ "callable" => "ZEND_STR_CALLABLE",
+ "iterable" => "ZEND_STR_ITERABLE",
+ "void" => "ZEND_STR_VOID",
+ "false" => "ZEND_STR_FALSE",
+ "null" => "ZEND_STR_NULL_LOWERCASE",
+ "mixed" => "ZEND_STR_MIXED",
+ ];
+
+ // NEW in 8.1
+ private const PHP_81_KNOWN = [
+ "Unknown" => "ZEND_STR_UNKNOWN_CAPITALIZED",
+ "never" => "ZEND_STR_NEVER",
+ "__sleep" => "ZEND_STR_SLEEP",
+ "__wakeup" => "ZEND_STR_WAKEUP",
+ "cases" => "ZEND_STR_CASES",
+ "from" => "ZEND_STR_FROM",
+ "tryFrom" => "ZEND_STR_TRYFROM",
+ "tryfrom" => "ZEND_STR_TRYFROM_LOWERCASE",
+ // Omit ZEND_STR_AUTOGLOBAL_(SERVER|ENV|REQUEST)
+ ];
+
+ // NEW in 8.2
+ private const PHP_82_KNOWN = [
+ "true" => "ZEND_STR_TRUE",
+ "Traversable" => "ZEND_STR_TRAVERSABLE",
+ "count" => "ZEND_STR_COUNT",
+ "SensitiveParameter" => "ZEND_STR_SENSITIVEPARAMETER",
+ ];
+
+ // Only new string in 8.3 is ZEND_STR_CONST_EXPR_PLACEHOLDER which is
+ // not a valid variable name ("[constant expression]")
+
+ // NEW in 8.4
+ private const PHP_84_KNOWN = [
+ "exit" => "ZEND_STR_EXIT",
+ "Deprecated" => "ZEND_STR_DEPRECATED_CAPITALIZED",
+ "since" => "ZEND_STR_SINCE",
+ "get" => "ZEND_STR_GET",
+ "set" => "ZEND_STR_SET",
+ ];
+
+ // NEW in 8.5
+ private const PHP_85_KNOWN = [
+ "self" => "ZEND_STR_SELF",
+ "parent" => "ZEND_STR_PARENT",
+ ];
/**
- * @var AttributeInfo[] $attributes
+ * @param AttributeInfo[] $attributes
*/
public function __construct(
PropertyName $name,
@@ -2945,11 +3083,6 @@ public function __construct(
parent::__construct($flags, $type, $phpDocType, $link, $phpVersionIdMinimumCompatibility, $attributes, $exposedDocComment);
}
- protected function getVariableTypeCode(): string
- {
- return "property";
- }
-
protected function getVariableTypeName(): string
{
return "property";
@@ -2959,7 +3092,7 @@ protected function getFieldSynopsisDefaultLinkend(): string
{
$className = str_replace(["\\", "_"], ["-", "-"], $this->name->class->toLowerString());
- return "$className.props." . strtolower(str_replace(["__", "_"], ["", "-"], $this->name->getDeclarationName()));
+ return "$className.props." . strtolower(str_replace("_", "-", trim($this->name->getDeclarationName(), "_")));
}
protected function getFieldSynopsisName(): string
@@ -3003,14 +3136,13 @@ public function getDeclaration(array $allConstInfos): string {
$code .= $defaultValue->initializeZval($zvalName);
}
- $code .= "\tzend_string *property_{$propertyName}_name = zend_string_init(\"$propertyName\", sizeof(\"$propertyName\") - 1, 1);\n";
- $nameCode = "property_{$propertyName}_name";
+ [$stringInit, $nameCode, $stringRelease] = $this->getString($propertyName);
+ $code .= $stringInit;
if ($this->exposedDocComment) {
$commentCode = "property_{$propertyName}_comment";
- $escapedComment = $this->exposedDocComment->escape();
- $escapedCommentLength = $this->exposedDocComment->getLength();
- $code .= "\tzend_string *$commentCode = zend_string_init_interned(\"$escapedComment\", $escapedCommentLength, 1);\n";
+ $escapedCommentInit = $this->exposedDocComment->getInitCode();
+ $code .= "\tzend_string *$commentCode = $escapedCommentInit\n";
} else {
$commentCode = "NULL";
}
@@ -3028,18 +3160,71 @@ public function getDeclaration(array $allConstInfos): string {
$template .= "zend_declare_property_ex(class_entry, $nameCode, &$zvalName, %s, $commentCode);\n";
}
- $flagsCode = generateVersionDependentFlagCode(
+ $code .= generateVersionDependentFlagCode(
$template,
$this->getFlagsByPhpVersion(),
$this->phpVersionIdMinimumCompatibility
);
- $code .= implode("", $flagsCode);
- $code .= "\tzend_string_release(property_{$propertyName}_name);\n";
+ $code .= $stringRelease;
return $code;
}
+ /**
+ * Get an array of three strings:
+ * - declaration of zend_string, if needed, or empty otherwise
+ * - usage of that zend_string, or usage with ZSTR_KNOWN()
+ * - freeing the zend_string, if needed
+ *
+ * @param string $propName
+ * @return string[]
+ */
+ private function getString(string $propName): array {
+ // Generally strings will not be known
+ $nameCode = "property_{$propName}_name";
+ $result = [
+ "\tzend_string *$nameCode = zend_string_init(\"$propName\", sizeof(\"$propName\") - 1, 1);\n",
+ $nameCode,
+ "\tzend_string_release($nameCode);\n"
+ ];
+ // If not set, use the current latest version
+ $allVersions = ALL_PHP_VERSION_IDS;
+ $minPhp = $this->phpVersionIdMinimumCompatibility ?? end($allVersions);
+ if ($minPhp < PHP_80_VERSION_ID) {
+ // No known strings in 7.0
+ return $result;
+ }
+ $include = self::PHP_80_KNOWN;
+ switch ($minPhp) {
+ case PHP_85_VERSION_ID:
+ $include = array_merge($include, self::PHP_85_KNOWN);
+ // Intentional fall through
+
+ case PHP_84_VERSION_ID:
+ $include = array_merge($include, self::PHP_84_KNOWN);
+ // Intentional fall through
+
+ case PHP_83_VERSION_ID:
+ case PHP_82_VERSION_ID:
+ $include = array_merge($include, self::PHP_82_KNOWN);
+ // Intentional fall through
+
+ case PHP_81_VERSION_ID:
+ $include = array_merge($include, self::PHP_81_KNOWN);
+ break;
+ }
+ if (array_key_exists($propName, $include)) {
+ $knownStr = $include[$propName];
+ return [
+ '',
+ "ZSTR_KNOWN($knownStr)",
+ '',
+ ];
+ }
+ return $result;
+ }
+
/**
* @return array
*/
@@ -3051,6 +3236,10 @@ protected function getFlagsByPhpVersion(): array
$flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_STATIC", PHP_70_VERSION_ID);
}
+ if ($this->flags & Modifiers::FINAL) {
+ $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_FINAL", PHP_84_VERSION_ID);
+ }
+
if ($this->flags & Modifiers::READONLY) {
$flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_READONLY", PHP_81_VERSION_ID);
} elseif ($this->classFlags & Modifiers::READONLY) {
@@ -3073,29 +3262,21 @@ protected function addModifiersToFieldSynopsis(DOMDocument $doc, DOMElement $fie
$fieldsynopsisElement->appendChild($doc->createElement("modifier", "static"));
}
- if ($this->flags & Modifiers::READONLY || $this->isDocReadonly) {
+ if ($this->flags & Modifiers::FINAL) {
$fieldsynopsisElement->appendChild(new DOMText("\n "));
- $fieldsynopsisElement->appendChild($doc->createElement("modifier", "readonly"));
+ $fieldsynopsisElement->appendChild($doc->createElement("modifier", "final"));
}
- }
- public function __clone()
- {
- if ($this->type) {
- $this->type = clone $this->type;
- }
- foreach ($this->attributes as $key => $attribute) {
- $this->attributes[$key] = clone $attribute;
- }
- if ($this->exposedDocComment) {
- $this->exposedDocComment = clone $this->exposedDocComment;
+ if ($this->flags & Modifiers::READONLY || $this->isDocReadonly) {
+ $fieldsynopsisElement->appendChild(new DOMText("\n "));
+ $fieldsynopsisElement->appendChild($doc->createElement("modifier", "readonly"));
}
}
}
class EnumCaseInfo {
- public string $name;
- public ?Expr $value;
+ private /* readonly */ string $name;
+ private /* readonly */ ?Expr $value;
public function __construct(string $name, ?Expr $value) {
$this->name = $name;
@@ -3119,10 +3300,12 @@ public function getDeclaration(array $allConstInfos): string {
}
}
+// Instances of AttributeInfo are immutable and do not need to be cloned
+// when held by an object that is cloned
class AttributeInfo {
- public string $class;
+ public /* readonly */ string $class;
/** @var \PhpParser\Node\Arg[] */
- public array $args;
+ private /* readonly */ array $args;
/** @param \PhpParser\Node\Arg[] $args */
public function __construct(string $class, array $args) {
@@ -3170,35 +3353,51 @@ public function generateCode(string $invocation, string $nameSuffix, array $allC
}
return $code;
}
+
+ /**
+ * @param AttributeGroup[] $attributeGroups
+ * @return AttributeInfo[]
+ */
+ public static function createFromGroups(array $attributeGroups): array {
+ $attributes = [];
+
+ foreach ($attributeGroups as $attrGroup) {
+ foreach ($attrGroup->attrs as $attr) {
+ $attributes[] = new AttributeInfo($attr->name->toString(), $attr->args);
+ }
+ }
+
+ return $attributes;
+ }
}
class ClassInfo {
- public Name $name;
- public int $flags;
+ public /* readonly */ Name $name;
+ private int $flags;
public string $type;
- public ?string $alias;
- public ?SimpleType $enumBackingType;
- public bool $isDeprecated;
- public bool $isStrictProperties;
+ public /* readonly */ ?string $alias;
+ private /* readonly */ ?SimpleType $enumBackingType;
+ private /* readonly */ bool $isDeprecated;
+ private bool $isStrictProperties;
/** @var AttributeInfo[] */
- public array $attributes;
- public ?ExposedDocComment $exposedDocComment;
- public bool $isNotSerializable;
+ private array $attributes;
+ private ?ExposedDocComment $exposedDocComment;
+ private bool $isNotSerializable;
/** @var Name[] */
- public array $extends;
+ private /* readonly */ array $extends;
/** @var Name[] */
- public array $implements;
+ private /* readonly */ array $implements;
/** @var ConstInfo[] */
- public array $constInfos;
+ public /* readonly */ array $constInfos;
/** @var PropertyInfo[] */
- public array $propertyInfos;
+ private /* readonly */ array $propertyInfos;
/** @var FuncInfo[] */
public array $funcInfos;
/** @var EnumCaseInfo[] */
- public array $enumCaseInfos;
- public ?string $cond;
+ private /* readonly */ array $enumCaseInfos;
+ public /* readonly */ ?string $cond;
public ?int $phpVersionIdMinimumCompatibility;
- public bool $isUndocumentable;
+ public /* readonly */ bool $isUndocumentable;
/**
* @param AttributeInfo[] $attributes
@@ -3282,18 +3481,13 @@ public function getRegistration(array $allConstInfos): string
$code .= "{\n";
- $flagCodes = generateVersionDependentFlagCode("%s", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility);
- $flags = implode("", $flagCodes);
-
$classMethods = ($this->funcInfos === []) ? 'NULL' : "class_{$escapedName}_methods";
if ($this->type === "enum") {
$name = addslashes((string) $this->name);
$backingType = $this->enumBackingType
? $this->enumBackingType->toTypeCode() : "IS_UNDEF";
$code .= "\tzend_class_entry *class_entry = zend_register_internal_enum(\"$name\", $backingType, $classMethods);\n";
- if ($flags !== "") {
- $code .= "\tclass_entry->ce_flags |= $flags\n";
- }
+ $code .= generateVersionDependentFlagCode("\tclass_entry->ce_flags = %s;\n", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility);
} else {
$code .= "\tzend_class_entry ce, *class_entry;\n\n";
if (count($this->name->getParts()) > 1) {
@@ -3310,22 +3504,25 @@ public function getRegistration(array $allConstInfos): string
$code .= "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n";
}
- $code .= "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ", " . ($flags ?: 0) . ");\n";
+ $template = "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ", %s);\n";
+ $entries = generateVersionDependentFlagCode($template, $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility ? max($this->phpVersionIdMinimumCompatibility, PHP_84_VERSION_ID) : null);
+ if ($entries !== '') {
+ $code .= $entries;
+ } else {
+ $code .= sprintf($template, "0");
+ }
if (!$php84MinimumCompatibility) {
$code .= "#else\n";
$code .= "\tclass_entry = zend_register_internal_class_ex(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ");\n";
- if ($flags !== "") {
- $code .= "\tclass_entry->ce_flags |= $flags;\n";
- }
+ $code .= generateVersionDependentFlagCode("\tclass_entry->ce_flags |= %s;\n", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility);
$code .= "#endif\n";
}
} else {
$code .= "\tclass_entry = zend_register_internal_interface(&ce);\n";
- if ($flags !== "") {
- $code .= "\tclass_entry->ce_flags |= $flags\n";
- }
+ $code .= generateVersionDependentFlagCode("\tclass_entry->ce_flags |= %s;\n", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility);
+
}
}
@@ -3334,7 +3531,7 @@ public function getRegistration(array $allConstInfos): string
$code .= "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n";
}
- $code .= "\tclass_entry->doc_comment = zend_string_init_interned(\"" . $this->exposedDocComment->escape() . "\", " . $this->exposedDocComment->getLength() . ", 1);\n";
+ $code .= "\tclass_entry->doc_comment = " . $this->exposedDocComment->getInitCode() . "\n";
if (!$php84MinimumCompatibility) {
$code .= "#endif\n";
@@ -3488,6 +3685,7 @@ private function getFlagsByPhpVersion(): array
$php83Flags = $php82Flags;
$php84Flags = $php83Flags;
+ $php85Flags = $php84Flags;
return [
PHP_70_VERSION_ID => $php70Flags,
@@ -3496,6 +3694,7 @@ private function getFlagsByPhpVersion(): array
PHP_82_VERSION_ID => $php82Flags,
PHP_83_VERSION_ID => $php83Flags,
PHP_84_VERSION_ID => $php84Flags,
+ PHP_85_VERSION_ID => $php85Flags,
];
}
@@ -3515,10 +3714,8 @@ public function discardInfoForOldPhpVersions(?int $phpVersionIdMinimumCompatibil
/**
* @param array $classMap
* @param array $allConstInfos
- * @param iterable $allConstInfo
*/
public function getClassSynopsisDocument(array $classMap, array $allConstInfos): ?string {
-
$doc = new DOMDocument();
$doc->formatOutput = true;
$classSynopsis = $this->getClassSynopsisElement($doc, $classMap, $allConstInfos);
@@ -3536,7 +3733,6 @@ public function getClassSynopsisDocument(array $classMap, array $allConstInfos):
* @param array $allConstInfos
*/
public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $allConstInfos): ?DOMElement {
-
$classSynopsis = $doc->createElement("classsynopsis");
$classSynopsis->setAttribute("class", $this->type === "interface" ? "interface" : "class");
@@ -3731,6 +3927,13 @@ private static function createOoElement(
$ooElement->appendChild($doc->createElement('modifier', $modifierOverride));
$ooElement->appendChild(new DOMText("\n$indentation "));
} elseif ($withModifiers) {
+ foreach ($classInfo->attributes as $attribute) {
+ $modifier = $doc->createElement("modifier", "#[\\" . $attribute->class . "]");
+ $modifier->setAttribute("role", "attribute");
+ $ooElement->appendChild($modifier);
+ $ooElement->appendChild(new DOMText("\n$indentation "));
+ }
+
if ($classInfo->flags & Modifiers::FINAL) {
$ooElement->appendChild($doc->createElement('modifier', 'final'));
$ooElement->appendChild(new DOMText("\n$indentation "));
@@ -3756,7 +3959,7 @@ public static function getClassSynopsisFilename(Name $name): string {
return strtolower(str_replace("_", "-", implode('-', $name->getParts())));
}
- public static function getClassSynopsisReference(Name $name): string {
+ private static function getClassSynopsisReference(Name $name): string {
return "class." . self::getClassSynopsisFilename($name);
}
@@ -3941,14 +4144,6 @@ public function __clone()
foreach ($this->funcInfos as $key => $funcInfo) {
$this->funcInfos[$key] = clone $funcInfo;
}
-
- foreach ($this->attributes as $key => $attribute) {
- $this->attributes[$key] = clone $attribute;
- }
-
- if ($this->exposedDocComment) {
- $this->exposedDocComment = clone $this->exposedDocComment;
- }
}
/**
@@ -3990,13 +4185,43 @@ class FileInfo {
public bool $generateFunctionEntries = false;
public string $declarationPrefix = "";
public bool $generateClassEntries = false;
- public bool $isUndocumentable = false;
- public bool $legacyArginfoGeneration = false;
+ private bool $isUndocumentable = false;
+ private bool $legacyArginfoGeneration = false;
private ?int $minimumPhpVersionIdCompatibility = null;
- /**
- * @return iterable
- */
+ /** @param array $fileTags */
+ public function __construct(array $fileTags) {
+ foreach ($fileTags as $tag) {
+ if ($tag->name === 'generate-function-entries') {
+ $this->generateFunctionEntries = true;
+ $this->declarationPrefix = $tag->value ? $tag->value . " " : "";
+ } else if ($tag->name === 'generate-legacy-arginfo') {
+ if ($tag->value && !in_array((int) $tag->value, ALL_PHP_VERSION_IDS, true)) {
+ throw new Exception(
+ "Legacy PHP version must be one of: \"" . PHP_70_VERSION_ID . "\" (PHP 7.0), \"" . PHP_80_VERSION_ID . "\" (PHP 8.0), " .
+ "\"" . PHP_81_VERSION_ID . "\" (PHP 8.1), \"" . PHP_82_VERSION_ID . "\" (PHP 8.2), \"" . PHP_83_VERSION_ID . "\" (PHP 8.3), " .
+ "\"" . PHP_84_VERSION_ID . "\" (PHP 8.4), \"" . PHP_85_VERSION_ID . "\" (PHP 8.5), \"" . $tag->value . "\" provided"
+ );
+ }
+
+ $this->minimumPhpVersionIdCompatibility = ($tag->value ? (int) $tag->value : PHP_70_VERSION_ID);
+ } else if ($tag->name === 'generate-class-entries') {
+ $this->generateClassEntries = true;
+ $this->declarationPrefix = $tag->value ? $tag->value . " " : "";
+ } else if ($tag->name === 'undocumentable') {
+ $this->isUndocumentable = true;
+ }
+ }
+
+ // Generating class entries require generating function/method entries
+ if ($this->generateClassEntries && !$this->generateFunctionEntries) {
+ $this->generateFunctionEntries = true;
+ }
+ }
+
+ /**
+ * @return iterable
+ */
public function getAllFuncInfos(): iterable {
yield from $this->funcInfos;
foreach ($this->classInfos as $classInfo) {
@@ -4021,15 +4246,6 @@ public function getAllConstInfos(): array {
return $result;
}
- /**
- * @return iterable
- */
- public function getAllClassInfos(): iterable {
- foreach ($this->classInfos as $classInfo) {
- yield $classInfo;
- }
- }
-
public function __clone()
{
foreach ($this->constInfos as $key => $constInfo) {
@@ -4045,10 +4261,6 @@ public function __clone()
}
}
- public function setMinimumPhpVersionIdCompatibility(?int $minimumPhpVersionIdCompatibility) {
- $this->minimumPhpVersionIdCompatibility = $minimumPhpVersionIdCompatibility;
- }
-
public function getMinimumPhpVersionIdCompatibility(): ?int {
// Non-legacy arginfo files are always PHP 8.0+ compatible
if (!$this->legacyArginfoGeneration &&
@@ -4064,11 +4276,220 @@ public function getMinimumPhpVersionIdCompatibility(): ?int {
public function shouldGenerateLegacyArginfo(): bool {
return $this->minimumPhpVersionIdCompatibility !== null && $this->minimumPhpVersionIdCompatibility < PHP_80_VERSION_ID;
}
+
+ public function getLegacyVersion(): FileInfo {
+ $legacyFileInfo = clone $this;
+ $legacyFileInfo->legacyArginfoGeneration = true;
+ $phpVersionIdMinimumCompatibility = $legacyFileInfo->getMinimumPhpVersionIdCompatibility();
+
+ foreach ($legacyFileInfo->getAllFuncInfos() as $funcInfo) {
+ $funcInfo->discardInfoForOldPhpVersions($phpVersionIdMinimumCompatibility);
+ }
+ foreach ($legacyFileInfo->classInfos as $classInfo) {
+ $classInfo->discardInfoForOldPhpVersions($phpVersionIdMinimumCompatibility);
+ }
+ foreach ($legacyFileInfo->getAllConstInfos() as $constInfo) {
+ $constInfo->discardInfoForOldPhpVersions($phpVersionIdMinimumCompatibility);
+ }
+ return $legacyFileInfo;
+ }
+
+ public static function parseStubFile(string $code): FileInfo {
+ $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative());
+ $nodeTraverser = new PhpParser\NodeTraverser;
+ $nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
+ $prettyPrinter = new class extends Standard {
+ protected function pName_FullyQualified(Name\FullyQualified $node): string {
+ return implode('\\', $node->getParts());
+ }
+ };
+
+ $stmts = $parser->parse($code);
+ $nodeTraverser->traverse($stmts);
+
+ $fileTags = DocCommentTag::parseDocComments(getFileDocComments($stmts));
+ $fileInfo = new FileInfo($fileTags);
+
+ $fileInfo->handleStatements($stmts, $prettyPrinter);
+ return $fileInfo;
+ }
+
+ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPrinter): void {
+ $conds = [];
+ foreach ($stmts as $stmt) {
+ $cond = self::handlePreprocessorConditions($conds, $stmt);
+
+ if ($stmt instanceof Stmt\Nop) {
+ continue;
+ }
+
+ if ($stmt instanceof Stmt\Namespace_) {
+ $this->handleStatements($stmt->stmts, $prettyPrinter);
+ continue;
+ }
+
+ if ($stmt instanceof Stmt\Const_) {
+ foreach ($stmt->consts as $const) {
+ $this->constInfos[] = parseConstLike(
+ $prettyPrinter,
+ new ConstName($const->namespacedName, $const->name->toString()),
+ $const,
+ 0,
+ null,
+ $stmt->getComments(),
+ $cond,
+ $this->isUndocumentable,
+ $this->getMinimumPhpVersionIdCompatibility(),
+ []
+ );
+ }
+ continue;
+ }
+
+ if ($stmt instanceof Stmt\Function_) {
+ $this->funcInfos[] = parseFunctionLike(
+ $prettyPrinter,
+ new FunctionName($stmt->namespacedName),
+ 0,
+ 0,
+ $stmt,
+ $cond,
+ $this->isUndocumentable,
+ $this->getMinimumPhpVersionIdCompatibility()
+ );
+ continue;
+ }
+
+ if ($stmt instanceof Stmt\ClassLike) {
+ $className = $stmt->namespacedName;
+ $constInfos = [];
+ $propertyInfos = [];
+ $methodInfos = [];
+ $enumCaseInfos = [];
+ foreach ($stmt->stmts as $classStmt) {
+ $cond = self::handlePreprocessorConditions($conds, $classStmt);
+ if ($classStmt instanceof Stmt\Nop) {
+ continue;
+ }
+
+ $classFlags = $stmt instanceof Class_ ? $stmt->flags : 0;
+ $abstractFlag = $stmt instanceof Stmt\Interface_ ? Modifiers::ABSTRACT : 0;
+
+ if ($classStmt instanceof Stmt\ClassConst) {
+ foreach ($classStmt->consts as $const) {
+ $constInfos[] = parseConstLike(
+ $prettyPrinter,
+ new ClassConstName($className, $const->name->toString()),
+ $const,
+ $classStmt->flags,
+ $classStmt->type,
+ $classStmt->getComments(),
+ $cond,
+ $this->isUndocumentable,
+ $this->getMinimumPhpVersionIdCompatibility(),
+ AttributeInfo::createFromGroups($classStmt->attrGroups)
+ );
+ }
+ } else if ($classStmt instanceof Stmt\Property) {
+ if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
+ throw new Exception("Visibility modifier is required");
+ }
+ foreach ($classStmt->props as $property) {
+ $propertyInfos[] = parseProperty(
+ $className,
+ $classFlags,
+ $classStmt->flags,
+ $property,
+ $classStmt->type,
+ $classStmt->getComments(),
+ $prettyPrinter,
+ $this->getMinimumPhpVersionIdCompatibility(),
+ AttributeInfo::createFromGroups($classStmt->attrGroups)
+ );
+ }
+ } else if ($classStmt instanceof Stmt\ClassMethod) {
+ if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
+ throw new Exception("Visibility modifier is required");
+ }
+ $methodInfos[] = parseFunctionLike(
+ $prettyPrinter,
+ new MethodName($className, $classStmt->name->toString()),
+ $classFlags,
+ $classStmt->flags | $abstractFlag,
+ $classStmt,
+ $cond,
+ $this->isUndocumentable,
+ $this->getMinimumPhpVersionIdCompatibility()
+ );
+ } else if ($classStmt instanceof Stmt\EnumCase) {
+ $enumCaseInfos[] = new EnumCaseInfo(
+ $classStmt->name->toString(), $classStmt->expr);
+ } else {
+ throw new Exception("Not implemented {$classStmt->getType()}");
+ }
+ }
+
+ $this->classInfos[] = parseClass(
+ $className,
+ $stmt,
+ $constInfos,
+ $propertyInfos,
+ $methodInfos,
+ $enumCaseInfos,
+ $cond,
+ $this->getMinimumPhpVersionIdCompatibility(),
+ $this->isUndocumentable
+ );
+ continue;
+ }
+
+ if ($stmt instanceof Stmt\Expression) {
+ $expr = $stmt->expr;
+ if ($expr instanceof Expr\Include_) {
+ $this->dependencies[] = (string)EvaluatedValue::createFromExpression($expr->expr, null, null, [])->value;
+ continue;
+ }
+ }
+
+ throw new Exception("Unexpected node {$stmt->getType()}");
+ }
+ if (!empty($conds)) {
+ throw new Exception("Unterminated preprocessor conditions");
+ }
+ }
+
+ private static function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string {
+ foreach ($stmt->getComments() as $comment) {
+ $text = trim($comment->getText());
+ if (preg_match('/^#\s*if\s+(.+)$/', $text, $matches)) {
+ $conds[] = $matches[1];
+ } else if (preg_match('/^#\s*ifdef\s+(.+)$/', $text, $matches)) {
+ $conds[] = "defined($matches[1])";
+ } else if (preg_match('/^#\s*ifndef\s+(.+)$/', $text, $matches)) {
+ $conds[] = "!defined($matches[1])";
+ } else if (preg_match('/^#\s*else$/', $text)) {
+ if (empty($conds)) {
+ throw new Exception("Encountered else without corresponding #if");
+ }
+ $cond = array_pop($conds);
+ $conds[] = "!($cond)";
+ } else if (preg_match('/^#\s*endif$/', $text)) {
+ if (empty($conds)) {
+ throw new Exception("Encountered #endif without corresponding #if");
+ }
+ array_pop($conds);
+ } else if ($text[0] === '#') {
+ throw new Exception("Unrecognized preprocessor directive \"$text\"");
+ }
+ }
+
+ return empty($conds) ? null : implode(' && ', $conds);
+ }
}
class DocCommentTag {
- public string $name;
- public ?string $value;
+ public /* readonly */ string $name;
+ public /* readonly */ ?string $value;
public function __construct(string $name, ?string $value) {
$this->name = $name;
@@ -4122,10 +4543,44 @@ public function getVariableName(): string {
return $matches["name"];
}
+
+ /** @return DocCommentTag[] */
+ public static function parseDocComments(array $comments): array {
+ $tags = [];
+ foreach ($comments as $comment) {
+ if (!($comment instanceof DocComment)) {
+ continue;
+ }
+ $commentText = substr($comment->getText(), 2, -2);
+ foreach (explode("\n", $commentText) as $commentLine) {
+ $regex = '/^\*\s*@([a-z-]+)(?:\s+(.+))?$/';
+ if (preg_match($regex, trim($commentLine), $matches)) {
+ $tags[] = new DocCommentTag($matches[1], $matches[2] ?? null);
+ }
+ }
+ }
+
+ return $tags;
+ }
+
+ /**
+ * @param DocCommentTag[] $tags
+ * @return array Mapping tag names to the value (or null),
+ * if a tag is present multiple times the last value is used
+ */
+ public static function makeTagMap(array $tags): array {
+ $map = [];
+ foreach ($tags as $tag) {
+ $map[$tag->name] = $tag->value;
+ }
+ return $map;
+ }
}
+// Instances of ExposedDocComment are immutable and do not need to be cloned
+// when held by an object that is cloned
class ExposedDocComment {
- private string $docComment;
+ private /* readonly */ string $docComment;
public function __construct(string $docComment) {
$this->docComment = $docComment;
@@ -4135,47 +4590,45 @@ public function escape(): string {
return str_replace("\n", '\n', addslashes($this->docComment));
}
- public function getLength(): int {
- return strlen($this->docComment);
+ public function getInitCode(): string {
+ return "zend_string_init_interned(\"" . $this->escape() . "\", " . strlen($this->docComment) . ", 1);";
}
-}
-/** @return DocCommentTag[] */
-function parseDocComments(array $comments): array {
- $tags = [];
- foreach ($comments as $comment) {
- if ($comment instanceof DocComment) {
- $tags = array_merge($tags, parseDocComment($comment));
- }
- }
+ /** @param array $comments */
+ public static function extractExposedComment(array $comments): ?ExposedDocComment {
+ $exposedDocComment = null;
- return $tags;
-}
+ foreach ($comments as $comment) {
+ $text = $comment->getText();
+ $matches = [];
+ $pattern = "#^(\s*\/\*\*)(\s*@genstubs-expose-comment-block)(\s*)$#m";
+
+ if (preg_match($pattern, $text, $matches) !== 1) {
+ continue;
+ }
-/** @return DocCommentTag[] */
-function parseDocComment(DocComment $comment): array {
- $commentText = substr($comment->getText(), 2, -2);
- $tags = [];
- foreach (explode("\n", $commentText) as $commentLine) {
- $regex = '/^\*\s*@([a-z-]+)(?:\s+(.+))?$/';
- if (preg_match($regex, trim($commentLine), $matches)) {
- $tags[] = new DocCommentTag($matches[1], $matches[2] ?? null);
+ if ($exposedDocComment !== null) {
+ throw new Exception("Only one PHPDoc comment block can be exposed");
+ }
+
+ $exposedDocComment = preg_replace($pattern, '$1$3', $text);
}
- }
- return $tags;
+ return $exposedDocComment ? new ExposedDocComment($exposedDocComment) : null;
+ }
}
+// Instances of FramelessFunctionInfo are immutable and do not need to be cloned
+// when held by an object that is cloned
class FramelessFunctionInfo {
- public int $arity;
-}
+ public /* readonly */ int $arity;
+
+ public function __construct(string $json) {
+ // FIXME: Should have some validation
+ $json = json_decode($json, true);
-function parseFramelessFunctionInfo(string $json): FramelessFunctionInfo {
- // FIXME: Should have some validation
- $json = json_decode($json, true);
- $framelessFunctionInfo = new FramelessFunctionInfo();
- $framelessFunctionInfo->arity = $json["arity"];
- return $framelessFunctionInfo;
+ $this->arity = $json["arity"];
+ }
}
function parseFunctionLike(
@@ -4203,7 +4656,14 @@ function parseFunctionLike(
$framelessFunctionInfos = [];
if ($comments) {
- $tags = parseDocComments($comments);
+ $tags = DocCommentTag::parseDocComments($comments);
+ $tagMap = DocCommentTag::makeTagMap($tags);
+
+ $isDeprecated = array_key_exists('deprecated', $tagMap);
+ $verify = !array_key_exists('no-verify', $tagMap);
+ $tentativeReturnType = array_key_exists('tentative-return-type', $tagMap);
+ $supportsCompileTimeEval = array_key_exists('compile-time-eval', $tagMap);
+ $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap);
foreach ($tags as $tag) {
switch ($tag->name) {
@@ -4218,18 +4678,6 @@ function parseFunctionLike(
}
break;
- case 'deprecated':
- $isDeprecated = true;
- break;
-
- case 'no-verify':
- $verify = false;
- break;
-
- case 'tentative-return-type':
- $tentativeReturnType = true;
- break;
-
case 'return':
$docReturnType = $tag->getType();
break;
@@ -4242,10 +4690,6 @@ function parseFunctionLike(
$refcount = $tag->getValue();
break;
- case 'compile-time-eval':
- $supportsCompileTimeEval = true;
- break;
-
case 'prefer-ref':
$varName = $tag->getVariableName();
if (!isset($paramMeta[$varName])) {
@@ -4254,12 +4698,8 @@ function parseFunctionLike(
$paramMeta[$varName][$tag->name] = true;
break;
- case 'undocumentable':
- $isUndocumentable = true;
- break;
-
case 'frameless-function':
- $framelessFunctionInfos[] = parseFramelessFunctionInfo($tag->getValue());
+ $framelessFunctionInfos[] = new FramelessFunctionInfo($tag->getValue());
break;
}
}
@@ -4323,7 +4763,7 @@ function parseFunctionLike(
$type,
isset($docParamTypes[$varName]) ? Type::fromString($docParamTypes[$varName]) : null,
$param->default ? $prettyPrinter->prettyPrintExpr($param->default) : null,
- createAttributes($param->attrGroups)
+ AttributeInfo::createFromGroups($param->attrGroups)
);
if (!$param->default && !$param->variadic) {
$numRequiredArgs = $i + 1;
@@ -4362,9 +4802,9 @@ function parseFunctionLike(
$cond,
$isUndocumentable,
$minimumPhpVersionIdCompatibility,
- createAttributes($func->attrGroups),
+ AttributeInfo::createFromGroups($func->attrGroups),
$framelessFunctionInfos,
- createExposedDocComment($comments)
+ ExposedDocComment::extractExposedComment($comments)
);
} catch (Exception $e) {
throw new Exception($name . "(): " .$e->getMessage());
@@ -4376,7 +4816,7 @@ function parseFunctionLike(
*/
function parseConstLike(
PrettyPrinterAbstract $prettyPrinter,
- ConstOrClassConstName $name,
+ AbstractConstName $name,
Node\Const_ $const,
int $flags,
?Node $type,
@@ -4387,26 +4827,19 @@ function parseConstLike(
array $attributes
): ConstInfo {
$phpDocType = null;
- $deprecated = false;
- $cValue = null;
- $link = null;
- $isFileCacheAllowed = true;
- if ($comments) {
- $tags = parseDocComments($comments);
- foreach ($tags as $tag) {
- if ($tag->name === 'var') {
- $phpDocType = $tag->getType();
- } elseif ($tag->name === 'deprecated') {
- $deprecated = true;
- } elseif ($tag->name === 'cvalue') {
- $cValue = $tag->value;
- } elseif ($tag->name === 'undocumentable') {
- $isUndocumentable = true;
- } elseif ($tag->name === 'link') {
- $link = $tag->value;
- } elseif ($tag->name === 'no-file-cache') {
- $isFileCacheAllowed = false;
- }
+
+ $tags = DocCommentTag::parseDocComments($comments);
+ $tagMap = DocCommentTag::makeTagMap($tags);
+
+ $deprecated = array_key_exists('deprecated', $tagMap);
+ $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap);
+ $isFileCacheAllowed = !array_key_exists('no-file-cache', $tagMap);
+ $cValue = $tagMap['cvalue'] ?? null;
+ $link = $tagMap['link'] ?? null;
+
+ foreach ($tags as $tag) {
+ if ($tag->name === 'var') {
+ $phpDocType = $tag->getType();
}
}
@@ -4441,7 +4874,7 @@ function parseConstLike(
$link,
$phpVersionIdMinimumCompatibility,
$attributes,
- createExposedDocComment($comments),
+ ExposedDocComment::extractExposedComment($comments),
$isFileCacheAllowed
);
}
@@ -4461,22 +4894,17 @@ function parseProperty(
array $attributes
): PropertyInfo {
$phpDocType = null;
- $isDocReadonly = false;
- $isVirtual = false;
- $link = null;
- if ($comments) {
- $tags = parseDocComments($comments);
- foreach ($tags as $tag) {
- if ($tag->name === 'var') {
- $phpDocType = $tag->getType();
- } elseif ($tag->name === 'readonly') {
- $isDocReadonly = true;
- } elseif ($tag->name === 'link') {
- $link = $tag->value;
- } elseif ($tag->name === 'virtual') {
- $isVirtual = true;
- }
+ $tags = DocCommentTag::parseDocComments($comments);
+ $tagMap = DocCommentTag::makeTagMap($tags);
+
+ $isDocReadonly = array_key_exists('readonly', $tagMap);
+ $link = $tagMap['link'] ?? null;
+ $isVirtual = array_key_exists('virtual', $tagMap);
+
+ foreach ($tags as $tag) {
+ if ($tag->name === 'var') {
+ $phpDocType = $tag->getType();
}
}
@@ -4508,7 +4936,7 @@ function parseProperty(
$link,
$phpVersionIdMinimumCompatibility,
$attributes,
- createExposedDocComment($comments)
+ ExposedDocComment::extractExposedComment($comments)
);
}
@@ -4529,33 +4957,24 @@ function parseClass(
?int $minimumPhpVersionIdCompatibility,
bool $isUndocumentable
): ClassInfo {
- $flags = $class instanceof Class_ ? $class->flags : 0;
$comments = $class->getComments();
$alias = null;
- $isDeprecated = false;
- $isStrictProperties = false;
- $isNotSerializable = false;
$allowsDynamicProperties = false;
- $attributes = [];
- if ($comments) {
- $tags = parseDocComments($comments);
- foreach ($tags as $tag) {
- if ($tag->name === 'alias') {
- $alias = $tag->getValue();
- } else if ($tag->name === 'deprecated') {
- $isDeprecated = true;
- } else if ($tag->name === 'strict-properties') {
- $isStrictProperties = true;
- } else if ($tag->name === 'not-serializable') {
- $isNotSerializable = true;
- } else if ($tag->name === 'undocumentable') {
- $isUndocumentable = true;
- }
+ $tags = DocCommentTag::parseDocComments($comments);
+ $tagMap = DocCommentTag::makeTagMap($tags);
+
+ $isDeprecated = array_key_exists('deprecated', $tagMap);
+ $isStrictProperties = array_key_exists('strict-properties', $tagMap);
+ $isNotSerializable = array_key_exists('not-serializable', $tagMap);
+ $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap);
+ foreach ($tags as $tag) {
+ if ($tag->name === 'alias') {
+ $alias = $tag->getValue();
}
}
- $attributes = createAttributes($class->attrGroups);
+ $attributes = AttributeInfo::createFromGroups($class->attrGroups);
foreach ($attributes as $attribute) {
switch ($attribute->class) {
case 'AllowDynamicProperties':
@@ -4597,7 +5016,7 @@ function parseClass(
return new ClassInfo(
$name,
- $flags,
+ $class instanceof Class_ ? $class->flags : 0,
$classKind,
$alias,
$class instanceof Enum_ && $class->scalarType !== null
@@ -4605,7 +5024,7 @@ function parseClass(
$isDeprecated,
$isStrictProperties,
$attributes,
- createExposedDocComment($comments),
+ ExposedDocComment::extractExposedComment($comments),
$isNotSerializable,
$extends,
$implements,
@@ -4619,398 +5038,16 @@ function parseClass(
);
}
-/**
- * @param array> $attributeGroups
- * @return Attribute[]
- */
-function createAttributes(array $attributeGroups): array {
- $attributes = [];
-
- foreach ($attributeGroups as $attrGroup) {
- foreach ($attrGroup->attrs as $attr) {
- $attributes[] = new AttributeInfo($attr->name->toString(), $attr->args);
- }
- }
-
- return $attributes;
-}
-
-/** @param array $comments */
-function createExposedDocComment(array $comments): ?ExposedDocComment {
- $exposedDocComment = null;
-
- foreach ($comments as $comment) {
- $text = $comment->getText();
- $matches = [];
- $pattern = "#^(\s*\/\*\*)(\s*@genstubs-expose-comment-block)(\s*)$#m";
-
- if (preg_match($pattern, $text, $matches) !== 1) {
- continue;
- }
-
- if ($exposedDocComment !== null) {
- throw new Exception("Only one PHPDoc comment block can be exposed");
- }
-
- $exposedDocComment = preg_replace($pattern, '$1$3', $text);
- }
-
- return $exposedDocComment ? new ExposedDocComment($exposedDocComment) : null;
-}
-
-function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string {
- foreach ($stmt->getComments() as $comment) {
- $text = trim($comment->getText());
- if (preg_match('/^#\s*if\s+(.+)$/', $text, $matches)) {
- $conds[] = $matches[1];
- } else if (preg_match('/^#\s*ifdef\s+(.+)$/', $text, $matches)) {
- $conds[] = "defined($matches[1])";
- } else if (preg_match('/^#\s*ifndef\s+(.+)$/', $text, $matches)) {
- $conds[] = "!defined($matches[1])";
- } else if (preg_match('/^#\s*else$/', $text)) {
- if (empty($conds)) {
- throw new Exception("Encountered else without corresponding #if");
- }
- $cond = array_pop($conds);
- $conds[] = "!($cond)";
- } else if (preg_match('/^#\s*endif$/', $text)) {
- if (empty($conds)) {
- throw new Exception("Encountered #endif without corresponding #if");
- }
- array_pop($conds);
- } else if ($text[0] === '#') {
- throw new Exception("Unrecognized preprocessor directive \"$text\"");
- }
- }
-
- return empty($conds) ? null : implode(' && ', $conds);
-}
-
/** @return DocComment[] */
function getFileDocComments(array $stmts): array {
if (empty($stmts)) {
return [];
}
- $comments = $stmts[0]->getComments();
-
- $result = [];
- foreach ($comments as $comment) {
- if ($comment instanceof DocComment) {
- $result[] = $comment;
- }
- }
-
- return $result;
-}
-
-function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstract $prettyPrinter) {
- $conds = [];
- foreach ($stmts as $stmt) {
- $cond = handlePreprocessorConditions($conds, $stmt);
-
- if ($stmt instanceof Stmt\Nop) {
- continue;
- }
-
- if ($stmt instanceof Stmt\Namespace_) {
- handleStatements($fileInfo, $stmt->stmts, $prettyPrinter);
- continue;
- }
-
- if ($stmt instanceof Stmt\Const_) {
- foreach ($stmt->consts as $const) {
- $fileInfo->constInfos[] = parseConstLike(
- $prettyPrinter,
- new ConstName($const->namespacedName, $const->name->toString()),
- $const,
- 0,
- null,
- $stmt->getComments(),
- $cond,
- $fileInfo->isUndocumentable,
- $fileInfo->getMinimumPhpVersionIdCompatibility(),
- []
- );
- }
- continue;
- }
-
- if ($stmt instanceof Stmt\Function_) {
- $fileInfo->funcInfos[] = parseFunctionLike(
- $prettyPrinter,
- new FunctionName($stmt->namespacedName),
- 0,
- 0,
- $stmt,
- $cond,
- $fileInfo->isUndocumentable,
- $fileInfo->getMinimumPhpVersionIdCompatibility()
- );
- continue;
- }
-
- if ($stmt instanceof Stmt\ClassLike) {
- $className = $stmt->namespacedName;
- $constInfos = [];
- $propertyInfos = [];
- $methodInfos = [];
- $enumCaseInfos = [];
- foreach ($stmt->stmts as $classStmt) {
- $cond = handlePreprocessorConditions($conds, $classStmt);
- if ($classStmt instanceof Stmt\Nop) {
- continue;
- }
-
- $classFlags = $stmt instanceof Class_ ? $stmt->flags : 0;
- $abstractFlag = $stmt instanceof Stmt\Interface_ ? Modifiers::ABSTRACT : 0;
-
- if ($classStmt instanceof Stmt\ClassConst) {
- foreach ($classStmt->consts as $const) {
- $constInfos[] = parseConstLike(
- $prettyPrinter,
- new ClassConstName($className, $const->name->toString()),
- $const,
- $classStmt->flags,
- $classStmt->type,
- $classStmt->getComments(),
- $cond,
- $fileInfo->isUndocumentable,
- $fileInfo->getMinimumPhpVersionIdCompatibility(),
- createAttributes($classStmt->attrGroups)
- );
- }
- } else if ($classStmt instanceof Stmt\Property) {
- if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
- throw new Exception("Visibility modifier is required");
- }
- foreach ($classStmt->props as $property) {
- $propertyInfos[] = parseProperty(
- $className,
- $classFlags,
- $classStmt->flags,
- $property,
- $classStmt->type,
- $classStmt->getComments(),
- $prettyPrinter,
- $fileInfo->getMinimumPhpVersionIdCompatibility(),
- createAttributes($classStmt->attrGroups)
- );
- }
- } else if ($classStmt instanceof Stmt\ClassMethod) {
- if (!($classStmt->flags & Class_::VISIBILITY_MODIFIER_MASK)) {
- throw new Exception("Visibility modifier is required");
- }
- $methodInfos[] = parseFunctionLike(
- $prettyPrinter,
- new MethodName($className, $classStmt->name->toString()),
- $classFlags,
- $classStmt->flags | $abstractFlag,
- $classStmt,
- $cond,
- $fileInfo->isUndocumentable,
- $fileInfo->getMinimumPhpVersionIdCompatibility()
- );
- } else if ($classStmt instanceof Stmt\EnumCase) {
- $enumCaseInfos[] = new EnumCaseInfo(
- $classStmt->name->toString(), $classStmt->expr);
- } else {
- throw new Exception("Not implemented {$classStmt->getType()}");
- }
- }
-
- $fileInfo->classInfos[] = parseClass(
- $className, $stmt, $constInfos, $propertyInfos, $methodInfos, $enumCaseInfos, $cond, $fileInfo->getMinimumPhpVersionIdCompatibility(), $fileInfo->isUndocumentable
- );
- continue;
- }
-
- if ($stmt instanceof Stmt\Expression) {
- $expr = $stmt->expr;
- if ($expr instanceof Expr\Include_) {
- $fileInfo->dependencies[] = (string)EvaluatedValue::createFromExpression($expr->expr, null, null, [])->value;
- continue;
- }
- }
-
- throw new Exception("Unexpected node {$stmt->getType()}");
- }
- if (!empty($conds)) {
- throw new Exception("Unterminated preprocessor conditions");
- }
-}
-
-function parseStubFile(string $code): FileInfo {
- $lexer = new PhpParser\Lexer\Emulative();
- $parser = new PhpParser\Parser\Php7($lexer);
- $nodeTraverser = new PhpParser\NodeTraverser;
- $nodeTraverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
- $prettyPrinter = new class extends Standard {
- protected function pName_FullyQualified(Name\FullyQualified $node): string {
- return implode('\\', $node->getParts());
- }
- };
-
- $stmts = $parser->parse($code);
- $nodeTraverser->traverse($stmts);
-
- $fileInfo = new FileInfo;
- $fileDocComments = getFileDocComments($stmts);
- if ($fileDocComments !== []) {
- $fileTags = parseDocComments($fileDocComments);
- foreach ($fileTags as $tag) {
- if ($tag->name === 'generate-function-entries') {
- $fileInfo->generateFunctionEntries = true;
- $fileInfo->declarationPrefix = $tag->value ? $tag->value . " " : "";
- } else if ($tag->name === 'generate-legacy-arginfo') {
- if ($tag->value && !in_array((int) $tag->value, ALL_PHP_VERSION_IDS, true)) {
- throw new Exception(
- "Legacy PHP version must be one of: \"" . PHP_70_VERSION_ID . "\" (PHP 7.0), \"" . PHP_80_VERSION_ID . "\" (PHP 8.0), " .
- "\"" . PHP_81_VERSION_ID . "\" (PHP 8.1), \"" . PHP_82_VERSION_ID . "\" (PHP 8.2), \"" . PHP_83_VERSION_ID . "\" (PHP 8.3), " .
- "\"" . PHP_84_VERSION_ID . "\" (PHP 8.4), \"" . $tag->value . "\" provided"
- );
- }
-
- $fileInfo->setMinimumPhpVersionIdCompatibility($tag->value ? (int) $tag->value : PHP_70_VERSION_ID);
- } else if ($tag->name === 'generate-class-entries') {
- $fileInfo->generateClassEntries = true;
- $fileInfo->declarationPrefix = $tag->value ? $tag->value . " " : "";
- } else if ($tag->name === 'undocumentable') {
- $fileInfo->isUndocumentable = true;
- }
- }
- }
-
- // Generating class entries require generating function/method entries
- if ($fileInfo->generateClassEntries && !$fileInfo->generateFunctionEntries) {
- $fileInfo->generateFunctionEntries = true;
- }
-
- handleStatements($fileInfo, $stmts, $prettyPrinter);
- return $fileInfo;
-}
-
-function funcInfoToCode(FileInfo $fileInfo, FuncInfo $funcInfo): string {
- $code = '';
- $returnType = $funcInfo->return->type;
- $isTentativeReturnType = $funcInfo->return->tentativeReturnType;
- $php81MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_81_VERSION_ID;
-
- if ($returnType !== null) {
- if ($isTentativeReturnType && !$php81MinimumCompatibility) {
- $code .= "#if (PHP_VERSION_ID >= " . PHP_81_VERSION_ID . ")\n";
- }
- if (null !== $simpleReturnType = $returnType->tryToSimpleType()) {
- if ($simpleReturnType->isBuiltin) {
- $code .= sprintf(
- "%s(%s, %d, %d, %s, %d)\n",
- $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX",
- $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
- $funcInfo->numRequiredArgs,
- $simpleReturnType->toTypeCode(), $returnType->isNullable()
- );
- } else {
- $code .= sprintf(
- "%s(%s, %d, %d, %s, %d)\n",
- $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX",
- $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
- $funcInfo->numRequiredArgs,
- $simpleReturnType->toEscapedName(), $returnType->isNullable()
- );
- }
- } else {
- $arginfoType = $returnType->toArginfoType();
- if ($arginfoType->hasClassType()) {
- $code .= sprintf(
- "%s(%s, %d, %d, %s, %s)\n",
- $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX",
- $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
- $funcInfo->numRequiredArgs,
- $arginfoType->toClassTypeString(), $arginfoType->toTypeMask()
- );
- } else {
- $code .= sprintf(
- "%s(%s, %d, %d, %s)\n",
- $isTentativeReturnType ? "ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX" : "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX",
- $funcInfo->getArgInfoName(), $funcInfo->return->byRef,
- $funcInfo->numRequiredArgs,
- $arginfoType->toTypeMask()
- );
- }
- }
- if ($isTentativeReturnType && !$php81MinimumCompatibility) {
- $code .= sprintf(
- "#else\nZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n#endif\n",
- $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
- );
- }
- } else {
- $code .= sprintf(
- "ZEND_BEGIN_ARG_INFO_EX(%s, 0, %d, %d)\n",
- $funcInfo->getArgInfoName(), $funcInfo->return->byRef, $funcInfo->numRequiredArgs
- );
- }
-
- foreach ($funcInfo->args as $argInfo) {
- $argKind = $argInfo->isVariadic ? "ARG_VARIADIC" : "ARG";
- $argDefaultKind = $argInfo->hasProperDefaultValue() ? "_WITH_DEFAULT_VALUE" : "";
- $argType = $argInfo->type;
- if ($argType !== null) {
- if (null !== $simpleArgType = $argType->tryToSimpleType()) {
- if ($simpleArgType->isBuiltin) {
- $code .= sprintf(
- "\tZEND_%s_TYPE_INFO%s(%s, %s, %s, %d%s)\n",
- $argKind, $argDefaultKind, $argInfo->getSendByString(), $argInfo->name,
- $simpleArgType->toTypeCode(), $argType->isNullable(),
- $argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : ""
- );
- } else {
- $code .= sprintf(
- "\tZEND_%s_OBJ_INFO%s(%s, %s, %s, %d%s)\n",
- $argKind,$argDefaultKind, $argInfo->getSendByString(), $argInfo->name,
- $simpleArgType->toEscapedName(), $argType->isNullable(),
- $argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : ""
- );
- }
- } else {
- $arginfoType = $argType->toArginfoType();
- if ($arginfoType->hasClassType()) {
- $code .= sprintf(
- "\tZEND_%s_OBJ_TYPE_MASK(%s, %s, %s, %s%s)\n",
- $argKind, $argInfo->getSendByString(), $argInfo->name,
- $arginfoType->toClassTypeString(), $arginfoType->toTypeMask(),
- !$argInfo->isVariadic ? ", " . $argInfo->getDefaultValueAsArginfoString() : ""
- );
- } else {
- $code .= sprintf(
- "\tZEND_%s_TYPE_MASK(%s, %s, %s, %s)\n",
- $argKind, $argInfo->getSendByString(), $argInfo->name,
- $arginfoType->toTypeMask(),
- $argInfo->getDefaultValueAsArginfoString()
- );
- }
- }
- } else {
- $code .= sprintf(
- "\tZEND_%s_INFO%s(%s, %s%s)\n",
- $argKind, $argDefaultKind, $argInfo->getSendByString(), $argInfo->name,
- $argInfo->hasProperDefaultValue() ? ", " . $argInfo->getDefaultValueAsArginfoString() : ""
- );
- }
- }
-
- $code .= "ZEND_END_ARG_INFO()";
- return $code . "\n";
-}
-
-/** @param FuncInfo[] $generatedFuncInfos */
-function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo): ?FuncInfo {
- foreach ($generatedFuncInfos as $generatedFuncInfo) {
- if ($generatedFuncInfo->equalsApartFromNameAndRefcount($funcInfo)) {
- return $generatedFuncInfo;
- }
- }
- return null;
+ return array_filter(
+ $stmts[0]->getComments(),
+ static fn ( $comment ): bool => $comment instanceof DocComment
+ );
}
/**
@@ -5022,7 +5059,7 @@ function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo):
function generateCodeWithConditions(
iterable $infos, string $separator, Closure $codeGenerator, ?string $parentCond = null): string {
$code = "";
-
+
// For combining the conditional blocks of the infos with the same condition
$openCondition = null;
foreach ($infos as $info) {
@@ -5086,13 +5123,13 @@ function generateArgInfoCode(
$fileInfo->getAllFuncInfos(), "\n",
static function (FuncInfo $funcInfo) use (&$generatedFuncInfos, $fileInfo) {
/* If there already is an equivalent arginfo structure, only emit a #define */
- if ($generatedFuncInfo = findEquivalentFuncInfo($generatedFuncInfos, $funcInfo)) {
+ if ($generatedFuncInfo = $funcInfo->findEquivalent($generatedFuncInfos)) {
$code = sprintf(
"#define %s %s\n",
$funcInfo->getArgInfoName(), $generatedFuncInfo->getArgInfoName()
);
} else {
- $code = funcInfoToCode($fileInfo, $funcInfo);
+ $code = $funcInfo->toArgInfoCode($fileInfo->getMinimumPhpVersionIdCompatibility());
}
$generatedFuncInfos[] = $funcInfo;
@@ -5108,8 +5145,7 @@ static function (FuncInfo $funcInfo) use (&$generatedFuncInfos, $fileInfo) {
$framelessFunctionCode = generateCodeWithConditions(
$fileInfo->getAllFuncInfos(), "\n",
static function (FuncInfo $funcInfo) {
- $code = $funcInfo->getFramelessDeclaration($funcInfo);
- return $code;
+ return $funcInfo->getFramelessDeclaration();
}
);
@@ -5310,7 +5346,6 @@ function generatePropertyAttributeInitialization(
/** @param array $funcMap */
function generateOptimizerInfo(array $funcMap): string {
-
$code = "/* This is a generated file, edit the .stub.php files instead. */\n\n";
$code .= "static const func_info_t func_infos[] = {\n";
@@ -5326,9 +5361,9 @@ function generateOptimizerInfo(array $funcMap): string {
/**
* @param array $flagsByPhpVersions
- * @return string[]
+ * @return string
*/
-function generateVersionDependentFlagCode(string $codeTemplate, array $flagsByPhpVersions, ?int $phpVersionIdMinimumCompatibility): array
+function generateVersionDependentFlagCode(string $codeTemplate, array $flagsByPhpVersions, ?int $phpVersionIdMinimumCompatibility): string
{
$phpVersions = ALL_PHP_VERSION_IDS;
sort($phpVersions);
@@ -5337,10 +5372,10 @@ function generateVersionDependentFlagCode(string $codeTemplate, array $flagsByPh
// No version compatibility is needed
if ($phpVersionIdMinimumCompatibility === null) {
if (empty($flagsByPhpVersions[$currentPhpVersion])) {
- return [];
+ return '';
}
- return [sprintf($codeTemplate, implode("|", $flagsByPhpVersions[$currentPhpVersion]))];
+ return sprintf($codeTemplate, implode("|", $flagsByPhpVersions[$currentPhpVersion]));
}
// Remove flags which depend on a PHP version below the minimally supported one
@@ -5352,15 +5387,11 @@ function generateVersionDependentFlagCode(string $codeTemplate, array $flagsByPh
$flagsByPhpVersions = array_slice($flagsByPhpVersions, $index, null, true);
// Remove empty version-specific flags
- $flagsByPhpVersions = array_filter(
- $flagsByPhpVersions,
- static function (array $value): bool {
- return !empty($value);
- });
+ $flagsByPhpVersions = array_filter($flagsByPhpVersions);
// There are no version-specific flags
if (empty($flagsByPhpVersions)) {
- return [];
+ return '';
}
// Remove version-specific flags which don't differ from the previous one
@@ -5380,16 +5411,14 @@ static function (array $value): bool {
reset($flagsByPhpVersions);
$firstVersion = key($flagsByPhpVersions);
if ($firstVersion === $phpVersionIdMinimumCompatibility) {
- return [sprintf($codeTemplate, implode("|", reset($flagsByPhpVersions)))];
+ return sprintf($codeTemplate, implode("|", reset($flagsByPhpVersions)));
}
}
// Add the necessary conditions around the code using the version-specific flags
- $result = [];
+ $code = '';
$i = 0;
foreach (array_reverse($flagsByPhpVersions, true) as $version => $versionFlags) {
- $code = "";
-
$if = $i === 0 ? "#if" : "#elif";
$endif = $i === $flagCount - 1 ? "#endif\n" : "";
@@ -5398,11 +5427,10 @@ static function (array $value): bool {
$code .= sprintf($codeTemplate, implode("|", $versionFlags));
$code .= $endif;
- $result[] = $code;
$i++;
}
- return $result;
+ return $code;
}
/**
@@ -5719,14 +5747,13 @@ function getReplacedSynopsisXml(string $xml): string
/**
* @param array $funcMap
- * @param array $aliasMap
* @return array
*/
-function generateMethodSynopses(array $funcMap, array $aliasMap): array {
+function generateMethodSynopses(array $funcMap): array {
$result = [];
foreach ($funcMap as $funcInfo) {
- $methodSynopsis = $funcInfo->getMethodSynopsisDocument($funcMap, $aliasMap);
+ $methodSynopsis = $funcInfo->getMethodSynopsisDocument();
if ($methodSynopsis !== null) {
$result[$funcInfo->name->getMethodSynopsisFilename() . ".xml"] = $methodSynopsis;
}
@@ -5737,7 +5764,6 @@ function generateMethodSynopses(array $funcMap, array $aliasMap): array {
/**
* @param array $funcMap
- * @param array $aliasMap
* @param array $methodSynopsisWarnings
* @param array $undocumentedFuncMap
* @return array
@@ -5745,7 +5771,6 @@ function generateMethodSynopses(array $funcMap, array $aliasMap): array {
function replaceMethodSynopses(
string $targetDirectory,
array $funcMap,
- array $aliasMap,
bool $isVerifyManual,
array &$methodSynopsisWarnings,
array &$undocumentedFuncMap
@@ -5844,7 +5869,7 @@ function replaceMethodSynopses(
$funcInfo = $funcMap[$funcName];
$documentedFuncMap[$funcInfo->name->__toString()] = $funcInfo->name->__toString();
- $newMethodSynopsis = $funcInfo->getMethodSynopsisElement($funcMap, $aliasMap, $doc);
+ $newMethodSynopsis = $funcInfo->getMethodSynopsisElement($doc);
if ($newMethodSynopsis === null) {
continue;
}
@@ -6005,7 +6030,7 @@ function initPhpParser() {
}
$isInitialized = true;
- $version = "5.0.0";
+ $version = "5.3.1";
$phpParserDir = __DIR__ . "/PHP-Parser-$version";
if (!is_dir($phpParserDir)) {
installPhpParser($version, $phpParserDir);
@@ -6231,9 +6256,7 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc
if ($replacePredefinedConstants) {
foreach ($predefinedConstants as $filename => $content) {
- if (file_put_contents($filename, $content)) {
- echo "Saved $filename\n";
- }
+ reportFilePutContents($filename, $content);
}
}
}
@@ -6248,9 +6271,7 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc
}
foreach ($classSynopses as $filename => $content) {
- if (file_put_contents("$classSynopsesDirectory/$filename", $content)) {
- echo "Saved $filename\n";
- }
+ reportFilePutContents("$classSynopsesDirectory/$filename", $content);
}
}
}
@@ -6260,36 +6281,36 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc
if ($replaceClassSynopses) {
foreach ($classSynopses as $filename => $content) {
- if (file_put_contents($filename, $content)) {
- echo "Saved $filename\n";
- }
+ reportFilePutContents($filename, $content);
}
}
}
if ($generateMethodSynopses) {
- $methodSynopses = generateMethodSynopses($funcMap, $aliasMap);
+ $methodSynopses = generateMethodSynopses($funcMap);
if (!file_exists($manualTarget)) {
mkdir($manualTarget);
}
foreach ($methodSynopses as $filename => $content) {
- if (!file_exists("$manualTarget/$filename")) {
- if (file_put_contents("$manualTarget/$filename", $content)) {
- echo "Saved $filename\n";
+ $path = "$manualTarget/$filename";
+
+ if (!file_exists($path)) {
+ if (!file_exists(dirname($path))) {
+ mkdir(dirname($path));
}
+
+ reportFilePutContents($path, $content);
}
}
}
if ($replaceMethodSynopses || $verifyManual) {
- $methodSynopses = replaceMethodSynopses($manualTarget, $funcMap, $aliasMap, $verifyManual, $methodSynopsisWarnings, $undocumentedFuncMap);
+ $methodSynopses = replaceMethodSynopses($manualTarget, $funcMap, $verifyManual, $methodSynopsisWarnings, $undocumentedFuncMap);
if ($replaceMethodSynopses) {
foreach ($methodSynopses as $filename => $content) {
- if (file_put_contents($filename, $content)) {
- echo "Saved $filename\n";
- }
+ reportFilePutContents($filename, $content);
}
}
}
@@ -6298,9 +6319,7 @@ function(?ArgInfo $aliasArg, ?ArgInfo $aliasedArg) use ($aliasFunc, $aliasedFunc
$filename = dirname(__FILE__, 2) . "/Zend/Optimizer/zend_func_infos.h";
$optimizerInfo = generateOptimizerInfo($funcMap);
- if (file_put_contents($filename, $optimizerInfo)) {
- echo "Saved $filename\n";
- }
+ reportFilePutContents($filename, $optimizerInfo);
}
if ($verifyManual) {
diff --git a/build/php.m4 b/build/php.m4
index d8a5cbf084293..640f01008009d 100644
--- a/build/php.m4
+++ b/build/php.m4
@@ -1788,7 +1788,7 @@ dnl
dnl Common setup macro for ICU.
dnl
AC_DEFUN([PHP_SETUP_ICU],[
- PKG_CHECK_MODULES([ICU], [icu-uc >= 50.1 icu-io icu-i18n])
+ PKG_CHECK_MODULES([ICU], [icu-uc >= 57.1 icu-io icu-i18n])
PHP_EVAL_INCLINE([$ICU_CFLAGS])
PHP_EVAL_LIBLINE([$ICU_LIBS], [$1])
diff --git a/configure.ac b/configure.ac
index 0e12d55b00963..01d9ded69b920 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice.
dnl ----------------------------------------------------------------------------
AC_PREREQ([2.68])
-AC_INIT([PHP],[8.4.7-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net])
+AC_INIT([PHP],[8.5.0-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net])
AC_CONFIG_SRCDIR([main/php_version.h])
AC_CONFIG_AUX_DIR([build])
AC_PRESERVE_HELP_ORDER
@@ -799,6 +799,8 @@ PHP_ARG_ENABLE([debug],
AS_VAR_IF([PHP_DEBUG], [yes], [
PHP_DEBUG=1
ZEND_DEBUG=yes
+ CFLAGS="$CFLAGS -UNDEBUG"
+ CXXFLAGS="$CXXFLAGS -UNDEBUG"
PHP_REMOVE_OPTIMIZATION_FLAGS
dnl Add -O0 only if GCC or ICC is used.
if test "$GCC" = "yes" || test "$ICC" = "yes"; then
@@ -817,6 +819,8 @@ AS_VAR_IF([PHP_DEBUG], [yes], [
], [
PHP_DEBUG=0
ZEND_DEBUG=no
+ CFLAGS="$CFLAGS -DNDEBUG"
+ CXXFLAGS="$CXXFLAGS -DNDEBUG"
])
PHP_ARG_ENABLE([debug-assertions],
@@ -829,6 +833,8 @@ PHP_ARG_ENABLE([debug-assertions],
AS_VAR_IF([PHP_DEBUG_ASSERTIONS], [yes], [
PHP_DEBUG=1
ZEND_DEBUG=yes
+ CFLAGS="$CFLAGS -UNDEBUG"
+ CXXFLAGS="$CXXFLAGS -UNDEBUG"
])
AC_ARG_ENABLE([zts],
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000000000..12f3a0600a949
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,36 @@
+# Makefile for php-src/docs
+# Copyright (c) The PHP Group
+
+# If people set these on the make command line, use 'em
+
+SPHINXBUILD ?= sphinx-build
+
+SOURCEDIR = source
+BUILDDIR = build
+RSTFMT = rstfmt
+RSTFMTFLAGS = -w 100
+
+rwildcard = $(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
+FILES = $(call rwildcard,$(SOURCEDIR),*.rst)
+
+all : html
+
+.PHONY : check-formatting clean html preflight
+.SUFFIXES : # Disable legacy behavior
+
+check-formatting :
+ $(RSTFMT) $(RSTFMTFLAGS) --check $(SOURCEDIR)
+
+clean :
+ rm -rf -- $(wildcard $(SOURCEDIR)/.~ $(BUILDDIR))
+
+html : preflight
+ $(SPHINXBUILD) -M $@ $(SOURCEDIR) $(BUILDDIR)
+ @printf 'Browse the \e]8;;%s\e\\%s\e]8;;\e\\.\n' \
+ "file://$(abspath $(BUILDDIR))/$@/index.$@" "php-src html docs locally"
+
+preflight : $(SOURCEDIR)/.~
+
+$(SOURCEDIR)/.~ : $(FILES)
+ $(RSTFMT) $(RSTFMTFLAGS) $?
+ touch $@
diff --git a/docs/README.md b/docs/README.md
index fbd58155a5141..3c0d7ddd2bcc3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,9 +1,10 @@
# php-src docs
This is the home of the php-src internal documentation, hosted at
-[php.github.io/php-src/](https://php.github.io/php-src/). It is in very early stages, but is
-intended to become the primary place where new information about php-src is documented. Over time,
-it is expected to replace various mediums like:
+[php.github.io/php-src/](https://php.github.io/php-src/). It is in very early
+stages, but is intended to become the primary place where new information about
+php-src is documented. Over time, it is expected to replace various mediums
+like:
* https://www.phpinternalsbook.com/
* https://wiki.php.net/internals
@@ -14,11 +15,15 @@ it is expected to replace various mediums like:
`python` 3 and `pip` are required.
```bash
-pip install sphinx sphinx-design sphinxawesome-theme
+cd docs
+# Recommended: Initialize and activate a Python virtual environment
+pip install --upgrade pip
+pip install -r requirements.txt
make html
```
-That's it! You can view the documentation under `./build/html/index.html` in your browser.
+That's it! You can view the documentation under `./build/html/index.html` in
+your browser.
## Formatting
@@ -29,5 +34,5 @@ The files in this documentation are formatted using the
rstfmt -w 100 source
```
-This tool is not perfect. It breaks on custom directives, so we might switch to either a fork or
-something else in the future.
+This tool is not perfect. It breaks on custom directives, so we might switch to
+either a fork or something else in the future.
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index 747ffb7b30336..0000000000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,35 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set SOURCEDIR=source
-set BUILDDIR=build
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.https://www.sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
diff --git a/docs/release-process.md b/docs/release-process.md
index 46557524988c5..5d603a22f078e 100644
--- a/docs/release-process.md
+++ b/docs/release-process.md
@@ -6,6 +6,7 @@ repository available according to the release schedule.
The release schedule for each version is published on the
[PHP wiki](https://wiki.php.net):
+- [PHP 8.4](https://wiki.php.net/todo/php84)
- [PHP 8.3](https://wiki.php.net/todo/php83)
- [PHP 8.2](https://wiki.php.net/todo/php82)
- [PHP 8.1](https://wiki.php.net/todo/php81)
@@ -73,7 +74,7 @@ releases.
> When you are unsure about anything, ask a previous RM before proceeding.
> Ideally, make sure a previous RM is available to answer questions during
> the first few releases of your version. For the steps related to the
- > `web-php`, `web-qa`, and `web-php-distributions` repositories, try to have
+ > `web-php` and `web-php-distributions` repositories, try to have
> someone from the webmaster team on hand.
5. Verify the tags to be extra sure everything was tagged properly.
@@ -257,7 +258,7 @@ slightly different steps. We'll call attention where the steps differ.
For example, if the RC is `8.2.1RC1` then the version numbers in the version
branch should be bumped to `8.2.2-dev`. We do this regardless of whether we
build a new RC to make sure `version_compare()` works correctly. See
- [Bump for 8.1.8-dev][] for a real example.
+ [PHP 8.3 is now for PHP 8.3.21-dev][] commit for a real example.
Commit the changes to the version branch.
@@ -291,6 +292,11 @@ slightly different steps. We'll call attention where the steps differ.
>
> Local-only release branches should not be pushed!
+ Do not forget to merge up PHP-X.Y all the way to master. When resolving
+ the conflicts, ignore the changes from PHP-X.Y in higher branches. It
+ means using something like `git checkout --ours .` when on PHP.X.Y+1 or
+ master after the merge resulting in the conflicts.
+
11. Run the following using the release tag to export the tree, create the
`configure` script, and build and compress three tarballs (`.tar.gz`,
`.tar.bz2` and `.tar.xz`).
@@ -320,7 +326,7 @@ slightly different steps. We'll call attention where the steps differ.
downloads.php.net.
```shell
- scp php-X.Y.ZRCn.tar.* downloads.php.net:~/public_html/
+ scp php-X.Y.ZRCn.tar.* downloads.internal.php.net:~/public_html/
```
> 💬 **Hint** \
@@ -354,11 +360,10 @@ slightly different steps. We'll call attention where the steps differ.
## Announcing a non-stable release (alpha/beta/RC)
-1. Switch to your local clone of the `web-qa` repository and update the
+1. Switch to your local clone of the `web-php` repository and update the
information in the `$QA_RELEASES` array in `include/release-qa.php`.
Follow the documentation in the file for editing the QA release information.
- See also [Announce 8.1.0RC3][] and [8.1.6RC1][] for examples.
Add, commit, and push your changes, when finished.
@@ -711,20 +716,7 @@ slightly different steps. We'll call attention where the steps differ.
```shell
./bin/news2html 'https://github.com/php/php-src/raw/php-X.Y.Z/NEWS' 'X.Y.Z' 'ChangeLog-X.php'
```
-
-9. Review all the changes in `web-php`, commit, and push them.
-
- ```shell
- git add -p
- git add archive/entries/*.xml releases/*.php
- git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.Z"
- git push upstream master
- ```
-
- See [Announce PHP 8.1.6][] for an example commit.
-
-10. Switch to your local clone of the `web-qa` repository and update the
- information in the `$QA_RELEASES` array in `include/release-qa.php`.
+8. Update the information in the `$QA_RELEASES` array in `include/release-qa.php`.
The array probably contains information about the RC released two weeks ago
in preparation for the current release. Since the current release is now GA,
@@ -732,18 +724,20 @@ slightly different steps. We'll call attention where the steps differ.
It is sufficient to set the `number` property for the release to `0` to
stop displaying the RC build on the QA website. You may also remove the
- sha256 hashes for the RC tarballs, but it's not necessary. For an example,
- see [PHP 8.1.6 released][].
+ sha256 hashes for the RC tarballs, but it's not necessary.
- Add, commit, and push your changes, when finished.
+9. Review all the changes in `web-php`, commit, and push them.
```shell
git add -p
- git commit --gpg-sign=YOURKEYID -m "PHP X.Y.Z released"
+ git add archive/entries/*.xml releases/*.php
+ git commit --gpg-sign=YOURKEYID -m "Announce PHP X.Y.Z"
git push upstream master
```
-11. 🚨 **Before sending announcement emails, check to make sure the websites have
+ See [Announce PHP 8.1.6][] for an example commit.
+
+10. 🚨 **Before sending announcement emails, check to make sure the websites have
synced.**
* Make sure the tarballs are available from, e.g.,
@@ -755,8 +749,8 @@ slightly different steps. We'll call attention where the steps differ.
e.g., https://www.php.net/ChangeLog-8.php
* Is there a release page for the new version?
e.g., `https://www.php.net/releases/X_Y_Z.php`
- * Does the RC for this version still appear on the QA home page?
- https://qa.php.net
+ * Does the RC for this version still appear on the Release Candidate Builds page?
+ https://www.php.net/release-candidates.php
Keep in mind it may take up to an hour for the websites to sync.
@@ -981,21 +975,22 @@ volunteers to begin the selection process for the next release managers.
## New release manager checklist
-1. Request membership to the
+1. Fill out [the form](https://www.php.net/git-php.php)
+ to get a PHP account (if you don't already have one).
+
+2. Request membership to the
[release managers group](https://github.com/orgs/php/teams/release-managers) on GitHub.
-2. Subscribe to the php-announce@lists.php.net mailing list by emailing
+3. Subscribe to the php-announce@lists.php.net mailing list by emailing
php-announce+subscribe@lists.php.net
-3. Email systems@php.net to get setup for access to downloads.php.net, to be
+4. Email systems@php.net to get setup for access to downloads.php.net, to be
added to the release-managers@php.net distribution list, and to be added to
the moderators for php-announce@lists.php.net so you are able to moderate
your release announcements.
Provide the following information in a single email:
- - Preferred Unix username (will also become part of location to download RCs,
- such as `https://downloads.php.net/~derick/`).
- An SSH public key, preferably a new unique one for PHP systems and
projects.
- Read [Machine Access](https://wiki.php.net/systems#machine_access) to set
@@ -1016,7 +1011,7 @@ volunteers to begin the selection process for the next release managers.
> "[Send emails from a different address or alias][]."
-4. Create a [GPG key][] for your @php.net address.
+5. Create a [GPG key][] for your @php.net address.
> 💡 **Tip** \
> If you're new to GPG, follow GitHub's instructions for
@@ -1081,11 +1076,10 @@ volunteers to begin the selection process for the next release managers.
git push
```
-5. Make sure you have the following repositories cloned locally:
+6. Make sure you have the following repositories cloned locally:
* https://github.com/php/php-src
* https://github.com/php/web-php
- * https://github.com/php/web-qa
* https://github.com/php/web-php-distributions
@@ -1098,15 +1092,12 @@ volunteers to begin the selection process for the next release managers.
[Prepare for PHP 8.1.0RC1]: https://github.com/php/php-src/commit/5764414eb8900ae98020a3c20693f4fb793efa99
[Update NEWS for PHP 8.2.0 alpha2]: https://github.com/php/php-src/commit/418f7211f71658d79d934861be20f277db96fe2c
[Update NEWS for PHP 8.2.0RC6]: https://github.com/php/php-src/commit/4ccc414961a70200d638ca281a35f893226d74e2
-[Bump for 8.1.8-dev]: https://github.com/php/php-src/commit/3b6ee1eb19c14c3339ebfcf5c967065a9f828971
+[PHP 8.3 is now for PHP 8.3.21-dev]: https://github.com/php/php-src/commit/b57f425cfe20a11003253427424cc0517483550b
[GitHub command line tool]: https://cli.github.com
-[Announce 8.1.0RC3]: https://github.com/php/web-qa/commit/f264b711fd3827803b79bbb342959eae57ea502b
-[8.1.6RC1]: https://github.com/php/web-qa/commit/e6d61ad7a9d8be0b1cd159af29f3b9cbdde33384
[merged upwards as usual]: https://wiki.php.net/vcs/gitworkflow
[Update versions for PHP 8.1.7]: https://github.com/php/php-src/commit/d35e577a1bd0b35b9386cea97cddc73fd98eed6d
[Update NEWS for PHP 8.1.7]: https://github.com/php/php-src/commit/b241f07f52ca9f87bf52be81817f475e6e727439
[Announce PHP 8.1.6]: https://github.com/php/web-php/commit/9f796a96c65f07e45845ec248933bfb0010b94a9
-[PHP 8.1.6 released]: https://github.com/php/web-qa/commit/bff725f8373cf6fd9d97ba62a8517b19721a4c2e
[feature freeze]: https://en.wikipedia.org/wiki/Freeze_(software_engineering)
[Prepare for PHP 8.2]: https://github.com/php/php-src/commit/1c33ddb5e5598c5385c4c965992c6e031fd00dd6
[Prepare for PHP 8.2 (bis)]: https://github.com/php/php-src/commit/a93e12f8a6dfc23e334339317c97aa35356db821
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000000000..ca19fe15c2e05
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,4 @@
+Sphinx
+sphinx-design
+sphinxawesome-theme
+rstfmt
diff --git a/docs/source/_static/css/code-no-font-ligatures.css b/docs/source/_static/css/code-no-font-ligatures.css
new file mode 100644
index 0000000000000..1ae51476f013e
--- /dev/null
+++ b/docs/source/_static/css/code-no-font-ligatures.css
@@ -0,0 +1,3 @@
+code {
+ font-variant-ligatures: none;
+}
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 2eb75d509cc07..f28102206a9fa 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -16,11 +16,12 @@
author = 'The PHP Group'
extensions = [
'sphinx_design',
- 'sphinxawesome_theme.highlighting',
+ 'sphinx.ext.autosectionlabel',
]
templates_path = ['_templates']
html_theme = 'sphinxawesome_theme'
html_static_path = ['_static']
+html_css_files = ['css/code-no-font-ligatures.css']
html_title = project
html_permalinks_icon = Icons.permalinks_icon
theme_options = ThemeOptions(
diff --git a/docs/source/core/data-structures/index.rst b/docs/source/core/data-structures/index.rst
index 327e025b92c72..8fcb860f686b2 100644
--- a/docs/source/core/data-structures/index.rst
+++ b/docs/source/core/data-structures/index.rst
@@ -8,5 +8,6 @@
zval
reference-counting
zend_string
+ zend_constant
This section provides an overview of the core data structures used in php-src.
diff --git a/docs/source/core/data-structures/zend_constant.rst b/docs/source/core/data-structures/zend_constant.rst
new file mode 100644
index 0000000000000..a5e85dc78638c
--- /dev/null
+++ b/docs/source/core/data-structures/zend_constant.rst
@@ -0,0 +1,75 @@
+###############
+ zend_constant
+###############
+
+PHP constants (referring to non-class constants) are stored in a dedicated structure
+``zend_constant``, which holds both the value of the constant and details for using it.
+
+************
+ definition
+************
+
+.. code:: c
+
+ typedef struct _zend_constant {
+ zval value;
+ zend_string *name;
+ zend_string *filename;
+ HashTable *attributes;
+ } zend_constant;
+
+The ``value`` field stores both the value itself and some metadata. The ``name`` and ``filename``
+store the name of the constant and the name of the file in which it was defined. The ``attributes``
+field stores the attributes applied to the constant.
+
+*******
+ value
+*******
+
+The value of the constant is stored in the :doc:`./zval` ``value``. However, since the ``zval``
+structure has extra space, for constants this is used to store both the number of the module that
+the constant was defined in, and a combination of the flags that affect the usage of the constant.
+
+This extra information is placed in the ``uint32_t`` field ``value.u2.constant_flags``.
+
+The bottom 16 bits are used to hold flags about the constant
+
+.. code:: c
+
+ #define CONST_PERSISTENT (1<<0) /* Persistent */
+ #define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
+ #define CONST_DEPRECATED (1<<2) /* Deprecated */
+ #define CONST_OWNED (1<<3) /* constant should be destroyed together
+ with class */
+
+These bottom 16 bits can be accessed with the ``ZEND_CONSTANT_FLAGS()`` macro, which is given a
+``zend_constant`` pointer as a parameter.
+
+On the other hand, the top 16 bits are used to store the number of the PHP module that registered
+the constant. For constants defined by the user, the module number stored will be
+``PHP_USER_CONSTANT``. This module number can be accessed with the ``ZEND_CONSTANT_MODULE_NUMBER()``
+macro, which is likewise given a ``zend_constant`` pointer as a parameter.
+
+******
+ name
+******
+
+The ``name`` holds a :doc:`zend_string` with the name of the constant, to allow searching for
+constants that have already been defined. This string is released when the constant itself is freed.
+
+**********
+ filename
+**********
+
+The ``filename`` holds another ``zend_string`` with the name of the file in which the constant was
+defined, or ``NULL`` if not defined userland code. This field provides the foundation for the PHP
+method ``ReflectionConstant::getFileName()``.
+
+************
+ attributes
+************
+
+The ``attributes`` holds a ``HashTable`` (essentially an array) with the details of the attributes
+that were applied to the constant. Note that attributes can only be added to constants declared at
+compile time via ``const``, e.g. ``const EXAMPLE = 123``, not those declared at runtime, e.g.
+``define( 'EXAMPLE', 123 );``.
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 53178820892a6..21e2526f47f64 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -20,6 +20,8 @@
:hidden:
miscellaneous/stubs
+ miscellaneous/writing-tests
+ miscellaneous/running-tests
Welcome to the php-src documentation!
diff --git a/docs/source/miscellaneous/running-tests.rst b/docs/source/miscellaneous/running-tests.rst
new file mode 100644
index 0000000000000..c85e9f4c24dea
--- /dev/null
+++ b/docs/source/miscellaneous/running-tests.rst
@@ -0,0 +1,160 @@
+###############
+ Running Tests
+###############
+
+The easiest way to test your PHP build is to run make test from the command line after successfully
+compiling. This will run the all tests for all enabled functionalities and extensions located in
+tests folders under the source root directory using the PHP CLI binary.
+
+``make test`` executes the ``run-tests.php`` script under the source root (parallel builds will not
+work). Therefore you can execute the script as follows:
+
+.. code:: shell
+
+ TEST_PHP_EXECUTABLE=sapi/cli/php \
+ sapi/cli/php [-c /path/to/php.ini] run-tests.php [ext/foo/tests/GLOB]
+
+******************************************
+ Which php executable does make test use?
+******************************************
+
+If you are running the ``run-tests.php`` script from the command line (as above) you must set the
+``TEST_PHP_EXECUTABLE`` environment variable to explicitly select the PHP executable that is to be
+tested, that is, used to run the test scripts.
+
+If you run the tests using make test, the PHP CLI and CGI executables are automatically set for you.
+``make test`` executes ``run-tests.php`` script with the CLI binary. Some test scripts such as
+session must be executed by CGI SAPI. Therefore, you must build PHP with CGI SAPI to perform all
+tests.
+
+**Note:** The PHP binary executing ``run-tests.php`` and the PHP binary used for executing test
+scripts may differ. If you use different PHP binary for executing ``run-tests.php`` script, you may
+get errors.
+
+************************
+ Which php.ini is used?
+************************
+
+``make test`` uses the same ``php.ini`` file as it would once installed. The tests have been written
+to be independent of that ``php.ini`` file, so if you find a test that is affected by a setting,
+please report this, so we can address the issue.
+
+**********************************
+ Which test scripts are executed?
+**********************************
+
+The ``run-tests.php`` (``make test``), without any arguments executes all test scripts by extracting
+all directories named tests from the source root and any subdirectories below. If there are files,
+which have a phpt extension, ``run-tests.php`` looks at the sections in these files, determines
+whether it should run it, by evaluating the ``SKIPIF`` section. If the test is eligible for
+execution, the ``FILE`` section is extracted into a ``.php`` file (with the same name besides the
+extension) and gets executed. When an argument is given or ``TESTS`` environment variable is set,
+the GLOB is expanded by the shell and any file with extension ``*.phpt`` is regarded as a test file.
+
+Tester can easily execute tests selectively with as follows:
+
+.. code:: shell
+
+ ./sapi/cli/php run-tests.php ext/mbstring/*
+ ./sapi/cli/php run-tests.php ext/mbstring/020.phpt
+
+**************
+ Test results
+**************
+
+Test results are printed to standard output. If there is a failed test, the ``run-tests.php`` script
+saves the result, the expected result and the code executed to the test script directory. For
+example, if ``ext/myext/tests/myext.phpt`` fails to pass, the following files are created:
+
+- ``ext/myext/tests/myext.php`` - actual test file executed
+- ``ext/myext/tests/myext.log`` - log of test execution (L)
+- ``ext/myext/tests/myext.exp`` - expected output (E)
+- ``ext/myext/tests/myext.out`` - output from test script (O)
+- ``ext/myext/tests/myext.diff`` - diff of .out and .exp (D)
+
+Failed tests are always bugs. Either the test is bugged or not considering factors applying to the
+tester's environment, or there is a bug in PHP. If this is a known bug, we strive to provide bug
+numbers, in either the test name or the file name. You can check the status of such a bug, by going
+to: ``https://bugs.php.net/12345`` where 12345 is the bug number. For clarity and automated
+processing, bug numbers are prefixed by a hash sign '#' in test names and/or test cases are named
+``bug12345.phpt``.
+
+**Note:** The files generated by tests can be selected by setting the environment variable
+``TEST_PHP_LOG_FORMAT``. For each file you want to be generated use the character in brackets as
+shown above (default is LEOD). The php file will be generated always.
+
+**Note**: You can set environment variable ``TEST_PHP_DETAILED`` to enable detailed test
+information.
+
+*******************
+ Automated testing
+*******************
+
+If you like to keep up to speed, with latest developments and quality assurance, setting the
+environment variable ``NO_INTERACTION`` to 1, will not prompt the tester for any user input.
+
+Normally, the exit status of make test is zero, regardless of the results of independent tests. Set
+the environment variable ``REPORT_EXIT_STATUS`` to ``1``, and make test will set the exit status
+("$?") to non-zero, when an individual test has failed.
+
+Example script to be run by cron:
+
+.. code:: shell
+
+ ========== qa-test.sh =============
+ #!/bin/sh
+
+ CO_DIR=$HOME/cvs/php7
+ MYMAIL=qa-test@domain.com
+ TMPDIR=/var/tmp
+ TODAY=`date +"%Y%m%d"`
+
+ # Make sure compilation environment is correct
+ CONFIGURE_OPTS='--disable-all --enable-cli --with-pcre'
+ export MAKE=gmake
+ export CC=gcc
+
+ # Set test environment
+ export NO_INTERACTION=1
+ export REPORT_EXIT_STATUS=1
+
+ cd $CO_DIR
+ cvs update . >>$TMPDIR/phpqatest.$TODAY
+ ./cvsclean ; ./buildconf ; ./configure $CONFIGURE_OPTS ; $MAKE
+ $MAKE test >>$TMPDIR/phpqatest.$TODAY 2>&1
+ if test $? -gt 0
+ then
+ cat $TMPDIR/phpqatest.$TODAY | mail -s"PHP-QA Test Failed for $TODAY" $MYMAIL
+ fi
+ ========== end of qa-test.sh =============
+
+**Note:** The exit status of ``run-tests.php`` will be ``1`` when ``REPORT_EXIT_STATUS`` is set. The
+result of make test may be higher than that. At present, gmake 3.79.1 returns 2, so it is advised to
+test for non-zero, rather then a specific value.
+
+When ``make test`` finished running tests, and if there are any failed tests, the script asks to
+send the logs to the PHP QA mailing list. Please answer ``y`` to this question so that we can
+efficiently process the results, entering your e-mail address (which will not be transmitted in
+plain text to any list) enables us to ask you some more information if a test failed. Note that this
+script also uploads php -i output so your hostname may be transmitted.
+
+Specific tests can also be executed, like running tests for a certain extension. To do this you can
+do like so (for example the standard library):
+
+.. code:: shell
+
+ make test TESTS=ext/standard.
+
+Where ``TESTS=`` points to a directory containing .phpt files or a single .phpt file like:
+
+.. code:: shell
+
+ make test TESTS=tests/basic/001.phpt.
+
+You can also pass options directly to the underlying script that runs the test suite
+(``run-tests.phpt``) using ``TESTS=``, for example to check for memory leaks using Valgrind, the
+``-m`` option can be passed along: ``make test TESTS="-m Zend/"``. For a full list of options that
+can be passed along, then run ``make test TESTS=-h``.
+
+*Windows users:* On Windows the ``make`` command is called ``nmake`` instead of ``make``. This means
+that on Windows you will have to run ``nmake test``, to run the test suite.
diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst
index 481c26ebc4a0f..3899fe3084196 100644
--- a/docs/source/miscellaneous/stubs.rst
+++ b/docs/source/miscellaneous/stubs.rst
@@ -13,11 +13,11 @@ code, but instead contain empty function and method bodies. A very basic stub lo
/** @var float */
const WEIGHT = 6.8;
- class Atmopshere {
+ class Atmosphere {
public function calculateBar(): float {}
}
- function fahrenheitToCelcius(float $fahrenheitToCelcius): float {}
+ function fahrenheitToCelsius(float $fahrenheitToCelsius): float {}
Any kind of symbol can be declared via stubs. Every type can be used, with the exception of
disjunctive normal form (DNF) types. Additional meta information can be added via PHPDoc blocks or
@@ -33,17 +33,17 @@ using namespace blocks:
/** @var float */
const WEIGHT_TON = 6.8;
- class Atmopshere {
+ class Atmosphere {
public function calculateBar(): float {}
}
}
namespace Algorithms {
- function fahrenheitToCelcius(float $fahrenheit): float {}
+ function fahrenheitToCelsius(float $fahrenheit): float {}
}
The above example declares the global constants ``ANIMAL`` and ``WEIGHT_TON``, and the class
-``Atmopshere`` in the top-level namespace. The ``fahrenheitToCelcius()`` function is declared to be
+``Atmosphere`` in the top-level namespace. The ``fahrenheitToCelsius()`` function is declared to be
in the ``Algorithms`` namespace.
********************
@@ -77,11 +77,11 @@ The arginfo file matching our first example looks like:
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: e4ed788d54a20272a92a3f6618b73d48ec848f97 */
- ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelcius, 0, 1, IS_DOUBLE, 0)
- ZEND_ARG_TYPE_INFO(0, fahrenheitToCelcius, IS_DOUBLE, 0)
+ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelsius, 0, 1, IS_DOUBLE, 0)
+ ZEND_ARG_TYPE_INFO(0, fahrenheitToCelsius, IS_DOUBLE, 0)
ZEND_END_ARG_INFO()
- ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Atmopshere_calculateBar, 0, 0, IS_DOUBLE, 0)
+ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Atmosphere_calculateBar, 0, 0, IS_DOUBLE, 0)
ZEND_END_ARG_INFO()
The hash that is included in the file makes sure that stub files are not reprocessed unless the stub
@@ -186,24 +186,24 @@ In order to generate these, add the file-level ``@generate-function-entries`` PH
public function calculateBar(): float {}
}
- function fahrenheitToCelcius(float $fahrenheit): float {}
+ function fahrenheitToCelsius(float $fahrenheit): float {}
Now, the following C code is generated:
.. code:: c
- ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelcius, 0, 1, IS_DOUBLE, 0)
+ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelsius, 0, 1, IS_DOUBLE, 0)
ZEND_ARG_TYPE_INFO(0, fahrenheit, IS_DOUBLE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Atmosphere_calculateBar, 0, 0, IS_DOUBLE, 0)
ZEND_END_ARG_INFO()
- ZEND_FUNCTION(fahrenheitToCelcius);
+ ZEND_FUNCTION(fahrenheitToCelsius);
ZEND_METHOD(Atmosphere, calculateBar);
static const zend_function_entry ext_functions[] = {
- ZEND_FE(fahrenheitToCelcius, arginfo_fahrenheitToCelcius)
+ ZEND_FE(fahrenheitToCelsius, arginfo_fahrenheitToCelsius)
ZEND_FE_END
};
@@ -226,7 +226,7 @@ Additional meta information can be attached to functions, with the following PHP
- ``@deprecated``: Triggers the usual deprecation notice when the function/method is called.
- ``@alias``: If a function/method is an alias of another function/method, then the aliased
- function/method name has to be provided as value. E.g. the function ``sizeof()` has the ``@alias
+ function/method name has to be provided as value. E.g. the function ``sizeof()`` has the ``@alias
count`` annotation.
- ``@implementation-alias``: This is very similar to ``@alias`` with some semantic differences.
@@ -255,7 +255,7 @@ Additional meta information can be attached to functions, with the following PHP
In order to generate code which is necessary for registering constants, classes, properties, enums,
and traits, use the ``@generate-class-entries`` file-level PHPDoc block.
-``@generate-class-entries`` implies ``@generate-function-entries```, so the latter is then
+``@generate-class-entries`` implies ``@generate-function-entries``, so the latter is then
superfluous.
Given the following stub:
@@ -573,8 +573,8 @@ Then notice the ``#if (PHP_VERSION_ID >= ...)`` conditions in the generated argi
return class_entry;
}
-The preprocessor conditions are necessary because ``enum``s, ``readonly`` properties, and the
-``not-serializable`` flag, are PHP 8.1 features and don't exist in PHP 8.0.
+The preprocessor conditions are necessary because enumerations (``enum``), ``readonly`` properties,
+and the ``not-serializable`` flag, are PHP 8.1 features and don't exist in PHP 8.0.
The registration of ``Number`` is therefore completely omitted, while the ``readonly`` flag is not
added for``Elephpant::$name`` for PHP versions before 8.1.
@@ -762,7 +762,7 @@ Running it with the stub examples that are used in this guide, the following war
Warning: Missing class synopsis for Number
Warning: Missing class synopsis for Elephant
Warning: Missing class synopsis for Atmosphere
- Warning: Missing method synopsis for fahrenheitToCelcius()
+ Warning: Missing method synopsis for fahrenheitToCelsius()
Warning: Missing method synopsis for Atmosphere::calculateBar()
**********************
diff --git a/docs/source/miscellaneous/writing-tests.rst b/docs/source/miscellaneous/writing-tests.rst
new file mode 100644
index 0000000000000..fd09d80f1275e
--- /dev/null
+++ b/docs/source/miscellaneous/writing-tests.rst
@@ -0,0 +1,2868 @@
+###############
+ Writing Tests
+###############
+
+******************
+ phpt Test Basics
+******************
+
+The first thing you need to know about tests is that we need more!!! Although PHP works just great
+99.99% of the time, not having a very comprehensive test suite means that we take more risks every
+time we add to or modify the PHP implementation. The second thing you need to know is that if you
+can write PHP you can write tests. Thirdly — we are a friendly and welcoming community, don't be
+scared about writing to (php-qa@lists.php.net) — we won't bite!
+
+So what are phpt tests?
+
+ A phpt test is a little script used by the php internal and quality assurance teams to test PHP's
+ functionality. It can be used with new releases to make sure they can do all the things that
+ previous releases can, or to help find bugs in current releases. By writing phpt tests you are
+ helping to make PHP more stable.
+
+What skills are needed to write a phpt test?
+
+ All that is really needed to write a phpt test is a basic understanding of the PHP language, a
+ text editor, and a way to get the results of your code. That is it. So if you have been writing
+ and running PHP scripts already — you have everything you need.
+
+What do you write phpt tests on?
+
+ Basically you can write a phpt test on one of the various php functions available. You can write
+ a test on a basic language function (a string function or an array function) , or a function
+ provided by one of PHP's numerous extensions (a mysql function or a image function or a mcrypt
+ function).
+
+ You can find out what functions already have phpt tests by looking in the `html version
+ `_ of the git repository (``ext/standard/tests/`` is a good place
+ to start looking — though not all the tests currently written are in there).
+
+ If you want more guidance than that you can always ask the PHP Quality Assurance Team on their
+ mailing list (php-qa@lists.php.net) where they would like you to direct your attentions.
+
+How is a phpt test is used?
+
+ When a test is called by the ``run-tests.php`` script it takes various parts of the phpt file to
+ name and create a .php file. That .php file is then executed. The output of the .php file is then
+ compared to a different section of the phpt file. If the output of the script "matches" the
+ output provided in the phpt script — it passes.
+
+What should a phpt test do?
+
+ Basically — it should try and break the PHP function. It should check not only the functions
+ normal parameters, but it should also check edge cases. Intentionally generating an error is
+ allowed and encouraged.
+
+********************
+ Writing phpt Tests
+********************
+
+Naming Conventions
+==================
+
+Phpt tests follow a very strict naming convention. This is done to easily identify what each phpt
+test is for. Tests should be named according to the following list:
+
+Tests for bugs
+ bug.phpt (bug17123.phpt)
+
+Tests for a function's basic behaviour
+ _basic.phpt (dba_open_basic.phpt)
+
+Tests for a function's error behaviour
+ _error.phpt (dba_open_error.phpt)
+
+Tests for variations in a function's behaviour
+ _variation.phpt (dba_open_variation.phpt)
+
+General tests for extensions
+ .phpt (dba_003.phpt)
+
+The convention of using _basic, _error and _variation was introduced when we found that writing a
+single test case for each function resulted in unacceptably large test cases. It's quite hard to
+debug problems when the test case generates 100s of lines of output.
+
+The "basic" test case for a function should just address the single most simple thing that the
+function is designed to do. For example, if writing a test for the sin() function a basic test would
+just be to check that sin() returns the correct values for some known angles — eg 30, 90, 180.
+
+The "error" tests for a function are test cases which are designed to provoke errors, warnings or
+notices. There can be more than one error case, if so the convention is to name the test cases
+mytest_error1.phpt, mytest_error2.phpt and so on.
+
+The "variation" tests are any tests that don't fit into "basic" or "error" tests. For example one
+might use a variation tests to test boundary conditions.
+
+How big is a test case?
+=======================
+
+Small. Really — the smaller the better, a good guide is no more than 10 lines of output. The reason
+for this is that if we break something in PHP and it breaks your test case we need to be able to
+find out quite quickly what we broke, going through 1000s of line of test case output is not easy.
+Having said that it's sometimes just not practical to stay within the 10 line guideline, in this
+case you can help a lot by commenting the output. You may find plenty of much longer tests in PHP -
+the small tests message is something that we learnt over time, in fact we are slowly going through
+and splitting tests up when we need to.
+
+Comments
+========
+
+Comments help. Not an essay — just a couple of lines on what the objective of the test is. It may
+seem completely obvious to you as you write it, but it might not be to someone looking at it later
+on.
+
+Basic Format
+============
+
+A test must contain the sections TEST, FILE and either EXPECT or EXPECTF at a minimum. The example
+below illustrates a minimal test.
+
+*ext/standard/tests/strings/strtr.phpt*
+
+.. code:: php
+
+ --TEST--
+ strtr() function — basic test for strtr()
+ --FILE--
+ "hi", "hi"=>"hello", "a"=>"A", "world"=>"planet");
+ var_dump(strtr("# hi all, I said hello world! #", $trans));
+ ?>
+ --EXPECT--
+ string(32) "# hello All, I sAid hi planet! #"
+
+As you can see the file is divided into several sections. The TEST section holds a one line title of
+the phpt test, this should be a simple description and shouldn't ever excede one line, if you need
+to write more explanation add comments in the body of the test case. The phpt files name is used
+when generating a .php file. The FILE section is used as the body of the .php file, so don't forget
+to open and close your php tags. The EXPECT section is the part used as a comparison to see if the
+test passes. It is a good idea to generate output with var_dump() calls.
+
+PHPT structure details
+======================
+
+A phpt test can have many more parts than just the minimum. In fact some of the mandatory parts have
+alternatives that may be used if the situation warrants it. The phpt sections are documented here.
+
+Analyzing failing tests
+=======================
+
+While writing tests you will probably run into tests not passing while you think they should. The
+'make test' command provides you with debug information. Several files will be added per test in the
+same directory as the .phpt file itself. Considering your test file is named foo.phpt, these files
+provide you with information that can help you find out what went wrong:
+
+foo.diff
+
+ A diff file between the expected output (be it in EXPECT, EXPECTF or another option) and the
+ actual output.
+
+foo.exp
+
+ The expected output.
+
+foo.log
+
+ A log containing expected output, actual output and results. Most likely very similar to info in
+ the other files.
+
+foo.out
+
+ The actual output of your .phpt test part.
+
+foo.php
+
+ The php code that was executed for this test.
+
+foo.sh
+
+ An executable file that executes the test for you as it was executed during failure.
+
+Testing your test cases
+=======================
+
+Most people who write tests for PHP don't have access to a huge number of operating systems but the
+tests are run on every system that runs PHP. It's good to test your test on as many platforms as you
+can — Linux and Windows are the most important, it's increasingly important to make sure that tests
+run on 64 bit as well as 32 bit platforms. If you only have access to one operating system — don't
+worry, if you have karma, commit the test but watch php-qa@lists.php.net for reports of failures on
+other platforms. If you don't have karma to commit have a look at the next section.
+
+When you are testing your test case it's really important to make sure that you clean up any
+temporary resources (eg files) that you used in the test. There is a special ``--CLEAN--`` section
+to help you do this — see `here <#clean>`_.
+
+Another good check is to look at what lines of code in the PHP source your test case covers. This is
+easy to do, there are some instructions on the `PHP Wiki
+`_.
+
+What should I do with my test case when I've written and tested it?
+===================================================================
+
+The next step is to get someone to review it. If it's short you can paste it into a note and send it
+to php-qa@lists.php.net. If the test is a bit too long for that then put it somewhere were people
+can download it (`pastebin `_ is sometimes used). Appending tests to notes as
+files doesn't work well - so please don't do that. Your note to php-qa@lists.php.net should say what
+level of PHP you have tested it on and what platform(s) you've run it on. Someone from the PHP QA
+group will review your test and reply to you. They may ask for some changes or suggest better ways
+to do things, or they may commit it to PHP.
+
+Writing Portable PHP Tests
+==========================
+
+Writing portable tests can be hard if you don't have access to all the many platforms that PHP can
+run on. Do your best. If in doubt, don't disable a test. It is better that the test runs in as many
+environments as possible.
+
+If you know a new test won't run in a specific environment, try to write the complementary test for
+that environment.
+
+Make sure sets of data are consistently ordered. SQL queries are not guaranteed to return results in
+the same order unless an ORDER BY clause is used. Directory listings are another example that can
+vary: use an appropriate PHP function to sort them before printing. Both of these examples have
+affected PHP tests in the past.
+
+Make sure that any test touching parsing or display of dates uses a hard-defined timezone —
+preferable 'UTC'. It is important that this is defined in the file section using:
+
+.. code:: php
+
+ date_default_timezone_set('UTC');
+
+and not in the INI section. This is because of the order in which settings are checked which is:
+
+.. code::
+
+ date_default_timezone_set() -> TZ environmental -> INI setting -> System Setting
+
+If a TZ environmental varaibale is found the INI setting will be ignored.
+
+Tests that run, or only have have matching EXPECT output, on 32bit platforms can use a SKIPIF
+section like:
+
+.. code:: php
+
+ --SKIPIF--
+
+
+Tests for 64bit platforms can use:
+
+.. code:: php
+
+ --SKIPIF--
+
+
+To run a test only on Windows:
+
+.. code:: php
+
+ --SKIPIF--
+
+
+To run a test only on Linux:
+
+.. code:: php
+
+ --SKIPIF--
+
+
+To skip a test on Mac OS X Darwin:
+
+.. code:: php
+
+ --SKIPIF--
+
+
+**********
+ Examples
+**********
+
+EXPECTF
+=======
+
+``/ext/standard/tests/strings/str_shuffle.phpt`` is a good example for using ``EXPECTF`` instead of
+``EXPECT``. From time to time the algorithm used for shuffle changed and sometimes the machine used
+to execute the code has influence on the result of shuffle. But it always returns a three character
+string detectable by ``%s`` (that matches any string until the end of the line). Other scan-able
+forms are ``%a`` for any amount of chars (at least one), ``%i`` for integers, ``%d`` for numbers
+only, ``%f`` for floating point values, ``%c`` for single characters, ``%x`` for hexadecimal values,
+``%w`` for any number of whitespace characters and ``%e`` for ``DIRECTORY_SEPARATOR`` (``'\'`` or
+``'/'``).
+
+See also `EXPECTF <#expectf>`_ details.
+
+*/ext/standard/tests/strings/str_shuffle.phpt*
+
+.. code:: php
+
+ --TEST--
+ Testing str_shuffle.
+ --FILE--
+
+ --EXPECTF--
+ string(3) "%s"
+ string(3) "123"
+
+EXPECTREGEX
+===========
+
+``/ext/standard/tests/strings/strings001.phpt`` is a good example for using ``EXPECTREGEX`` instead
+of ``EXPECT``. This test also shows that in ``EXPECTREGEX`` some characters need to be escaped since
+otherwise they would be interpreted as a regular expression.
+
+*/ext/standard/tests/strings/strings001.phpt*
+
+.. code:: php
+
+ --TEST--
+ Test whether strstr() and strrchr() are binary safe.
+ --FILE--
+
+ --EXPECTREGEX--
+ string\(18\) \"nica\x00turska panica\"
+ string\(19\) \" nica\x00turska panica\"
+
+EXTENSIONS
+==========
+
+Some tests depend on PHP extensions that may be unavailable. These extensions should be listed in
+the ``EXTENSIONS`` section. If an extension is missing, PHP will try to find it in a shared module
+and skip the test if it's not there.
+
+*/ext/sodium/tests/crypto_scalarmult.phpt*
+
+.. code:: php
+
+ --TEST--
+ Check for libsodium scalarmult
+ --EXTENSIONS--
+ sodium
+ --FILE--
+
+ --FILE--
+ [snip]
+
+Test script and ``SKIPIF`` code should be directly written into ``\*.phpt``. However, it is
+recommended to use include files when more test scripts depend on the same ``SKIPIF`` code or when
+certain test files need the same values for some input.
+
+Note: no file used by any test should have one of the following extensions: ".php", ".log", ".mem",
+".exp", ".out" or ".diff". When you use an include file for the ``SKIPIF`` section it should be
+named "skipif.inc" and an include file used in the ``FILE`` section of many tests should be named
+"test.inc".
+
+*************
+ Final Notes
+*************
+
+Cleaning up after running a test
+================================
+
+Sometimes test cases create files or directories as part of the test case and it's important to
+remove these after the test ends, the ``--CLEAN--`` section is provided to help with this.
+
+The PHP code in the ``--CLEAN--`` section is executed separately from the code in the ``--FILE--``
+section. For example, this code:
+
+.. code:: php
+
+ --TEST--
+ Will fail to clean up
+ --FILE--
+
+ --CLEAN--
+
+ --EXPECT--
+
+will not remove the temporary file because the variable $temp_filename is not defined in the
+``--CLEAN--`` section.
+
+Here is a better way to write the code:
+
+.. code:: php
+
+ --TEST--
+ This will remove temporary files
+ --FILE--
+
+ --CLEAN--
+
+ --EXPECT--
+
+Note the use of the ``__DIR__`` construct which will ensure that the temporary file is created in
+the same directory as the phpt test script.
+
+When creating temporary files it is a good idea to use an extension that indicates the use of the
+file, eg .tmp. It's also a good idea to avoid using extensions that are already used for other
+purposes, eg .inc, .php. Similarly, it is helpful to give the temporary file a name that is clearly
+related to the test case. For example, mytest.phpt should create mytest.tmp (or mytestN.tmp, N=1,
+2,3,...) then if by any chance the temporary file isnt't removed properly it will be obvious which
+test case created it.
+
+When writing and debugging a test case with a ``--CLEAN--`` section it is helpful to remember that
+the php code in the ``--CLEAN--`` section is executed separately from the code in the ``--FILE--``
+section. For example, in a test case called mytest.phpt, code from the ``--FILE--`` section is run
+from a file called mytest.php and code from the ``--CLEAN--`` section is run from a file called
+mytest.clean.php. If the test passes, both the .php and .clean.php files are removed by
+``run-tests.php``. You can prevent the removal by using the --keep option of ``run-tests.php``, this
+is a very useful option if you need to check that the ``--CLEAN--`` section code is working as you
+intended.
+
+Finally — if you are using CVS it's helpful to add the extension that you use for test-related
+temporary files to the .cvsignore file — this will help to prevent you from accidentally checking
+temporary files into CVS.
+
+Redirecting tests
+=================
+
+Using ``--REDIRECTTEST--`` it is possible to redirect from one test to a bunch of other tests. That
+way multiple extensions can refer to the same set of test scripts probably using it with a different
+configuration.
+
+The block is eval'd and supposed to return an array describing how to redirect. The resulting array
+must contain the key 'TEST' that stores the redirect target as a string. This string usually is the
+directory where the test scripts are located and should be relative. Optionally you can use the
+'ENV' as an array configuring the environment to be set when executing the tests. This way you can
+pass configuration to the executed tests.
+
+Redirect tests may especially contain ``--SKIPIF--``, ``--ENV--``, and ``--ARGS--`` sections but
+they no not use any ``--EXPECT--`` section.
+
+The redirected tests themselves are just normal tests.
+
+Error reporting in tests
+========================
+
+All tests should run correctly with error_reporting(E_ALL) and display_errors=1. This is the default
+when called from ``run-tests.php``. If you have a good reason for lowering the error reporting, use
+``--INI--`` section and comment this in your testcode.
+
+If your test intentionally generates a PHP warning message use $php_errormsg variable, which you can
+then output. This will result in a consistent error message output across all platforms and PHP
+configurations, preventing your test from failing due inconsistencies in the error message content.
+Alternatively you can use ``--EXPECTF--`` and check for the message by replacing the path of the
+source of the message with ``%s`` and the line number with ``%d``. The end of a message in a test
+file ``example.phpt`` then looks like ``in %sexample.php on line %d``. We explicitly dropped the
+last path divider as that is a system dependent character ``/`` or ``\``.
+
+Last bit
+========
+
+Often you want to run test scripts without ``run-tests.php`` by executing them on command line like
+any other php script. But sometimes it disturbs having a long ``--EXPECT--`` block, so that you
+don't see the actual output as it scrolls away overwritten by the blocks following the actual file
+block. The workaround is to use terminate the ``--FILE--`` section with the two lines ``===DONE===``
+and ````. When doing so ``run-tests.php`` does not execute the line containing the
+exit call as that would suppress leak messages. Actually ``run-tests.php`` ignores any part after a
+line consisting only of ``===DONE===``.
+
+Here is an example:
+
+.. code:: php
+
+ --TEST--
+ Test hypot() — dealing with mixed number/character input
+ --INI--
+ precision=14
+ --FILE--
+
+ ===DONE===
+
+ --EXPECTF--
+ 23abc :-33 float(40.224370722238)
+ ===DONE===
+
+If executed as PHP script the output will stop after the code on the ``--FILE--`` section has been
+run.
+
+***********
+ Reference
+***********
+
+PHPT Sections
+=============
+
+``--TEST--``
+------------
+
+**Description:** Title of test as a single line short description.
+
+**Required:** Yes
+
+**Format:** Plain text. We recommend a single line only.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --TEST--
+ Test filter_input() with GET and POST data.
+
+Example 1 (full): :ref:`sample001.phpt`
+
+``--DESCRIPTION--``
+-------------------
+
+**Description:** If your test requires more than a single line title to adequately describe it, you
+can use this section for further explanation. Multiple lines are allowed and besides being used for
+information, this section is completely ignored by the test binary.
+
+**Required:** No
+
+**Format:** Plain text, multiple lines.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --DESCRIPTION--
+ This test covers both valid and invalid usages of filter_input() with INPUT_GET and INPUT_POST data and several differnet filter sanitizers.
+
+Example 1 (full): :ref:`sample001.phpt`
+
+``--CREDITS--``
+---------------
+
+**Description:** Used to credit contributors without CVS commit rights, who put their name and email
+on the first line. If the test was part of a TestFest event, then # followed by the name of the
+event and the date (YYYY-MM-DD) on the second line.
+
+**Required:** No. For newly created tests this section should no longer be included, as test
+authorship is already accurately tracked by Git. If multiple authors should be credited, the
+`Co-authored-by` tag in the commit message may be used.
+
+**Format:** Name Email [Event]
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --CREDITS--
+ Felipe Pena
+
+Example 1 (full): :ref:`sample001.phpt`
+
+Example 2 (snippet):
+
+.. code:: text
+
+ --CREDITS--
+ Zoe Slattery zoe@php.net
+ # TestFest Munich 2009-05-19
+
+Example 2 (full): :ref:`sample002.phpt`
+
+``--SKIPIF--``
+--------------
+
+**Description:** A condition or set of conditions used to determine if a test should be skipped.
+Tests that are only applicable to a certain platform, extension or PHP version are good reasons for
+using a ``--SKIPIF--`` section.
+
+A common practice for extension tests is to write your ``--SKIPIF--`` extension criteria into a file
+call skipif.inc and then including that file in the ``--SKIPIF--`` section of all your extension
+tests. This promotes the DRY principle and reduces future code maintenance.
+
+**Required:** No.
+
+**Format:** PHP code enclosed by PHP tags. If the output of this scripts starts with "skip", the
+test is skipped. If the output starts with "xfail", the test is marked as expected failure. If the
+output starts with "flaky", the test is marked as flaky test. The "xfail" convention is supported as
+of PHP 7.2.0. The "flaky" convention is supported as of PHP 8.2.25 and PHP 8.3.13, respectively.
+
+Example 1 (snippet):
+
+.. code:: php
+
+ --SKIPIF--
+
+
+Example 1 (full): :ref:`sample001.phpt`
+
+Example 2 (snippet):
+
+.. code:: php
+
+ --SKIPIF--
+
+
+Example 2 (full): :ref:`sample003.phpt`
+
+Example 3 (snippet):
+
+.. code:: php
+
+ --SKIPIF--
+
+
+Example 3 (full): :ref:`xfailif.phpt`
+
+Example 4 (snippet):
+
+.. code:: php
+
+ --SKIPIF--
+ string
&d=12345.7
+
+Example 1 (full): :ref:`sample001.phpt`
+
+Example 2 (snippet):
+
+.. code:: xml
+
+ --POST--
+
+
+
+
+
+
+Example 2 (full): :ref:`sample005.phpt`
+
+``--POST_RAW--``
+----------------
+
+**Description:** Raw POST data to be passed to the test script. This differs from the section above
+because it doesn't automatically set the Content-Type, this leaves you free to define your own
+within the section. This section forces the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** Follows the HTTP post data format.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --POST_RAW--
+ Content-type: multipart/form-data, boundary=AaB03x
+
+ --AaB03x content-disposition: form-data; name="field1"
+
+ Joe Blow
+ --AaB03x
+ content-disposition: form-data; name="pics"; filename="file1.txt"
+ Content-Type: text/plain
+
+ abcdef123456789
+ --AaB03x--
+
+Example 1 (full): :ref:`sample006.phpt`
+
+``--PUT--``
+-----------
+
+**Description:** Similar to the section above, PUT data to be passed to the test script. This
+section forces the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** Raw data optionally preceded by a Content-Type header.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --PUT--
+ Content-Type: text/json
+
+ {"name":"default output handler","type":0,"flags":112,"level":0,"chunk_size":0,"buffer_size":16384,"buffer_used":3}
+
+``--GZIP_POST--``
+-----------------
+
+**Description:** When this section exists, the POST data will be gzencode()'d. This section forces
+the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** Just add the content to be gzencode()'d in the section.
+
+Example 1 (snippet):
+
+.. code:: xml
+
+ --GZIP_POST--
+
+
+
+
+
+
+Example 1 (full): :ref:`sample005.phpt`
+
+``--DEFLATE_POST--``
+--------------------
+
+**Description:** When this section exists, the POST data will be gzcompress()'ed. This section
+forces the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements:
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** Just add the content to be gzcompress()'ed in the section.
+
+Example 1 (snippet):
+
+.. code:: xml
+
+ --DEFLATE_POST--
+
+
+
+
+
+
+
+Example 1 (full): :ref:`sample007.phpt`
+
+``--GET--``
+-----------
+
+**Description:** GET variables to be passed to the test script. This section forces the use of the
+CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Format:** A single line of text passed as the GET data to the script.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --GET--
+ a=test&b=http://example.com
+
+Example 1 (full): :ref:`sample001.phpt`
+
+Example 2 (snippet):
+
+.. code:: text
+
+ --GET--
+ ar[elm1]=1234&ar[elm2]=0660&a=0234
+
+Example 2 (full): :ref:`sample008.phpt`
+
+``--COOKIE--``
+--------------
+
+**Description:** Cookies to be passed to the test script. This section forces the use of the CGI
+binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** A single line of text in a valid HTTP cookie format.
+
+Example 1 (snippet):
+
+.. code::
+
+ --COOKIE--
+ hello=World;goodbye=MrChips
+
+Example 1 (full): :ref:`sample002.phpt`
+
+``--STDIN--``
+-------------
+
+**Description:** Data to be fed to the test script's standard input.
+
+**Required:** No.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** Any text within this section is passed as STDIN to PHP.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --STDIN--
+ fooBar
+ use this to input some thing to the php script
+
+Example 1 (full): :ref:`sample009.phpt`
+
+``--INI--``
+-----------
+
+**Description:** To be used if you need a specific php.ini setting for the test.
+
+**Required:** No.
+
+**Format:** Key value pairs including automatically replaced tags. One setting per line. Content
+that is not a valid ini setting may cause failures.
+
+The following is a list of all tags and what they are used to represent:
+
+- ``{PWD}``: Represents the directory of the file containing the ``--INI--`` section.
+- ``{TMP}``: Represents the system's temporary directory. Available as of PHP 7.2.19 and 7.3.6.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --INI--
+ precision=14
+
+Example 1 (full): :ref:`sample001.phpt`
+
+Example 2 (snippet):
+
+.. code:: text
+
+ --INI--
+ session.use_cookies=0
+ session.cache_limiter=
+ register_globals=1
+ session.serialize_handler=php
+ session.save_handler=files
+
+Example 2 (full): :ref:`sample003.phpt`
+
+``--ARGS--``
+------------
+
+**Description:** A single line defining the arguments passed to PHP.
+
+**Required:** No.
+
+**Format:** A single line of text that is passed as the argument(s) to the PHP CLI.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --ARGS--
+ --arg value --arg=value -avalue -a=value -a value
+
+Example 1 (full): :ref:`sample010.phpt`
+
+``--ENV--``
+-----------
+
+**Description:** Configures environment variables such as those found in the ``$_SERVER`` global
+array.
+
+**Required:** No.
+
+**Format:** Key value pairs. One setting per line.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --ENV--
+ SCRIPT_NAME=/frontcontroller10.php
+ REQUEST_URI=/frontcontroller10.php/hi
+ PATH_INFO=/hi
+
+Example 1 (full): :ref:`sample018.phpt`
+
+``--PHPDBG--``
+--------------
+
+**Description:** This section takes arbitrary phpdbg commands and executes the test file according
+to them as it would be run in the phpdbg prompt.
+
+**Required:** No.
+
+**Format:** Arbitrary phpdbg commands
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --PHPDBG--
+ b
+ 4
+ b
+ del
+ 0
+ b
+ 5
+ r
+ b
+ del
+ 1
+ r
+ y
+ q
+
+Example 1 (full): :ref:`phpdbg_1.phpt`
+
+``--FILE--``
+------------
+
+**Description:** The test source code.
+
+**Required:** One of the ``FILE`` type sections is required.
+
+**Format:** PHP source code enclosed by PHP tags.
+
+Example 1 (snippet):
+
+.. code:: php
+
+ --FILE--
+
+
+Example 1 (full): :ref:`sample001.phpt`
+
+``--FILEEOF--``
+---------------
+
+**Description:** An alternative to ``--FILE--`` where any trailing line breaks (\n || \r || \r\n
+found at the end of the section) are omitted. This is an extreme edge-case feature, so 99.99% of the
+time you won't need this section.
+
+**Required:** One of the ``FILE`` type sections is required.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** PHP source code enclosed by PHP tags.
+
+Example 1 (snippet):
+
+.. code:: php
+
+ --FILEEOF--
+ array(
+ 'PDOTEST_DSN' => 'sqlite2::memory:'
+ ),
+ 'TESTS' => 'ext/pdo/tests'
+ );
+
+Example 1 (full): :ref:`sample013.phpt` Note: The destination tests for this example are not
+included. See the PDO extension tests for reference to live tests using this section.
+
+Example 2 (snippet):
+
+.. code:: php
+
+ --REDIRECTTEST--
+ # magic auto-configuration
+
+ $config = array(
+ 'TESTS' => 'ext/pdo/tests'
+ );
+
+ if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
+ $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
+ $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
+ if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
+ }
+ } else {
+ $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
+ $config['ENV']['PDOTEST_USER'] = 'root';
+ $config['ENV']['PDOTEST_PASS'] = '';
+ }
+
+ return $config;
+
+Example 2 (full): :ref:`sample014.phpt`
+
+Note: The destination tests for this example are not included. See the PDO extension tests for
+reference to live tests using this section.
+
+``--CGI--``
+-----------
+
+**Description:** This section takes no value. It merely provides a simple marker for tests that MUST
+be run as CGI, even if there is no ``--POST--`` or ``--GET--`` sections in the test file.
+
+**Required:** No.
+
+**Format:** No value, just the ``--CGI--`` statement.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --CGI--
+
+Example 1 (full): :ref:`sample016.phpt`
+
+``--XFAIL--``
+-------------
+
+**Description:** This section identifies this test as one that is currently expected to fail. It
+should include a brief description of why it's expected to fail. Reasons for such expectations
+include tests that are written before the functionality they are testing is implemented or notice of
+a bug which is due to upstream code such as an extension which provides PHP support for some other
+software.
+
+Please do NOT include an ``--XFAIL--`` without providing a text description for the reason it's
+being used.
+
+**Required:** No.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** A short plain text description of why this test is currently expected to fail.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --XFAIL--
+ This bug might be still open on aix5.2-ppc64 and hpux11.23-ia64
+
+Example 1 (full): :ref:`sample017.phpt`
+
+``--FLAKY--``
+-------------
+
+**Description:** This section identifies this test as one that occassionally fails. If the test
+actually fails, it will be retried one more time, and that result will be reported. The section
+should include a brief description of why the test is flaky. Reasons for this include tests that
+rely on relatively precise timing, or temporary disc states. Available as of PHP 8.1.22 and 8.2.9,
+respectively.
+
+Please do NOT include a ``--FLAKY--`` section without providing a text description for the reason it
+is being used.
+
+**Required:** No.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** A short plain text description of why this test is flaky.
+
+Example 1 (snippet):
+
+.. code::
+
+ --FLAKY--
+ This test frequently fails in CI
+
+Example 1 (full): flaky.phpt
+
+``--EXPECTHEADERS--``
+---------------------
+
+**Description:** The expected headers. Any header specified here must exist in the response and have
+the same value or the test fails. Additional headers found in the actual tests while running are
+ignored.
+
+**Required:** No.
+
+**Format:** HTTP style headers. May include multiple lines.
+
+Example 1 (snippet):
+
+--EXPECTHEADERS-- Status: 404
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --EXPECTHEADERS--
+ Content-type: text/html; charset=UTF-8
+ Status: 403 Access Denied
+
+Example 1 (full): :ref:`sample018.phpt`
+
+Note: The destination tests for this example are not included. See the phar extension tests for
+reference to live tests using this section.
+
+``--EXPECT--``
+--------------
+
+**Description:** The expected output from the test script. This must match the actual output from
+the test script exactly for the test to pass.
+
+**Required:** One of the ``EXPECT`` type sections is required.
+
+**Format:** Plain text. Multiple lines of text are allowed.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --EXPECT--
+ array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+ }
+
+Example 1 (full): :ref:`sample002.phpt`
+
+``--EXPECT_EXTERNAL--``
+-----------------------
+
+**Description:** Similar to to ``--EXPECT--`` section, but just stating a filename where to load the
+expected output from.
+
+**Required:** One of the ``EXPECT`` type sections is required.
+
+**Test Script Support:** ``run-tests.php``
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --EXPECT_EXTERNAL--
+ test001.expected.txt
+
+*test001.expected.txt*
+
+.. code:: php
+
+ array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+ }
+
+``--EXPECTF--``
+---------------
+
+**Description:** An alternative of ``--EXPECT--``. Where it differs from ``--EXPECT--`` is that it
+uses a number of substitution tags for strings, spaces, digits, etc. that appear in test case output
+but which may vary between test runs. The most common example of this is to use %s and %d to match
+the file path and line number which are output by PHP Warnings.
+
+**Required:** One of the ``EXPECT`` type sections is required.
+
+**Format:** Plain text including tags which are inserted to represent different types of output
+which are not guaranteed to have the same value on subsequent runs or when run on different
+platforms.
+
+The following is a list of all tags and what they are used to represent:
+
+ - ``%e``: Represents a directory separator, for example / on Linux.
+ - ``%s``: One or more of anything (character or white space) except the end of line character.
+ - ``%S``: Zero or more of anything (character or white space) except the end of line character.
+ - ``%a``: One or more of anything (character or white space) including the end of line
+ character.
+ - ``%A``: Zero or more of anything (character or white space) including the end of line
+ character.
+ - ``%w``: Zero or more white space characters.
+ - ``%i``: A signed integer value, for example +3142, -3142, 3142.
+ - ``%d``: An unsigned integer value, for example 123456.
+ - ``%x``: One or more hexadecimal character. That is, characters in the range 0-9, a-f, A-F.
+ - ``%f``: A floating point number, for example: 3.142, -3.142, 3.142E-10, 3.142e+10.
+ - ``%c``: A single character of any sort (.).
+ - ``%r...%r``: Any string (...) enclosed between two ``%r`` will be treated as a regular
+ expression.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --EXPECTF--
+ string(4) "test"
+ string(18) "http://example.com"
+ string(27) "<b>test</b>"
+
+ Notice: Object of class stdClass could not be converted to int in %ssample001.php on line %d
+ bool(false)
+ string(6) "string"
+ float(12345.7)
+ string(29) "<p>string</p>"
+ bool(false)
+
+ Warning: filter_var() expects parameter 2 to be long, string given in %s011.php on line %d
+ NULL
+
+ Warning: filter_input() expects parameter 3 to be long, string given in %s011.php on line %d
+ NULL
+
+ Warning: filter_var() expects at most 3 parameters, 5 given in %s011.php on line %d
+ NULL
+
+ Warning: filter_var() expects at most 3 parameters, 5 given in %s011.php on line %d
+ NULL
+ Done
+
+Example 1 (full): :ref:`sample001.phpt`
+
+Example 2 (snippet):
+
+.. code:: text
+
+ --EXPECTF--
+ Warning: bzopen() expects exactly 2 parameters, 0 given in %s on line %d NULL
+
+ Warning: bzopen(): '' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): filename cannot be empty in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): filename cannot be empty in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): 'x' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): 'rw' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+ bool(false)
+
+ Warning: bzopen(no_such_file): failed to open stream: No such file or directory in %s on line %d
+ bool(false)
+ resource(%d) of type (stream) Done
+
+Example 2 (full): :ref:`sample019.phpt`
+
+Example 3 (snippet):
+
+.. code:: text
+
+ --EXPECTF--
+ object(DOMNodeList)#%d (0) {
+ }
+ int(0)
+ bool(true)
+ bool(true)
+ string(0) ""
+ bool(true)
+ bool(true)
+ bool(false)
+ bool(false)
+
+Example 2 (full): :ref:`sample020.phpt`
+
+``--EXPECTF_EXTERNAL--``
+------------------------
+
+**Description:** Similar to to ``--EXPECTF--`` section, but like the ``--EXPECT_EXTERNAL--`` section
+just stating a filename where to load the expected output from.
+
+**Required:** One of the ``EXPECT`` type sections is required.
+
+**Test Script Support:** ``run-tests.php``
+
+``--EXPECTREGEX--``
+-------------------
+
+**Description:** An alternative of ``--EXPECT--``. This form allows the tester to specify the result
+in a regular expression.
+
+**Required:** One of the ``EXPECT`` type sections is required.
+
+**Format:** Plain text including regular expression patterns which represent data that can vary
+between subsequent runs of a test or when run on different platforms.
+
+Example 1 (snippet):
+
+.. code:: text
+
+ --EXPECTREGEX--
+ M_E : 2.718281[0-9]*
+ M_LOG2E : 1.442695[0-9]*
+ M_LOG10E : 0.434294[0-9]*
+ M_LN2 : 0.693147[0-9]*
+ M_LN10 : 2.302585[0-9]*
+ M_PI : 3.141592[0-9]*
+ M_PI_2 : 1.570796[0-9]*
+ M_PI_4 : 0.785398[0-9]*
+ M_1_PI : 0.318309[0-9]*
+ M_2_PI : 0.636619[0-9]*
+ M_SQRTPI : 1.772453[0-9]*
+ M_2_SQRTPI: 1.128379[0-9]*
+ M_LNPI : 1.144729[0-9]*
+ M_EULER : 0.577215[0-9]*
+ M_SQRT2 : 1.414213[0-9]*
+ M_SQRT1_2 : 0.707106[0-9]*
+ M_SQRT3 : 1.732050[0-9]*
+
+Example 1 (full): :ref:`sample021.phpt`
+
+Example 2 (snippet):
+
+.. code:: text
+
+ --EXPECTF--
+ *** Testing imap_append() : basic functionality ***
+ Create a new mailbox for test
+ Create a temporary mailbox and add 0 msgs
+ .. mailbox '%s' created
+ Add a couple of msgs to new mailbox {%s}INBOX.%s
+ bool(true)
+ bool(true)
+ Msg Count after append : 2
+ List the msg headers
+ array(2) {
+ [0]=>
+ string(%d) "%w%s 1)%s webmaster@something. Test message (%d chars)"
+ [1]=>
+ string(%d) "%w%s 2)%s webmaster@something. Another test (%d chars)"
+ }
+
+Example 2 (full): :ref:`sample025.phpt`
+
+Example 3 (snippet):
+
+.. code:: text
+
+ --EXPECTREGEX--
+ string\(4\) \"-012\"
+ string\(8\) \"2d303132\"
+ (string\(13\) \" 4294967284\"|string\(20\) \"18446744073709551604\")
+ (string\(26\) \"20202034323934393637323834\"|string\(40\) \"3138343436373434303733373039353531363034\")
+
+ Example 3 (full): :ref:`sample023.phpt`
+
+``--EXPECTREGEX_EXTERNAL--``
+----------------------------
+
+**Description:** Similar to to ``--EXPECTREGEX--`` section, but like the ``--EXPECT_EXTERNAL--``
+section just stating a filename where to load the expected output from.
+
+**Required:** One of the ``EXPECT`` type sections is required.
+
+**Test Script Support:** ``run-tests.php``
+
+``--CLEAN--``
+-------------
+
+**Description:** Code that is executed after a test completes. It's main purpose is to allow you to
+clean up after yourself. You might need to remove files created during the test or close sockets or
+database connections following a test. Infact, even if a test fails or encounters a fatal error
+during the test, the code found in the ``--CLEAN--`` section will still run.
+
+Code in the clean section is run in a completely different process than the one the test was run in.
+So do not try accessing variables you created in the ``--FILE--`` section from inside the
+``--CLEAN--`` section, they won't exist.
+
+Using the switch ``--no-clean`` on ``run-tests.php``, you can prevent the code found in the
+``--CLEAN--`` section of a test from running. This allows you to inspect generated data or files
+without them being removed by the ``--CLEAN--`` section.
+
+**Required:** No.
+
+**Test Script Support:** ``run-tests.php``
+
+**Format:** PHP source code enclosed by PHP tags.
+
+Example 1 (snippet):
+
+.. code:: php
+
+ --CLEAN--
+
+
+Example 1 (full): :ref:`sample024.phpt`
+
+Example 2 (snippet):
+
+.. code:: php
+
+ --CLEAN--
+
+
+Example 2 (full): :ref:`sample025.phpt`
+
+Example 3 (snippet):
+
+.. code:: php
+
+ --CLEAN--
+
+
+Example 3 (full): :ref:`sample022.phpt`
+
+Samples
+=======
+
+capture_stdio_1.phpt
+--------------------
+
+.. code:: php
+
+ --TEST--
+ Test covering the I/O stdin and stdout streams.
+ --DESCRIPTION--
+ This tests checks if the output of stdin and stdout I/O streams match the
+ expected content.
+ --CAPTURE_STDIO--
+ STDIN STDERR
+ --FILE--
+
+ --EXPECT--
+ This is error sent to the stderr I/O stream
+
+capture_stdio_2.phpt
+--------------------
+
+.. code:: php
+
+ --TEST--
+ Test covering the I/O stdin and stderr streams.
+ --DESCRIPTION--
+ This tests checks if the output of stdin and stderr I/O streams match the
+ expected content.
+ --CAPTURE_STDIO--
+ STDIN STDOUT
+ --FILE--
+
+ --EXPECT--
+ Hello, world. This is sent to the stdout I/O stream
+
+capture_stdio_3.phpt
+--------------------
+
+.. code:: php
+
+ --TEST--
+ Test covering the all standard I/O streams.
+ --DESCRIPTION--
+ This tests checks if the output of stdin, stdout and stderr I/O streams match
+ the expected content.
+ --CAPTURE_STDIO--
+ STDIN STDOUT STDERR
+ --FILE--
+
+ --EXPECT--
+ Hello, world. This is sent to the stdout I/O stream
+ This is error sent to the stderr I/O stream
+
+clean.php
+---------
+
+.. code:: php
+
+ Nmsgs; $i++) {
+ imap_delete($imap_stream, $i);
+ }
+
+ $mailboxes = imap_getmailboxes($imap_stream, $server, '*');
+
+ foreach($mailboxes as $value) {
+ // Only delete mailboxes with our prefix
+ if (preg_match('/\{.*?\}INBOX\.(.+)/', $value->name, $match) == 1) {
+ if (strlen($match[1]) >= strlen($mailbox_prefix)
+ && substr_compare($match[1], $mailbox_prefix, 0, strlen($mailbox_prefix)) == 0) {
+ imap_deletemailbox($imap_stream, $value->name);
+ }
+ }
+ }
+
+ imap_close($imap_stream, CL_EXPUNGE);
+ ?>
+
+conflicts_1.phpt
+----------------
+
+.. code:: php
+
+ --TEST--
+ Test get_headers() function : test with context
+ --CONFLICTS--
+ server
+ --FILE--
+ array(
+ 'method' => 'HEAD'
+ )
+ );
+
+ $context = stream_context_create($opts);
+ $headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1, $context);
+ echo $headers["X-Request-Method"]."\n";
+
+ stream_context_set_default($opts);
+ $headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1);
+ echo $headers["X-Request-Method"]."\n";
+
+ echo "Done";
+ ?>
+ --EXPECT--
+ HEAD
+ HEAD
+ Done
+
+extensions.phpt
+---------------
+
+.. code:: php
+
+ --TEST--
+ phpt EXTENSIONS directive with shared extensions
+ --DESCRIPTION--
+ This test covers the presence of some loaded extensions with a list of additional
+ extensions to be loaded when running test.
+ --EXTENSIONS--
+ curl
+ imagick
+ tokenizer
+ --FILE--
+
+ --EXPECT--
+ bool(true)
+ bool(true)
+ bool(true)
+
+file012.phpt
+------------
+
+.. code:: php
+
+
+
+phpdbg_1.phpt
+-------------
+
+.. code:: php
+
+ --TEST--
+ Test deleting breakpoints
+ --PHPDBG--
+ b 4
+ b del 0
+ b 5
+ r
+ b del 1
+ r
+ y
+ q
+ --EXPECTF--
+ [Successful compilation of %s]
+ prompt> [Breakpoint #0 added at %s:4]
+ prompt> [Deleted breakpoint #0]
+ prompt> [Breakpoint #1 added at %s:5]
+ prompt> 12
+ [Breakpoint #1 at %s:5, hits: 1]
+ >00005: echo $i++;
+ 00006: echo $i++;
+ 00007:
+ prompt> [Deleted breakpoint #1]
+ prompt> Do you really want to restart execution? (type y or n): 1234
+ [Script ended normally]
+ prompt>
+ --FILE--
+
+ --INI--
+ precision=14
+ --SKIPIF--
+
+ --GET--
+ a=test&b=https://example.com
+ --POST--
+ c=string
&d=12345.7
+ --FILE--
+
+ --EXPECTF--
+ string(4) "test"
+ string(19) "https://example.com"
+ string(27) "<b>test</b>"
+
+ Notice: Object of class stdClass could not be converted to int in %ssample001.php on line %d
+ bool(false)
+ string(6) "string"
+ float(12345.7)
+ string(29) "<p>string</p>"
+ bool(false)
+
+ Warning: filter_var() expects parameter 2 to be long, string given in %ssample001.php on line %d
+ NULL
+
+ Warning: filter_input() expects parameter 3 to be long, string given in %ssample001.php on line %d
+ NULL
+
+ Warning: filter_var() expects at most 3 parameters, 5 given in %ssample001.php on line %d
+ NULL
+
+ Warning: filter_var() expects at most 3 parameters, 5 given in %ssample001.php on line %d
+ NULL
+ Done
+
+sample002.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Test receipt of cookie data.
+ --CREDITS--
+ Zoe Slattery zoe@php.net
+ # TestFest Munich 2009-05-19
+ --COOKIE--
+ hello=World;goodbye=MrChips
+ --FILE--
+
+ --EXPECT--
+ array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+ }
+
+sample003.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ session object deserialization
+ --SKIPIF--
+
+ --INI--
+ session.use_cookies=0
+ session.cache_limiter=
+ register_globals=1
+ session.serialize_handler=php
+ session.save_handler=files
+ --FILE--
+ yes++; }
+ }
+
+ session_id("abtest");
+ session_start();
+ session_decode('baz|O:3:"foo":2:{s:3:"bar";s:2:"ok";s:3:"yes";i:1;}arr|a:1:{i:3;O:3:"foo":2:{s:3:"bar";s:2:"ok";s:3:"yes";i:1;}}');
+
+ $baz->method();
+ $arr[3]->method();
+
+ var_dump($baz);
+ var_dump($arr);
+ session_destroy();
+ --EXPECT--
+ object(foo)#1 (2) {
+ ["bar"]=>
+ string(2) "ok"
+ ["yes"]=>
+ int(2)
+ }
+ array(1) {
+ [3]=>
+ object(foo)#2 (2) {
+ ["bar"]=>
+ string(2) "ok"
+ ["yes"]=>
+ int(2)
+ }
+ }
+
+sample005.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ SOAP Server 19: compressed request (gzip)
+ --SKIPIF--
+
+ --INI--
+ precision=14
+ --GZIP_POST--
+
+
+
+
+
+ --FILE--
+ "http://testuri.org"));
+ $server->addfunction("test");
+ $server->handle();
+ echo "ok\n";
+ ?>
+ --EXPECT--
+
+ Hello World
+ ok
+
+sample006.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ is_uploaded_file() function
+ --CREDITS--
+ Dave Kelsey
+ --SKIPIF--
+
+ --POST_RAW--
+ Content-type: multipart/form-data, boundary=AaB03x
+
+ --AaB03x
+ content-disposition: form-data; name="field1"
+
+ Joe Blow
+ --AaB03x
+ content-disposition: form-data; name="pics"; filename="file1.txt"
+ Content-Type: text/plain
+
+ abcdef123456789
+ --AaB03x--
+ --FILE--
+
+ --EXPECTF--
+ bool(true)
+ bool(false)
+ bool(false)
+ bool(false)
+
+ Warning: is_uploaded_file() expects exactly 1 parameter, 0 given in %s on line %d
+ NULL
+
+ Warning: is_uploaded_file() expects exactly 1 parameter, 2 given in %s on line %d
+ NULL
+
+sample007.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ SOAP Server 20: compressed request (deflate)
+ --SKIPIF--
+
+ --INI--
+ precision=14
+ --DEFLATE_POST--
+
+
+
+
+
+
+ --FILE--
+ "http://testuri.org"));
+ $server->addfunction("test");
+ $server->handle();
+ echo "ok\n";
+ ?>
+ --EXPECT--
+
+ Hello World
+ ok
+
+sample008.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ GET/POST/REQUEST Test with input_filter
+ --SKIPIF--
+
+ --POST--
+ d=379
+ --GET--
+ ar[elm1]=1234&ar[elm2]=0660&a=0234
+ --FILE--
+ FILTER_FLAG_ALLOW_OCTAL));
+ var_dump($ret);
+
+ $ret = filter_input(INPUT_GET, 'ar', FILTER_VALIDATE_INT, array('flags'=>FILTER_REQUIRE_ARRAY));
+ var_dump($ret);
+
+ $ret = filter_input(INPUT_GET, 'ar', FILTER_VALIDATE_INT, array('flags'=>FILTER_FLAG_ALLOW_OCTAL|FILTER_REQUIRE_ARRAY));
+ var_dump($ret);
+
+ ?>
+ --EXPECT--
+ bool(false)
+ int(156)
+ array(2) {
+ ["elm1"]=>
+ int(1234)
+ ["elm2"]=>
+ bool(false)
+ }
+ array(2) {
+ ["elm1"]=>
+ int(1234)
+ ["elm2"]=>
+ int(432)
+ }
+
+sample009.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ STDIN input
+ --FILE--
+
+ --STDIN--
+ fooBar
+ use this to input some thing to the php script
+ --EXPECT--
+ string(54) "fooBar
+ use this to input some thing to the php script
+ "
+
+sample010.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ getopt#005 (Required values)
+ --ARGS--
+ --arg value --arg=value -avalue -a=value -a value
+ --INI--
+ register_argc_argv=On
+ variables_order=GPS
+ --FILE--
+
+ --EXPECT--
+ array(2) {
+ ["arg"]=>
+ array(2) {
+ [0]=>
+ string(5) "value"
+ [1]=>
+ string(5) "value"
+ }
+ ["a"]=>
+ array(3) {
+ [0]=>
+ string(5) "value"
+ [1]=>
+ string(5) "value"
+ [2]=>
+ string(5) "value"
+ }
+ }
+
+sample011.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Bug #35382 (Comment in end of file produces fatal error)
+ --FILEEOF--
+
+ --REDIRECTTEST--
+ return array(
+ 'ENV' => array(
+ 'PDOTEST_DSN' => 'sqlite2::memory:'
+ ),
+ 'TESTS' => 'ext/pdo/tests'
+ );
+
+sample014.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ MySQL
+ --SKIPIF--
+
+ --REDIRECTTEST--
+ # magic auto-configuration
+
+ $config = array(
+ 'TESTS' => 'ext/pdo/tests'
+ );
+
+ if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
+ $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
+ $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
+ if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
+ }
+ } else {
+ $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
+ $config['ENV']['PDOTEST_USER'] = 'root';
+ $config['ENV']['PDOTEST_PASS'] = '';
+ }
+
+ return $config;
+
+sample016.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Test get variables with CGI binary
+ --GET--
+ hello=World&goodbye=MrChips
+ --CGI--
+ --FILE--
+
+ --EXPECT--
+ array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+ }
+
+sample017.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ PDO Common: Bug #34630 (inserting streams as LOBs)
+ --SKIPIF--
+
+ --FILE--
+ getAttribute(PDO::ATTR_DRIVER_NAME);
+ $is_oci = $driver == 'oci';
+
+ if ($is_oci) {
+ $db->exec('CREATE TABLE test (id int NOT NULL PRIMARY KEY, val BLOB)');
+ } else {
+ $db->exec('CREATE TABLE test (id int NOT NULL PRIMARY KEY, val VARCHAR(256))');
+ }
+ $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ $fp = tmpfile();
+ fwrite($fp, "I am the LOB data");
+ rewind($fp);
+
+ if ($is_oci) {
+ /* oracle is a bit different; you need to initiate a transaction otherwise
+ * the empty blob will be committed implicitly when the statement is
+ * executed */
+ $db->beginTransaction();
+ $insert = $db->prepare("insert into test (id, val) values (1, EMPTY_BLOB()) RETURNING val INTO :blob");
+ } else {
+ $insert = $db->prepare("insert into test (id, val) values (1, :blob)");
+ }
+ $insert->bindValue(':blob', $fp, PDO::PARAM_LOB);
+ $insert->execute();
+ $insert = null;
+
+ $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
+ var_dump($db->query("SELECT * from test")->fetchAll(PDO::FETCH_ASSOC));
+
+ ?>
+ --XFAIL--
+ This bug might be still open on aix5.2-ppc64 and hpux11.23-ia64
+ --EXPECT--
+ array(1) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["val"]=>
+ string(17) "I am the LOB data"
+ }
+ }
+
+sample018.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Phar front controller rewrite access denied [cache_list]
+ --INI--
+ default_charset=UTF-8
+ phar.cache_list={PWD}/frontcontroller10.php
+ --SKIPIF--
+
+ --ENV--
+ SCRIPT_NAME=/frontcontroller10.php
+ REQUEST_URI=/frontcontroller10.php/hi
+ PATH_INFO=/hi
+ --FILE_EXTERNAL--
+ files/frontcontroller4.phar
+ --EXPECTHEADERS--
+ Content-type: text/html; charset=UTF-8
+ Status: 403 Access Denied
+ --EXPECT--
+
+
+ Access Denied
+
+
+ 403 - File /hi Access Denied
+
+
+
+sample019.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ bzopen() and invalid parameters
+ --SKIPIF--
+
+ --FILE--
+
+ --EXPECTF--
+ Warning: bzopen() expects exactly 2 parameters, 0 given in %s on line %d
+ NULL
+
+ Warning: bzopen(): '' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): filename cannot be empty in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): filename cannot be empty in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): 'x' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+ bool(false)
+
+ Warning: bzopen(): 'rw' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+ bool(false)
+
+ Warning: bzopen(no_such_file): failed to open stream: No such file or directory in %s on line %d
+ bool(false)
+ resource(%d) of type (stream)
+ Done
+
+sample020.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Bug #42082 (NodeList length zero should be empty)
+ --FILE--
+ query('*');
+ var_dump($nodes);
+ var_dump($nodes->length);
+ $length = $nodes->length;
+ var_dump(empty($nodes->length), empty($length));
+
+ $doc->loadXML("");
+ var_dump($doc->firstChild->nodeValue, empty($doc->firstChild->nodeValue), isset($doc->firstChild->nodeValue));
+ var_dump(empty($doc->nodeType), empty($doc->firstChild->nodeType))
+ ?>
+ --EXPECTF--
+ object(DOMNodeList)#%d (0) {
+ }
+ int(0)
+ bool(true)
+ bool(true)
+ string(0) ""
+ bool(true)
+ bool(true)
+ bool(false)
+ bool(false)
+
+sample021.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Math constants
+ --INI--
+ precision=14
+ --FILE--
+
+ --EXPECTREGEX--
+ M_E : 2.718281[0-9]*
+ M_LOG2E : 1.442695[0-9]*
+ M_LOG10E : 0.434294[0-9]*
+ M_LN2 : 0.693147[0-9]*
+ M_LN10 : 2.302585[0-9]*
+ M_PI : 3.141592[0-9]*
+ M_PI_2 : 1.570796[0-9]*
+ M_PI_4 : 0.785398[0-9]*
+ M_1_PI : 0.318309[0-9]*
+ M_2_PI : 0.636619[0-9]*
+ M_SQRTPI : 1.772453[0-9]*
+ M_2_SQRTPI: 1.128379[0-9]*
+ M_LNPI : 1.144729[0-9]*
+ M_EULER : 0.577215[0-9]*
+ M_SQRT2 : 1.414213[0-9]*
+ M_SQRT1_2 : 0.707106[0-9]*
+ M_SQRT3 : 1.732050[0-9]*
+
+sample022.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ shm_detach() tests
+ --SKIPIF--
+
+ --FILE--
+
+ --CLEAN--
+
+ --EXPECTF--
+ Warning: shm_detach() expects exactly 1 parameter, 0 given in %ssample022.php on line %d
+ NULL
+
+ Warning: shm_detach() expects exactly 1 parameter, 2 given in %ssample022.php on line %d
+ NULL
+ bool(true)
+
+ Warning: shm_detach(): %d is not a valid sysvshm resource in %ssample022.php on line %d
+ bool(false)
+
+ Warning: shm_remove(): %d is not a valid sysvshm resource in %ssample022.php on line %d
+
+ Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
+ NULL
+
+ Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
+ NULL
+
+ Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
+ NULL
+ Done
+
+sample023.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Bug #23894 (sprintf() decimal specifiers problem)
+ --FILE--
+
+ --EXPECTREGEX--
+ string\(4\) \"-012\"
+ string\(8\) \"2d303132\"
+ (string\(13\) \" 4294967284\"|string\(20\) \"18446744073709551604\")
+ (string\(26\) \"20202034323934393637323834\"|string\(40\) \"3138343436373434303733373039353531363034\")
+
+sample024.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ DOMDocument::save Test basic function of save method
+ --SKIPIF--
+
+ --FILE--
+ formatOutput = true;
+
+ $root = $doc->createElement('book');
+
+ $root = $doc->appendChild($root);
+
+ $title = $doc->createElement('title');
+ $title = $root->appendChild($title);
+
+ $text = $doc->createTextNode('This is the title');
+ $text = $title->appendChild($text);
+
+ $temp_filename = __DIR__.'/DomDocument_save_basic.tmp';
+
+ echo 'Wrote: ' . $doc->save($temp_filename) . ' bytes'; // Wrote: 72 bytes
+ ?>
+ --CLEAN--
+
+ --EXPECTF--
+ Wrote: 72 bytes
+
+sample025.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ Test imap_append() function : basic functionality
+ --SKIPIF--
+
+ --FILE--
+ Mailbox . "\n";
+ var_dump(imap_append($imap_stream, $mb_details->Mailbox
+ , "From: webmaster@something.com\r\n"
+ . "To: info@something.com\r\n"
+ . "Subject: Test message\r\n"
+ . "\r\n"
+ . "this is a test message, please ignore\r\n"
+ ));
+
+ var_dump(imap_append($imap_stream, $mb_details->Mailbox
+ , "From: webmaster@something.com\r\n"
+ . "To: info@something.com\r\n"
+ . "Subject: Another test\r\n"
+ . "\r\n"
+ . "this is another test message, please ignore it too!!\r\n"
+ ));
+
+ $check = imap_check($imap_stream);
+ echo "Msg Count after append : ". $check->Nmsgs . "\n";
+
+ echo "List the msg headers\n";
+ var_dump(imap_headers($imap_stream));
+
+ imap_close($imap_stream);
+ ?>
+ --CLEAN--
+
+ --EXPECTF--
+ *** Testing imap_append() : basic functionality ***
+ Create a new mailbox for test
+ Create a temporary mailbox and add 0 msgs
+ .. mailbox '%s' created
+ Add a couple of msgs to new mailbox {%s}INBOX.%s
+ bool(true)
+ bool(true)
+ Msg Count after append : 2
+ List the msg headers
+ array(2) {
+ [0]=>
+ string(%d) "%w%s 1)%s webmaster@something. Test message (%d chars)"
+ [1]=>
+ string(%d) "%w%s 2)%s webmaster@something. Another test (%d chars)"
+ }
+
+sample026.phpt
+--------------
+
+.. code:: php
+
+ --TEST--
+ SPL: ArrayIterator implementing RecursiveIterator
+ --FILE--
+ array(21, 22 => array(221, 222), 23 => array(231)), 3);
+
+ $dir = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY);
+
+ foreach ($dir as $file) {
+ print "$file\n";
+ }
+
+ ?>
+ ===DONE===
+
+ --EXPECT--
+ 1
+ 21
+ 221
+ 222
+ 231
+ 3
+
+skipif2.phpt
+------------
+
+.. code:: php
+
+
+
+skipif.phpt
+-----------
+
+.. code:: php
+
+
+
+xfailif.phpt
+------------
+
+.. code:: php
+
+ --TEST--
+ Handling of errors during linking
+ --INI--
+ opcache.enable=1
+ opcache.enable_cli=1
+ opcache.optimization_level=-1
+ opcache.preload={PWD}/preload_inheritance_error_ind.inc
+ --SKIPIF--
+
+ --FILE--
+
+ --EXPECTF--
+ Fatal error: Declaration of B::foo($bar) must be compatible with A::foo() in %spreload_inheritance_error.inc on line 8
diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c
index 233045bd7cd7e..2e9cb6c3ad774 100644
--- a/ext/bcmath/bcmath.c
+++ b/ext/bcmath/bcmath.c
@@ -179,6 +179,26 @@ static zend_result php_str2num(bc_num *num, const zend_string *str)
}
/* }}} */
+static void bc_pow_err(bc_raise_status status, uint32_t arg_num)
+{
+ /* If arg_num is 0, it means it is an op */
+ switch (status) {
+ case BC_RAISE_STATUS_DIVIDE_BY_ZERO:
+ zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
+ break;
+ case BC_RAISE_STATUS_LEN_IS_OVERFLOW:
+ case BC_RAISE_STATUS_SCALE_IS_OVERFLOW:
+ case BC_RAISE_STATUS_FULLLEN_IS_OVERFLOW:
+ if (arg_num == 0) {
+ zend_value_error("exponent is too large, the number of digits overflowed");
+ } else {
+ zend_argument_value_error(arg_num, "exponent is too large, the number of digits overflowed");
+ }
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+}
+
/* {{{ Returns the sum of two arbitrary precision numbers */
PHP_FUNCTION(bcadd)
{
@@ -615,11 +635,11 @@ PHP_FUNCTION(bcpow)
goto cleanup;
}
- if (!bc_raise(first, exponent, &result, scale)) {
- zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
+ bc_raise_status ret_status = bc_raise(first, exponent, &result, scale);
+ if (UNEXPECTED(ret_status != BC_RAISE_STATUS_OK)) {
+ bc_pow_err(ret_status, 2);
goto cleanup;
}
-
RETVAL_NEW_STR(bc_num2str_ex(result, scale));
cleanup: {
@@ -807,8 +827,8 @@ PHP_FUNCTION(bcround)
goto cleanup;
}
- bc_round(num, precision, mode, &result);
- RETVAL_NEW_STR(bc_num2str_ex(result, result->n_scale));
+ size_t scale = bc_round(num, precision, mode, &result);
+ RETVAL_NEW_STR(bc_num2str_ex(result, scale));
cleanup: {
bc_free_num(&num);
@@ -1144,8 +1164,9 @@ static zend_result bcmath_number_pow_internal(
}
return FAILURE;
}
- if (!bc_raise(n1, exponent, ret, *scale)) {
- zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Negative power of zero");
+ bc_raise_status ret_status = bc_raise(n1, exponent, ret, *scale);
+ if (UNEXPECTED(ret_status != BC_RAISE_STATUS_OK)) {
+ bc_pow_err(ret_status, is_op ? 0 : 1);
return FAILURE;
}
bc_rm_trailing_zeros(*ret);
@@ -1164,7 +1185,7 @@ static zend_always_inline bcmath_number_obj_t *bcmath_number_new_obj(bc_num ret,
return intern;
}
-static zend_result bcmath_number_parse_num(zval *zv, zend_object **obj, zend_string **str, zend_long *lval)
+static zend_result bcmath_number_parse_num(const zval *zv, zend_object **obj, zend_string **str, zend_long *lval)
{
if (Z_TYPE_P(zv) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), bcmath_number_ce)) {
*obj = Z_OBJ_P(zv);
@@ -1372,7 +1393,7 @@ static int bcmath_number_compare(zval *op1, zval *op2)
}
static zend_always_inline zend_result bc_num_from_obj_or_str_or_long_with_err(
- bc_num *num, size_t *scale, zend_object *obj, zend_string *str, zend_long lval, uint32_t arg_num)
+ bc_num *num, size_t *scale, const zend_object *obj, const zend_string *str, zend_long lval, uint32_t arg_num)
{
size_t full_scale = 0;
if (UNEXPECTED(bc_num_from_obj_or_str_or_long(num, &full_scale, obj, str, lval) == FAILURE)) {
@@ -1799,9 +1820,10 @@ PHP_METHOD(BcMath_Number, round)
bcmath_number_obj_t *intern = get_bcmath_number_from_zval(ZEND_THIS);
bc_num ret = NULL;
- bc_round(intern->num, precision, rounding_mode, &ret);
+ size_t scale = bc_round(intern->num, precision, rounding_mode, &ret);
+ bc_rm_trailing_zeros(ret);
- bcmath_number_obj_t *new_intern = bcmath_number_new_obj(ret, ret->n_scale);
+ bcmath_number_obj_t *new_intern = bcmath_number_new_obj(ret, scale);
RETURN_OBJ(&new_intern->std);
}
diff --git a/ext/bcmath/bcmath_arginfo.h b/ext/bcmath/bcmath_arginfo.h
index 886be0292a1c7..9edfd5cd65760 100644
--- a/ext/bcmath/bcmath_arginfo.h
+++ b/ext/bcmath/bcmath_arginfo.h
@@ -204,9 +204,7 @@ static zend_class_entry *register_class_BcMath_Number(zend_class_entry *class_en
zval property_value_default_value;
ZVAL_UNDEF(&property_value_default_value);
- zend_string *property_value_name = zend_string_init("value", sizeof("value") - 1, 1);
- zend_declare_typed_property(class_entry, property_value_name, &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_value_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_VALUE), &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_scale_default_value;
ZVAL_UNDEF(&property_scale_default_value);
diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h
index 214d616d0acb3..fa335ae404808 100644
--- a/ext/bcmath/libbcmath/src/bcmath.h
+++ b/ext/bcmath/libbcmath/src/bcmath.h
@@ -74,11 +74,6 @@ typedef struct bc_struct {
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MIN(a, b) ((a)>(b)?(b):(a))
-#ifndef LONG_MAX
-#define LONG_MAX 0x7fffffff
-#endif
-
-
/* Function Prototypes */
void bc_init_numbers(void);
@@ -152,8 +147,6 @@ bc_num bc_multiply(bc_num n1, bc_num n2, size_t scale);
*(result) = mul_ex; \
} while (0)
-bc_num bc_square(bc_num n1, size_t scale);
-
bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, size_t scale);
bool bc_modulo(bc_num num1, bc_num num2, bc_num *resul, size_t scale);
@@ -162,7 +155,15 @@ bool bc_divmod(bc_num num1, bc_num num2, bc_num *quo, bc_num *rem, size_t scale)
bc_num bc_floor_or_ceil(bc_num num, bool is_floor);
-void bc_round(bc_num num, zend_long places, zend_long mode, bc_num *result);
+size_t bc_round(bc_num num, zend_long places, zend_long mode, bc_num *result);
+
+typedef enum {
+ BC_RAISE_STATUS_OK,
+ BC_RAISE_STATUS_LEN_IS_OVERFLOW,
+ BC_RAISE_STATUS_SCALE_IS_OVERFLOW,
+ BC_RAISE_STATUS_FULLLEN_IS_OVERFLOW,
+ BC_RAISE_STATUS_DIVIDE_BY_ZERO,
+} bc_raise_status;
typedef enum {
OK,
@@ -175,7 +176,7 @@ typedef enum {
raise_mod_status bc_raisemod(bc_num base, bc_num exponent, bc_num mod, bc_num *result, size_t scale);
-bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale);
+bc_raise_status bc_raise(bc_num base, long exponent, bc_num *result, size_t scale);
void bc_raise_bc_exponent(bc_num base, bc_num exponent, bc_num *resul, size_t scale);
diff --git a/ext/bcmath/libbcmath/src/compare.c b/ext/bcmath/libbcmath/src/compare.c
index 2c24dab777059..06ef246782089 100644
--- a/ext/bcmath/libbcmath/src/compare.c
+++ b/ext/bcmath/libbcmath/src/compare.c
@@ -41,8 +41,6 @@
bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool use_sign)
{
- char *n1ptr, *n2ptr;
-
/* First, compare signs. */
if (use_sign && n1->n_sign != n2->n_sign) {
/*
@@ -91,8 +89,8 @@ bcmath_compare_result _bc_do_compare(bc_num n1, bc_num n2, size_t scale, bool us
/* If we get here, they have the same number of integer digits.
check the integer part and the equal length part of the fraction. */
size_t count = n1->n_len + MIN (n1_scale, n2_scale);
- n1ptr = n1->n_value;
- n2ptr = n2->n_value;
+ const char *n1ptr = n1->n_value;
+ const char *n2ptr = n2->n_value;
while ((count > 0) && (*n1ptr == *n2ptr)) {
n1ptr++;
diff --git a/ext/bcmath/libbcmath/src/convert.c b/ext/bcmath/libbcmath/src/convert.c
index bf3d9a9a415bf..5438b4c1c44e5 100644
--- a/ext/bcmath/libbcmath/src/convert.c
+++ b/ext/bcmath/libbcmath/src/convert.c
@@ -17,24 +17,22 @@
#include "bcmath.h"
#include "convert.h"
#include "private.h"
-#ifdef __SSE2__
-# include
-#endif
+#include "simd.h"
char *bc_copy_and_toggle_bcd(char *restrict dest, const char *source, const char *source_end)
{
const size_t bulk_shift = SWAR_REPEAT('0');
-#ifdef __SSE2__
- /* SIMD SSE2 bulk shift + copy */
- __m128i shift_vector = _mm_set1_epi8('0');
- while (source + sizeof(__m128i) <= source_end) {
- __m128i bytes = _mm_loadu_si128((const __m128i *) source);
- bytes = _mm_xor_si128(bytes, shift_vector);
- _mm_storeu_si128((__m128i *) dest, bytes);
+#ifdef HAVE_BC_SIMD_128
+ /* SIMD SSE2 or NEON bulk shift + copy */
+ bc_simd_128_t shift_vector = bc_simd_set_8x16('0');
+ while (source + sizeof(bc_simd_128_t) <= source_end) {
+ bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) source);
+ bytes = bc_simd_xor_8x16(bytes, shift_vector);
+ bc_simd_store_8x16((bc_simd_128_t *) dest, bytes);
- source += sizeof(__m128i);
- dest += sizeof(__m128i);
+ source += sizeof(bc_simd_128_t);
+ dest += sizeof(bc_simd_128_t);
}
#endif
diff --git a/ext/bcmath/libbcmath/src/convert.h b/ext/bcmath/libbcmath/src/convert.h
index 6ddd447c8048e..73c38cd130107 100644
--- a/ext/bcmath/libbcmath/src/convert.h
+++ b/ext/bcmath/libbcmath/src/convert.h
@@ -34,11 +34,11 @@ static inline BC_VECTOR bc_partial_convert_to_vector(const char *n, size_t len)
}
BC_VECTOR num = 0;
- BC_VECTOR base = 1;
+ BC_VECTOR digit_base_value = 1;
for (size_t i = 0; i < len; i++) {
- num += *n * base;
- base *= BASE;
+ num += *n * digit_base_value;
+ digit_base_value *= BASE;
n--;
}
@@ -57,4 +57,58 @@ static inline void bc_convert_to_vector(BC_VECTOR *n_vector, const char *nend, s
}
}
+static inline void bc_convert_to_vector_with_zero_pad(BC_VECTOR *n_vector, const char *nend, size_t nlen, size_t zeros)
+{
+ while (zeros >= BC_VECTOR_SIZE) {
+ *n_vector = 0;
+ n_vector++;
+ zeros -= BC_VECTOR_SIZE;
+ }
+
+ if (zeros > 0) {
+ *n_vector = 0;
+ BC_VECTOR digit_base_value = BC_POW_10_LUT[zeros];
+ size_t len_to_write = MIN(BC_VECTOR_SIZE - zeros, nlen);
+ for (size_t i = 0; i < len_to_write; i++) {
+ *n_vector += *nend * digit_base_value;
+ digit_base_value *= BASE;
+ nend--;
+ }
+ n_vector++;
+ nlen -= len_to_write;
+ }
+
+ if (nlen == 0) {
+ return;
+ }
+
+ bc_convert_to_vector(n_vector, nend, nlen);
+}
+
+static inline void bc_convert_vector_to_char(const BC_VECTOR *vector, char *nptr, char *nend, size_t arr_size)
+{
+ size_t i = 0;
+ while (i < arr_size - 1) {
+#if BC_VECTOR_SIZE == 4
+ bc_write_bcd_representation(vector[i], nend - 3);
+ nend -= 4;
+#else
+ bc_write_bcd_representation(vector[i] / 10000, nend - 7);
+ bc_write_bcd_representation(vector[i] % 10000, nend - 3);
+ nend -= 8;
+#endif
+ i++;
+ }
+
+ /*
+ * The last digit may carry over.
+ * Also need to fill it to the end with zeros, so loop until the end of the string.
+ */
+ BC_VECTOR last = vector[i];
+ while (nend >= nptr) {
+ *nend-- = last % BASE;
+ last /= BASE;
+ }
+}
+
#endif
diff --git a/ext/bcmath/libbcmath/src/div.c b/ext/bcmath/libbcmath/src/div.c
index ce9ae1e1dd792..24ec9a64d77fc 100644
--- a/ext/bcmath/libbcmath/src/div.c
+++ b/ext/bcmath/libbcmath/src/div.c
@@ -37,10 +37,6 @@
#include
#include "zend_alloc.h"
-static const BC_VECTOR POW_10_LUT[9] = {
- 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000
-};
-
/*
* This function should be used when the divisor is not split into multiple chunks, i.e. when the size of the array is one.
* This is because the algorithm can be simplified.
@@ -51,7 +47,7 @@ static inline void bc_fast_div(
{
size_t numerator_top_index = numerator_arr_size - 1;
size_t quot_top_index = quot_arr_size - 1;
- for (size_t i = 0; i < quot_arr_size - 1; i++) {
+ for (size_t i = 0; i < quot_top_index; i++) {
if (numerator_vectors[numerator_top_index - i] < divisor_vector) {
quot_vectors[quot_top_index - i] = 0;
/* numerator_vectors[numerator_top_index - i] < divisor_vector, so there will be no overflow. */
@@ -74,7 +70,7 @@ static inline void bc_fast_div(
*/
static inline void bc_standard_div(
BC_VECTOR *numerator_vectors, size_t numerator_arr_size,
- BC_VECTOR *divisor_vectors, size_t divisor_arr_size, size_t divisor_len,
+ const BC_VECTOR *divisor_vectors, size_t divisor_arr_size, size_t divisor_len,
BC_VECTOR *quot_vectors, size_t quot_arr_size
) {
size_t numerator_top_index = numerator_arr_size - 1;
@@ -174,8 +170,8 @@ static inline void bc_standard_div(
divisor_top_digits = BC_VECTOR_SIZE;
}
- size_t high_part_shift = POW_10_LUT[BC_VECTOR_SIZE - divisor_top_digits + 1];
- size_t low_part_shift = POW_10_LUT[divisor_top_digits - 1];
+ size_t high_part_shift = BC_POW_10_LUT[BC_VECTOR_SIZE - divisor_top_digits + 1];
+ size_t low_part_shift = BC_POW_10_LUT[divisor_top_digits - 1];
BC_VECTOR divisor_high_part = divisor_vectors[divisor_top_index] * high_part_shift + divisor_vectors[divisor_top_index - 1] / low_part_shift;
for (size_t i = 0; i < quot_arr_size; i++) {
BC_VECTOR numerator_high_part = numerator_vectors[numerator_top_index - i] * high_part_shift + numerator_vectors[numerator_top_index - i - 1] / low_part_shift;
@@ -255,77 +251,85 @@ static inline void bc_standard_div(
}
static void bc_do_div(
- const char *numerator, size_t numerator_readable_len, size_t numerator_bottom_extension,
- const char *divisor, size_t divisor_len, bc_num *quot, size_t quot_len
+ const char *numerator, size_t numerator_size, size_t numerator_readable_size,
+ const char *divisor, size_t divisor_size,
+ bc_num *quot, size_t quot_size
) {
- size_t divisor_arr_size = (divisor_len + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
- size_t numerator_arr_size = (numerator_readable_len + numerator_bottom_extension + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
+ size_t numerator_arr_size = BC_ARR_SIZE_FROM_LEN(numerator_size);
+ size_t divisor_arr_size = BC_ARR_SIZE_FROM_LEN(divisor_size);
size_t quot_arr_size = numerator_arr_size - divisor_arr_size + 1;
- size_t quot_real_arr_size = MIN(quot_arr_size, (quot_len + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE);
+ size_t quot_real_arr_size = MIN(quot_arr_size, BC_ARR_SIZE_FROM_LEN(quot_size));
- BC_VECTOR *numerator_vectors = safe_emalloc(numerator_arr_size + divisor_arr_size + quot_arr_size, sizeof(BC_VECTOR), 0);
- BC_VECTOR *divisor_vectors = numerator_vectors + numerator_arr_size;
- BC_VECTOR *quot_vectors = divisor_vectors + divisor_arr_size;
+ BC_VECTOR stack_vectors[BC_STACK_VECTOR_SIZE];
+ size_t allocation_arr_size = numerator_arr_size + divisor_arr_size + quot_arr_size;
- /* Fill with zeros and convert as many vector elements as needed */
- size_t numerator_vector_count = 0;
- while (numerator_bottom_extension >= BC_VECTOR_SIZE) {
- numerator_vectors[numerator_vector_count] = 0;
- numerator_bottom_extension -= BC_VECTOR_SIZE;
- numerator_vector_count++;
+ BC_VECTOR *numerator_vectors;
+ if (allocation_arr_size <= BC_STACK_VECTOR_SIZE) {
+ numerator_vectors = stack_vectors;
+ } else {
+ numerator_vectors = safe_emalloc(allocation_arr_size, sizeof(BC_VECTOR), 0);
}
+ BC_VECTOR *divisor_vectors = numerator_vectors + numerator_arr_size;
+ BC_VECTOR *quot_vectors = divisor_vectors + divisor_arr_size;
- size_t numerator_bottom_read_len = BC_VECTOR_SIZE - numerator_bottom_extension;
-
- size_t base;
- size_t numerator_read = 0;
- if (numerator_bottom_read_len < BC_VECTOR_SIZE) {
- numerator_read = MIN(numerator_bottom_read_len, numerator_readable_len);
- base = POW_10_LUT[numerator_bottom_extension];
- numerator_vectors[numerator_vector_count] = 0;
- for (size_t i = 0; i < numerator_read; i++) {
- numerator_vectors[numerator_vector_count] += *numerator * base;
- base *= BASE;
- numerator--;
- }
- numerator_vector_count++;
- }
+ size_t numerator_extension = numerator_size > numerator_readable_size ? numerator_size - numerator_readable_size : 0;
/* Bulk convert numerator and divisor to vectors */
- if (numerator_readable_len > numerator_read) {
- bc_convert_to_vector(numerator_vectors + numerator_vector_count, numerator, numerator_readable_len - numerator_read);
- }
- bc_convert_to_vector(divisor_vectors, divisor, divisor_len);
+ size_t numerator_use_size = numerator_size - numerator_extension;
+ const char *numerator_end = numerator + numerator_use_size - 1;
+ bc_convert_to_vector_with_zero_pad(numerator_vectors, numerator_end, numerator_use_size, numerator_extension);
+
+ const char *divisor_end = divisor + divisor_size - 1;
+ bc_convert_to_vector(divisor_vectors, divisor_end, divisor_size);
/* Do the division */
if (divisor_arr_size == 1) {
bc_fast_div(numerator_vectors, numerator_arr_size, divisor_vectors[0], quot_vectors, quot_arr_size);
} else {
- bc_standard_div(numerator_vectors, numerator_arr_size, divisor_vectors, divisor_arr_size, divisor_len, quot_vectors, quot_arr_size);
+ bc_standard_div(numerator_vectors, numerator_arr_size, divisor_vectors, divisor_arr_size, divisor_size, quot_vectors, quot_arr_size);
}
/* Convert to bc_num */
char *qptr = (*quot)->n_value;
- char *qend = qptr + quot_len - 1;
-
- size_t i;
- for (i = 0; i < quot_real_arr_size - 1; i++) {
-#if BC_VECTOR_SIZE == 4
- bc_write_bcd_representation(quot_vectors[i], qend - 3);
- qend -= 4;
-#else
- bc_write_bcd_representation(quot_vectors[i] / 10000, qend - 7);
- bc_write_bcd_representation(quot_vectors[i] % 10000, qend - 3);
- qend -= 8;
-#endif
+ char *qend = qptr + (*quot)->n_len + (*quot)->n_scale - 1;
+
+ bc_convert_vector_to_char(quot_vectors, qptr, qend, quot_real_arr_size);
+
+ if (allocation_arr_size > BC_STACK_VECTOR_SIZE) {
+ efree(numerator_vectors);
}
+}
+
+static inline void bc_divide_by_one(bc_num numerator, bc_num *quot, size_t quot_scale)
+{
+ quot_scale = MIN(numerator->n_scale, quot_scale);
+ *quot = bc_new_num_nonzeroed(numerator->n_len, quot_scale);
+ char *qptr = (*quot)->n_value;
+ memcpy(qptr, numerator->n_value, numerator->n_len + quot_scale);
+}
- while (qend >= qptr) {
- *qend-- = quot_vectors[i] % BASE;
- quot_vectors[i] /= BASE;
+static inline void bc_divide_by_pow_10(
+ const char *numeratorptr, size_t numerator_readable_size, bc_num *quot, size_t quot_size, size_t quot_scale)
+{
+ char *qptr = (*quot)->n_value;
+ for (size_t i = quot_size; i <= quot_scale; i++) {
+ *qptr++ = 0;
}
- efree(numerator_vectors);
+ size_t numerator_use_size = quot_size > numerator_readable_size ? numerator_readable_size : quot_size;
+ memcpy(qptr, numeratorptr, numerator_use_size);
+ qptr += numerator_use_size;
+
+ if (numerator_use_size < (*quot)->n_len) {
+ /* e.g. 12.3 / 0.01 <=> 1230 */
+ for (size_t i = numerator_use_size; i < (*quot)->n_len; i++) {
+ *qptr++ = 0;
+ }
+ (*quot)->n_scale = 0;
+ } else {
+ char *qend = (*quot)->n_value + (*quot)->n_len + (*quot)->n_scale;
+ (*quot)->n_scale -= qend - qptr;
+ }
}
bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale)
@@ -336,166 +340,92 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale)
}
bc_free_num(quot);
+ size_t quot_scale = scale;
/* If numerator is zero, the quotient is always zero. */
if (bc_is_zero(numerator)) {
- *quot = bc_copy_num(BCG(_zero_));
- return true;
+ goto quot_zero;
}
/* If divisor is 1 / -1, the quotient's n_value is equal to numerator's n_value. */
if (_bc_do_compare(divisor, BCG(_one_), divisor->n_scale, false) == BCMATH_EQUAL) {
- size_t quot_scale = MIN(numerator->n_scale, scale);
- *quot = bc_new_num_nonzeroed(numerator->n_len, quot_scale);
- char *qptr = (*quot)->n_value;
- memcpy(qptr, numerator->n_value, numerator->n_len + quot_scale);
+ bc_divide_by_one(numerator, quot, quot_scale);
(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
- _bc_rm_leading_zeros(*quot);
return true;
}
- char *numeratorptr = numerator->n_value;
- char *numeratorend = numeratorptr + numerator->n_len + numerator->n_scale - 1;
- size_t numerator_len = numerator->n_len;
- size_t numerator_scale = numerator->n_scale;
-
- char *divisorptr = divisor->n_value;
- char *divisorend = divisorptr + divisor->n_len + divisor->n_scale - 1;
- size_t divisor_len = divisor->n_len;
- size_t divisor_scale = divisor->n_scale;
- size_t divisor_int_right_zeros = 0;
-
- /* remove divisor trailing zeros */
- while (*divisorend == 0 && divisor_scale > 0) {
- divisorend--;
- divisor_scale--;
- }
- while (*divisorend == 0) {
- divisorend--;
- divisor_int_right_zeros++;
- }
+ const char *numeratorptr = numerator->n_value;
+ size_t numerator_size = numerator->n_len + quot_scale + divisor->n_scale;
- if (*numeratorptr == 0 && numerator_len == 1) {
- numeratorptr++;
- numerator_len = 0;
- }
-
- size_t numerator_top_extension = 0;
- size_t numerator_bottom_extension = 0;
- if (divisor_scale > 0) {
- /*
- * e.g. divisor_scale = 4
- * divisor = .0002, to be 2 or divisor = 200.001, to be 200001
- * numerator = .03, to be 300 or numerator = .000003, to be .03
- * numerator may become longer than the original data length due to the addition of
- * trailing zeros in the integer part.
- */
- numerator_len += divisor_scale;
- numerator_bottom_extension = numerator_scale < divisor_scale ? divisor_scale - numerator_scale : 0;
- numerator_scale = numerator_scale > divisor_scale ? numerator_scale - divisor_scale : 0;
- divisor_len += divisor_scale;
- divisor_scale = 0;
- } else if (divisor_int_right_zeros > 0) {
- /*
- * e.g. divisor_int_right_zeros = 4
- * divisor = 2000, to be 2
- * numerator = 30, to be .03 or numerator = 30000, to be 30
- * Also, numerator may become longer than the original data length due to the addition of
- * leading zeros in the fractional part.
- */
- numerator_top_extension = numerator_len < divisor_int_right_zeros ? divisor_int_right_zeros - numerator_len : 0;
- numerator_len = numerator_len > divisor_int_right_zeros ? numerator_len - divisor_int_right_zeros : 0;
- numerator_scale += divisor_int_right_zeros;
- divisor_len -= divisor_int_right_zeros;
- divisor_scale = 0;
- }
+ const char *divisorptr = divisor->n_value;
+ size_t divisor_size = divisor->n_len + divisor->n_scale;
- /* remove numerator leading zeros */
- while (*numeratorptr == 0 && numerator_len > 0) {
+ /* check and remove numerator leading zeros */
+ size_t numerator_leading_zeros = 0;
+ while (*numeratorptr == 0) {
numeratorptr++;
- numerator_len--;
+ numerator_leading_zeros++;
+ if (numerator_leading_zeros == numerator_size) {
+ goto quot_zero;
+ }
}
- /* remove divisor leading zeros */
+ numerator_size -= numerator_leading_zeros;
+
+ /* check and remove divisor leading zeros */
while (*divisorptr == 0) {
divisorptr++;
- divisor_len--;
+ divisor_size--;
}
- /* Considering the scale specification, the quotient is always 0 if this condition is met */
- if (divisor_len > numerator_len + scale) {
- *quot = bc_copy_num(BCG(_zero_));
- return true;
+ if (divisor_size > numerator_size) {
+ goto quot_zero;
}
- /* Length of numerator data that can be read */
- size_t numerator_readable_len = numeratorend - numeratorptr + 1;
-
- /* set scale to numerator */
- if (numerator_scale > scale) {
- size_t scale_diff = numerator_scale - scale;
- if (numerator_bottom_extension > scale_diff) {
- numerator_bottom_extension -= scale_diff;
- } else {
- numerator_bottom_extension = 0;
- if (EXPECTED(numerator_readable_len > scale_diff)) {
- numerator_readable_len -= scale_diff;
- numeratorend -= scale_diff;
- } else {
- numerator_readable_len = 0;
- numeratorend = numeratorptr;
- }
+ /* check and remove divisor trailing zeros. The divisor is not 0, so leave only one digit */
+ size_t divisor_trailing_zeros = 0;
+ for (size_t i = divisor_size - 1; i > 0; i--) {
+ if (divisorptr[i] != 0) {
+ break;
}
- numerator_top_extension = MIN(numerator_top_extension, scale);
- } else {
- numerator_bottom_extension += scale - numerator_scale;
+ divisor_trailing_zeros++;
}
- numerator_scale = scale;
+ divisor_size -= divisor_trailing_zeros;
+ numerator_size -= divisor_trailing_zeros;
- if (divisor_len > numerator_readable_len + numerator_bottom_extension) {
- *quot = bc_copy_num(BCG(_zero_));
- return true;
+ size_t quot_size = numerator_size - divisor_size + 1; /* numerator_size >= divisor_size */
+ if (quot_size > quot_scale) {
+ *quot = bc_new_num_nonzeroed(quot_size - quot_scale, quot_scale);
+ } else {
+ *quot = bc_new_num_nonzeroed(1, quot_scale); /* 1 is for 0 */
}
- /* If divisor is 1 here, return the result of adjusting the decimal point position of numerator. */
- if (divisor_len == 1 && *divisorptr == 1) {
- if (numerator_len == 0) {
- numerator_len = 1;
- numerator_top_extension++;
- }
- size_t quot_scale = numerator_scale > numerator_bottom_extension ? numerator_scale - numerator_bottom_extension : 0;
- numerator_bottom_extension = numerator_scale < numerator_bottom_extension ? numerator_bottom_extension - numerator_scale : 0;
+ /* Size that can be read from numeratorptr */
+ size_t numerator_readable_size = numerator->n_len + numerator->n_scale - numerator_leading_zeros;
- *quot = bc_new_num_nonzeroed(numerator_len, quot_scale);
- char *qptr = (*quot)->n_value;
- for (size_t i = 0; i < numerator_top_extension; i++) {
- *qptr++ = 0;
- }
- memcpy(qptr, numeratorptr, numerator_readable_len);
- qptr += numerator_readable_len;
- for (size_t i = 0; i < numerator_bottom_extension; i++) {
- *qptr++ = 0;
- }
+ /* If divisor is 1 here, return the result of adjusting the decimal point position of numerator. */
+ if (divisor_size == 1 && *divisorptr == 1) {
+ bc_divide_by_pow_10(numeratorptr, numerator_readable_size, quot, quot_size, quot_scale);
(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
return true;
}
- size_t quot_full_len;
- if (divisor_len > numerator_len) {
- *quot = bc_new_num_nonzeroed(1, scale);
- quot_full_len = 1 + scale;
- } else {
- *quot = bc_new_num_nonzeroed(numerator_len - divisor_len + 1, scale);
- quot_full_len = numerator_len - divisor_len + 1 + scale;
- }
-
/* do divide */
- bc_do_div(numeratorend, numerator_readable_len, numerator_bottom_extension, divisorend, divisor_len, quot, quot_full_len);
+ bc_do_div(
+ numeratorptr, numerator_size, numerator_readable_size,
+ divisorptr, divisor_size,
+ quot, quot_size
+ );
+
_bc_rm_leading_zeros(*quot);
if (bc_is_zero(*quot)) {
(*quot)->n_sign = PLUS;
+ (*quot)->n_scale = 0;
} else {
(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
}
+ return true;
+quot_zero:
+ *quot = bc_copy_num(BCG(_zero_));
return true;
}
diff --git a/ext/bcmath/libbcmath/src/divmod.c b/ext/bcmath/libbcmath/src/divmod.c
index 294a281b2e687..477ec30e916ea 100644
--- a/ext/bcmath/libbcmath/src/divmod.c
+++ b/ext/bcmath/libbcmath/src/divmod.c
@@ -74,6 +74,7 @@ bool bc_divmod(bc_num num1, bc_num num2, bc_num *quot, bc_num *rem, size_t scale
(*rem)->n_scale = MIN(scale, (*rem)->n_scale);
if (bc_is_zero(*rem)) {
(*rem)->n_sign = PLUS;
+ (*rem)->n_scale = 0;
}
return true;
diff --git a/ext/bcmath/libbcmath/src/doaddsub.c b/ext/bcmath/libbcmath/src/doaddsub.c
index feb50120c70c3..f516f2bda72df 100644
--- a/ext/bcmath/libbcmath/src/doaddsub.c
+++ b/ext/bcmath/libbcmath/src/doaddsub.c
@@ -46,7 +46,7 @@ bc_num _bc_do_add(bc_num n1, bc_num n2)
size_t min_len = MIN (n1->n_len, n2->n_len);
size_t min_scale = MIN(n1->n_scale, n2->n_scale);
size_t min_bytes = min_len + min_scale;
- char *n1ptr, *n2ptr, *sumptr;
+ char *sumptr;
bool carry = 0;
size_t count;
@@ -54,8 +54,8 @@ bc_num _bc_do_add(bc_num n1, bc_num n2)
sum = bc_new_num_nonzeroed(sum_len, sum_scale);
/* Start with the fraction part. Initialize the pointers. */
- n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
- n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
+ const char *n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
+ const char *n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
sumptr = (char *) (sum->n_value + sum_scale + sum_len - 1);
/* Add the fraction part. First copy the longer fraction.*/
@@ -182,14 +182,14 @@ bc_num _bc_do_sub(bc_num n1, bc_num n2)
size_t borrow = 0;
size_t count;
int val;
- char *n1ptr, *n2ptr, *diffptr;
+ char *diffptr;
/* Allocate temporary storage. */
diff = bc_new_num_nonzeroed(diff_len, diff_scale);
/* Initialize the subtract. */
- n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
- n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
+ const char *n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
+ const char *n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
diffptr = (char *) (diff->n_value + diff_len + diff_scale - 1);
/* Take care of the longer scaled number. */
diff --git a/ext/bcmath/libbcmath/src/private.h b/ext/bcmath/libbcmath/src/private.h
index 5c0087901a4a2..de9045a16c7e5 100644
--- a/ext/bcmath/libbcmath/src/private.h
+++ b/ext/bcmath/libbcmath/src/private.h
@@ -35,6 +35,9 @@
#include
#include "zend_portability.h"
+#ifndef _BCMATH_PRIV_H_
+#define _BCMATH_PRIV_H_
+
/* This will be 0x01010101 for 32-bit and 0x0101010101010101 for 64-bit */
#define SWAR_ONES (~((size_t) 0) / 0xFF)
/* This repeats a byte `x` into an entire 32/64-bit word.
@@ -61,15 +64,29 @@
# define BC_LITTLE_ENDIAN 1
#endif
+/* 64-bytes for 64-bit */
+#define BC_STACK_VECTOR_SIZE 8
+
+#define BC_ARR_SIZE_FROM_LEN(len) (((len) + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE)
+
/*
* Adding more than this many times may cause uint32_t/uint64_t to overflow.
* Typically this is 1844 for 64bit and 42 for 32bit.
*/
#define BC_VECTOR_NO_OVERFLOW_ADD_COUNT (~((BC_VECTOR) 0) / (BC_VECTOR_BOUNDARY_NUM * BC_VECTOR_BOUNDARY_NUM))
+static const BC_VECTOR BC_POW_10_LUT[9] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000
+};
+
/* routines */
bcmath_compare_result _bc_do_compare (bc_num n1, bc_num n2, size_t scale, bool use_sign);
bc_num _bc_do_add (bc_num n1, bc_num n2);
bc_num _bc_do_sub (bc_num n1, bc_num n2);
+void bc_multiply_vector(
+ const BC_VECTOR *n1_vector, size_t n1_arr_size, const BC_VECTOR *n2_vector, size_t n2_arr_size,
+ BC_VECTOR *prod_vector, size_t prod_arr_size);
void _bc_rm_leading_zeros (bc_num num);
+
+#endif
diff --git a/ext/bcmath/libbcmath/src/raise.c b/ext/bcmath/libbcmath/src/raise.c
index 1e283864694b6..5df8130c24219 100644
--- a/ext/bcmath/libbcmath/src/raise.c
+++ b/ext/bcmath/libbcmath/src/raise.c
@@ -30,31 +30,155 @@
*************************************************************************/
#include "bcmath.h"
+#include "convert.h"
+#include "private.h"
#include
#include
#include
-void bc_square_ex(bc_num n1, bc_num *result, size_t scale_min) {
- bc_num square_ex = bc_square(n1, scale_min);
- bc_free_num(result);
- *(result) = square_ex;
+static inline size_t bc_multiply_vector_ex(
+ BC_VECTOR **n1_vector, size_t n1_arr_size, BC_VECTOR *n2_vector, size_t n2_arr_size, BC_VECTOR **result_vector)
+{
+ size_t result_arr_size = n1_arr_size + n2_arr_size;
+ bc_multiply_vector(*n1_vector, n1_arr_size, n2_vector, n2_arr_size, *result_vector, result_arr_size);
+
+ /* Eliminate extra zeros because they increase the number of calculations. */
+ while ((*result_vector)[result_arr_size - 1] == 0) {
+ result_arr_size--;
+ }
+
+ /* Swap n1_vector and result_vector. */
+ BC_VECTOR *tmp = *n1_vector;
+ *n1_vector = *result_vector;
+ *result_vector = tmp;
+
+ return result_arr_size;
+}
+
+static inline size_t bc_square_vector_ex(BC_VECTOR **base_vector, size_t base_arr_size, BC_VECTOR **result_vector)
+{
+ return bc_multiply_vector_ex(base_vector, base_arr_size, *base_vector, base_arr_size, result_vector);
+}
+
+/* Use "exponentiation by squaring". This is the fast path when the results are small. */
+static inline bc_num bc_fast_raise(
+ const char *base_end, long exponent, size_t base_len, size_t power_len, size_t power_scale, size_t power_full_len)
+{
+ BC_VECTOR base_vector = 0;
+
+ /* Convert to BC_VECTOR[] */
+ bc_convert_to_vector(&base_vector, base_end, base_len);
+
+ while ((exponent & 1) == 0) {
+ base_vector *= base_vector;
+ exponent >>= 1;
+ }
+
+ /* copy base to power */
+ BC_VECTOR power_vector = base_vector;
+ exponent >>= 1;
+
+ while (exponent > 0) {
+ base_vector *= base_vector;
+ if ((exponent & 1) == 1) {
+ power_vector *= base_vector;
+ }
+ exponent >>= 1;
+ }
+
+ bc_num power = bc_new_num_nonzeroed(power_len, power_scale);
+ char *pptr = power->n_value;
+ char *pend = pptr + power_full_len - 1;
+
+ while (pend >= pptr) {
+ *pend-- = power_vector % BASE;
+ power_vector /= BASE;
+ }
+ return power;
+}
+
+/* Use "exponentiation by squaring". This is the standard path. */
+static bc_num bc_standard_raise(
+ const char *base_ptr, const char *base_end, long exponent, size_t base_len, size_t power_scale)
+{
+ /* Remove the leading zeros as they will be filled in later. */
+ while (*base_ptr == 0) {
+ base_ptr++;
+ base_len--;
+ }
+
+ size_t base_arr_size = BC_ARR_SIZE_FROM_LEN(base_len);
+ /* Since it is guaranteed that base_len * exponent does not overflow, there is no possibility of overflow here. */
+ size_t max_power_arr_size = base_arr_size * exponent;
+
+ /* The allocated memory area is reused on a rotational basis, so the same size is required. */
+ BC_VECTOR *buf = safe_emalloc(max_power_arr_size, sizeof(BC_VECTOR) * 3, 0);
+ BC_VECTOR *base_vector = buf;
+ BC_VECTOR *power_vector = base_vector + max_power_arr_size;
+ BC_VECTOR *tmp_result_vector = power_vector + max_power_arr_size;
+
+ /* Convert to BC_VECTOR[] */
+ bc_convert_to_vector(base_vector, base_end, base_len);
+
+ while ((exponent & 1) == 0) {
+ base_arr_size = bc_square_vector_ex(&base_vector, base_arr_size, &tmp_result_vector);
+ exponent >>= 1;
+ }
+
+ /* copy base to power */
+ size_t power_arr_size = base_arr_size;
+ for (size_t i = 0; i < base_arr_size; i++) {
+ power_vector[i] = base_vector[i];
+ }
+ exponent >>= 1;
+
+ while (exponent > 0) {
+ base_arr_size = bc_square_vector_ex(&base_vector, base_arr_size, &tmp_result_vector);
+ if ((exponent & 1) == 1) {
+ power_arr_size = bc_multiply_vector_ex(&power_vector, power_arr_size, base_vector, base_arr_size, &tmp_result_vector);
+ }
+ exponent >>= 1;
+ }
+
+ /* Convert to bc_num */
+ size_t power_leading_zeros = 0;
+ size_t power_len;
+ size_t power_full_len = power_arr_size * BC_VECTOR_SIZE;
+ if (power_full_len > power_scale) {
+ power_len = power_full_len - power_scale;
+ } else {
+ power_len = 1;
+ power_leading_zeros = power_scale - power_full_len + 1;
+ power_full_len = power_scale + 1;
+ }
+ bc_num power = bc_new_num_nonzeroed(power_len, power_scale);
+
+ char *pptr = power->n_value;
+ char *pend = pptr + power_full_len - 1;
+
+ /* Pad with leading zeros if necessary. */
+ memset(pptr, 0, power_leading_zeros);
+ pptr += power_leading_zeros;
+
+ bc_convert_vector_to_char(power_vector, pptr, pend, power_arr_size);
+
+ efree(buf);
+
+ return power;
}
/* Raise "base" to the "exponent" power. The result is placed in RESULT.
Maximum exponent is LONG_MAX. If a "exponent" is not an integer,
only the integer part is used. */
-bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
- bc_num temp, power;
+bc_raise_status bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
size_t rscale;
- size_t pwrscale;
- size_t calcscale;
bool is_neg;
/* Special case if exponent is a zero. */
if (exponent == 0) {
bc_free_num (result);
*result = bc_copy_num(BCG(_one_));
- return true;
+ return BC_RAISE_STATUS_OK;
}
/* Other initializations. */
@@ -67,44 +191,66 @@ bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
rscale = MIN (base->n_scale * exponent, MAX(scale, base->n_scale));
}
- /* Set initial value of temp. */
- power = bc_copy_num(base);
- pwrscale = base->n_scale;
- while ((exponent & 1) == 0) {
- pwrscale = 2 * pwrscale;
- bc_square_ex(power, &power, pwrscale);
- exponent = exponent >> 1;
+ if (bc_is_zero(base)) {
+ /* If the exponent is negative, it divides by 0 */
+ return is_neg ? BC_RAISE_STATUS_DIVIDE_BY_ZERO : BC_RAISE_STATUS_OK;
}
- temp = bc_copy_num(power);
- calcscale = pwrscale;
- exponent = exponent >> 1;
- /* Do the calculation. */
- while (exponent > 0) {
- pwrscale = 2 * pwrscale;
- bc_square_ex(power, &power, pwrscale);
- if ((exponent & 1) == 1) {
- calcscale = pwrscale + calcscale;
- bc_multiply_ex(temp, power, &temp, calcscale);
- }
- exponent = exponent >> 1;
+ /* check overflow */
+ if (UNEXPECTED(base->n_len > SIZE_MAX / exponent)) {
+ return BC_RAISE_STATUS_LEN_IS_OVERFLOW;
+ }
+ if (UNEXPECTED(base->n_scale > SIZE_MAX / exponent)) {
+ return BC_RAISE_STATUS_SCALE_IS_OVERFLOW;
+ }
+
+ size_t base_len = base->n_len + base->n_scale;
+ size_t power_len = base->n_len * exponent;
+ size_t power_scale = base->n_scale * exponent;
+
+ /* check overflow */
+ if (UNEXPECTED(power_len > SIZE_MAX - power_scale)) {
+ return BC_RAISE_STATUS_FULLLEN_IS_OVERFLOW;
+ }
+ size_t power_full_len = power_len + power_scale;
+
+ sign power_sign;
+ if (base->n_sign == MINUS && (exponent & 1) == 1) {
+ power_sign = MINUS;
+ } else {
+ power_sign = PLUS;
+ }
+
+ const char *base_end = base->n_value + base_len - 1;
+
+ bc_num power;
+ if (base_len <= BC_VECTOR_SIZE && power_full_len <= BC_VECTOR_SIZE * 2) {
+ power = bc_fast_raise(base_end, exponent, base_len, power_len, power_scale, power_full_len);
+ } else {
+ power = bc_standard_raise(base->n_value, base_end, exponent, base_len, power_scale);
+ }
+
+ _bc_rm_leading_zeros(power);
+ if (bc_is_zero(power)) {
+ power->n_sign = PLUS;
+ power->n_scale = 0;
+ } else {
+ power->n_sign = power_sign;
}
/* Assign the value. */
if (is_neg) {
- if (bc_divide(BCG(_one_), temp, result, rscale) == false) {
- bc_free_num (&temp);
+ if (bc_divide(BCG(_one_), power, result, rscale) == false) {
bc_free_num (&power);
- return false;
+ return BC_RAISE_STATUS_DIVIDE_BY_ZERO;
}
- bc_free_num (&temp);
+ bc_free_num (&power);
} else {
bc_free_num (result);
- *result = temp;
+ *result = power;
(*result)->n_scale = MIN(scale, (*result)->n_scale);
}
- bc_free_num (&power);
- return true;
+ return BC_RAISE_STATUS_OK;
}
/* This is used internally by BCMath */
diff --git a/ext/bcmath/libbcmath/src/recmul.c b/ext/bcmath/libbcmath/src/recmul.c
index b92a1045ac3b5..5e69fd7eb0336 100644
--- a/ext/bcmath/libbcmath/src/recmul.c
+++ b/ext/bcmath/libbcmath/src/recmul.c
@@ -72,62 +72,37 @@ static inline void bc_fast_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len,
}
}
-/*
- * Equivalent of bc_fast_mul for small numbers to perform computations
- * without using array.
- */
-static inline void bc_fast_square(bc_num n1, size_t n1len, bc_num *prod)
+static inline void bc_standard_vector_mul(
+ const BC_VECTOR *n1_vector, size_t n1_arr_size, const BC_VECTOR *n2_vector, size_t n2_arr_size,
+ BC_VECTOR *prod_vector, size_t prod_arr_size)
{
- const char *n1end = n1->n_value + n1len - 1;
-
- BC_VECTOR n1_vector = bc_partial_convert_to_vector(n1end, n1len);
- BC_VECTOR prod_vector = n1_vector * n1_vector;
-
- size_t prodlen = n1len + n1len;
- *prod = bc_new_num_nonzeroed(prodlen, 0);
- char *pptr = (*prod)->n_value;
- char *pend = pptr + prodlen - 1;
+ for (size_t i = 0; i < prod_arr_size; i++) {
+ prod_vector[i] = 0;
+ }
- while (pend >= pptr) {
- *pend-- = prod_vector % BASE;
- prod_vector /= BASE;
+ /* Multiplication and addition */
+ size_t count = 0;
+ for (size_t i = 0; i < n1_arr_size; i++) {
+ /*
+ * This calculation adds the result multiple times to the array entries.
+ * When multiplying large numbers of digits, there is a possibility of
+ * overflow, so digit adjustment is performed beforehand.
+ */
+ if (UNEXPECTED(count >= BC_VECTOR_NO_OVERFLOW_ADD_COUNT)) {
+ bc_mul_carry_calc(prod_vector, prod_arr_size);
+ count = 0;
+ }
+ count++;
+ for (size_t j = 0; j < n2_arr_size; j++) {
+ prod_vector[i + j] += n1_vector[i] * n2_vector[j];
+ }
}
-}
-/* Common part of functions bc_standard_mul and bc_standard_square
- * that takes a vector and converts it to a bc_num */
-static inline void bc_mul_finish_from_vector(BC_VECTOR *prod_vector, size_t prod_arr_size, size_t prodlen, bc_num *prod) {
/*
* Move a value exceeding 4/8 digits by carrying to the next digit.
* However, the last digit does nothing.
*/
bc_mul_carry_calc(prod_vector, prod_arr_size);
-
- /* Convert to bc_num */
- *prod = bc_new_num_nonzeroed(prodlen, 0);
- char *pptr = (*prod)->n_value;
- char *pend = pptr + prodlen - 1;
- size_t i = 0;
- while (i < prod_arr_size - 1) {
-#if BC_VECTOR_SIZE == 4
- bc_write_bcd_representation(prod_vector[i], pend - 3);
- pend -= 4;
-#else
- bc_write_bcd_representation(prod_vector[i] / 10000, pend - 7);
- bc_write_bcd_representation(prod_vector[i] % 10000, pend - 3);
- pend -= 8;
-#endif
- i++;
- }
-
- /*
- * The last digit may carry over.
- * Also need to fill it to the end with zeros, so loop until the end of the string.
- */
- while (pend >= pptr) {
- *pend-- = prod_vector[i] % BASE;
- prod_vector[i] /= BASE;
- }
}
/*
@@ -140,106 +115,49 @@ static inline void bc_mul_finish_from_vector(BC_VECTOR *prod_vector, size_t prod
*/
static void bc_standard_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len, bc_num *prod)
{
- size_t i;
const char *n1end = n1->n_value + n1len - 1;
const char *n2end = n2->n_value + n2len - 1;
size_t prodlen = n1len + n2len;
- size_t n1_arr_size = (n1len + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
- size_t n2_arr_size = (n2len + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
- size_t prod_arr_size = (prodlen + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
-
- /*
- * let's say that N is the max of n1len and n2len (and a multiple of BC_VECTOR_SIZE for simplicity),
- * then this sum is <= N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE - 1
- * which is equal to N - 1 if BC_VECTOR_SIZE is 4, and N/2 - 1 if BC_VECTOR_SIZE is 8.
- */
- BC_VECTOR *buf = safe_emalloc(n1_arr_size + n2_arr_size + prod_arr_size, sizeof(BC_VECTOR), 0);
-
- BC_VECTOR *n1_vector = buf;
- BC_VECTOR *n2_vector = buf + n1_arr_size;
- BC_VECTOR *prod_vector = n2_vector + n2_arr_size;
-
- for (i = 0; i < prod_arr_size; i++) {
- prod_vector[i] = 0;
- }
+ size_t n1_arr_size = BC_ARR_SIZE_FROM_LEN(n1len);
+ size_t n2_arr_size = BC_ARR_SIZE_FROM_LEN(n2len);
+ size_t prod_arr_size = BC_ARR_SIZE_FROM_LEN(prodlen);
- /* Convert to BC_VECTOR[] */
- bc_convert_to_vector(n1_vector, n1end, n1len);
- bc_convert_to_vector(n2_vector, n2end, n2len);
+ BC_VECTOR stack_vectors[BC_STACK_VECTOR_SIZE];
+ size_t allocation_arr_size = n1_arr_size + n2_arr_size + prod_arr_size;
- /* Multiplication and addition */
- size_t count = 0;
- for (i = 0; i < n1_arr_size; i++) {
+ BC_VECTOR *n1_vector;
+ if (allocation_arr_size <= BC_STACK_VECTOR_SIZE) {
+ n1_vector = stack_vectors;
+ } else {
/*
- * This calculation adds the result multiple times to the array entries.
- * When multiplying large numbers of digits, there is a possibility of
- * overflow, so digit adjustment is performed beforehand.
+ * let's say that N is the max of n1len and n2len (and a multiple of BC_VECTOR_SIZE for simplicity),
+ * then this sum is <= N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE + N/BC_VECTOR_SIZE - 1
+ * which is equal to N - 1 if BC_VECTOR_SIZE is 4, and N/2 - 1 if BC_VECTOR_SIZE is 8.
*/
- if (UNEXPECTED(count >= BC_VECTOR_NO_OVERFLOW_ADD_COUNT)) {
- bc_mul_carry_calc(prod_vector, prod_arr_size);
- count = 0;
- }
- count++;
- for (size_t j = 0; j < n2_arr_size; j++) {
- prod_vector[i + j] += n1_vector[i] * n2_vector[j];
- }
- }
-
- bc_mul_finish_from_vector(prod_vector, prod_arr_size, prodlen, prod);
-
- efree(buf);
-}
-
-/** This is bc_standard_mul implementation for square */
-static void bc_standard_square(bc_num n1, size_t n1len, bc_num *prod)
-{
- size_t i;
- const char *n1end = n1->n_value + n1len - 1;
- size_t prodlen = n1len + n1len;
-
- size_t n1_arr_size = (n1len + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
- size_t prod_arr_size = (prodlen + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE;
-
- BC_VECTOR *buf = safe_emalloc(n1_arr_size + n1_arr_size + prod_arr_size, sizeof(BC_VECTOR), 0);
-
- BC_VECTOR *n1_vector = buf;
- BC_VECTOR *prod_vector = n1_vector + n1_arr_size + n1_arr_size;
-
- for (i = 0; i < prod_arr_size; i++) {
- prod_vector[i] = 0;
+ n1_vector = safe_emalloc(allocation_arr_size, sizeof(BC_VECTOR), 0);
}
+ BC_VECTOR *n2_vector = n1_vector + n1_arr_size;
+ BC_VECTOR *prod_vector = n2_vector + n2_arr_size;
/* Convert to BC_VECTOR[] */
bc_convert_to_vector(n1_vector, n1end, n1len);
+ bc_convert_to_vector(n2_vector, n2end, n2len);
- /* Multiplication and addition */
- size_t count = 0;
- for (i = 0; i < n1_arr_size; i++) {
- /*
- * This calculation adds the result multiple times to the array entries.
- * When multiplying large numbers of digits, there is a possibility of
- * overflow, so digit adjustment is performed beforehand.
- */
- if (UNEXPECTED(count >= BC_VECTOR_NO_OVERFLOW_ADD_COUNT)) {
- bc_mul_carry_calc(prod_vector, prod_arr_size);
- count = 0;
- }
- count++;
- for (size_t j = 0; j < n1_arr_size; j++) {
- prod_vector[i + j] += n1_vector[i] * n1_vector[j];
- }
- }
+ /* Do multiply */
+ bc_standard_vector_mul(n1_vector, n1_arr_size, n2_vector, n2_arr_size, prod_vector, prod_arr_size);
- bc_mul_finish_from_vector(prod_vector, prod_arr_size, prodlen, prod);
+ /* Convert to bc_num */
+ *prod = bc_new_num_nonzeroed(prodlen, 0);
+ char *pptr = (*prod)->n_value;
+ char *pend = pptr + prodlen - 1;
+ bc_convert_vector_to_char(prod_vector, pptr, pend, prod_arr_size);
- efree(buf);
+ if (allocation_arr_size > BC_STACK_VECTOR_SIZE) {
+ efree(n1_vector);
+ }
}
-/* The multiply routine. N2 times N1 is put int PROD with the scale of
- the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)).
- */
-
bc_num bc_multiply(bc_num n1, bc_num n2, size_t scale)
{
bc_num prod;
@@ -264,28 +182,22 @@ bc_num bc_multiply(bc_num n1, bc_num n2, size_t scale)
_bc_rm_leading_zeros(prod);
if (bc_is_zero(prod)) {
prod->n_sign = PLUS;
+ prod->n_scale = 0;
}
return prod;
}
-bc_num bc_square(bc_num n1, size_t scale)
+void bc_multiply_vector(
+ const BC_VECTOR *n1_vector, size_t n1_arr_size, const BC_VECTOR *n2_vector, size_t n2_arr_size,
+ BC_VECTOR *prod_vector, size_t prod_arr_size)
{
- bc_num prod;
-
- size_t len1 = n1->n_len + n1->n_scale;
- size_t full_scale = n1->n_scale + n1->n_scale;
- size_t prod_scale = MIN(full_scale, MAX(scale, n1->n_scale));
-
- if (len1 <= BC_VECTOR_SIZE) {
- bc_fast_square(n1, len1, &prod);
+ if (n1_arr_size == 1 && n2_arr_size == 1) {
+ prod_vector[0] = *n1_vector * *n2_vector;
+ if (prod_arr_size == 2) {
+ prod_vector[1] = prod_vector[0] / BC_VECTOR_BOUNDARY_NUM;
+ prod_vector[0] %= BC_VECTOR_BOUNDARY_NUM;
+ }
} else {
- bc_standard_square(n1, len1, &prod);
+ bc_standard_vector_mul(n1_vector, n1_arr_size, n2_vector, n2_arr_size, prod_vector, prod_arr_size);
}
-
- prod->n_sign = PLUS;
- prod->n_len -= full_scale;
- prod->n_scale = prod_scale;
- _bc_rm_leading_zeros(prod);
-
- return prod;
}
diff --git a/ext/bcmath/libbcmath/src/round.c b/ext/bcmath/libbcmath/src/round.c
index ac3c7c41a315e..44df6036cbe3b 100644
--- a/ext/bcmath/libbcmath/src/round.c
+++ b/ext/bcmath/libbcmath/src/round.c
@@ -18,7 +18,8 @@
#include "private.h"
#include
-void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
+/* Returns the scale of the value after rounding. */
+size_t bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
{
/* clear result */
bc_free_num(result);
@@ -43,19 +44,19 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
case PHP_ROUND_HALF_ODD:
case PHP_ROUND_TOWARD_ZERO:
*result = bc_copy_num(BCG(_zero_));
- return;
+ return 0;
case PHP_ROUND_CEILING:
if (num->n_sign == MINUS) {
*result = bc_copy_num(BCG(_zero_));
- return;
+ return 0;
}
break;
case PHP_ROUND_FLOOR:
if (num->n_sign == PLUS) {
*result = bc_copy_num(BCG(_zero_));
- return;
+ return 0;
}
break;
@@ -67,7 +68,7 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
if (bc_is_zero(num)) {
*result = bc_copy_num(BCG(_zero_));
- return;
+ return 0;
}
/* If precision is -3, it becomes 1000. */
@@ -78,7 +79,7 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
}
(*result)->n_value[0] = 1;
(*result)->n_sign = num->n_sign;
- return;
+ return 0;
}
/* Just like bcadd('1', '1', 4) becomes '2.0000', it pads with zeros at the end if necessary. */
@@ -90,7 +91,7 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
(*result)->n_sign = num->n_sign;
memcpy((*result)->n_value, num->n_value, num->n_len + num->n_scale);
}
- return;
+ return precision;
}
/*
@@ -222,7 +223,12 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
}
check_zero:
- if (bc_is_zero(*result)) {
- (*result)->n_sign = PLUS;
+ {
+ size_t scale = (*result)->n_scale;
+ if (bc_is_zero(*result)) {
+ (*result)->n_sign = PLUS;
+ (*result)->n_scale = 0;
+ }
+ return scale;
}
}
diff --git a/ext/bcmath/libbcmath/src/simd.h b/ext/bcmath/libbcmath/src/simd.h
new file mode 100644
index 0000000000000..af38f8349618c
--- /dev/null
+++ b/ext/bcmath/libbcmath/src/simd.h
@@ -0,0 +1,59 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Saki Takamachi |
+ +----------------------------------------------------------------------+
+*/
+
+
+#ifndef _BCMATH_SIMD_H_
+#define _BCMATH_SIMD_H_
+
+#ifdef __SSE2__
+# include
+ typedef __m128i bc_simd_128_t;
+# define HAVE_BC_SIMD_128
+# define bc_simd_set_8x16(x) _mm_set1_epi8(x)
+# define bc_simd_load_8x16(ptr) _mm_loadu_si128((const __m128i *) (ptr))
+# define bc_simd_xor_8x16(a, b) _mm_xor_si128(a, b)
+# define bc_simd_store_8x16(ptr, val) _mm_storeu_si128((__m128i *) (ptr), val)
+# define bc_simd_add_8x16(a, b) _mm_add_epi8(a, b)
+# define bc_simd_cmpeq_8x16(a, b) _mm_cmpeq_epi8(a, b)
+# define bc_simd_cmplt_8x16(a, b) _mm_cmplt_epi8(a, b)
+# define bc_simd_movemask_8x16(a) _mm_movemask_epi8(a)
+
+#elif defined(__aarch64__) || defined(_M_ARM64)
+# include
+ typedef int8x16_t bc_simd_128_t;
+# define HAVE_BC_SIMD_128
+# define bc_simd_set_8x16(x) vdupq_n_s8(x)
+# define bc_simd_load_8x16(ptr) vld1q_s8((const int8_t *) (ptr))
+# define bc_simd_xor_8x16(a, b) veorq_s8(a, b)
+# define bc_simd_store_8x16(ptr, val) vst1q_s8((int8_t *) (ptr), val)
+# define bc_simd_add_8x16(a, b) vaddq_s8(a, b)
+# define bc_simd_cmpeq_8x16(a, b) (vreinterpretq_s8_u8(vceqq_s8(a, b)))
+# define bc_simd_cmplt_8x16(a, b) (vreinterpretq_s8_u8(vcltq_s8(a, b)))
+ static inline int bc_simd_movemask_8x16(int8x16_t vec)
+ {
+ /**
+ * based on code from
+ * https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
+ */
+ uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(vreinterpretq_u8_s8(vec), 7));
+ uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7));
+ uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14));
+ uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28));
+ return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8);
+ }
+#endif
+
+#endif
diff --git a/ext/bcmath/libbcmath/src/str2num.c b/ext/bcmath/libbcmath/src/str2num.c
index bd9a44a240503..945de0cf60003 100644
--- a/ext/bcmath/libbcmath/src/str2num.c
+++ b/ext/bcmath/libbcmath/src/str2num.c
@@ -32,30 +32,28 @@
#include "bcmath.h"
#include "convert.h"
#include "private.h"
+#include "simd.h"
#include
#include
-#ifdef __SSE2__
-# include
-#endif
/* Convert strings to bc numbers. Base 10 only.*/
-static const char *bc_count_digits(const char *str, const char *end)
+static inline const char *bc_count_digits(const char *str, const char *end)
{
/* Process in bulk */
-#ifdef __SSE2__
- const __m128i offset = _mm_set1_epi8((signed char) (SCHAR_MIN - '0'));
+#ifdef HAVE_BC_SIMD_128
+ const bc_simd_128_t offset = bc_simd_set_8x16((signed char) (SCHAR_MIN - '0'));
/* we use the less than comparator, so add 1 */
- const __m128i threshold = _mm_set1_epi8(SCHAR_MIN + ('9' + 1 - '0'));
+ const bc_simd_128_t threshold = bc_simd_set_8x16(SCHAR_MIN + ('9' + 1 - '0'));
- while (str + sizeof(__m128i) <= end) {
- __m128i bytes = _mm_loadu_si128((const __m128i *) str);
+ while (str + sizeof(bc_simd_128_t) <= end) {
+ bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) str);
/* Wrapping-add the offset to the bytes, such that all bytes below '0' are positive and others are negative.
* More specifically, '0' will be -128 and '9' will be -119. */
- bytes = _mm_add_epi8(bytes, offset);
+ bytes = bc_simd_add_8x16(bytes, offset);
/* Now mark all bytes that are <= '9', i.e. <= -119, i.e. < -118, i.e. the threshold. */
- bytes = _mm_cmplt_epi8(bytes, threshold);
+ bytes = bc_simd_cmplt_8x16(bytes, threshold);
- int mask = _mm_movemask_epi8(bytes);
+ int mask = bc_simd_movemask_8x16(bytes);
if (mask != 0xffff) {
/* At least one of the bytes is not within range. Move to the first offending byte. */
#ifdef PHP_HAVE_BUILTIN_CTZL
@@ -65,7 +63,7 @@ static const char *bc_count_digits(const char *str, const char *end)
#endif
}
- str += sizeof(__m128i);
+ str += sizeof(bc_simd_128_t);
}
#endif
@@ -79,19 +77,19 @@ static const char *bc_count_digits(const char *str, const char *end)
static inline const char *bc_skip_zero_reverse(const char *scanner, const char *stop)
{
/* Check in bulk */
-#ifdef __SSE2__
- const __m128i c_zero_repeat = _mm_set1_epi8('0');
- while (scanner - sizeof(__m128i) >= stop) {
- scanner -= sizeof(__m128i);
- __m128i bytes = _mm_loadu_si128((const __m128i *) scanner);
+#ifdef HAVE_BC_SIMD_128
+ const bc_simd_128_t c_zero_repeat = bc_simd_set_8x16('0');
+ while (scanner - sizeof(bc_simd_128_t) >= stop) {
+ scanner -= sizeof(bc_simd_128_t);
+ bc_simd_128_t bytes = bc_simd_load_8x16((const bc_simd_128_t *) scanner);
/* Checks if all numeric strings are equal to '0'. */
- bytes = _mm_cmpeq_epi8(bytes, c_zero_repeat);
+ bytes = bc_simd_cmpeq_8x16(bytes, c_zero_repeat);
- int mask = _mm_movemask_epi8(bytes);
+ int mask = bc_simd_movemask_8x16(bytes);
/* The probability of having 16 trailing 0s in a row is very low, so we use EXPECTED. */
if (EXPECTED(mask != 0xffff)) {
/* Move the pointer back and check each character in loop. */
- scanner += sizeof(__m128i);
+ scanner += sizeof(bc_simd_128_t);
break;
}
}
diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c
index 86de6a5ca5f2d..9ed5342a7df8f 100644
--- a/ext/bz2/bz2.c
+++ b/ext/bz2/bz2.c
@@ -303,16 +303,15 @@ static PHP_MINFO_FUNCTION(bz2)
/* {{{ Reads up to length bytes from a BZip2 stream, or 1024 bytes if length is not specified */
PHP_FUNCTION(bzread)
{
- zval *bz;
zend_long len = 1024;
php_stream *stream;
zend_string *data;
- if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &bz, &len)) {
- RETURN_THROWS();
- }
-
- php_stream_from_zval(stream, bz);
+ ZEND_PARSE_PARAMETERS_START(1, 2)
+ PHP_Z_PARAM_STREAM(stream)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_LONG(len)
+ ZEND_PARSE_PARAMETERS_END();
if (len < 0) {
zend_argument_value_error(2, "must be greater than or equal to 0");
@@ -439,40 +438,40 @@ PHP_FUNCTION(bzerror)
/* {{{ Compresses a string into BZip2 encoded data */
PHP_FUNCTION(bzcompress)
{
- char *source; /* Source data to compress */
- zend_long zblock_size = 0; /* Optional block size to use */
- zend_long zwork_factor = 0;/* Optional work factor to use */
- zend_string *dest = NULL; /* Destination to place the compressed data into */
- int error, /* Error Container */
- block_size = 4, /* Block size for compression algorithm */
- work_factor = 0, /* Work factor for compression algorithm */
- argc = ZEND_NUM_ARGS(); /* Argument count */
- size_t source_len; /* Length of the source data */
- unsigned int dest_len; /* Length of the destination buffer */
-
- if (zend_parse_parameters(argc, "s|ll", &source, &source_len, &zblock_size, &zwork_factor) == FAILURE) {
+ char *source; /* Source data to compress */
+ zend_long zblock_size = 4; /* Block size for compression algorithm */
+ zend_long zwork_factor = 0; /* Work factor for compression algorithm */
+ zend_string *dest = NULL; /* Destination to place the compressed data into */
+ size_t source_len; /* Length of the source data */
+ unsigned int dest_len; /* Length of the destination buffer */
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &source, &source_len, &zblock_size, &zwork_factor) == FAILURE) {
+ RETURN_THROWS();
+ }
+
+ if (zblock_size < 1 || zblock_size > 9) {
+ zend_argument_value_error(2, "must be between 1 and 9");
+ RETURN_THROWS();
+ }
+ int block_size = (int) zblock_size;
+
+ if (zwork_factor < 0 || zwork_factor > 250) {
+ zend_argument_value_error(3, "must be between 0 and 250");
RETURN_THROWS();
}
+ int work_factor = (int) zwork_factor;
/* Assign them to easy to use variables, dest_len is initially the length of the data
+ .01 x length of data + 600 which is the largest size the results of the compression
could possibly be, at least that's what the libbz2 docs say (thanks to jeremy@nirvani.net
for pointing this out). */
+ // TODO Check source string length fits in unsigned int
dest_len = (unsigned int) (source_len + (0.01 * source_len) + 600);
/* Allocate the destination buffer */
dest = zend_string_alloc(dest_len, 0);
- /* Handle the optional arguments */
- if (argc > 1) {
- block_size = zblock_size;
- }
-
- if (argc > 2) {
- work_factor = zwork_factor;
- }
-
- error = BZ2_bzBuffToBuffCompress(ZSTR_VAL(dest), &dest_len, source, source_len, block_size, 0, work_factor);
+ int error = BZ2_bzBuffToBuffCompress(ZSTR_VAL(dest), &dest_len, source, source_len, block_size, 0, work_factor);
if (error != BZ_OK) {
zend_string_efree(dest);
RETURN_LONG(error);
@@ -512,6 +511,7 @@ PHP_FUNCTION(bzdecompress)
RETURN_FALSE;
}
+ // TODO Check source string length fits in unsigned int
bzs.next_in = source;
bzs.avail_in = source_len;
@@ -562,17 +562,14 @@ PHP_FUNCTION(bzdecompress)
The central error handling interface, does the work for bzerrno, bzerrstr and bzerror */
static void php_bz2_error(INTERNAL_FUNCTION_PARAMETERS, int opt)
{
- zval *bzp; /* BZip2 Resource Pointer */
php_stream *stream;
const char *errstr; /* Error string */
int errnum; /* Error number */
struct php_bz2_stream_data_t *self;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &bzp) == FAILURE) {
- RETURN_THROWS();
- }
-
- php_stream_from_zval(stream, bzp);
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ PHP_Z_PARAM_STREAM(stream)
+ ZEND_PARSE_PARAMETERS_END();
if (!php_stream_is(stream, PHP_STREAM_IS_BZIP2)) {
zend_argument_type_error(1, "must be a bz2 stream");
diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c
index bdeb280bfa690..2fbdf9452dad3 100644
--- a/ext/bz2/bz2_filter.c
+++ b/ext/bz2/bz2_filter.c
@@ -50,12 +50,12 @@ typedef struct _php_bz2_filter_data {
static void *php_bz2_alloc(void *opaque, int items, int size)
{
- return (void *)safe_pemalloc(items, size, 0, ((php_bz2_filter_data*)opaque)->persistent);
+ return safe_pemalloc(items, size, 0, ((php_bz2_filter_data*)opaque)->persistent);
}
static void php_bz2_free(void *opaque, void *address)
{
- pefree((void *)address, ((php_bz2_filter_data*)opaque)->persistent);
+ pefree(address, ((php_bz2_filter_data*)opaque)->persistent);
}
/* }}} */
diff --git a/ext/bz2/tests/004.phpt b/ext/bz2/tests/004.phpt
index e644bfa6ce962..240cef37a17a2 100644
--- a/ext/bz2/tests/004.phpt
+++ b/ext/bz2/tests/004.phpt
@@ -112,8 +112,8 @@ array(2) {
}
string(10) "DATA_ERROR"
int(-4)
-bzread(): supplied resource is not a valid stream resource
-bzerror(): supplied resource is not a valid stream resource
-bzerrstr(): supplied resource is not a valid stream resource
-bzerrno(): supplied resource is not a valid stream resource
+bzread(): Argument #1 ($bz) must be an open stream resource
+bzerror(): Argument #1 ($bz) must be an open stream resource
+bzerrstr(): Argument #1 ($bz) must be an open stream resource
+bzerrno(): Argument #1 ($bz) must be an open stream resource
Done
diff --git a/ext/bz2/tests/005.phpt b/ext/bz2/tests/005.phpt
index 769cace33fb7c..d32d5b687f0f5 100644
--- a/ext/bz2/tests/005.phpt
+++ b/ext/bz2/tests/005.phpt
@@ -11,10 +11,6 @@ Getting lost within myself
Nothing matters no one else";
var_dump(bzcompress(1,1,1));
-var_dump(bzcompress($string, 100));
-var_dump(bzcompress($string, 100, -1));
-var_dump(bzcompress($string, 100, 1000));
-var_dump(bzcompress($string, -1, 1));
$data = bzcompress($string);
$data2 = bzcompress($string, 1, 10);
@@ -35,10 +31,6 @@ echo "Done\n";
?>
--EXPECTF--
string(%d) "BZ%a"
-int(-2)
-int(-2)
-int(-2)
-int(-2)
int(-5)
int(-5)
int(-5)
diff --git a/ext/bz2/tests/bzcompress_programming_errors.phpt b/ext/bz2/tests/bzcompress_programming_errors.phpt
new file mode 100644
index 0000000000000..59c2643f66d2b
--- /dev/null
+++ b/ext/bz2/tests/bzcompress_programming_errors.phpt
@@ -0,0 +1,39 @@
+--TEST--
+bzcompress(): providing invalid options
+--EXTENSIONS--
+bz2
+--FILE--
+getMessage(), PHP_EOL;
+}
+try {
+ var_dump(bzcompress($string, 100));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+try {
+ var_dump(bzcompress($string, work_factor: -1));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+try {
+ var_dump(bzcompress($string, work_factor: 255));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+ValueError: bzcompress(): Argument #2 ($block_size) must be between 1 and 9
+ValueError: bzcompress(): Argument #2 ($block_size) must be between 1 and 9
+ValueError: bzcompress(): Argument #3 ($work_factor) must be between 0 and 250
+ValueError: bzcompress(): Argument #3 ($work_factor) must be between 0 and 250
diff --git a/ext/calendar/calendar.c b/ext/calendar/calendar.c
index f9212f621f7ea..0f9a4b99a37bc 100644
--- a/ext/calendar/calendar.c
+++ b/ext/calendar/calendar.c
@@ -131,8 +131,10 @@ static void _php_cal_info(int cal, zval *ret)
calendar = &cal_conversion_table[cal];
array_init(ret);
- array_init(&months);
- array_init(&smonths);
+ array_init_size(&months, calendar->num_months + 1);
+ array_init_size(&smonths, calendar->num_months + 1);
+ zend_hash_real_init_packed(Z_ARRVAL(months));
+ zend_hash_real_init_packed(Z_ARRVAL(smonths));
for (i = 1; i <= calendar->num_months; i++) {
add_index_string(&months, i, calendar->month_name_long[i]);
@@ -160,7 +162,8 @@ PHP_FUNCTION(cal_info)
int i;
zval val;
- array_init(return_value);
+ array_init_size(return_value, CAL_NUM_CALS);
+ zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
for (i = 0; i < CAL_NUM_CALS; i++) {
_php_cal_info(i, &val);
@@ -398,7 +401,7 @@ static char *heb_number_to_chars(int n, int fl, char **ret)
n -= 400;
}
-/* meot (hundreads) case */
+/* meot (hundreds) case */
if (n >= 100) {
*p = alef_bet[18 + n / 100];
p++;
diff --git a/ext/calendar/julian.c b/ext/calendar/julian.c
index ac580aa08e061..baff0771532ef 100644
--- a/ext/calendar/julian.c
+++ b/ext/calendar/julian.c
@@ -67,7 +67,7 @@
* the Julian calendar.
*
* The details are unknown, but the lengths of the months were adjusted
- * until they finally stablized in 8 A.D. with their current lengths:
+ * until they finally stabilized in 8 A.D. with their current lengths:
*
* January 31
* February 28/29
diff --git a/ext/com_dotnet/Makefile.frag.w32 b/ext/com_dotnet/Makefile.frag.w32
new file mode 100644
index 0000000000000..ec16dfc2974e5
--- /dev/null
+++ b/ext/com_dotnet/Makefile.frag.w32
@@ -0,0 +1,30 @@
+$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.c: ext\com_dotnet\tests\comtest\comtest.idl
+ -md $(BUILD_DIR)\ext\com_dotnet\tests\comtest
+ midl /nologo /h $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.h /iid $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.c /tlb $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.tlb ext\com_dotnet\tests\comtest\comtest.idl
+
+$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.obj $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.obj: ext\com_dotnet\tests\comtest\comtest.cpp $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.c
+ $(PHP_CL) /nologo /c /Fo$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.obj /I $(BUILD_DIR)\ext\com_dotnet\tests\comtest ext\com_dotnet\tests\comtest\comtest.cpp
+ $(PHP_CL) /nologo /c /Fo$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.obj $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.c
+
+$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.dll: $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.obj $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.obj ext\com_dotnet\tests\comtest\comtest.def
+ "$(LINK)" /nologo /dll /out:$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.dll $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.obj $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest_i.obj /def:ext\com_dotnet\tests\comtest\comtest.def OleAut32.lib
+
+comtest.dll: $(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.dll
+
+register_comtest:
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747} /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747}\1.0 /d "PHP COM Test Library" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747}\1.0\0 /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747}\1.0\0\win32 /d "$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.tlb" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747}\1.0\0\win64 /d "$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.tlb" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747}\1.0\FLAGS /d 0 /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747}\1.0\HELPDIR /d "$(BUILD_DIR)\ext\com_dotnet\tests\comtest" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\CLSID\{B13FE324-D595-44C7-97D7-82CE20EDF878} /d "PHP COM Test Document" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\CLSID\{B13FE324-D595-44C7-97D7-82CE20EDF878}\InprocServer32 /d "$(BUILD_DIR)\ext\com_dotnet\tests\comtest\comtest.dll" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\PHPTest.Document /d "PHP COM Test Document" /f > NUL
+ @reg add HKCU\SOFTWARE\Classes\PHPTest.Document\CLSID /d "{B13FE324-D595-44C7-97D7-82CE20EDF878}" /f > NUL
+
+unregister_comtest:
+ -@reg delete HKCU\SOFTWARE\Classes\PHPTest.Document /f > NUL
+ -@reg delete HKCU\SOFTWARE\Classes\CLSID\{B13FE324-D595-44C7-97D7-82CE20EDF878} /f > NUL
+ -@reg delete HKCU\SOFTWARE\Classes\TypeLib\{AE8685BE-3758-4BDA-91DB-1459EBA24747} /f > NUL
diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c
index fd2d1ee9a4394..cf44858c93b78 100644
--- a/ext/com_dotnet/com_com.c
+++ b/ext/com_dotnet/com_com.c
@@ -38,7 +38,7 @@ PHP_METHOD(com, __construct)
OLECHAR *moniker;
CLSID clsid;
CLSCTX ctx = CLSCTX_SERVER;
- HRESULT res = E_FAIL;
+ HRESULT res = E_FAIL, res2;
ITypeLib *TL = NULL;
COSERVERINFO info;
COAUTHIDENTITY authid = {0};
@@ -142,7 +142,7 @@ PHP_METHOD(com, __construct)
}
}
- if (FAILED(CLSIDFromString(moniker, &clsid))) {
+ if (FAILED(res2 = CLSIDFromString(moniker, &clsid))) {
/* try to use it as a moniker */
IBindCtx *pBindCtx = NULL;
IMoniker *pMoniker = NULL;
@@ -182,6 +182,9 @@ PHP_METHOD(com, __construct)
if (pBindCtx) {
IBindCtx_Release(pBindCtx);
}
+ if (FAILED(res) && res2 == CO_E_CLASSSTRING && !wcspbrk(moniker, L"\\:")) {
+ res = res2;
+ }
} else if (server_name) {
MULTI_QI qi;
@@ -306,7 +309,7 @@ PHP_FUNCTION(com_get_active_object)
if (FAILED(res)) {
php_com_throw_exception(res, NULL);
} else {
- res = IUnknown_QueryInterface(unk, &IID_IDispatch, &obj);
+ res = IUnknown_QueryInterface(unk, &IID_IDispatch, (void **) &obj);
if (FAILED(res)) {
php_com_throw_exception(res, NULL);
diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c
index 3ce2daddaa7b0..f8b4a828e154a 100644
--- a/ext/com_dotnet/com_dotnet.c
+++ b/ext/com_dotnet/com_dotnet.c
@@ -303,7 +303,7 @@ PHP_METHOD(dotnet, __construct)
IObjectHandle *handle = NULL;
where = "QI: IObjectHandle";
- hr = IUnknown_QueryInterface(unk, &IID_IObjectHandle, &handle);
+ hr = IUnknown_QueryInterface(unk, &IID_IObjectHandle, (void**) &handle);
if (SUCCEEDED(hr)) {
where = "IObjectHandle_Unwrap";
@@ -312,7 +312,7 @@ PHP_METHOD(dotnet, __construct)
if (V_VT(&unwrapped) == VT_UNKNOWN) {
where = "Unwrapped, QI for IDispatch";
- hr = IUnknown_QueryInterface(V_UNKNOWN(&unwrapped), &IID_IDispatch, &V_DISPATCH(&obj->v));
+ hr = IUnknown_QueryInterface(V_UNKNOWN(&unwrapped), &IID_IDispatch, (void **) &V_DISPATCH(&obj->v));
if (SUCCEEDED(hr)) {
V_VT(&obj->v) = VT_DISPATCH;
diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c
index 1f18d2a8573ab..df8314fa7613e 100644
--- a/ext/com_dotnet/com_extension.c
+++ b/ext/com_dotnet/com_extension.c
@@ -182,6 +182,7 @@ PHP_MINIT_FUNCTION(com_dotnet)
php_com_saproxy_class_entry = register_class_com_safearray_proxy();
/* php_com_saproxy_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED; */
+ php_com_saproxy_class_entry->create_object = php_com_saproxy_create_object;
php_com_saproxy_class_entry->default_object_handlers = &php_com_saproxy_handlers;
php_com_saproxy_class_entry->get_iterator = php_com_saproxy_iter_get;
diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c
index a5c68bb7b9987..af980b7b86f2a 100644
--- a/ext/com_dotnet/com_handlers.c
+++ b/ext/com_dotnet/com_handlers.c
@@ -249,7 +249,6 @@ static void function_dtor(zval *zv)
static PHP_FUNCTION(com_method_handler)
{
zval *object = getThis();
- zend_string *method = EX(func)->common.function_name;
zval *args = NULL;
php_com_dotnet_object *obj = CDNO_FETCH(object);
int nargs;
@@ -307,7 +306,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name
f.type = ZEND_INTERNAL_FUNCTION;
f.num_args = 0;
f.arg_info = NULL;
- f.scope = obj->ce;
+ f.scope = obj->zo.ce;
f.fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
f.function_name = zend_string_copy(name);
f.handler = PHP_FN(com_method_handler);
@@ -353,7 +352,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name
ITypeComp_Release(bindptr.lptcomp);
break;
- case DESCKIND_NONE:
+ default:
break;
}
if (TI) {
@@ -392,7 +391,7 @@ static zend_string* com_class_name_get(const zend_object *object)
{
php_com_dotnet_object *obj = (php_com_dotnet_object *)object;
- return zend_string_copy(obj->ce->name);
+ return zend_string_copy(obj->zo.ce->name);
}
/* This compares two variants for equality */
@@ -625,7 +624,6 @@ zend_object* php_com_object_new(zend_class_entry *ce)
VariantInit(&obj->v);
obj->code_page = CP_ACP;
- obj->ce = ce;
zend_object_std_init(&obj->zo, ce);
diff --git a/ext/com_dotnet/com_misc.c b/ext/com_dotnet/com_misc.c
index 3c85fc4ce5e4a..a5de6415b9a73 100644
--- a/ext/com_dotnet/com_misc.c
+++ b/ext/com_dotnet/com_misc.c
@@ -50,8 +50,6 @@ PHP_COM_DOTNET_API void php_com_wrap_dispatch(zval *z, IDispatch *disp,
obj = emalloc(sizeof(*obj));
memset(obj, 0, sizeof(*obj));
obj->code_page = codepage;
- obj->ce = php_com_variant_class_entry;
- obj->zo.ce = php_com_variant_class_entry;
VariantInit(&obj->v);
V_VT(&obj->v) = VT_DISPATCH;
@@ -72,8 +70,6 @@ PHP_COM_DOTNET_API void php_com_wrap_variant(zval *z, VARIANT *v,
obj = emalloc(sizeof(*obj));
memset(obj, 0, sizeof(*obj));
obj->code_page = codepage;
- obj->ce = php_com_variant_class_entry;
- obj->zo.ce = php_com_variant_class_entry;
VariantInit(&obj->v);
VariantCopyInd(&obj->v, v);
diff --git a/ext/com_dotnet/com_persist.c b/ext/com_dotnet/com_persist.c
index a4544cd88549a..742e007a2fba7 100644
--- a/ext/com_dotnet/com_persist.c
+++ b/ext/com_dotnet/com_persist.c
@@ -290,7 +290,7 @@ static zend_class_entry *helper_ce;
static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
{
if (!helper->ips && helper->unk) {
- return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, &helper->ips);
+ return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, (void **) &helper->ips);
}
return helper->ips ? S_OK : E_NOTIMPL;
}
@@ -298,7 +298,7 @@ static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
{
if (!helper->ipsi && helper->unk) {
- return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, &helper->ipsi);
+ return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, (void **) &helper->ipsi);
}
return helper->ipsi ? S_OK : E_NOTIMPL;
}
@@ -306,7 +306,7 @@ static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
static inline HRESULT get_persist_file(php_com_persist_helper *helper)
{
if (!helper->ipf && helper->unk) {
- return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, &helper->ipf);
+ return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, (void **) &helper->ipf);
}
return helper->ipf ? S_OK : E_NOTIMPL;
}
@@ -545,7 +545,7 @@ CPH_METHOD(LoadFromStream)
IDispatch *disp = NULL;
/* we need to create an object and load using OleLoadFromStream */
- res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
+ res = OleLoadFromStream(stm, &IID_IDispatch, (void **) &disp);
if (SUCCEEDED(res)) {
php_com_wrap_dispatch(return_value, disp, COMG(code_page));
@@ -557,7 +557,7 @@ CPH_METHOD(LoadFromStream)
} else {
res = get_persist_stream(helper);
if (helper->ips) {
- res = IPersistStreamInit_Load(helper->ipsi, stm);
+ res = IPersistStream_Load(helper->ips, stm);
}
}
}
diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c
index c68001b1b311f..ea0e9e47a13d9 100644
--- a/ext/com_dotnet/com_saproxy.c
+++ b/ext/com_dotnet/com_saproxy.c
@@ -57,6 +57,14 @@ typedef struct {
#define SA_FETCH(zv) (php_com_saproxy*)Z_OBJ_P(zv)
+zend_object *php_com_saproxy_create_object(zend_class_entry *class_type)
+{
+ php_com_saproxy *intern = emalloc(sizeof(*intern));
+ memset(intern, 0, sizeof(*intern));
+ zend_object_std_init(&intern->std, class_type);
+ return &intern->std;
+}
+
static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
{
int i;
@@ -317,7 +325,7 @@ static zend_function *saproxy_method_get(zend_object **object, zend_string *name
static zend_function *saproxy_constructor_get(zend_object *object)
{
- /* user cannot instantiate */
+ zend_throw_error(NULL, "Cannot directly construct com_safeproxy_array; it is for internal usage only");
return NULL;
}
@@ -365,7 +373,9 @@ static void saproxy_free_storage(zend_object *object)
//??? }
//??? }
- OBJ_RELEASE(&proxy->obj->zo);
+ if (proxy->obj != NULL) {
+ OBJ_RELEASE(&proxy->obj->zo);
+ }
zend_object_std_dtor(object);
diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c
index 2e254e51a6c56..23cbd078fb472 100644
--- a/ext/com_dotnet/com_variant.c
+++ b/ext/com_dotnet/com_variant.c
@@ -60,7 +60,7 @@ static void safe_array_from_zval(VARIANT *v, zval *z, int codepage)
sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
/* get a lock on the array itself */
- SafeArrayAccessData(sa, &va);
+ SafeArrayAccessData(sa, (void **) &va);
va = (VARIANT*)sa->pvData;
/* now fill it in */
@@ -247,7 +247,7 @@ PHP_COM_DOTNET_API zend_result php_com_zval_from_variant(zval *z, VARIANT *v, in
if (V_UNKNOWN(v) != NULL) {
IDispatch *disp;
- if (SUCCEEDED(IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IDispatch, &disp))) {
+ if (SUCCEEDED(IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IDispatch, (void **) &disp))) {
php_com_wrap_dispatch(z, disp, codepage);
IDispatch_Release(disp);
} else {
diff --git a/ext/com_dotnet/com_wrapper.c b/ext/com_dotnet/com_wrapper.c
index e505dc654026f..6e885fa802e9f 100644
--- a/ext/com_dotnet/com_wrapper.c
+++ b/ext/com_dotnet/com_wrapper.c
@@ -256,37 +256,34 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex(
/* TODO: if PHP raises an exception here, we should catch it
* and expose it as a COM exception */
-
- if (wFlags & DISPATCH_PROPERTYGET) {
- retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name)+1, 1, &rv);
- } else if (wFlags & DISPATCH_PROPERTYPUT) {
+ zend_fcall_info_cache fcc;
+ if (wFlags & DISPATCH_PROPERTYPUT) {
zend_update_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), ¶ms[0]);
- } else if (wFlags & DISPATCH_METHOD) {
+ ret = S_OK;
+ } else if (wFlags & DISPATCH_METHOD && zend_is_callable_ex(name, Z_OBJ(disp->object), 0, NULL, &fcc, NULL)) {
zend_try {
retval = &rv;
- if (SUCCESS == call_user_function(NULL, &disp->object, name,
- retval, pdp->cArgs, params)) {
- ret = S_OK;
- trace("function called ok\n");
-
- /* Copy any modified values to callers copy of variant*/
- for (i = 0; i < pdp->cArgs; i++) {
- php_com_dotnet_object *obj = CDNO_FETCH(¶ms[i]);
- VARIANT *srcvar = &obj->v;
- VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i];
- if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) {
- trace("percolate modified value for arg %u VT=%08x\n", i, V_VT(dstvar));
- php_com_copy_variant(dstvar, srcvar);
- }
+ zend_call_known_fcc(&fcc, retval, pdp->cArgs, params, NULL);
+ ret = S_OK;
+ trace("function called ok\n");
+
+ /* Copy any modified values to callers copy of variant*/
+ for (i = 0; i < pdp->cArgs; i++) {
+ php_com_dotnet_object *obj = CDNO_FETCH(¶ms[i]);
+ VARIANT *srcvar = &obj->v;
+ VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i];
+ if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) {
+ trace("percolate modified value for arg %u VT=%08x\n", i, V_VT(dstvar));
+ php_com_copy_variant(dstvar, srcvar);
}
- } else {
- trace("failed to call func\n");
- ret = DISP_E_EXCEPTION;
}
} zend_catch {
trace("something blew up\n");
ret = DISP_E_EXCEPTION;
} zend_end_try();
+ } else if (wFlags & DISPATCH_PROPERTYGET) {
+ retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), 1, &rv);
+ ret = S_OK;
} else {
trace("Don't know how to handle this invocation %08x\n", wFlags);
}
@@ -305,7 +302,9 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex(
VariantInit(pvarRes);
php_com_variant_from_zval(pvarRes, retval, COMG(code_page));
}
- zval_ptr_dtor(retval);
+ if (retval == &rv) {
+ zval_ptr_dtor(retval);
+ }
} else if (pvarRes) {
VariantInit(pvarRes);
}
@@ -425,7 +424,7 @@ static void generate_dispids(php_dispatchex *disp)
zend_string *name = NULL;
zval *tmp, tmp2;
int keytype;
- zend_ulong pid;
+ zend_long pid;
if (disp->dispid_to_name == NULL) {
ALLOC_HASHTABLE(disp->dispid_to_name);
@@ -458,8 +457,8 @@ static void generate_dispids(php_dispatchex *disp)
/* add the mappings */
ZVAL_STR_COPY(&tmp2, name);
- pid = zend_hash_next_free_element(disp->dispid_to_name);
- zend_hash_index_update(disp->dispid_to_name, pid, &tmp2);
+ zend_hash_next_index_insert(disp->dispid_to_name, &tmp2);
+ pid = zend_hash_next_free_element(disp->dispid_to_name) - 1;
ZVAL_LONG(&tmp2, pid);
zend_hash_update(disp->name_to_dispid, name, &tmp2);
@@ -493,8 +492,8 @@ static void generate_dispids(php_dispatchex *disp)
/* add the mappings */
ZVAL_STR_COPY(&tmp2, name);
- pid = zend_hash_next_free_element(disp->dispid_to_name);
- zend_hash_index_update(disp->dispid_to_name, pid, &tmp2);
+ zend_hash_next_index_insert(disp->dispid_to_name, &tmp2);
+ pid = zend_hash_next_free_element(disp->dispid_to_name) - 1;
ZVAL_LONG(&tmp2, pid);
zend_hash_update(disp->name_to_dispid, name, &tmp2);
diff --git a/ext/com_dotnet/config.w32 b/ext/com_dotnet/config.w32
index cc548ac48c777..c4fba4fc539d0 100644
--- a/ext/com_dotnet/config.w32
+++ b/ext/com_dotnet/config.w32
@@ -1,6 +1,6 @@
// vim:ft=javascript
-ARG_ENABLE("com-dotnet", "COM and .Net support", "yes");
+ARG_ENABLE("com-dotnet", "COM and .Net support", "yes,shared");
if (PHP_COM_DOTNET == "yes") {
CHECK_LIB('oleaut32.lib', 'com_dotnet');
@@ -10,4 +10,5 @@ if (PHP_COM_DOTNET == "yes") {
null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
AC_DEFINE('HAVE_COM_DOTNET', 1, "Define to 1 if the PHP extension 'com_dotnet' is available.");
CHECK_HEADER_ADD_INCLUDE('mscoree.h', 'CFLAGS_COM_DOTNET');
+ ADD_MAKEFILE_FRAGMENT();
}
diff --git a/ext/com_dotnet/php_com_dotnet_internal.h b/ext/com_dotnet/php_com_dotnet_internal.h
index 6735afba94289..09fe494393470 100644
--- a/ext/com_dotnet/php_com_dotnet_internal.h
+++ b/ext/com_dotnet/php_com_dotnet_internal.h
@@ -35,8 +35,6 @@ typedef struct _php_com_dotnet_object {
ITypeInfo *typeinfo;
- zend_class_entry *ce;
-
/* associated event sink */
IDispatch *sink_dispatch;
GUID sink_id;
@@ -76,6 +74,7 @@ extern zend_object_handlers php_com_object_handlers;
void php_com_object_enable_event_sink(php_com_dotnet_object *obj, bool enable);
/* com_saproxy.c */
+zend_object *php_com_saproxy_create_object(zend_class_entry *class_type);
zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref);
void php_com_saproxy_create(zend_object *com_object, zval *proxy_out, zval *index);
extern zend_object_handlers php_com_saproxy_handlers;
diff --git a/ext/com_dotnet/tests/bug77578.phpt b/ext/com_dotnet/tests/bug77578.phpt
index a820935d5d775..e25230f76f497 100644
--- a/ext/com_dotnet/tests/bug77578.phpt
+++ b/ext/com_dotnet/tests/bug77578.phpt
@@ -6,18 +6,33 @@ com_dotnet
--EXPECT--
+bool(true)
array(0) {
}
int(0)
diff --git a/ext/com_dotnet/tests/comtest/comtest.cpp b/ext/com_dotnet/tests/comtest/comtest.cpp
new file mode 100644
index 0000000000000..a4f91d3121e46
--- /dev/null
+++ b/ext/com_dotnet/tests/comtest/comtest.cpp
@@ -0,0 +1,317 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | https://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Christoph M. Becker |
+ | Based on: |
+ +----------------------------------------------------------------------+
+ */
+
+#include "comtest.h" // Generated by MIDL
+
+long g_cComponents = 0;
+long g_cLocks = 0;
+
+class CDocument : public IDocument, IPersistStream
+{
+public:
+ // IUnknown
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+
+ // IDispatch
+ STDMETHODIMP GetTypeInfoCount(UINT* pCountTypeInfo);
+ STDMETHODIMP GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo);
+ STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
+ STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
+
+ // IDocument
+ STDMETHODIMP get_Content(BSTR* retvalue);
+ STDMETHODIMP put_Content(BSTR newvalue);
+
+ // IPersist
+ STDMETHODIMP GetClassID(CLSID *pClassID);
+
+ // IPersistStream
+ STDMETHODIMP IsDirty( void);
+ STDMETHODIMP Load(IStream *pStm);
+ STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty);
+ STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize);
+
+ CDocument() : m_content(SysAllocString(L"")), m_cRef(1) { g_cComponents++; }
+ ~CDocument() { SysFreeString(m_content); g_cComponents--; }
+ HRESULT Init(void);
+
+private:
+ BSTR m_content;
+ ULONG m_cRef;
+ ITypeInfo* m_pTypeInfo;
+ BOOL m_dirty;
+};
+
+ULONG CDocument::AddRef()
+{
+ return ++m_cRef;
+}
+
+ULONG CDocument::Release()
+{
+ if (--m_cRef != 0) {
+ return m_cRef;
+ }
+ m_pTypeInfo->Release();
+ delete this;
+ return 0;
+}
+
+HRESULT CDocument::QueryInterface(REFIID riid, void** ppv)
+{
+ if (riid == IID_IUnknown) {
+ *ppv = static_cast(this);
+ } else if (riid == IID_IDocument) {
+ *ppv = static_cast(this);
+ } else if (riid == IID_IDispatch) {
+ *ppv = static_cast(this);
+ } else if (riid == IID_IPersistStream) {
+ *ppv = static_cast(this);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+HRESULT CDocument::GetTypeInfoCount(UINT* pCountTypeInfo)
+{
+ *pCountTypeInfo = 1;
+ return S_OK;
+}
+
+HRESULT CDocument::GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo)
+{
+ (void) lcid;
+ *ppITypeInfo = NULL;
+ if (iTypeInfo != 0) {
+ return DISP_E_BADINDEX;
+ }
+ m_pTypeInfo->AddRef();
+ *ppITypeInfo = m_pTypeInfo;
+ return S_OK;
+}
+
+HRESULT CDocument::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
+{
+ (void) lcid;
+ if (riid != IID_NULL) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+ return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId);
+}
+
+HRESULT CDocument::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
+ VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
+{
+ (void) lcid;
+ if (riid != IID_NULL) {
+ return DISP_E_UNKNOWNINTERFACE;
+ }
+ return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+}
+
+HRESULT CDocument::get_Content(BSTR* retvalue)
+{
+ *retvalue = SysAllocString(m_content);
+ return S_OK;
+}
+
+HRESULT CDocument::put_Content(BSTR newvalue)
+{
+ SysFreeString(m_content);
+ m_content = SysAllocString(newvalue);
+ return S_OK;
+}
+
+HRESULT CDocument::Init(void)
+{
+ ITypeLib* pTypeLib;
+ if (FAILED(LoadRegTypeLib(LIBID_ComTest, 1, 0, LANG_NEUTRAL, &pTypeLib))) {
+ return E_FAIL;
+ }
+ HRESULT hr = pTypeLib->GetTypeInfoOfGuid(IID_IDocument, &m_pTypeInfo);
+ if (FAILED(hr)) {
+ pTypeLib->Release();
+ return hr;
+ }
+
+ pTypeLib->Release();
+ return S_OK;
+}
+
+HRESULT CDocument::GetClassID(CLSID *pClassID)
+{
+ *pClassID = CLSID_Document;
+ return S_OK;
+}
+
+HRESULT CDocument::IsDirty(void)
+{
+ return m_dirty ? S_OK : S_FALSE;
+}
+
+HRESULT CDocument::Load(IStream *pStm)
+{
+ ULONG read = 0;
+ ULONG cbSize;
+
+ if (FAILED(pStm->Read(&cbSize, sizeof cbSize, &read))) {
+ return S_FALSE;
+ }
+ if (!SysReAllocStringLen(&m_content, NULL, cbSize)) {
+ return S_FALSE;
+ }
+ if (FAILED(pStm->Read(m_content, cbSize, &read))) {
+ // we may have garbage in m_content, but don't mind
+ return S_FALSE;
+ }
+ m_dirty = FALSE;
+ return S_OK;
+}
+
+HRESULT CDocument::Save(IStream *pStm, BOOL fClearDirty)
+{
+ ULONG written = 0;
+ ULONG cbSize = SysStringByteLen(m_content);
+ HRESULT hr;
+ if (FAILED(hr = pStm->Write(&cbSize, sizeof cbSize, &written))) {
+ return S_FALSE;
+ }
+ if (FAILED(hr = pStm->Write(m_content, cbSize, &written))) {
+ return S_FALSE;
+ }
+
+ if (fClearDirty) {
+ m_dirty = FALSE;
+ }
+
+ return S_OK;
+}
+
+HRESULT CDocument::GetSizeMax(ULARGE_INTEGER *pcbSize)
+{
+ (*pcbSize).QuadPart = sizeof(ULONG) + SysStringByteLen(m_content);
+ return S_OK;
+}
+
+class CDocumentFactory : public IClassFactory
+{
+public:
+ // IUnknown
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
+
+ // IClassFactory
+ STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppv);
+ STDMETHODIMP LockServer(BOOL bLock);
+
+ CDocumentFactory() : m_cRef(1) { g_cLocks++; }
+ ~CDocumentFactory() { g_cLocks--; }
+
+private:
+ ULONG m_cRef;
+};
+
+ULONG CDocumentFactory::AddRef()
+{
+ return ++m_cRef;
+}
+
+ULONG CDocumentFactory::Release()
+{
+ if (--m_cRef != 0) {
+ return m_cRef;
+ }
+ delete this;
+ return 0;
+}
+
+HRESULT CDocumentFactory::QueryInterface(REFIID riid, void** ppv)
+{
+ if (riid == IID_IUnknown) {
+ *ppv = (IUnknown*)this;
+ } else if (riid == IID_IClassFactory) {
+ *ppv = (IClassFactory*)this;
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+HRESULT CDocumentFactory::CreateInstance(IUnknown *pUnknownOuter, REFIID riid, void** ppv)
+{
+ if (pUnknownOuter != NULL) {
+ return CLASS_E_NOAGGREGATION;
+ }
+
+ CDocument *pDocument = new CDocument;
+ if (pDocument == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ HRESULT hr;
+ if (FAILED(hr = pDocument->Init())) {
+ return hr;
+ }
+ hr = pDocument->QueryInterface(riid, ppv);
+ pDocument->Release();
+ return hr;
+}
+
+HRESULT CDocumentFactory::LockServer(BOOL bLock)
+{
+ if (bLock) {
+ g_cLocks++;
+ } else {
+ g_cLocks --;
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall DllCanUnloadNow()
+{
+ if (g_cLocks == 0) {
+ return S_OK;
+ } else {
+ return S_FALSE;
+ }
+}
+
+HRESULT __stdcall DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv)
+{
+ if (clsid != CLSID_Document) {
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+
+ CDocumentFactory* pFactory = new CDocumentFactory;
+ if (pFactory == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ // riid is probably IID_IClassFactory.
+ HRESULT hr = pFactory->QueryInterface(riid, ppv);
+ pFactory->Release();
+ return hr;
+}
diff --git a/ext/com_dotnet/tests/comtest/comtest.def b/ext/com_dotnet/tests/comtest/comtest.def
new file mode 100644
index 0000000000000..1a8f9f7fc519e
--- /dev/null
+++ b/ext/com_dotnet/tests/comtest/comtest.def
@@ -0,0 +1,4 @@
+LIBRARY comtest.dll
+EXPORTS
+DllGetClassObject PRIVATE
+DllCanUnloadNow PRIVATE
diff --git a/ext/com_dotnet/tests/comtest/comtest.idl b/ext/com_dotnet/tests/comtest/comtest.idl
new file mode 100644
index 0000000000000..baf5f9ded8825
--- /dev/null
+++ b/ext/com_dotnet/tests/comtest/comtest.idl
@@ -0,0 +1,23 @@
+[ uuid(AE8685BE-3758-4BDA-91DB-1459EBA24747),
+ helpstring("PHP COM Test Library"),
+ version(1.0) ]
+library ComTest
+{
+ importlib("stdole32.tlb");
+
+ [ object,
+ uuid(66177FCA-36B3-436B-B475-BE4249DDE0A0),
+ dual ]
+ interface IDocument : IDispatch
+ {
+ [id(1), propget] HRESULT Content([out, retval] BSTR* retvalue);
+ [id(1), propput] HRESULT Content(BSTR newvalue);
+ }
+
+ [ uuid(B13FE324-D595-44C7-97D7-82CE20EDF878) ]
+ coclass Document
+ {
+ interface IDocument;
+ interface IPersistStream;
+ }
+}
diff --git a/ext/com_dotnet/tests/gh17658.phpt b/ext/com_dotnet/tests/gh17658.phpt
new file mode 100644
index 0000000000000..7b7418dce0e30
--- /dev/null
+++ b/ext/com_dotnet/tests/gh17658.phpt
@@ -0,0 +1,36 @@
+--TEST--
+GH-17658 (COMPersistHelper::LoadFromStream() segfaults for IPersistStream)
+--EXTENSIONS--
+com_dotnet
+--SKIPIF--
+getCode()) . ")");
+}
+?>
+--FILE--
+Content = "GH-17658";
+$ph = new COMPersistHelper($doc);
+$stream = fopen("php://memory", "w+");
+$ph->SaveToStream($stream);
+$doc->Content = "";
+fseek($stream, 0);
+$ph->LoadFromStream($stream);
+fclose($stream);
+echo $doc->Content, "\n";
+
+// verify that PHP.Test.Document does not implement IPersistStreamInit
+try {
+ $ph->InitNew();
+} catch (com_exception $ex) {
+ // supposed to fail with E_NOINTERFACE
+ echo dechex($ex->getCode()), "\n";
+}
+?>
+--EXPECT--
+GH-17658
+80004002
diff --git a/ext/com_dotnet/tests/gh8778.phpt b/ext/com_dotnet/tests/gh8778.phpt
index 7198f2add4bee..72f89f506e853 100644
--- a/ext/com_dotnet/tests/gh8778.phpt
+++ b/ext/com_dotnet/tests/gh8778.phpt
@@ -1,5 +1,5 @@
--TEST--
-Bug GH-8778 (Integer arithmethic with large number variants fails)
+Bug GH-8778 (Integer arithmetic with large number variants fails)
--EXTENSIONS--
com_dotnet
--SKIPIF--
diff --git a/ext/com_dotnet/tests/saproxy_instantiate.phpt b/ext/com_dotnet/tests/saproxy_instantiate.phpt
new file mode 100644
index 0000000000000..303305add09e2
--- /dev/null
+++ b/ext/com_dotnet/tests/saproxy_instantiate.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Manual instantiation of com_safearray_proxy is not allowed
+--EXTENSIONS--
+com_dotnet
+--FILE--
+getMessage(), PHP_EOL;
+}
+?>
+--EXPECT--
+Cannot directly construct com_safeproxy_array; it is for internal usage only
diff --git a/ext/com_dotnet/tests/variant_variation2.phpt b/ext/com_dotnet/tests/variant_variation2.phpt
new file mode 100644
index 0000000000000..5a93967b26383
--- /dev/null
+++ b/ext/com_dotnet/tests/variant_variation2.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Testing reading and writing of properties
+--EXTENSIONS--
+com_dotnet
+--FILE--
+foo);
+var_dump($v->bar);
+$v->foo = "new foo";
+var_dump($v->foo instanceof variant);
+var_dump((string) $v->foo);
+var_dump($o->foo instanceof variant);
+var_dump((string) $o->foo);
+$v->bar = "new bar";
+var_dump($v->bar);
+var_dump($o->bar);
+?>
+--EXPECT--
+string(3) "foo"
+string(3) "bar"
+bool(true)
+string(7) "new foo"
+bool(true)
+string(7) "new foo"
+string(7) "new bar"
+string(7) "new bar"
diff --git a/ext/com_dotnet/tests/variant_variation3.phpt b/ext/com_dotnet/tests/variant_variation3.phpt
new file mode 100644
index 0000000000000..cb77f6982c871
--- /dev/null
+++ b/ext/com_dotnet/tests/variant_variation3.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Testing reading properties and calling functions
+--EXTENSIONS--
+com_dotnet
+--FILE--
+foo);
+var_dump($v->foo());
+var_dump($v->bar);
+var_dump($v->bar());
+var_dump($v->stdclass);
+var_dump($v->stdclass());
+try {
+ var_dump($v->qux);
+} catch (com_exception $ex) {
+ echo $ex->getMessage(), "\n";
+}
+?>
+--EXPECTF--
+string(6) "method"
+string(6) "method"
+string(3) "bar"
+string(3) "bar"
+object(variant)#%d (0) {
+}
+object(variant)#%d (0) {
+}
+Unable to lookup `qux': %s
diff --git a/ext/ctype/tests/ctype_xdigit_variation1.phpt b/ext/ctype/tests/ctype_xdigit_variation1.phpt
index 3dbd7cec37667..0db93fd7db25f 100644
--- a/ext/ctype/tests/ctype_xdigit_variation1.phpt
+++ b/ext/ctype/tests/ctype_xdigit_variation1.phpt
@@ -1,5 +1,5 @@
--TEST--
-Test ctype_xdigit() function : usage variations - different data typse as $c arg
+Test ctype_xdigit() function : usage variations - different data types as $c arg
--EXTENSIONS--
ctype
--FILE--
diff --git a/ext/curl/config.w32 b/ext/curl/config.w32
index 407659cf6517b..e097a697eb553 100644
--- a/ext/curl/config.w32
+++ b/ext/curl/config.w32
@@ -3,25 +3,13 @@
ARG_WITH("curl", "cURL support", "no");
if (PHP_CURL != "no") {
- var ver_num = NaN;
- var f = PHP_PHP_BUILD + "/include/curl/curlver.h";
- if (FSO.FileExists(f)) {
- var reg = /LIBCURL_VERSION_NUM\s+(0x[a-z0-9]+)/gi;
- var m = reg.exec(file_get_contents(PHP_PHP_BUILD + "/include/curl/curlver.h"));
- if (!!m && m.length >= 2) {
- ver_num = parseInt(m[1]);
- }
- }
-
var curl_location;
if ((curl_location = CHECK_LIB("libcurl_a.lib;libcurl.lib", "curl", PHP_CURL)) &&
CHECK_HEADER_ADD_INCLUDE("curl/easy.h", "CFLAGS_CURL") &&
SETUP_OPENSSL("curl", PHP_CURL) >= 2 &&
CHECK_LIB("winmm.lib", "curl", PHP_CURL) &&
CHECK_LIB("wldap32.lib", "curl", PHP_CURL) &&
- (((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib;zlib.lib", "curl", PHP_CURL))) ||
- (PHP_ZLIB_SHARED && CHECK_LIB("zlib.lib", "curl", PHP_CURL)) || (PHP_ZLIB == "yes" && (!PHP_ZLIB_SHARED))) &&
- !isNaN(ver_num) &&
+ SETUP_ZLIB_LIB("curl", PHP_CURL) &&
(CHECK_LIB("normaliz.lib", "curl", PHP_CURL) &&
CHECK_LIB("libssh2.lib", "curl", PHP_CURL) &&
CHECK_LIB("nghttp2.lib", "curl", PHP_CURL))
diff --git a/ext/curl/curl.stub.php b/ext/curl/curl.stub.php
index 49aa7d9646200..cbcb52bc7b0a2 100644
--- a/ext/curl/curl.stub.php
+++ b/ext/curl/curl.stub.php
@@ -188,6 +188,11 @@
* @cvalue CURLOPT_INFILESIZE
*/
const CURLOPT_INFILESIZE = UNKNOWN;
+/**
+ * @var int
+ * @cvalue CURLOPT_INFILESIZE_LARGE
+ */
+const CURLOPT_INFILESIZE_LARGE = UNKNOWN;
/**
* @var int
* @cvalue CURLOPT_INTERFACE
@@ -492,6 +497,24 @@
*/
const CURLOPT_DEBUGFUNCTION = UNKNOWN;
+#if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */
+/**
+ * @var int
+ * @cvalue CURLFOLLOW_ALL
+ */
+const CURLFOLLOW_ALL = UNKNOWN;
+/**
+ * @var int
+ * @cvalue CURLFOLLOW_OBEYCODE
+ */
+const CURLFOLLOW_OBEYCODE = UNKNOWN;
+/**
+ * @var int
+ * @cvalue CURLFOLLOW_FIRSTONLY
+ */
+const CURLFOLLOW_FIRSTONLY = UNKNOWN;
+#endif
+
/**
* @var int
* @cvalue CURLINFO_TEXT
@@ -1000,6 +1023,18 @@
*/
const CURLINFO_CAINFO = UNKNOWN;
#endif
+#if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */
+/**
+ * @var int
+ * @cvalue CURLINFO_HTTPAUTH_USED
+ */
+const CURLINFO_HTTPAUTH_USED = UNKNOWN;
+/**
+ * @var int
+ * @cvalue CURLINFO_PROXYAUTH_USED
+ */
+const CURLINFO_PROXYAUTH_USED = UNKNOWN;
+#endif
/* Other */
/**
@@ -3054,6 +3089,13 @@
* @cvalue CURLINFO_TOTAL_TIME_T
*/
const CURLINFO_TOTAL_TIME_T = UNKNOWN;
+#if LIBCURL_VERSION_NUM >= 0x080700 /* Available since 8.7.0 */
+/**
+ * @var int
+ * @cvalue CURLINFO_USED_PROXY
+ */
+const CURLINFO_USED_PROXY = UNKNOWN;
+#endif
#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */
/**
* @var int
@@ -3668,6 +3710,15 @@ final class CurlShareHandle
{
}
+/**
+ * @strict-properties
+ * @not-serializable
+ */
+final class CurlSharePersistentHandle
+{
+ public readonly array $options;
+}
+
function curl_close(CurlHandle $handle): void {}
/** @refcount 1 */
@@ -3702,6 +3753,8 @@ function curl_upkeep(CurlHandle $handle): bool {}
function curl_multi_add_handle(CurlMultiHandle $multi_handle, CurlHandle $handle): int {}
+function curl_multi_get_handles(CurlMultiHandle $multi_handle): array {}
+
function curl_multi_close(CurlMultiHandle $multi_handle): void {}
function curl_multi_errno(CurlMultiHandle $multi_handle): int {}
@@ -3748,6 +3801,9 @@ function curl_share_setopt(CurlShareHandle $share_handle, int $option, mixed $va
/** @refcount 1 */
function curl_share_strerror(int $error_code): ?string {}
+/** @refcount 1 */
+function curl_share_init_persistent(array $share_options): CurlSharePersistentHandle {}
+
/** @refcount 1 */
function curl_strerror(int $error_code): ?string {}
diff --git a/ext/curl/curl_arginfo.h b/ext/curl/curl_arginfo.h
index 7b56622b0a615..8928da3f47453 100644
--- a/ext/curl/curl_arginfo.h
+++ b/ext/curl/curl_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 6a6a7461b475bb10cef3048ee2c11ab0dd32f328 */
+ * Stub hash: 792cdfa8a8ce190d73dffe679c51a41a2ee46cd7 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0)
ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
@@ -60,6 +60,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_add_handle, 0, 2, IS_
ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_get_handles, 0, 1, IS_ARRAY, 0)
+ ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_multi_close, 0, 1, IS_VOID, 0)
ZEND_ARG_OBJ_INFO(0, multi_handle, CurlMultiHandle, 0)
ZEND_END_ARG_INFO()
@@ -133,6 +137,10 @@ ZEND_END_ARG_INFO()
#define arginfo_curl_share_strerror arginfo_curl_multi_strerror
+ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_curl_share_init_persistent, 0, 1, CurlSharePersistentHandle, 0)
+ ZEND_ARG_TYPE_INFO(0, share_options, IS_ARRAY, 0)
+ZEND_END_ARG_INFO()
+
#define arginfo_curl_strerror arginfo_curl_multi_strerror
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_curl_version, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
@@ -153,6 +161,7 @@ ZEND_FUNCTION(curl_init);
ZEND_FUNCTION(curl_upkeep);
#endif
ZEND_FUNCTION(curl_multi_add_handle);
+ZEND_FUNCTION(curl_multi_get_handles);
ZEND_FUNCTION(curl_multi_close);
ZEND_FUNCTION(curl_multi_errno);
ZEND_FUNCTION(curl_multi_exec);
@@ -171,6 +180,7 @@ ZEND_FUNCTION(curl_share_errno);
ZEND_FUNCTION(curl_share_init);
ZEND_FUNCTION(curl_share_setopt);
ZEND_FUNCTION(curl_share_strerror);
+ZEND_FUNCTION(curl_share_init_persistent);
ZEND_FUNCTION(curl_strerror);
ZEND_FUNCTION(curl_version);
@@ -190,6 +200,7 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(curl_upkeep, arginfo_curl_upkeep)
#endif
ZEND_FE(curl_multi_add_handle, arginfo_curl_multi_add_handle)
+ ZEND_FE(curl_multi_get_handles, arginfo_curl_multi_get_handles)
ZEND_FE(curl_multi_close, arginfo_curl_multi_close)
ZEND_FE(curl_multi_errno, arginfo_curl_multi_errno)
ZEND_FE(curl_multi_exec, arginfo_curl_multi_exec)
@@ -208,6 +219,7 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(curl_share_init, arginfo_curl_share_init)
ZEND_FE(curl_share_setopt, arginfo_curl_share_setopt)
ZEND_FE(curl_share_strerror, arginfo_curl_share_strerror)
+ ZEND_FE(curl_share_init_persistent, arginfo_curl_share_init_persistent)
ZEND_FE(curl_strerror, arginfo_curl_strerror)
ZEND_FE(curl_version, arginfo_curl_version)
ZEND_FE_END
@@ -251,6 +263,7 @@ static void register_curl_symbols(int module_number)
REGISTER_LONG_CONSTANT("CURLOPT_HTTP_VERSION", CURLOPT_HTTP_VERSION, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_INFILE", CURLOPT_INFILE, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_INFILESIZE", CURLOPT_INFILESIZE, CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("CURLOPT_INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_INTERFACE", CURLOPT_INTERFACE, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_KRB4LEVEL", CURLOPT_KRB4LEVEL, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CONST_PERSISTENT);
@@ -313,6 +326,15 @@ static void register_curl_symbols(int module_number)
REGISTER_LONG_CONSTANT("CURLOPT_WRITEHEADER", CURLOPT_WRITEHEADER, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLOPT_DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CONST_PERSISTENT);
+#if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */
+ REGISTER_LONG_CONSTANT("CURLFOLLOW_ALL", CURLFOLLOW_ALL, CONST_PERSISTENT);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */
+ REGISTER_LONG_CONSTANT("CURLFOLLOW_OBEYCODE", CURLFOLLOW_OBEYCODE, CONST_PERSISTENT);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x080d00 /* Available since 8.13.0 */
+ REGISTER_LONG_CONSTANT("CURLFOLLOW_FIRSTONLY", CURLFOLLOW_FIRSTONLY, CONST_PERSISTENT);
+#endif
REGISTER_LONG_CONSTANT("CURLINFO_TEXT", CURLINFO_TEXT, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLINFO_HEADER_IN", CURLINFO_HEADER_IN, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLINFO_DATA_IN", CURLINFO_DATA_IN, CONST_PERSISTENT);
@@ -417,6 +439,12 @@ static void register_curl_symbols(int module_number)
#endif
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
REGISTER_LONG_CONSTANT("CURLINFO_CAINFO", CURLINFO_CAINFO, CONST_PERSISTENT);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */
+ REGISTER_LONG_CONSTANT("CURLINFO_HTTPAUTH_USED", CURLINFO_HTTPAUTH_USED, CONST_PERSISTENT);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */
+ REGISTER_LONG_CONSTANT("CURLINFO_PROXYAUTH_USED", CURLINFO_PROXYAUTH_USED, CONST_PERSISTENT);
#endif
REGISTER_LONG_CONSTANT("CURLMSG_DONE", CURLMSG_DONE, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLVERSION_NOW", CURLVERSION_NOW, CONST_PERSISTENT);
@@ -799,6 +827,9 @@ static void register_curl_symbols(int module_number)
REGISTER_LONG_CONSTANT("CURLINFO_REDIRECT_TIME_T", CURLINFO_REDIRECT_TIME_T, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLINFO_STARTTRANSFER_TIME_T", CURLINFO_STARTTRANSFER_TIME_T, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CURLINFO_TOTAL_TIME_T", CURLINFO_TOTAL_TIME_T, CONST_PERSISTENT);
+#if LIBCURL_VERSION_NUM >= 0x080700 /* Available since 8.7.0 */
+ REGISTER_LONG_CONSTANT("CURLINFO_USED_PROXY", CURLINFO_USED_PROXY, CONST_PERSISTENT);
+#endif
#if LIBCURL_VERSION_NUM >= 0x080a00 /* Available since 8.10.0 */
REGISTER_LONG_CONSTANT("CURLINFO_POSTTRANSFER_TIME_T", CURLINFO_POSTTRANSFER_TIME_T, CONST_PERSISTENT);
#endif
@@ -1131,3 +1162,19 @@ static zend_class_entry *register_class_CurlShareHandle(void)
return class_entry;
}
+
+static zend_class_entry *register_class_CurlSharePersistentHandle(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ INIT_CLASS_ENTRY(ce, "CurlSharePersistentHandle", NULL);
+ class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE);
+
+ zval property_options_default_value;
+ ZVAL_UNDEF(&property_options_default_value);
+ zend_string *property_options_name = zend_string_init("options", sizeof("options") - 1, 1);
+ zend_declare_typed_property(class_entry, property_options_name, &property_options_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY));
+ zend_string_release(property_options_name);
+
+ return class_entry;
+}
diff --git a/ext/curl/curl_file_arginfo.h b/ext/curl/curl_file_arginfo.h
index bbf6900546a15..e409c6e772513 100644
--- a/ext/curl/curl_file_arginfo.h
+++ b/ext/curl/curl_file_arginfo.h
@@ -60,9 +60,7 @@ static zend_class_entry *register_class_CURLFile(void)
zval property_name_default_value;
ZVAL_EMPTY_STRING(&property_name_default_value);
- zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
- zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_name_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_mime_default_value;
ZVAL_EMPTY_STRING(&property_mime_default_value);
diff --git a/ext/curl/curl_private.h b/ext/curl/curl_private.h
index 19e43094574de..dc23b5910cfbc 100644
--- a/ext/curl/curl_private.h
+++ b/ext/curl/curl_private.h
@@ -40,9 +40,20 @@
#define SAVE_CURL_ERROR(__handle, __err) \
do { (__handle)->err.no = (int) __err; } while (0)
+
+ZEND_BEGIN_MODULE_GLOBALS(curl)
+ HashTable persistent_curlsh;
+ZEND_END_MODULE_GLOBALS(curl)
+
+ZEND_EXTERN_MODULE_GLOBALS(curl)
+
+#define CURL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(curl, v)
+
PHP_MINIT_FUNCTION(curl);
PHP_MSHUTDOWN_FUNCTION(curl);
PHP_MINFO_FUNCTION(curl);
+PHP_GINIT_FUNCTION(curl);
+PHP_GSHUTDOWN_FUNCTION(curl);
typedef struct {
zend_fcall_info_cache fcc;
@@ -89,7 +100,7 @@ struct _php_curl_send_headers {
struct _php_curl_free {
zend_llist post;
zend_llist stream;
- HashTable *slist;
+ HashTable slist;
};
typedef struct {
@@ -153,6 +164,8 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) {
void curl_multi_register_handlers(void);
void curl_share_register_handlers(void);
+void curl_share_persistent_register_handlers(void);
+void curl_share_free_persistent_curlsh(zval *data);
void curlfile_register_class(void);
zend_result curl_cast_object(zend_object *obj, zval *result, int type);
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index 255fe98faf48b..dcfdf7de359f1 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -61,12 +61,14 @@
#ifdef __GNUC__
/* don't complain about deprecated CURLOPT_* we're exposing to PHP; we
- need to keep using those to avoid breaking PHP API compatibiltiy */
+ need to keep using those to avoid breaking PHP API compatibility */
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "curl_arginfo.h"
+ZEND_DECLARE_MODULE_GLOBALS(curl)
+
#ifdef PHP_CURL_NEED_OPENSSL_TSL /* {{{ */
static MUTEX_T *php_curl_openssl_tsl = NULL;
@@ -215,7 +217,11 @@ zend_module_entry curl_module_entry = {
NULL,
PHP_MINFO(curl),
PHP_CURL_VERSION,
- STANDARD_MODULE_PROPERTIES
+ PHP_MODULE_GLOBALS(curl),
+ PHP_GINIT(curl),
+ PHP_GSHUTDOWN(curl),
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
@@ -223,10 +229,22 @@ zend_module_entry curl_module_entry = {
ZEND_GET_MODULE (curl)
#endif
+PHP_GINIT_FUNCTION(curl)
+{
+ zend_hash_init(&curl_globals->persistent_curlsh, 0, NULL, curl_share_free_persistent_curlsh, true);
+ GC_MAKE_PERSISTENT_LOCAL(&curl_globals->persistent_curlsh);
+}
+
+PHP_GSHUTDOWN_FUNCTION(curl)
+{
+ zend_hash_destroy(&curl_globals->persistent_curlsh);
+}
+
/* CurlHandle class */
zend_class_entry *curl_ce;
zend_class_entry *curl_share_ce;
+zend_class_entry *curl_share_persistent_ce;
static zend_object_handlers curl_object_handlers;
static zend_object *curl_create_object(zend_class_entry *class_type);
@@ -410,6 +428,10 @@ PHP_MINIT_FUNCTION(curl)
curl_share_ce = register_class_CurlShareHandle();
curl_share_register_handlers();
+
+ curl_share_persistent_ce = register_class_CurlSharePersistentHandle();
+ curl_share_persistent_register_handlers();
+
curlfile_register_class();
return SUCCESS;
@@ -524,7 +546,9 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
zend_get_gc_buffer_use(gc_buffer, table, n);
- return zend_std_get_properties(object);
+ /* CurlHandle can never have properties as it's final and has strict-properties on.
+ * Avoid building a hash table. */
+ return NULL;
}
zend_result curl_cast_object(zend_object *obj, zval *result, int type)
@@ -821,7 +845,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
{
php_curl *ch = (php_curl *)ctx;
php_curl_read *read_handler = ch->handlers.read;
- int length = 0;
+ size_t length = 0;
switch (read_handler->method) {
case PHP_CURL_DIRECT:
@@ -1143,8 +1167,7 @@ void init_curl_handle(php_curl *ch)
zend_llist_init(&ch->to_free->post, sizeof(struct HttpPost *), (llist_dtor_func_t)curl_free_post, 0);
zend_llist_init(&ch->to_free->stream, sizeof(struct mime_data_cb_arg *), (llist_dtor_func_t)curl_free_cb_arg, 0);
- ch->to_free->slist = emalloc(sizeof(HashTable));
- zend_hash_init(ch->to_free->slist, 4, NULL, curl_free_slist, 0);
+ zend_hash_init(&ch->to_free->slist, 4, NULL, curl_free_slist, 0);
ZVAL_UNDEF(&ch->postfields);
}
@@ -1163,7 +1186,6 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode)
array_init(&certhash);
for (slist = ci->certinfo[i]; slist; slist = slist->next) {
- int len;
char s[64];
char *tmp;
strncpy(s, slist->data, sizeof(s));
@@ -1171,7 +1193,7 @@ static void create_certinfo(struct curl_certinfo *ci, zval *listcode)
tmp = memchr(s, ':', sizeof(s));
if(tmp) {
*tmp = '\0';
- len = strlen(s);
+ size_t len = strlen(s);
add_assoc_string(&certhash, s, &slist->data[len+1]);
} else {
php_error_docref(NULL, E_WARNING, "Could not extract hash key from certificate info");
@@ -1312,7 +1334,6 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
ZVAL_COPY(&ch->private_data, &source->private_data);
- efree(ch->to_free->slist);
efree(ch->to_free);
ch->to_free = source->to_free;
efree(ch->clone);
@@ -1425,7 +1446,6 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo
zval *prop, rv;
char *type = NULL, *filename = NULL;
struct mime_data_cb_arg *cb_arg;
- php_stream *stream;
php_stream_statbuf ssb;
size_t filesize = -1;
curl_seek_callback seekfunc = seek_cb;
@@ -1455,6 +1475,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo
zval_ptr_dtor(&ch->postfields);
ZVAL_COPY(&ch->postfields, zpostfields);
+ php_stream *stream;
if ((stream = php_stream_open_wrapper(ZSTR_VAL(postval), "rb", STREAM_MUST_SEEK, NULL))) {
if (!stream->readfilters.head && !php_stream_stat(stream, &ssb)) {
filesize = ssb.sb.st_size;
@@ -1541,7 +1562,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo
if (Z_TYPE_P(current) == IS_ARRAY) {
zval *current_element;
- ZEND_HASH_FOREACH_VAL(HASH_OF(current), current_element) {
+ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(current), current_element) {
add_simple_field(mime, string_key, current_element);
} ZEND_HASH_FOREACH_END();
@@ -1842,6 +1863,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
#if LIBCURL_VERSION_NUM >= 0x080900 /* Available since 8.9.0 */
case CURLOPT_TCP_KEEPCNT:
#endif
+ case CURLOPT_FOLLOWLOCATION:
lval = zval_get_long(zvalue);
if ((option == CURLOPT_PROTOCOLS || option == CURLOPT_REDIR_PROTOCOLS) &&
(PG(open_basedir) && *PG(open_basedir)) && (lval & CURLPROTO_FILE)) {
@@ -1874,14 +1896,11 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
case CURLOPT_SSLKEYTYPE:
case CURLOPT_SSL_CIPHER_LIST:
case CURLOPT_USERAGENT:
- case CURLOPT_USERPWD:
case CURLOPT_COOKIELIST:
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
- case CURLOPT_PASSWORD:
case CURLOPT_PROXYPASSWORD:
case CURLOPT_PROXYUSERNAME:
- case CURLOPT_USERNAME:
case CURLOPT_NOPROXY:
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
case CURLOPT_MAIL_FROM:
@@ -1975,6 +1994,12 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
case CURLOPT_HSTS:
#endif
case CURLOPT_KRBLEVEL:
+ // Authorization header would be implictly set
+ // with an empty string thus we explictly set the option
+ // to null to avoid this unwarranted side effect
+ case CURLOPT_USERPWD:
+ case CURLOPT_USERNAME:
+ case CURLOPT_PASSWORD:
{
if (Z_ISNULL_P(zvalue)) {
error = curl_easy_setopt(ch->cp, option, NULL);
@@ -2173,9 +2198,9 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
if (slist) {
if ((*ch->clone) == 1) {
- zend_hash_index_update_ptr(ch->to_free->slist, option, slist);
+ zend_hash_index_update_ptr(&ch->to_free->slist, option, slist);
} else {
- zend_hash_next_index_insert_ptr(ch->to_free->slist, slist);
+ zend_hash_next_index_insert_ptr(&ch->to_free->slist, slist);
}
}
@@ -2189,14 +2214,9 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
/* Do nothing, just backward compatibility */
break;
- case CURLOPT_FOLLOWLOCATION:
- lval = zend_is_true(zvalue);
- error = curl_easy_setopt(ch->cp, option, lval);
- break;
-
case CURLOPT_POSTFIELDS:
if (Z_TYPE_P(zvalue) == IS_ARRAY) {
- if (zend_hash_num_elements(HASH_OF(zvalue)) == 0) {
+ if (zend_hash_num_elements(Z_ARRVAL_P(zvalue)) == 0) {
/* no need to build the mime structure for empty hashtables;
also works around https://github.com/curl/curl/issues/6455 */
curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, "");
@@ -2223,6 +2243,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
break;
/* Curl off_t options */
+ case CURLOPT_INFILESIZE_LARGE:
case CURLOPT_MAX_RECV_SPEED_LARGE:
case CURLOPT_MAX_SEND_SPEED_LARGE:
case CURLOPT_MAXFILESIZE_LARGE:
@@ -2283,16 +2304,24 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
case CURLOPT_SHARE:
{
- if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) {
- php_curlsh *sh = Z_CURL_SHARE_P(zvalue);
- curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);
+ if (Z_TYPE_P(zvalue) != IS_OBJECT) {
+ break;
+ }
- if (ch->share) {
- OBJ_RELEASE(&ch->share->std);
- }
- GC_ADDREF(&sh->std);
- ch->share = sh;
+ if (Z_OBJCE_P(zvalue) != curl_share_ce && Z_OBJCE_P(zvalue) != curl_share_persistent_ce) {
+ break;
+ }
+
+ php_curlsh *sh = Z_CURL_SHARE_P(zvalue);
+
+ curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share);
+
+ if (ch->share) {
+ OBJ_RELEASE(&ch->share->std);
}
+
+ GC_ADDREF(&sh->std);
+ ch->share = sh;
}
break;
@@ -2381,7 +2410,7 @@ PHP_FUNCTION(curl_setopt_array)
ch = Z_CURL_P(zid);
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), option, string_key, entry) {
- if (string_key) {
+ if (UNEXPECTED(string_key)) {
zend_argument_value_error(2, "contains an invalid cURL option");
RETURN_THROWS();
}
@@ -2634,6 +2663,19 @@ PHP_FUNCTION(curl_getinfo)
if (curl_easy_getinfo(ch->cp, CURLINFO_CAINFO, &s_code) == CURLE_OK) {
CAAS("cainfo", s_code);
}
+#endif
+#if LIBCURL_VERSION_NUM >= 0x080700 /* Available since 8.7.0 */
+ if (curl_easy_getinfo(ch->cp, CURLINFO_USED_PROXY, &l_code) == CURLE_OK) {
+ CAAL("used_proxy", l_code);
+ }
+#endif
+#if LIBCURL_VERSION_NUM >= 0x080c00 /* Available since 8.12.0 */
+ if (curl_easy_getinfo(ch->cp, CURLINFO_HTTPAUTH_USED, &l_code) == CURLE_OK) {
+ CAAL("httpauth_used", l_code);
+ }
+ if (curl_easy_getinfo(ch->cp, CURLINFO_PROXYAUTH_USED, &l_code) == CURLE_OK) {
+ CAAL("proxyauth_used", l_code);
+ }
#endif
} else {
switch (option) {
@@ -2704,6 +2746,7 @@ PHP_FUNCTION(curl_getinfo)
if (curl_easy_getinfo(ch->cp, option, &slist) == CURLE_OK) {
struct curl_slist *current = slist;
array_init(return_value);
+ zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
while (current) {
add_next_index_string(return_value, current->data);
current = current->next;
@@ -2816,8 +2859,7 @@ static void curl_free_obj(zend_object *object)
zend_llist_clean(&ch->to_free->post);
zend_llist_clean(&ch->to_free->stream);
- zend_hash_destroy(ch->to_free->slist);
- efree(ch->to_free->slist);
+ zend_hash_destroy(&ch->to_free->slist);
efree(ch->to_free);
efree(ch->clone);
}
diff --git a/ext/curl/multi.c b/ext/curl/multi.c
index bb601f575dbba..f664bc05e0e89 100644
--- a/ext/curl/multi.c
+++ b/ext/curl/multi.c
@@ -174,6 +174,30 @@ PHP_FUNCTION(curl_multi_remove_handle)
}
/* }}} */
+PHP_FUNCTION(curl_multi_get_handles)
+{
+ zval *z_mh;
+ php_curlm *mh;
+
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_OBJECT_OF_CLASS(z_mh, curl_multi_ce)
+ ZEND_PARSE_PARAMETERS_END();
+
+ mh = Z_CURL_MULTI_P(z_mh);
+
+ array_init_size(return_value, zend_llist_count(&mh->easyh));
+ zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
+ zend_llist_position pos;
+ zval *pz_ch;
+
+ for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
+ pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
+
+ Z_TRY_ADDREF_P(pz_ch);
+ add_next_index_zval(return_value, pz_ch);
+ }
+}
+
/* {{{ Get all the sockets associated with the cURL extension, which can then be "selected" */
PHP_FUNCTION(curl_multi_select)
{
@@ -191,8 +215,8 @@ PHP_FUNCTION(curl_multi_select)
mh = Z_CURL_MULTI_P(z_mh);
- if (!(timeout >= 0.0 && timeout <= ((double)INT_MAX / 1000.0))) {
- zend_argument_value_error(2, "must be between 0 and %d", (int)ceilf((double)INT_MAX / 1000));
+ if (!(timeout >= 0.0 && timeout <= (INT_MAX / 1000.0))) {
+ zend_argument_value_error(2, "must be between 0 and %f", INT_MAX / 1000.0);
RETURN_THROWS();
}
@@ -380,7 +404,7 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
php_curl *ch;
php_curl *parent;
php_curlm *mh = (php_curlm *)userp;
- size_t rval = CURL_PUSH_DENY;
+ int rval = CURL_PUSH_DENY;
zval *pz_parent_ch = NULL;
zval pz_ch;
zval headers;
@@ -397,10 +421,11 @@ static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_hea
ch->cp = easy;
_php_setup_easy_copy_handlers(ch, parent);
- array_init(&headers);
+ array_init_size(&headers, num_headers);
+ zend_hash_real_init_packed(Z_ARRVAL(headers));
for (size_t i = 0; i < num_headers; i++) {
char *header = curl_pushheader_bynum(push_headers, i);
- add_next_index_string(&headers, header);
+ add_index_string(&headers, i, header);
}
ZEND_ASSERT(pz_parent_ch);
@@ -501,11 +526,7 @@ PHP_FUNCTION(curl_multi_setopt)
mh = Z_CURL_MULTI_P(z_mh);
- if (_php_curl_multi_setopt(mh, options, zvalue, return_value)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(_php_curl_multi_setopt(mh, options, zvalue, return_value));
}
/* }}} */
@@ -575,7 +596,9 @@ static HashTable *curl_multi_get_gc(zend_object *object, zval **table, int *n)
zend_get_gc_buffer_use(gc_buffer, table, n);
- return zend_std_get_properties(object);
+ /* CurlMultiHandle can never have properties as it's final and has strict-properties on.
+ * Avoid building a hash table. */
+ return NULL;
}
static zend_object_handlers curl_multi_handlers;
diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h
index bc92c51121ec8..6084d5935c706 100644
--- a/ext/curl/php_curl.h
+++ b/ext/curl/php_curl.h
@@ -37,6 +37,7 @@ extern zend_module_entry curl_module_entry;
PHP_CURL_API extern zend_class_entry *curl_ce;
PHP_CURL_API extern zend_class_entry *curl_share_ce;
+PHP_CURL_API extern zend_class_entry *curl_share_persistent_ce;
PHP_CURL_API extern zend_class_entry *curl_multi_ce;
PHP_CURL_API extern zend_class_entry *curl_CURLFile_class;
PHP_CURL_API extern zend_class_entry *curl_CURLStringFile_class;
diff --git a/ext/curl/share.c b/ext/curl/share.c
index 406982d203275..e5f9e3d807dc2 100644
--- a/ext/curl/share.c
+++ b/ext/curl/share.c
@@ -21,6 +21,7 @@
#endif
#include "php.h"
+#include "Zend/zend_exceptions.h"
#include "curl_private.h"
@@ -53,7 +54,7 @@ PHP_FUNCTION(curl_share_close)
}
/* }}} */
-static bool _php_curl_share_setopt(php_curlsh *sh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
+static bool _php_curl_share_setopt(php_curlsh *sh, zend_long option, const zval *zvalue) /* {{{ */
{
CURLSHcode error = CURLSHE_OK;
@@ -90,11 +91,7 @@ PHP_FUNCTION(curl_share_setopt)
sh = Z_CURL_SHARE_P(z_sh);
- if (_php_curl_share_setopt(sh, options, zvalue, return_value)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(_php_curl_share_setopt(sh, options, zvalue));
}
/* }}} */
@@ -134,6 +131,151 @@ PHP_FUNCTION(curl_share_strerror)
}
/* }}} */
+/**
+ * Initialize a persistent curl share handle with the given share options, reusing an existing one if found.
+ *
+ * Throws an exception if the share options are invalid.
+ */
+PHP_FUNCTION(curl_share_init_persistent)
+{
+ // Options specified by the user.
+ HashTable *share_opts = NULL;
+
+ // De-duplicated and sorted options.
+ zval share_opts_prop;
+ ZVAL_UNDEF(&share_opts_prop);
+
+ // An ID representing the unique set of options specified by the user.
+ zend_ulong persistent_id = 0;
+
+ php_curlsh *sh = NULL;
+
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ARRAY_HT(share_opts)
+ ZEND_PARSE_PARAMETERS_END();
+
+ if (zend_hash_num_elements(share_opts) == 0) {
+ zend_argument_must_not_be_empty_error(1);
+ goto error;
+ }
+
+ ZEND_HASH_FOREACH_VAL(share_opts, zval *entry) {
+ ZVAL_DEREF(entry);
+
+ bool failed = false;
+ zend_ulong option = zval_try_get_long(entry, &failed);
+
+ if (failed) {
+ zend_argument_type_error(1, "must contain only int values, %s given", zend_zval_value_name(entry));
+ goto error;
+ }
+
+ switch (option) {
+ // Disallowed options
+ case CURL_LOCK_DATA_COOKIE:
+ zend_argument_value_error(1, "must not contain CURL_LOCK_DATA_COOKIE because sharing cookies across PHP requests is unsafe");
+ goto error;
+
+ // Allowed options
+ case CURL_LOCK_DATA_DNS:
+ persistent_id |= 1 << 0;
+ break;
+ case CURL_LOCK_DATA_SSL_SESSION:
+ persistent_id |= 1 << 1;
+ break;
+ case CURL_LOCK_DATA_CONNECT:
+ persistent_id |= 1 << 2;
+ break;
+ case CURL_LOCK_DATA_PSL:
+ persistent_id |= 1 << 3;
+ break;
+
+ // Unknown options
+ default:
+ zend_argument_value_error(1, "must contain only CURL_LOCK_DATA_* constants");
+ goto error;
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ // We're now decently confident that we'll be returning a CurlSharePersistentHandle object, so we construct it here.
+ object_init_ex(return_value, curl_share_persistent_ce);
+
+ // Next we initialize a property field for the CurlSharePersistentHandle object with the enabled share options.
+ array_init(&share_opts_prop);
+
+ if (persistent_id & (1 << 0)) {
+ add_next_index_long(&share_opts_prop, CURL_LOCK_DATA_DNS);
+ }
+
+ if (persistent_id & (1 << 1)) {
+ add_next_index_long(&share_opts_prop, CURL_LOCK_DATA_SSL_SESSION);
+ }
+
+ if (persistent_id & (1 << 2)) {
+ add_next_index_long(&share_opts_prop, CURL_LOCK_DATA_CONNECT);
+ }
+
+ if (persistent_id & (1 << 3)) {
+ add_next_index_long(&share_opts_prop, CURL_LOCK_DATA_PSL);
+ }
+
+ zend_update_property(curl_share_persistent_ce, Z_OBJ_P(return_value), ZEND_STRL("options"), &share_opts_prop);
+
+ sh = Z_CURL_SHARE_P(return_value);
+
+ // If we are able to find an existing persistent share handle, we can use it and return early.
+ zval *persisted = zend_hash_index_find(&CURL_G(persistent_curlsh), persistent_id);
+
+ if (persisted) {
+ sh->share = Z_PTR_P(persisted);
+
+ goto cleanup;
+ }
+
+ // We could not find an existing share handle, so we'll have to create one.
+ sh->share = curl_share_init();
+
+ // Apply the options property to the handle. We avoid using $share_opts as zval_get_long may not necessarily return
+ // the same value as it did in the initial ZEND_HASH_FOREACH_VAL above.
+ ZEND_HASH_PACKED_FOREACH_VAL(Z_ARRVAL_P(&share_opts_prop), zval *entry) {
+ CURLSHcode curlsh_error = curl_share_setopt(sh->share, CURLSHOPT_SHARE, Z_LVAL_P(entry));
+
+ if (curlsh_error != CURLSHE_OK) {
+ zend_throw_exception_ex(NULL, 0, "Could not construct persistent cURL share handle: %s", curl_share_strerror(curlsh_error));
+
+ goto error;
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ zend_hash_index_add_new_ptr(&CURL_G(persistent_curlsh), persistent_id, sh->share);
+
+ cleanup:
+ zval_ptr_dtor(&share_opts_prop);
+
+ return;
+
+ error:
+ if (sh) {
+ curl_share_cleanup(sh->share);
+ }
+
+ zval_ptr_dtor(&share_opts_prop);
+
+ RETURN_THROWS();
+}
+
+/**
+ * Free a persistent curl share handle from the module global HashTable.
+ *
+ * See PHP_GINIT_FUNCTION in ext/curl/interface.c.
+ */
+void curl_share_free_persistent_curlsh(zval *data)
+{
+ CURLSH *handle = Z_PTR_P(data);
+
+ curl_share_cleanup(handle);
+}
+
/* CurlShareHandle class */
static zend_object *curl_share_create_object(zend_class_entry *class_type) {
@@ -171,3 +313,23 @@ void curl_share_register_handlers(void) {
curl_share_handlers.clone_obj = NULL;
curl_share_handlers.compare = zend_objects_not_comparable;
}
+
+/* CurlSharePersistentHandle class */
+
+static zend_object_handlers curl_share_persistent_handlers;
+
+static zend_function *curl_share_persistent_get_constructor(zend_object *object) {
+ zend_throw_error(NULL, "Cannot directly construct CurlSharePersistentHandle, use curl_share_init_persistent() instead");
+ return NULL;
+}
+
+void curl_share_persistent_register_handlers(void) {
+ curl_share_persistent_ce->create_object = curl_share_create_object;
+ curl_share_persistent_ce->default_object_handlers = &curl_share_persistent_handlers;
+
+ memcpy(&curl_share_persistent_handlers, &std_object_handlers, sizeof(zend_object_handlers));
+ curl_share_persistent_handlers.offset = XtOffsetOf(php_curlsh, std);
+ curl_share_persistent_handlers.get_constructor = curl_share_persistent_get_constructor;
+ curl_share_persistent_handlers.clone_obj = NULL;
+ curl_share_persistent_handlers.compare = zend_objects_not_comparable;
+}
diff --git a/ext/curl/sync-constants.php b/ext/curl/sync-constants.php
index 87868bb5b31d9..e24c773a52093 100755
--- a/ext/curl/sync-constants.php
+++ b/ext/curl/sync-constants.php
@@ -19,6 +19,30 @@
'CURLOPT_XFERINFODATA',
'CURLOPT_PREREQDATA',
'CURLOPT_DEBUGDATA',
+ 'CURLOPT_SSL_CTX_DATA',
+ 'CURLOPT_SOCKOPTDATA',
+ 'CURLOPT_OPENSOCKETDATA',
+ 'CURLOPT_SEEKDATA',
+ 'CURLOPT_FNMATCH_DATA',
+ 'CURLOPT_CLOSESOCKETDATA',
+ 'CURLOPT_RESOLVER_START_DATA',
+ 'CURLOPT_TRAILERDATA',
+ 'CURLOPT_HSTSREADDATA',
+ 'CURLOPT_SSH_HOSTKEYDATA',
+ 'CURLOPT_WRITEDATA',
+ 'CURLOPT_HEADERDATA',
+ 'CURLOPT_IOCTLDATA',
+ 'CURLOPT_SSH_KEYDATA',
+ 'CURLOPT_INTERLEAVEDATA',
+ 'CURLOPT_HSTSWRITEDATA',
+ 'CURLINFO_TYPEMASK',
+ 'CURLINFO_STRING',
+ 'CURLINFO_NONE',
+ 'CURLINFO_MASK',
+ 'CURLINFO_LONG',
+ 'CURLINFO_DOUBLE',
+ 'CURLOPT_CLOSEPOLICY',
+ 'CURLINFO_END',
];
const IGNORED_PHP_CONSTANTS = [
diff --git a/ext/curl/tests/Caddyfile b/ext/curl/tests/Caddyfile
index 28deb40926fc0..67b82434ba4c6 100644
--- a/ext/curl/tests/Caddyfile
+++ b/ext/curl/tests/Caddyfile
@@ -11,3 +11,13 @@ respond / "Caddy is up and running"
respond /serverpush "main response"
respond /serverpush/pushed "pushed response"
push /serverpush /serverpush/pushed
+
+route /show_upload_size {
+ templates
+ respond `Content-length: ={{.Req.Header.Get "Content-length"}}=`
+}
+
+basic_auth /http-basic-auth {
+ # bcrypt password hash for "password", calculated with 'caddy hash-password'
+ user $2a$14$yUKl9SGqVTAAqPTzLup.DefsbXXx3kfreNnzpJOUHcIrKnr5lgef2
+}
diff --git a/ext/curl/tests/bug71144.phpt b/ext/curl/tests/bug71144.phpt
index 1684ccbbb2d1b..b808aec2637e8 100644
--- a/ext/curl/tests/bug71144.phpt
+++ b/ext/curl/tests/bug71144.phpt
@@ -1,5 +1,5 @@
--TEST--
-Bug #71144 (Sementation fault when using cURL with ZTS)
+Bug #71144 (Segmentation fault when using cURL with ZTS)
--DESCRIPTION--
Since Curl 7.62, CURLOPT_DNS_USE_GLOBAL_CACHE has no effect, and is
silently ignored.
diff --git a/ext/curl/tests/bug77535.phpt b/ext/curl/tests/bug77535.phpt
index 522818516ae69..600bfef62b9aa 100644
--- a/ext/curl/tests/bug77535.phpt
+++ b/ext/curl/tests/bug77535.phpt
@@ -2,7 +2,7 @@
Bug #77535 (Invalid callback, h2 server push)
--EXTENSIONS--
curl
---XLEAK--
+--XFAIL--
--SKIPIF--
= 8.12.0");
+include 'skipif-nocaddy.inc';
+?>
+--FILE--
+
+--EXPECT--
+bool(true)
+bool(true)
diff --git a/ext/curl/tests/curl_getinfo_CURLINFO_HTTPAUTH_USED.phpt b/ext/curl/tests/curl_getinfo_CURLINFO_HTTPAUTH_USED.phpt
new file mode 100644
index 0000000000000..ea004d3dc9eb4
--- /dev/null
+++ b/ext/curl/tests/curl_getinfo_CURLINFO_HTTPAUTH_USED.phpt
@@ -0,0 +1,66 @@
+--TEST--
+curl_getinfo - CURLINFO_HTTPAUTH_USED
+--EXTENSIONS--
+curl
+--SKIPIF--
+= 8.12.0");
+?>
+--FILE--
+
+--EXPECT--
+httpauth_used and proxyauth_used empty
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+httpauth_used and proxyauth_used empty after request
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+httpauth_used set after request
+int(0)
diff --git a/ext/curl/tests/curl_getinfo_CURLINFO_USED_PROXY.phpt b/ext/curl/tests/curl_getinfo_CURLINFO_USED_PROXY.phpt
new file mode 100644
index 0000000000000..7020a70f5247d
--- /dev/null
+++ b/ext/curl/tests/curl_getinfo_CURLINFO_USED_PROXY.phpt
@@ -0,0 +1,38 @@
+--TEST--
+curl_getinfo - CURLINFO_USED_PROXY
+--EXTENSIONS--
+curl
+--SKIPIF--
+= 8.7.0");
+?>
+--FILE--
+
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
diff --git a/ext/curl/tests/curl_multi_get_handles.phpt b/ext/curl/tests/curl_multi_get_handles.phpt
new file mode 100644
index 0000000000000..41413ac098489
--- /dev/null
+++ b/ext/curl/tests/curl_multi_get_handles.phpt
@@ -0,0 +1,71 @@
+--TEST--
+array curl_multi_get_handles ( CurlMultiHandle $mh );
+--EXTENSIONS--
+curl
+--FILE--
+
+--EXPECTF--
+Initializing %scurl_testdata1.txt.
+1 handles are attached
+Initializing %scurl_testdata2.txt.
+2 handles are attached
+Request to %scurl_testdata%d.txt finished.
+2 handles are attached
+1 handles are attached
+Success.
+Request to %scurl_testdata%d.txt finished.
+1 handles are attached
+0 handles are attached
+Success.
+0 handles are attached
diff --git a/ext/curl/tests/curl_multi_setopt_callables.phpt b/ext/curl/tests/curl_multi_setopt_callables.phpt
index c0de5add49101..5da1fbf8ef45c 100644
--- a/ext/curl/tests/curl_multi_setopt_callables.phpt
+++ b/ext/curl/tests/curl_multi_setopt_callables.phpt
@@ -1,5 +1,5 @@
--TEST--
-Test curl_multi_setopt() with options that take callabes
+Test curl_multi_setopt() with options that take callables
--EXTENSIONS--
curl
--FILE--
diff --git a/ext/curl/tests/curl_persistent_share_001.phpt b/ext/curl/tests/curl_persistent_share_001.phpt
new file mode 100644
index 0000000000000..70da0d5880f75
--- /dev/null
+++ b/ext/curl/tests/curl_persistent_share_001.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Basic curl persistent share handle test
+--EXTENSIONS--
+curl
+--SKIPIF--
+
+--FILE--
+options);
+
+$sh2 = curl_share_init_persistent([CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_CONNECT]);
+$ch2 = get_localhost_curl_handle($sh2);
+
+// Expect the connect time on the subsequent request to be zero, since it's reusing the connection.
+var_dump(curl_exec($ch1));
+var_dump(curl_exec($ch2));
+var_dump("second connect_time: " . (curl_getinfo($ch2)["connect_time"]));
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ int(3)
+ [1]=>
+ int(5)
+}
+string(23) "Caddy is up and running"
+string(23) "Caddy is up and running"
+string(22) "second connect_time: 0"
diff --git a/ext/curl/tests/curl_persistent_share_002.phpt b/ext/curl/tests/curl_persistent_share_002.phpt
new file mode 100644
index 0000000000000..fbd6631b9ba13
--- /dev/null
+++ b/ext/curl/tests/curl_persistent_share_002.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Curl persistent share handle test with different options
+--EXTENSIONS--
+curl
+--SKIPIF--
+
+--FILE--
+options != $sh2->options);
+
+// Expect the connect time on the subsequent request to be non-zero, since it's *not* reusing the connection.
+var_dump(curl_exec($ch1));
+var_dump(curl_exec($ch2));
+var_dump("second connect_time: " . (curl_getinfo($ch2)["connect_time"]));
+
+?>
+--EXPECTREGEX--
+bool\(true\)
+string\(23\) "Caddy is up and running"
+string\(23\) "Caddy is up and running"
+string\(\d+\) "second connect_time: .*[1-9].*"
diff --git a/ext/curl/tests/curl_persistent_share_003.phpt b/ext/curl/tests/curl_persistent_share_003.phpt
new file mode 100644
index 0000000000000..60a30de4d4641
--- /dev/null
+++ b/ext/curl/tests/curl_persistent_share_003.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Curl persistent share handle test with invalid option
+--EXTENSIONS--
+curl
+--FILE--
+getMessage() . PHP_EOL;
+}
+
+?>
+--EXPECT--
+curl_share_init_persistent(): Argument #1 ($share_options) must contain only CURL_LOCK_DATA_* constants
diff --git a/ext/curl/tests/curl_persistent_share_004.phpt b/ext/curl/tests/curl_persistent_share_004.phpt
new file mode 100644
index 0000000000000..fe0e7ec2cf0ed
--- /dev/null
+++ b/ext/curl/tests/curl_persistent_share_004.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Curl persistent share handle test with disallowed option
+--EXTENSIONS--
+curl
+--FILE--
+getMessage() . PHP_EOL;
+}
+
+?>
+--EXPECT--
+curl_share_init_persistent(): Argument #1 ($share_options) must not contain CURL_LOCK_DATA_COOKIE because sharing cookies across PHP requests is unsafe
diff --git a/ext/curl/tests/curl_persistent_share_005.phpt b/ext/curl/tests/curl_persistent_share_005.phpt
new file mode 100644
index 0000000000000..8737ba6c39a42
--- /dev/null
+++ b/ext/curl/tests/curl_persistent_share_005.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Curl persistent share handles cannot be used with curl_share_setopt
+--EXTENSIONS--
+curl
+--FILE--
+getMessage() . PHP_EOL;
+}
+
+?>
+--EXPECT--
+curl_share_setopt(): Argument #1 ($share_handle) must be of type CurlShareHandle, CurlSharePersistentHandle given
diff --git a/ext/curl/tests/curl_persistent_share_006.phpt b/ext/curl/tests/curl_persistent_share_006.phpt
new file mode 100644
index 0000000000000..ffca764757fbb
--- /dev/null
+++ b/ext/curl/tests/curl_persistent_share_006.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Curl persistent share handles must be initialized with a non-empty $share_opts
+--EXTENSIONS--
+curl
+--FILE--
+getMessage() . PHP_EOL;
+}
+
+?>
+--EXPECT--
+curl_share_init_persistent(): Argument #1 ($share_options) must not be empty
diff --git a/ext/curl/tests/curl_pushfunction_trampoline.phpt b/ext/curl/tests/curl_pushfunction_trampoline.phpt
index 73a23097968e2..55eda0de1065a 100644
--- a/ext/curl/tests/curl_pushfunction_trampoline.phpt
+++ b/ext/curl/tests/curl_pushfunction_trampoline.phpt
@@ -2,7 +2,7 @@
Test trampoline for curl option CURLMOPT_PUSHFUNCTION
--EXTENSIONS--
curl
---XLEAK--
+--XFAIL--
--SKIPIF--
= 8.13.0');
+?>
+--FILE--
+
+--EXPECTF--
+bool(true)
+string(%d) "%s"
+bool(true)
+string(%d) "%s"
+bool(true)
+string(%d) "%s"
+bool(true)
+string(%d) "%s"
+
diff --git a/ext/curl/tests/curl_setopt_CURLOPT_INFILESIZE_LARGE.phpt b/ext/curl/tests/curl_setopt_CURLOPT_INFILESIZE_LARGE.phpt
new file mode 100644
index 0000000000000..5063958412b5a
--- /dev/null
+++ b/ext/curl/tests/curl_setopt_CURLOPT_INFILESIZE_LARGE.phpt
@@ -0,0 +1,58 @@
+--TEST--
+Curl option CURLOPT_INFILESIZE_LARGE
+--EXTENSIONS--
+curl
+--SKIPIF--
+
+--FILE--
+ true,
+ CURLOPT_UPLOAD => true,
+ CURLOPT_INFILE => $f,
+ CURLOPT_INFILESIZE => filesize(__FILE__),
+ CURLOPT_CONNECTTIMEOUT => 3,
+ CURLOPT_TIMEOUT => 3,
+ CURLOPT_SSL_VERIFYHOST => 0,
+ CURLOPT_SSL_VERIFYPEER => 0,
+]));
+
+var_dump(curl_exec($ch));
+
+var_dump(curl_setopt($ch, CURLOPT_INFILESIZE, -1));
+var_dump(curl_exec($ch));
+
+var_dump(curl_setopt($ch, CURLOPT_INFILESIZE_LARGE, -1));
+var_dump(curl_exec($ch));
+
+rewind($f);
+var_dump(curl_setopt($ch, CURLOPT_INFILESIZE, filesize(__FILE__)));
+var_dump(curl_exec($ch));
+var_dump(curl_setopt($ch, CURLOPT_INFILESIZE_LARGE, -1));
+var_dump(curl_exec($ch));
+
+rewind($f);
+var_dump(curl_setopt($ch, CURLOPT_INFILESIZE_LARGE, filesize(__FILE__)));
+var_dump(curl_exec($ch));
+
+var_dump(curl_error($ch));
+
+?>
+--EXPECTF--
+bool(true)
+string(21) "Content-length: =%d="
+bool(true)
+string(18) "Content-length: =="
+bool(true)
+string(18) "Content-length: =="
+bool(true)
+string(21) "Content-length: =%d="
+bool(true)
+string(18) "Content-length: =="
+bool(true)
+string(21) "Content-length: =%d="
+string(0) ""
diff --git a/ext/curl/tests/curl_setopt_CURLOPT_PREREQFUNCTION.phpt b/ext/curl/tests/curl_setopt_CURLOPT_PREREQFUNCTION.phpt
index d11e29f078c09..58d01df2a8866 100644
--- a/ext/curl/tests/curl_setopt_CURLOPT_PREREQFUNCTION.phpt
+++ b/ext/curl/tests/curl_setopt_CURLOPT_PREREQFUNCTION.phpt
@@ -48,6 +48,7 @@ $result = curl_exec($ch);
var_dump($result);
var_dump(curl_error($ch));
var_dump(curl_errno($ch));
+var_dump(curl_errno($ch) === CURLE_ABORTED_BY_CALLBACK);
$returnValue = CURL_PREREQFUNC_OK;
@@ -133,6 +134,7 @@ bool(true)
bool(false)
string(41) "operation aborted by pre-request callback"
int(42)
+bool(true)
Testing with CURL_PREREQFUNC_OK
string(8) "callback"
diff --git a/ext/curl/tests/curl_setopt_callables.phpt b/ext/curl/tests/curl_setopt_callables.phpt
index 6cde8fef6d9c7..aaa83102afac9 100644
--- a/ext/curl/tests/curl_setopt_callables.phpt
+++ b/ext/curl/tests/curl_setopt_callables.phpt
@@ -1,5 +1,5 @@
--TEST--
-Test curl_setopt(_array)() with options that take callabes
+Test curl_setopt(_array)() with options that take callables
--EXTENSIONS--
curl
--FILE--
diff --git a/ext/curl/tests/curl_setopt_ssl.phpt b/ext/curl/tests/curl_setopt_ssl.phpt
index 11d8fff702a88..3f345930f6244 100644
--- a/ext/curl/tests/curl_setopt_ssl.phpt
+++ b/ext/curl/tests/curl_setopt_ssl.phpt
@@ -7,7 +7,7 @@ curl
if (!function_exists("proc_open")) die("skip no proc_open");
exec('openssl version', $out, $code);
if ($code > 0) die("skip couldn't locate openssl binary");
-if (PHP_OS_FAMILY === 'Windows') die('skip not for Windows');
+
if (PHP_OS_FAMILY === 'Darwin') die('skip Fails intermittently on macOS');
if (PHP_OS === 'FreeBSD') die('skip proc_open seems to be stuck on FreeBSD');
$curl_version = curl_version();
@@ -62,7 +62,7 @@ $port = 14430;
// set up local server
$cmd = "openssl s_server -key $serverKeyPath -cert $serverCertPath -accept $port -www -CAfile $clientCertPath -verify_return_error -Verify 1";
-$process = proc_open($cmd, [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]], $pipes);
+$process = proc_open($cmd, [["pipe", "r"], ["pipe", "w"], ["pipe", "w"]], $pipes, null, null, ["bypass_shell" => true]);
if ($process === false) {
die('failed to start server');
diff --git a/ext/curl/tests/gh15547.phpt b/ext/curl/tests/gh15547.phpt
index cee3f00cdb9f8..489fdd0b999a0 100644
--- a/ext/curl/tests/gh15547.phpt
+++ b/ext/curl/tests/gh15547.phpt
@@ -24,6 +24,6 @@ $mh = curl_multi_init();
var_dump(curl_multi_select($mh, 1000000));
?>
--EXPECTF--
-curl_multi_select(): Argument #2 ($timeout) must be between %d and %d
-curl_multi_select(): Argument #2 ($timeout) must be between %d and %d
+curl_multi_select(): Argument #2 ($timeout) must be between %d and %f
+curl_multi_select(): Argument #2 ($timeout) must be between %d and %f
int(0)
diff --git a/ext/curl/tests/gh18458.phpt b/ext/curl/tests/gh18458.phpt
new file mode 100644
index 0000000000000..702737ac369ba
--- /dev/null
+++ b/ext/curl/tests/gh18458.phpt
@@ -0,0 +1,33 @@
+--TEST--
+GH-18458 (authorization header is set despite CURLOPT_USERPWD set to null)
+--EXTENSIONS--
+curl
+--SKIPIF--
+
+--FILE--
+
+--EXPECTF--
+%A
+bool(false)
+%A
+bool(false)
diff --git a/ext/curl/tests/skipif-nocaddy.inc b/ext/curl/tests/skipif-nocaddy.inc
index 21a06c12af106..ae5442ff28ede 100644
--- a/ext/curl/tests/skipif-nocaddy.inc
+++ b/ext/curl/tests/skipif-nocaddy.inc
@@ -2,6 +2,7 @@
$ch = curl_init("https://localhost");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$body = curl_exec($ch);
diff --git a/ext/date/config0.m4 b/ext/date/config0.m4
index 5af6be13faf71..9091803edfa45 100644
--- a/ext/date/config0.m4
+++ b/ext/date/config0.m4
@@ -8,15 +8,24 @@ AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough],
[PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -Wno-implicit-fallthrough"],,
[-Werror])
-PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -DHAVE_TIMELIB_CONFIG_H=1"
+PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -DHAVE_TIMELIB_CONFIG_H=1"
+PHP_TIMELIB_CFLAGS="$PHP_DATE_CFLAGS"
+PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"
+
+AX_CHECK_COMPILE_FLAG([-fwrapv],
+ [PHP_TIMELIB_CFLAGS="$PHP_TIMELIB_CFLAGS -fwrapv"],,
+ [-Werror])
+
timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c lib/parse_posix.c
lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c lib/parse_iso_intervals.c lib/interval.c"
PHP_NEW_EXTENSION([date],
- [php_date.c $timelib_sources],
+ [php_date.c],
[no],,
[$PHP_DATE_CFLAGS])
+PHP_ADD_SOURCES([$ext_dir], [$timelib_sources], [$PHP_TIMELIB_CFLAGS])
+
PHP_ADD_BUILD_DIR([$ext_builddir/lib], [1])
PHP_ADD_INCLUDE([$ext_builddir/lib])
PHP_ADD_INCLUDE([$ext_srcdir/lib])
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 2148048be4fd4..a991c5072f941 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -360,10 +360,12 @@ static int date_interval_compare_objects(zval *o1, zval *o2);
static zval *date_interval_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv);
static zval *date_interval_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot);
static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot);
+static int date_period_has_property(zend_object *object, zend_string *name, int type, void **cache_slot);
static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv);
static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot);
static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot);
-
+static void date_period_unset_property(zend_object *object, zend_string *name, void **cache_slot);
+static HashTable *date_period_get_properties_for(zend_object *object, zend_prop_purpose purpose);
static int date_object_compare_timezone(zval *tz1, zval *tz2);
/* {{{ Module struct */
@@ -671,7 +673,7 @@ static const char *php_date_short_day_name(timelib_sll y, timelib_sll m, timelib
/* }}} */
/* {{{ date_format - (gm)date helper */
-static zend_string *date_format(const char *format, size_t format_len, timelib_time *t, bool localtime)
+static zend_string *date_format(const char *format, size_t format_len, const timelib_time *t, bool localtime)
{
smart_str string = {0};
size_t i;
@@ -843,7 +845,7 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t
return string.s;
}
-PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj)
+PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, const php_date_obj *date_obj)
{
if (!date_obj->time) {
return NULL;
@@ -1505,45 +1507,6 @@ static void create_date_period_interval(timelib_rel_time *interval, zval *zv)
}
}
-static void write_date_period_property(zend_object *obj, const char *name, const size_t name_len, zval *zv)
-{
- zend_string *property_name = zend_string_init(name, name_len, 0);
-
- zend_std_write_property(obj, property_name, zv, NULL);
-
- zval_ptr_dtor(zv);
- zend_string_release(property_name);
-}
-
-static void initialize_date_period_properties(php_period_obj *period_obj)
-{
- zval zv;
-
- /* rebuild properties */
- zend_std_get_properties_ex(&period_obj->std);
-
- create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv);
- write_date_period_property(&period_obj->std, "start", sizeof("start") - 1, &zv);
-
- create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv);
- write_date_period_property(&period_obj->std, "current", sizeof("current") - 1, &zv);
-
- create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv);
- write_date_period_property(&period_obj->std, "end", sizeof("end") - 1, &zv);
-
- create_date_period_interval(period_obj->interval, &zv);
- write_date_period_property(&period_obj->std, "interval", sizeof("interval") - 1, &zv);
-
- ZVAL_LONG(&zv, (zend_long) period_obj->recurrences);
- write_date_period_property(&period_obj->std, "recurrences", sizeof("recurrences") - 1, &zv);
-
- ZVAL_BOOL(&zv, period_obj->include_start_date);
- write_date_period_property(&period_obj->std, "include_start_date", sizeof("include_start_date") - 1, &zv);
-
- ZVAL_BOOL(&zv, period_obj->include_end_date);
- write_date_period_property(&period_obj->std, "include_end_date", sizeof("include_end_date") - 1, &zv);
-}
-
/* define an overloaded iterator structure */
typedef struct {
zend_object_iterator intern;
@@ -1663,10 +1626,7 @@ static void date_period_it_move_forward(zend_object_iterator *iter)
zend_std_get_properties_ex(&object->std);
create_date_period_datetime(object->current, object->start_ce, ¤t_zv);
- zend_string *property_name = ZSTR_INIT_LITERAL("current", 0);
- zend_std_write_property(&object->std, property_name, ¤t_zv, NULL);
zval_ptr_dtor(¤t_zv);
- zend_string_release(property_name);
iterator->current_index++;
date_period_it_invalidate_current(iter);
@@ -1837,8 +1797,11 @@ static void date_register_classes(void) /* {{{ */
date_object_handlers_period.clone_obj = date_object_clone_period;
date_object_handlers_period.get_gc = date_object_get_gc_period;
date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
+ date_object_handlers_period.has_property = date_period_has_property;
date_object_handlers_period.read_property = date_period_read_property;
date_object_handlers_period.write_property = date_period_write_property;
+ date_object_handlers_period.get_properties_for = date_period_get_properties_for;
+ date_object_handlers_period.unset_property = date_period_unset_property;
date_ce_date_error = register_class_DateError(zend_ce_error);
date_ce_date_object_error = register_class_DateObjectError(date_ce_date_error);
@@ -2736,7 +2699,7 @@ PHP_METHOD(DateTime, createFromInterface)
}
/* }}} */
-/* {{{ Creates new DateTime object from given unix timetamp */
+/* {{{ Creates new DateTime object from given unix timestamp */
PHP_METHOD(DateTime, createFromTimestamp)
{
zval *value;
@@ -2844,7 +2807,7 @@ PHP_METHOD(DateTimeImmutable, createFromTimestamp)
}
/* }}} */
-static bool php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht)
+static bool php_date_initialize_from_hash(php_date_obj **dateobj, const HashTable *myht)
{
zval *z_date;
zval *z_timezone_type;
@@ -2985,7 +2948,7 @@ PHP_METHOD(DateTimeImmutable, __serialize)
}
/* }}} */
-static bool date_time_is_internal_property(zend_string *name)
+static bool date_time_is_internal_property(const zend_string *name)
{
if (
zend_string_equals_literal(name, "date") ||
@@ -2997,7 +2960,7 @@ static bool date_time_is_internal_property(zend_string *name)
return 0;
}
-static void restore_custom_datetime_properties(zval *object, HashTable *myht)
+static void restore_custom_datetime_properties(zval *object, const HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;
@@ -3015,15 +2978,13 @@ PHP_METHOD(DateTime, __unserialize)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
dateobj = Z_PHPDATE_P(object);
- myht = Z_ARRVAL_P(array);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTime object");
@@ -3039,15 +3000,13 @@ PHP_METHOD(DateTimeImmutable, __unserialize)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
dateobj = Z_PHPDATE_P(object);
- myht = Z_ARRVAL_P(array);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
@@ -3058,12 +3017,13 @@ PHP_METHOD(DateTimeImmutable, __unserialize)
}
/* }}} */
-/* {{{ */
-PHP_METHOD(DateTime, __wakeup)
+/* {{{
+ * Common implementation for DateTime::__wakeup() and DateTimeImmutable::__wakeup() */
+static void php_do_date_time_wakeup(INTERNAL_FUNCTION_PARAMETERS, const char *class_name)
{
zval *object = ZEND_THIS;
php_date_obj *dateobj;
- HashTable *myht;
+ const HashTable *myht;
ZEND_PARSE_PARAMETERS_NONE();
@@ -3072,34 +3032,28 @@ PHP_METHOD(DateTime, __wakeup)
myht = Z_OBJPROP_P(object);
if (!php_date_initialize_from_hash(&dateobj, myht)) {
- zend_throw_error(NULL, "Invalid serialization data for DateTime object");
+ zend_throw_error(NULL, "Invalid serialization data for %s object", class_name);
RETURN_THROWS();
}
}
/* }}} */
/* {{{ */
-PHP_METHOD(DateTimeImmutable, __wakeup)
+PHP_METHOD(DateTime, __wakeup)
{
- zval *object = ZEND_THIS;
- php_date_obj *dateobj;
- HashTable *myht;
-
- ZEND_PARSE_PARAMETERS_NONE();
-
- dateobj = Z_PHPDATE_P(object);
-
- myht = Z_OBJPROP_P(object);
+ php_do_date_time_wakeup(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DateTime");
+}
+/* }}} */
- if (!php_date_initialize_from_hash(&dateobj, myht)) {
- zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
- RETURN_THROWS();
- }
+/* {{{ */
+PHP_METHOD(DateTimeImmutable, __wakeup)
+{
+ php_do_date_time_wakeup(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DateTimeImmutable");
}
/* }}} */
/* Helper function used to add an associative array of warnings and errors to a zval */
-static void zval_from_error_container(zval *z, timelib_error_container *error) /* {{{ */
+static void zval_from_error_container(zval *z, const timelib_error_container *error) /* {{{ */
{
int i;
zval element;
@@ -3540,7 +3494,7 @@ PHP_METHOD(DateTimeImmutable, sub)
}
/* }}} */
-static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
+static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, const timelib_time *t)
{
/* Free abbreviation if already set */
if (tzobj->initialized && tzobj->type == TIMELIB_ZONETYPE_ABBR) {
@@ -4099,7 +4053,7 @@ PHP_METHOD(DateTimeZone, __construct)
}
/* }}} */
-static bool php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht) /* {{{ */
+static bool php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, const HashTable *myht) /* {{{ */
{
zval *z_timezone_type;
@@ -4129,15 +4083,12 @@ static bool php_date_timezone_initialize_from_hash(zval **return_value, php_time
PHP_METHOD(DateTimeZone, __set_state)
{
php_timezone_obj *tzobj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
- myht = Z_ARRVAL_P(array);
-
php_date_instantiate(date_ce_timezone, return_value);
tzobj = Z_PHPTIMEZONE_P(return_value);
if (!php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht)) {
@@ -4152,7 +4103,7 @@ PHP_METHOD(DateTimeZone, __wakeup)
{
zval *object = ZEND_THIS;
php_timezone_obj *tzobj;
- HashTable *myht;
+ const HashTable *myht;
ZEND_PARSE_PARAMETERS_NONE();
@@ -4187,7 +4138,7 @@ PHP_METHOD(DateTimeZone, __serialize)
}
/* }}} */
-static bool date_timezone_is_internal_property(zend_string *name)
+static bool date_timezone_is_internal_property(const zend_string *name)
{
if (
zend_string_equals_literal(name, "timezone_type") ||
@@ -4198,7 +4149,7 @@ static bool date_timezone_is_internal_property(zend_string *name)
return 0;
}
-static void restore_custom_datetimezone_properties(zval *object, HashTable *myht)
+static void restore_custom_datetimezone_properties(zval *object, const HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;
@@ -4216,15 +4167,13 @@ PHP_METHOD(DateTimeZone, __unserialize)
{
zval *object = ZEND_THIS;
php_timezone_obj *tzobj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
tzobj = Z_PHPTIMEZONE_P(object);
- myht = Z_ARRVAL_P(array);
if (!php_date_timezone_initialize_from_hash(&object, &tzobj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DateTimeZone object");
@@ -4464,7 +4413,7 @@ PHP_FUNCTION(timezone_location_get)
}
/* }}} */
-static bool date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, size_t format_length) /* {{{ */
+static bool date_interval_initialize(timelib_rel_time **rt, const char *format, size_t format_length) /* {{{ */
{
timelib_time *b = NULL, *e = NULL;
timelib_rel_time *p = NULL;
@@ -4651,10 +4600,10 @@ PHP_METHOD(DateInterval, __construct)
}
/* }}} */
-static void php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht) /* {{{ */
+static void php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, const HashTable *myht) /* {{{ */
{
/* If we have a date_string, use that instead */
- zval *date_str = zend_hash_str_find(myht, "date_string", strlen("date_string"));
+ const zval *date_str = zend_hash_str_find(myht, "date_string", strlen("date_string"));
if (date_str && Z_TYPE_P(date_str) == IS_STRING) {
timelib_time *time;
timelib_error_container *err = NULL;
@@ -4726,9 +4675,10 @@ static void php_date_interval_initialize_from_hash(zval **return_value, php_inte
if (z_arg && Z_TYPE_P(z_arg) == IS_FALSE) { \
(*intobj)->diff->member = TIMELIB_UNSET; \
} else if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
- zend_string *str = zval_get_string(z_arg); \
+ zend_string *tmp_str; \
+ zend_string *str = zval_get_tmp_string(z_arg, &tmp_str); \
DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \
- zend_string_release(str); \
+ zend_tmp_string_release(tmp_str); \
} else { \
(*intobj)->diff->member = -1LL; \
} \
@@ -4751,7 +4701,7 @@ static void php_date_interval_initialize_from_hash(zval **return_value, php_inte
PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
{
- zval *z_arg = zend_hash_str_find(myht, "f", sizeof("f") - 1);
+ const zval *z_arg = zend_hash_str_find(myht, "f", sizeof("f") - 1);
if (z_arg) {
(*intobj)->diff->us = zend_dval_to_lval(zval_get_double(z_arg) * 1000000.0);
}
@@ -4766,7 +4716,7 @@ static void php_date_interval_initialize_from_hash(zval **return_value, php_inte
PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
{
- zval *z_arg = zend_hash_str_find(myht, "civil_or_wall", sizeof("civil_or_wall") - 1);
+ const zval *z_arg = zend_hash_str_find(myht, "civil_or_wall", sizeof("civil_or_wall") - 1);
(*intobj)->civil_or_wall = PHP_DATE_CIVIL;
if (z_arg) {
zend_long val = zval_get_long(z_arg);
@@ -4781,15 +4731,12 @@ static void php_date_interval_initialize_from_hash(zval **return_value, php_inte
PHP_METHOD(DateInterval, __set_state)
{
php_interval_obj *intobj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
- myht = Z_ARRVAL_P(array);
-
php_date_instantiate(date_ce_interval, return_value);
intobj = Z_PHPINTERVAL_P(return_value);
php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
@@ -4816,7 +4763,7 @@ PHP_METHOD(DateInterval, __serialize)
}
/* }}} */
-static bool date_interval_is_internal_property(zend_string *name)
+static bool date_interval_is_internal_property(const zend_string *name)
{
if (
zend_string_equals_literal(name, "date_string") ||
@@ -4836,7 +4783,7 @@ static bool date_interval_is_internal_property(zend_string *name)
return 0;
}
-static void restore_custom_dateinterval_properties(zval *object, HashTable *myht)
+static void restore_custom_dateinterval_properties(zval *object, const HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;
@@ -4855,15 +4802,13 @@ PHP_METHOD(DateInterval, __unserialize)
{
zval *object = ZEND_THIS;
php_interval_obj *intervalobj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
intervalobj = Z_PHPINTERVAL_P(object);
- myht = Z_ARRVAL_P(array);
php_date_interval_initialize_from_hash(&object, &intervalobj, myht);
restore_custom_dateinterval_properties(object, myht);
@@ -4875,7 +4820,7 @@ PHP_METHOD(DateInterval, __wakeup)
{
zval *object = ZEND_THIS;
php_interval_obj *intobj;
- HashTable *myht;
+ const HashTable *myht;
ZEND_PARSE_PARAMETERS_NONE();
@@ -4967,7 +4912,7 @@ PHP_METHOD(DateInterval, createFromDateString)
/* }}} */
/* {{{ date_interval_format - */
-static zend_string *date_interval_format(char *format, size_t format_len, timelib_rel_time *t)
+static zend_string *date_interval_format(const char *format, size_t format_len, timelib_rel_time *t)
{
smart_str string = {0};
size_t i;
@@ -5041,7 +4986,7 @@ PHP_FUNCTION(date_interval_format)
{
zval *object;
php_interval_obj *diobj;
- char *format;
+ const char *format;
size_t format_len;
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) {
@@ -5054,7 +4999,7 @@ PHP_FUNCTION(date_interval_format)
}
/* }}} */
-static bool date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, zend_long *recurrences, /*const*/ char *format, size_t format_length) /* {{{ */
+static bool date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, zend_long *recurrences, const char *format, size_t format_length) /* {{{ */
{
timelib_time *b = NULL, *e = NULL;
timelib_rel_time *p = NULL;
@@ -5087,7 +5032,7 @@ static bool date_period_initialize(timelib_time **st, timelib_time **et, timelib
return retval;
} /* }}} */
-static bool date_period_init_iso8601_string(php_period_obj *dpobj, zend_class_entry* base_ce, char *isostr, size_t isostr_len, zend_long options, zend_long *recurrences)
+static bool date_period_init_iso8601_string(php_period_obj *dpobj, zend_class_entry* base_ce, const char *isostr, size_t isostr_len, zend_long *recurrences)
{
if (!date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), recurrences, isostr, isostr_len)) {
return false;
@@ -5152,8 +5097,6 @@ static bool date_period_init_finish(php_period_obj *dpobj, zend_long options, ze
dpobj->initialized = 1;
- initialize_date_period_properties(dpobj);
-
return true;
}
@@ -5175,7 +5118,7 @@ PHP_METHOD(DatePeriod, createFromISO8601String)
dpobj->current = NULL;
- if (!date_period_init_iso8601_string(dpobj, date_ce_immutable, isostr, isostr_len, options, &recurrences)) {
+ if (!date_period_init_iso8601_string(dpobj, date_ce_immutable, isostr, isostr_len, &recurrences)) {
RETURN_THROWS();
}
@@ -5214,7 +5157,7 @@ PHP_METHOD(DatePeriod, __construct)
RETURN_THROWS();
}
- if (!date_period_init_iso8601_string(dpobj, date_ce_date, isostr, isostr_len, options, &recurrences)) {
+ if (!date_period_init_iso8601_string(dpobj, date_ce_date, isostr, isostr_len, &recurrences)) {
RETURN_THROWS();
}
} else {
@@ -5350,7 +5293,7 @@ PHP_METHOD(DatePeriod, getIterator)
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
-static int check_id_allowed(char *id, zend_long what) /* {{{ */
+static bool check_id_allowed(const char *id, zend_long what) /* {{{ */
{
if ((what & PHP_DATE_TIMEZONE_GROUP_AFRICA) && strncasecmp(id, "Africa/", 7) == 0) return 1;
if ((what & PHP_DATE_TIMEZONE_GROUP_AMERICA) && strncasecmp(id, "America/", 8) == 0) return 1;
@@ -5573,7 +5516,7 @@ static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, bool calc_s
if (N > 24 || N < 0) {
N -= floor(N / 24) * 24;
}
- if (N > 24 || N < 0) {
+ if (!(N <= 24 && N >= 0)) {
RETURN_FALSE;
}
@@ -5645,7 +5588,7 @@ PHP_FUNCTION(date_sun_info)
array_init(return_value);
/* Get sun up/down and transit */
- rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -50.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
+ rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -35.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
switch (rs) {
case -1: /* always below */
add_assoc_bool(return_value, "sunrise", 0);
@@ -5756,7 +5699,7 @@ static void date_period_object_to_hash(php_period_obj *period_obj, HashTable *pr
zend_hash_str_update(props, "include_end_date", sizeof("include_end_date")-1, &zv);
}
-static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht) /* {{{ */
+static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, const HashTable *myht) /* {{{ */
{
zval *ht_entry;
@@ -5873,8 +5816,6 @@ static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, Has
period_obj->initialized = 1;
- initialize_date_period_properties(period_obj);
-
return 1;
} /* }}} */
@@ -5882,15 +5823,12 @@ static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, Has
PHP_METHOD(DatePeriod, __set_state)
{
php_period_obj *period_obj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
- myht = Z_ARRVAL_P(array);
-
object_init_ex(return_value, date_ce_period);
period_obj = Z_PHPPERIOD_P(return_value);
if (!php_date_period_initialize_from_hash(period_obj, myht)) {
@@ -5924,7 +5862,7 @@ PHP_METHOD(DatePeriod, __serialize)
* Common for date_period_read_property(), date_period_write_property(), and
* restore_custom_dateperiod_properties functions
*/
-static bool date_period_is_internal_property(zend_string *name)
+static bool date_period_is_internal_property(const zend_string *name)
{
if (
zend_string_equals_literal(name, "start") ||
@@ -5941,7 +5879,7 @@ static bool date_period_is_internal_property(zend_string *name)
}
/* }}} */
-static void restore_custom_dateperiod_properties(zval *object, HashTable *myht)
+static void restore_custom_dateperiod_properties(zval *object, const HashTable *myht)
{
zend_string *prop_name;
zval *prop_val;
@@ -5959,15 +5897,13 @@ PHP_METHOD(DatePeriod, __unserialize)
{
zval *object = ZEND_THIS;
php_period_obj *period_obj;
- zval *array;
HashTable *myht;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_ARRAY(array)
+ Z_PARAM_ARRAY_HT(myht)
ZEND_PARSE_PARAMETERS_END();
period_obj = Z_PHPPERIOD_P(object);
- myht = Z_ARRVAL_P(array);
if (!php_date_period_initialize_from_hash(period_obj, myht)) {
zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
@@ -5982,7 +5918,7 @@ PHP_METHOD(DatePeriod, __wakeup)
{
zval *object = ZEND_THIS;
php_period_obj *period_obj;
- HashTable *myht;
+ const HashTable *myht;
ZEND_PARSE_PARAMETERS_NONE();
@@ -5994,14 +5930,84 @@ PHP_METHOD(DatePeriod, __wakeup)
zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
RETURN_THROWS();
}
+
+ restore_custom_dateperiod_properties(object, myht);
}
/* }}} */
+static int date_period_has_property(zend_object *object, zend_string *name, int type, void **cache_slot)
+{
+ zval rv;
+ zval *prop;
+
+ if (!date_period_is_internal_property(name)) {
+ return zend_std_has_property(object, name, type, cache_slot);
+ }
+
+ php_period_obj *period_obj = php_period_obj_from_obj(object);
+ if (!period_obj->initialized) {
+ switch (type) {
+ case ZEND_PROPERTY_ISSET: /* Intentional fallthrough */
+ case ZEND_PROPERTY_NOT_EMPTY:
+ return 0;
+ case ZEND_PROPERTY_EXISTS:
+ return 1;
+ EMPTY_SWITCH_DEFAULT_CASE()
+ }
+ }
+
+ if (type == ZEND_PROPERTY_EXISTS) {
+ return 1;
+ }
+
+ prop = date_period_read_property(object, name, BP_VAR_IS, cache_slot, &rv);
+ ZEND_ASSERT(prop != &EG(uninitialized_zval));
+
+ bool result;
+
+ if (type == ZEND_PROPERTY_NOT_EMPTY) {
+ result = zend_is_true(prop);
+ } else if (type == ZEND_PROPERTY_ISSET) {
+ result = Z_TYPE_P(prop) != IS_NULL;
+ } else {
+ ZEND_UNREACHABLE();
+ }
+
+ zval_ptr_dtor(prop);
+
+ return result;
+}
+
/* {{{ date_period_read_property */
static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
{
- if (type != BP_VAR_IS && type != BP_VAR_R) {
- if (date_period_is_internal_property(name)) {
+ if (date_period_is_internal_property(name)) {
+ if (type == BP_VAR_IS || type == BP_VAR_R) {
+ php_period_obj *period_obj = php_period_obj_from_obj(object);
+
+ if (zend_string_equals_literal(name, "start")) {
+ create_date_period_datetime(period_obj->start, period_obj->start_ce, rv);
+ return rv;
+ } else if (zend_string_equals_literal(name, "current")) {
+ create_date_period_datetime(period_obj->current, period_obj->start_ce, rv);
+ return rv;
+ } else if (zend_string_equals_literal(name, "end")) {
+ create_date_period_datetime(period_obj->end, period_obj->start_ce, rv);
+ return rv;
+ } else if (zend_string_equals_literal(name, "interval")) {
+ create_date_period_interval(period_obj->interval, rv);
+ return rv;
+ } else if (zend_string_equals_literal(name, "recurrences")) {
+ ZVAL_LONG(rv, period_obj->recurrences);
+ return rv;
+ } else if (zend_string_equals_literal(name, "include_start_date")) {
+ ZVAL_BOOL(rv, period_obj->include_start_date);
+ return rv;
+ } else if (zend_string_equals_literal(name, "include_end_date")) {
+ ZVAL_BOOL(rv, period_obj->include_end_date);
+ return rv;
+ }
+ } else {
zend_readonly_property_modification_error_ex("DatePeriod", ZSTR_VAL(name));
return &EG(uninitialized_zval);
}
@@ -6030,3 +6036,26 @@ static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *
return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
}
+
+static HashTable *date_period_get_properties_for(zend_object *object, zend_prop_purpose purpose)
+{
+ php_period_obj *period_obj = php_period_obj_from_obj(object);
+ HashTable *props = zend_array_dup(zend_std_get_properties(object));
+ if (!period_obj->initialized) {
+ return props;
+ }
+
+ date_period_object_to_hash(period_obj, props);
+
+ return props;
+}
+
+static void date_period_unset_property(zend_object *object, zend_string *name, void **cache_slot)
+{
+ if (date_period_is_internal_property(name)) {
+ zend_throw_error(NULL, "Cannot unset %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
+ return;
+ }
+
+ zend_std_unset_property(object, name, cache_slot);
+}
diff --git a/ext/date/php_date.h b/ext/date/php_date.h
index f1b7c892001a9..f5f43bc7dfb0f 100644
--- a/ext/date/php_date.h
+++ b/ext/date/php_date.h
@@ -89,9 +89,9 @@ static inline php_timezone_obj *php_timezone_obj_from_obj(zend_object *obj) {
struct _php_interval_obj {
timelib_rel_time *diff;
int civil_or_wall;
+ bool initialized;
bool from_string;
zend_string *date_string;
- bool initialized;
zend_object std;
};
@@ -140,7 +140,7 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime);
PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, bool gm);
PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_t ts, bool localtime);
-PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj);
+PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, const php_date_obj *date_obj);
/* Mechanism to set new TZ database */
PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb);
diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php
index e18221e712a5d..f375c60ff0a4c 100644
--- a/ext/date/php_date.stub.php
+++ b/ext/date/php_date.stub.php
@@ -524,29 +524,38 @@ public function getMicrosecond(): int {}
public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::modify() does not modify the object itself")]
public function modify(string $modifier): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::add() does not modify the object itself")]
public function add(DateInterval $interval): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::sub() does not modify the object itself")]
public function sub(DateInterval $interval): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::setTimezone() does not modify the object itself")]
public function setTimezone(DateTimeZone $timezone): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::setTime() does not modify the object itself")]
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::setDate() does not modify the object itself")]
public function setDate(int $year, int $month, int $day): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::setISODate() does not modify the object itself")]
public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTimeImmutable {}
/** @tentative-return-type */
+ #[\NoDiscard(message: "as DateTimeImmutable::setTimestamp() does not modify the object itself")]
public function setTimestamp(int $timestamp): DateTimeImmutable {}
+ #[\NoDiscard(message: "as DateTimeImmutable::setMicrosecond() does not modify the object itself")]
public function setMicrosecond(int $microsecond): static {}
/** @tentative-return-type */
@@ -673,19 +682,40 @@ class DatePeriod implements IteratorAggregate
/** @cvalue PHP_DATE_PERIOD_INCLUDE_END_DATE */
public const int INCLUDE_END_DATE = UNKNOWN;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public ?DateTimeInterface $start;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public ?DateTimeInterface $current;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public ?DateTimeInterface $end;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public ?DateInterval $interval;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public int $recurrences;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public bool $include_start_date;
- /** @readonly */
+ /**
+ * @readonly
+ * @virtual
+ */
public bool $include_end_date;
public static function createFromISO8601String(string $specification, int $options = 0): static {}
diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h
index 8f26db6302345..82e9f0f1718ae 100644
--- a/ext/date/php_date_arginfo.h
+++ b/ext/date/php_date_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 41c7662745d19808dd4550b37cd1b9f2aa94d75b */
+ * Stub hash: 093743b4fe7a698d1262cc1a81b60a85064fdccb */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0)
@@ -725,15 +725,15 @@ static const zend_function_entry class_DateTimeImmutable_methods[] = {
ZEND_RAW_FENTRY("getTimestamp", zif_date_timestamp_get, arginfo_class_DateTimeImmutable_getTimestamp, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("getMicrosecond", zim_DateTime_getMicrosecond, arginfo_class_DateTimeImmutable_getMicrosecond, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("diff", zif_date_diff, arginfo_class_DateTimeImmutable_diff, ZEND_ACC_PUBLIC, NULL, NULL)
- ZEND_ME(DateTimeImmutable, modify, arginfo_class_DateTimeImmutable_modify, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, add, arginfo_class_DateTimeImmutable_add, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, sub, arginfo_class_DateTimeImmutable_sub, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, setTimezone, arginfo_class_DateTimeImmutable_setTimezone, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, setTime, arginfo_class_DateTimeImmutable_setTime, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, setDate, arginfo_class_DateTimeImmutable_setDate, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, setISODate, arginfo_class_DateTimeImmutable_setISODate, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, setTimestamp, arginfo_class_DateTimeImmutable_setTimestamp, ZEND_ACC_PUBLIC)
- ZEND_ME(DateTimeImmutable, setMicrosecond, arginfo_class_DateTimeImmutable_setMicrosecond, ZEND_ACC_PUBLIC)
+ ZEND_ME(DateTimeImmutable, modify, arginfo_class_DateTimeImmutable_modify, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, add, arginfo_class_DateTimeImmutable_add, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, sub, arginfo_class_DateTimeImmutable_sub, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, setTimezone, arginfo_class_DateTimeImmutable_setTimezone, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, setTime, arginfo_class_DateTimeImmutable_setTime, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, setDate, arginfo_class_DateTimeImmutable_setDate, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, setISODate, arginfo_class_DateTimeImmutable_setISODate, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, setTimestamp, arginfo_class_DateTimeImmutable_setTimestamp, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
+ ZEND_ME(DateTimeImmutable, setMicrosecond, arginfo_class_DateTimeImmutable_setMicrosecond, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD)
ZEND_ME(DateTimeImmutable, createFromMutable, arginfo_class_DateTimeImmutable_createFromMutable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(DateTimeImmutable, createFromInterface, arginfo_class_DateTimeImmutable_createFromInterface, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_FE_END
@@ -989,6 +989,88 @@ static zend_class_entry *register_class_DateTimeImmutable(zend_class_entry *clas
class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0);
zend_class_implements(class_entry, 1, class_entry_DateTimeInterface);
+
+ zend_string *attribute_name_NoDiscard_func_modify_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), attribute_name_NoDiscard_func_modify_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_modify_0);
+ zval attribute_NoDiscard_func_modify_0_arg0;
+ zend_string *attribute_NoDiscard_func_modify_0_arg0_str = zend_string_init("as DateTimeImmutable::modify() does not modify the object itself", strlen("as DateTimeImmutable::modify() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_modify_0_arg0, attribute_NoDiscard_func_modify_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_modify_0->args[0].value, &attribute_NoDiscard_func_modify_0_arg0);
+ attribute_NoDiscard_func_modify_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_add_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), attribute_name_NoDiscard_func_add_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_add_0);
+ zval attribute_NoDiscard_func_add_0_arg0;
+ zend_string *attribute_NoDiscard_func_add_0_arg0_str = zend_string_init("as DateTimeImmutable::add() does not modify the object itself", strlen("as DateTimeImmutable::add() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_add_0_arg0, attribute_NoDiscard_func_add_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_add_0->args[0].value, &attribute_NoDiscard_func_add_0_arg0);
+ attribute_NoDiscard_func_add_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_sub_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), attribute_name_NoDiscard_func_sub_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_sub_0);
+ zval attribute_NoDiscard_func_sub_0_arg0;
+ zend_string *attribute_NoDiscard_func_sub_0_arg0_str = zend_string_init("as DateTimeImmutable::sub() does not modify the object itself", strlen("as DateTimeImmutable::sub() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_sub_0_arg0, attribute_NoDiscard_func_sub_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_sub_0->args[0].value, &attribute_NoDiscard_func_sub_0_arg0);
+ attribute_NoDiscard_func_sub_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_settimezone_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), attribute_name_NoDiscard_func_settimezone_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_settimezone_0);
+ zval attribute_NoDiscard_func_settimezone_0_arg0;
+ zend_string *attribute_NoDiscard_func_settimezone_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimezone() does not modify the object itself", strlen("as DateTimeImmutable::setTimezone() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_settimezone_0_arg0, attribute_NoDiscard_func_settimezone_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settimezone_0->args[0].value, &attribute_NoDiscard_func_settimezone_0_arg0);
+ attribute_NoDiscard_func_settimezone_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_settime_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), attribute_name_NoDiscard_func_settime_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_settime_0);
+ zval attribute_NoDiscard_func_settime_0_arg0;
+ zend_string *attribute_NoDiscard_func_settime_0_arg0_str = zend_string_init("as DateTimeImmutable::setTime() does not modify the object itself", strlen("as DateTimeImmutable::setTime() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_settime_0_arg0, attribute_NoDiscard_func_settime_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settime_0->args[0].value, &attribute_NoDiscard_func_settime_0_arg0);
+ attribute_NoDiscard_func_settime_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_setdate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), attribute_name_NoDiscard_func_setdate_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_setdate_0);
+ zval attribute_NoDiscard_func_setdate_0_arg0;
+ zend_string *attribute_NoDiscard_func_setdate_0_arg0_str = zend_string_init("as DateTimeImmutable::setDate() does not modify the object itself", strlen("as DateTimeImmutable::setDate() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_setdate_0_arg0, attribute_NoDiscard_func_setdate_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setdate_0->args[0].value, &attribute_NoDiscard_func_setdate_0_arg0);
+ attribute_NoDiscard_func_setdate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_setisodate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), attribute_name_NoDiscard_func_setisodate_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_setisodate_0);
+ zval attribute_NoDiscard_func_setisodate_0_arg0;
+ zend_string *attribute_NoDiscard_func_setisodate_0_arg0_str = zend_string_init("as DateTimeImmutable::setISODate() does not modify the object itself", strlen("as DateTimeImmutable::setISODate() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_setisodate_0_arg0, attribute_NoDiscard_func_setisodate_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setisodate_0->args[0].value, &attribute_NoDiscard_func_setisodate_0_arg0);
+ attribute_NoDiscard_func_setisodate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_settimestamp_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), attribute_name_NoDiscard_func_settimestamp_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_settimestamp_0);
+ zval attribute_NoDiscard_func_settimestamp_0_arg0;
+ zend_string *attribute_NoDiscard_func_settimestamp_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimestamp() does not modify the object itself", strlen("as DateTimeImmutable::setTimestamp() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_settimestamp_0_arg0, attribute_NoDiscard_func_settimestamp_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settimestamp_0->args[0].value, &attribute_NoDiscard_func_settimestamp_0_arg0);
+ attribute_NoDiscard_func_settimestamp_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
+ zend_string *attribute_name_NoDiscard_func_setmicrosecond_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1);
+ zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), attribute_name_NoDiscard_func_setmicrosecond_0, 1);
+ zend_string_release(attribute_name_NoDiscard_func_setmicrosecond_0);
+ zval attribute_NoDiscard_func_setmicrosecond_0_arg0;
+ zend_string *attribute_NoDiscard_func_setmicrosecond_0_arg0_str = zend_string_init("as DateTimeImmutable::setMicrosecond() does not modify the object itself", strlen("as DateTimeImmutable::setMicrosecond() does not modify the object itself"), 1);
+ ZVAL_STR(&attribute_NoDiscard_func_setmicrosecond_0_arg0, attribute_NoDiscard_func_setmicrosecond_0_arg0_str);
+ ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setmicrosecond_0->args[0].value, &attribute_NoDiscard_func_setmicrosecond_0_arg0);
+ attribute_NoDiscard_func_setmicrosecond_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE);
+
return class_entry;
}
@@ -1120,46 +1202,46 @@ static zend_class_entry *register_class_DatePeriod(zend_class_entry *class_entry
ZVAL_UNDEF(&property_start_default_value);
zend_string *property_start_name = zend_string_init("start", sizeof("start") - 1, 1);
zend_string *property_start_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
- zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_start_class_DateTimeInterface, 0, MAY_BE_NULL));
+ zend_declare_typed_property(class_entry, property_start_name, &property_start_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_start_class_DateTimeInterface, 0, MAY_BE_NULL));
zend_string_release(property_start_name);
zval property_current_default_value;
ZVAL_UNDEF(&property_current_default_value);
zend_string *property_current_name = zend_string_init("current", sizeof("current") - 1, 1);
zend_string *property_current_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
- zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_current_class_DateTimeInterface, 0, MAY_BE_NULL));
+ zend_declare_typed_property(class_entry, property_current_name, &property_current_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_current_class_DateTimeInterface, 0, MAY_BE_NULL));
zend_string_release(property_current_name);
zval property_end_default_value;
ZVAL_UNDEF(&property_end_default_value);
zend_string *property_end_name = zend_string_init("end", sizeof("end") - 1, 1);
zend_string *property_end_class_DateTimeInterface = zend_string_init("DateTimeInterface", sizeof("DateTimeInterface")-1, 1);
- zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_end_class_DateTimeInterface, 0, MAY_BE_NULL));
+ zend_declare_typed_property(class_entry, property_end_name, &property_end_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_end_class_DateTimeInterface, 0, MAY_BE_NULL));
zend_string_release(property_end_name);
zval property_interval_default_value;
ZVAL_UNDEF(&property_interval_default_value);
zend_string *property_interval_name = zend_string_init("interval", sizeof("interval") - 1, 1);
zend_string *property_interval_class_DateInterval = zend_string_init("DateInterval", sizeof("DateInterval")-1, 1);
- zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_interval_class_DateInterval, 0, MAY_BE_NULL));
+ zend_declare_typed_property(class_entry, property_interval_name, &property_interval_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_interval_class_DateInterval, 0, MAY_BE_NULL));
zend_string_release(property_interval_name);
zval property_recurrences_default_value;
ZVAL_UNDEF(&property_recurrences_default_value);
zend_string *property_recurrences_name = zend_string_init("recurrences", sizeof("recurrences") - 1, 1);
- zend_declare_typed_property(class_entry, property_recurrences_name, &property_recurrences_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
+ zend_declare_typed_property(class_entry, property_recurrences_name, &property_recurrences_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
zend_string_release(property_recurrences_name);
zval property_include_start_date_default_value;
ZVAL_UNDEF(&property_include_start_date_default_value);
zend_string *property_include_start_date_name = zend_string_init("include_start_date", sizeof("include_start_date") - 1, 1);
- zend_declare_typed_property(class_entry, property_include_start_date_name, &property_include_start_date_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_declare_typed_property(class_entry, property_include_start_date_name, &property_include_start_date_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
zend_string_release(property_include_start_date_name);
zval property_include_end_date_default_value;
ZVAL_UNDEF(&property_include_end_date_default_value);
zend_string *property_include_end_date_name = zend_string_init("include_end_date", sizeof("include_end_date") - 1, 1);
- zend_declare_typed_property(class_entry, property_include_end_date_name, &property_include_end_date_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
+ zend_declare_typed_property(class_entry, property_include_end_date_name, &property_include_end_date_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL));
zend_string_release(property_include_end_date_name);
return class_entry;
diff --git a/ext/date/tests/DateTimeZone_clone_basic1.phpt b/ext/date/tests/DateTimeZone_clone_basic1.phpt
index 3d154512f6373..03af7108c4975 100644
--- a/ext/date/tests/DateTimeZone_clone_basic1.phpt
+++ b/ext/date/tests/DateTimeZone_clone_basic1.phpt
@@ -22,7 +22,7 @@ if ($clone != $orig) {
}else if ($clone === $orig) {
echo "TEST FAILED : objects identical\n";
} else {
- echo "TEST PASSED : Objects equal but not indetical\n";
+ echo "TEST PASSED : Objects equal but not identical\n";
}
?>
@@ -40,4 +40,4 @@ object(DateTimeZone)#%d (2) {
["timezone"]=>
string(3) "GMT"
}
-TEST PASSED : Objects equal but not indetical
+TEST PASSED : Objects equal but not identical
diff --git a/ext/date/tests/DateTimeZone_serialize_type_1.phpt b/ext/date/tests/DateTimeZone_serialize_type_1.phpt
index f35a133cfb4fd..0243f68764a2d 100644
--- a/ext/date/tests/DateTimeZone_serialize_type_1.phpt
+++ b/ext/date/tests/DateTimeZone_serialize_type_1.phpt
@@ -12,7 +12,7 @@ var_dump($serialized);
$tz2 = unserialize($serialized);
var_dump($tz2);
-// Try to use unserialzied object
+// Try to use unserialized object
var_dump( $tz2->getName() );
?>
diff --git a/ext/date/tests/DateTimeZone_serialize_type_2.phpt b/ext/date/tests/DateTimeZone_serialize_type_2.phpt
index a823ee08c022a..5240fa954420a 100644
--- a/ext/date/tests/DateTimeZone_serialize_type_2.phpt
+++ b/ext/date/tests/DateTimeZone_serialize_type_2.phpt
@@ -12,7 +12,7 @@ var_dump($serialized);
$tz2 = unserialize($serialized);
var_dump($tz2);
-// Try to use unserialzied object
+// Try to use unserialized object
var_dump( $tz2->getName() );
?>
diff --git a/ext/date/tests/DateTimeZone_serialize_type_3.phpt b/ext/date/tests/DateTimeZone_serialize_type_3.phpt
index 92ac958dc34af..e1c6719a03d42 100644
--- a/ext/date/tests/DateTimeZone_serialize_type_3.phpt
+++ b/ext/date/tests/DateTimeZone_serialize_type_3.phpt
@@ -12,7 +12,7 @@ var_dump($serialized);
$tz2 = unserialize($serialized);
var_dump($tz2);
-// Try to use unserialzied object
+// Try to use unserialized object
var_dump( $tz2->getName() );
?>
diff --git a/ext/date/tests/DateTime_serialize.phpt b/ext/date/tests/DateTime_serialize.phpt
index 8424a3e082e74..427b43f70e16c 100644
--- a/ext/date/tests/DateTime_serialize.phpt
+++ b/ext/date/tests/DateTime_serialize.phpt
@@ -12,7 +12,7 @@ var_dump($serialized);
$date2 = unserialize($serialized);
var_dump($date2);
-// Try to use unserialzied object
+// Try to use unserialized object
var_dump( $date2->format( "F j, Y, g:i a") );
?>
diff --git a/ext/date/tests/ExtendDateTime.phpt b/ext/date/tests/ExtendDateTime.phpt
index 70ed2793e6efe..ba445338403e7 100644
--- a/ext/date/tests/ExtendDateTime.phpt
+++ b/ext/date/tests/ExtendDateTime.phpt
@@ -1,5 +1,5 @@
--TEST--
-Extendig DatTime and calling __set_state without args
+Extending DateTime and calling __set_state without args
--CREDITS--
Gabriel Caruso (carusogabriel34@gmail.com)
--FILE--
diff --git a/ext/date/tests/bug-gh18076.phpt b/ext/date/tests/bug-gh18076.phpt
new file mode 100644
index 0000000000000..2c1eaa200fad3
--- /dev/null
+++ b/ext/date/tests/bug-gh18076.phpt
@@ -0,0 +1,12 @@
+--TEST--
+GH-18076 (Since PHP 8, date_sun_info() returns inaccurate sunrise and sunset times)
+--FILE--
+
+--EXPECT--
+05:59:21
+18:14:48
diff --git a/ext/date/tests/bug36224.phpt b/ext/date/tests/bug36224.phpt
index 1690f4e7b2ce5..7fe6165fb42ae 100644
--- a/ext/date/tests/bug36224.phpt
+++ b/ext/date/tests/bug36224.phpt
@@ -1,5 +1,5 @@
--TEST--
-Bug #36224 (date(DATE_ATOM) gives wrong resulsts)
+Bug #36224 (date(DATE_ATOM) gives wrong results)
--FILE--
newInstanceWithoutConstructor();
+
+var_dump(isset($di->start));
+var_dump(empty($di->start));
+var_dump(property_exists($di, "start"));
+
+var_dump(isset($di->recurrences));
+var_dump(empty($di->recurrences));
+var_dump(property_exists($di, "recurrences"));
+
+var_dump(isset($di->end));
+var_dump(empty($di->end));
+var_dump(property_exists($di, "end"));
+
+var_dump(isset($di->my));
+var_dump(empty($di->my));
+var_dump(property_exists($di, "my"));
+
+?>
+--EXPECT--
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
diff --git a/ext/date/tests/date_period_properties.phpt b/ext/date/tests/date_period_properties.phpt
new file mode 100644
index 0000000000000..150677ca71e4e
--- /dev/null
+++ b/ext/date/tests/date_period_properties.phpt
@@ -0,0 +1,187 @@
+--TEST--
+Test different usages of DatePeriod properties
+--FILE--
+
+--EXPECTF--
+object(MyDatePeriod)#%d (%d) {
+ ["prop"]=>
+ int(3)
+ ["start"]=>
+ object(DateTimeImmutable)#%d (%d) {
+ ["date"]=>
+ string(26) "2012-07-01 00:00:00.000000"
+ ["timezone_type"]=>
+ int(1)
+ ["timezone"]=>
+ string(6) "+00:00"
+ }
+ ["current"]=>
+ NULL
+ ["end"]=>
+ NULL
+ ["interval"]=>
+ object(DateInterval)#%d (%d) {
+ ["y"]=>
+ int(0)
+ ["m"]=>
+ int(0)
+ ["d"]=>
+ int(7)
+ ["h"]=>
+ int(0)
+ ["i"]=>
+ int(0)
+ ["s"]=>
+ int(0)
+ ["f"]=>
+ float(0)
+ ["invert"]=>
+ int(0)
+ ["days"]=>
+ bool(false)
+ ["from_string"]=>
+ bool(false)
+ }
+ ["recurrences"]=>
+ int(5)
+ ["include_start_date"]=>
+ bool(true)
+ ["include_end_date"]=>
+ bool(false)
+}
+string(%d) "{"prop":3,"start":{"date":"2012-07-01 00:00:00.000000","timezone_type":1,"timezone":"+00:00"},"current":null,"end":null,"interval":{"y":0,"m":0,"d":7,"h":0,"i":0,"s":0,"f":0,"invert":0,"days":false,"from_string":false},"recurrences":5,"include_start_date":true,"include_end_date":false}"
+string(%d) "O:12:"MyDatePeriod":8:{s:5:"start";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2012-07-01 00:00:00.000000";s:13:"timezone_type";i:1;s:8:"timezone";s:6:"+00:00";}s:7:"current";N;s:3:"end";N;s:8:"interval";O:12:"DateInterval":10:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:7;s:1:"h";i:0;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:6:"invert";i:0;s:4:"days";b:0;s:11:"from_string";b:0;}s:11:"recurrences";i:5;s:18:"include_start_date";b:1;s:16:"include_end_date";b:0;s:4:"prop";i:3;}"
+array(%d) {
+ ["prop"]=>
+ int(3)
+ ["start"]=>
+ object(DateTimeImmutable)#%d (%d) {
+ ["date"]=>
+ string(26) "2012-07-01 00:00:00.000000"
+ ["timezone_type"]=>
+ int(1)
+ ["timezone"]=>
+ string(6) "+00:00"
+ }
+ ["current"]=>
+ NULL
+ ["end"]=>
+ NULL
+ ["interval"]=>
+ object(DateInterval)#%d (%d) {
+ ["y"]=>
+ int(0)
+ ["m"]=>
+ int(0)
+ ["d"]=>
+ int(7)
+ ["h"]=>
+ int(0)
+ ["i"]=>
+ int(0)
+ ["s"]=>
+ int(0)
+ ["f"]=>
+ float(0)
+ ["invert"]=>
+ int(0)
+ ["days"]=>
+ bool(false)
+ ["from_string"]=>
+ bool(false)
+ }
+ ["recurrences"]=>
+ int(5)
+ ["include_start_date"]=>
+ bool(true)
+ ["include_end_date"]=>
+ bool(false)
+}
+\MyDatePeriod::__set_state(array(
+ 'prop' => 3,
+ 'start' =>
+ \DateTimeImmutable::__set_state(array(
+ 'date' => '2012-07-01 00:00:00.000000',
+ 'timezone_type' => 1,
+ 'timezone' => '+00:00',
+ )),
+ 'current' => NULL,
+ 'end' => NULL,
+ 'interval' =>
+ \DateInterval::__set_state(array(
+ 'y' => 0,
+ 'm' => 0,
+ 'd' => 7,
+ 'h' => 0,
+ 'i' => 0,
+ 's' => 0,
+ 'f' => 0.0,
+ 'invert' => 0,
+ 'days' => false,
+ 'from_string' => false,
+ )),
+ 'recurrences' => 5,
+ 'include_start_date' => true,
+ 'include_end_date' => false,
+))NULL
+array(%d) {
+ ["prop"]=>
+ int(3)
+ ["start"]=>
+ object(DateTimeImmutable)#%d (%d) {
+ ["date"]=>
+ string(26) "2012-07-01 00:00:00.000000"
+ ["timezone_type"]=>
+ int(1)
+ ["timezone"]=>
+ string(6) "+00:00"
+ }
+ ["current"]=>
+ NULL
+ ["end"]=>
+ NULL
+ ["interval"]=>
+ object(DateInterval)#%d (%d) {
+ ["y"]=>
+ int(0)
+ ["m"]=>
+ int(0)
+ ["d"]=>
+ int(7)
+ ["h"]=>
+ int(0)
+ ["i"]=>
+ int(0)
+ ["s"]=>
+ int(0)
+ ["f"]=>
+ float(0)
+ ["invert"]=>
+ int(0)
+ ["days"]=>
+ bool(false)
+ ["from_string"]=>
+ bool(false)
+ }
+ ["recurrences"]=>
+ int(5)
+ ["include_start_date"]=>
+ bool(true)
+ ["include_end_date"]=>
+ bool(false)
+}
diff --git a/ext/date/tests/date_period_unserialize3.phpt b/ext/date/tests/date_period_unserialize3.phpt
index d5d4a57456d9a..4570e3de631fc 100644
--- a/ext/date/tests/date_period_unserialize3.phpt
+++ b/ext/date/tests/date_period_unserialize3.phpt
@@ -11,9 +11,9 @@ $period = new DatePeriod($start, $interval, $end);
try {
$period->__unserialize(
[
- "current" => new DateTime,
- "start" => new DateTime,
- "end" => new DateTime,
+ "current" => new DateTime("2024-08-27 00:00:00"),
+ "start" => new DateTime("2024-08-28 00:00:00"),
+ "end" => new DateTime("2024-08-29 00:00:00"),
"interval" => new DateInterval('P2D'),
"recurrences" => 2,
"include_start_date" => "wrong type",
@@ -33,18 +33,25 @@ object(DatePeriod)#%d (%d) {
["start"]=>
object(DateTime)#%d (%d) {
["date"]=>
- string(26) "2022-07-14 00:00:00.000000"
+ string(26) "2024-08-28 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
["current"]=>
- NULL
+ object(DateTime)#%d (%d) {
+ ["date"]=>
+ string(26) "2024-08-27 00:00:00.000000"
+ ["timezone_type"]=>
+ int(3)
+ ["timezone"]=>
+ string(3) "UTC"
+ }
["end"]=>
object(DateTime)#%d (%d) {
["date"]=>
- string(26) "2022-07-16 00:00:00.000000"
+ string(26) "2024-08-29 00:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
@@ -57,7 +64,7 @@ object(DatePeriod)#%d (%d) {
["m"]=>
int(0)
["d"]=>
- int(1)
+ int(2)
["h"]=>
int(0)
["i"]=>
@@ -74,7 +81,7 @@ object(DatePeriod)#%d (%d) {
bool(false)
}
["recurrences"]=>
- int(1)
+ int(2)
["include_start_date"]=>
bool(true)
["include_end_date"]=>
diff --git a/ext/date/tests/date_period_unset_property.phpt b/ext/date/tests/date_period_unset_property.phpt
new file mode 100644
index 0000000000000..7948d61d268d6
--- /dev/null
+++ b/ext/date/tests/date_period_unset_property.phpt
@@ -0,0 +1,71 @@
+--TEST--
+Test unsetting DatePeriod properties
+--FILE--
+prop);
+
+try {
+ $period->prop;
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->start);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->current);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->end);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->interval);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->recurrences);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->include_start_date);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+try {
+ unset($period->include_end_date);
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+Typed property MyDatePeriod::$prop must not be accessed before initialization
+Cannot unset MyDatePeriod::$start
+Cannot unset MyDatePeriod::$current
+Cannot unset MyDatePeriod::$end
+Cannot unset MyDatePeriod::$interval
+Cannot unset MyDatePeriod::$recurrences
+Cannot unset MyDatePeriod::$include_start_date
+Cannot unset MyDatePeriod::$include_end_date
diff --git a/ext/date/tests/date_sun_info_001.phpt b/ext/date/tests/date_sun_info_001.phpt
index 708d2550ccac5..c9096af46d4b1 100644
--- a/ext/date/tests/date_sun_info_001.phpt
+++ b/ext/date/tests/date_sun_info_001.phpt
@@ -10,9 +10,9 @@ echo "Done\n";
--EXPECT--
array(9) {
["sunrise"]=>
- int(1165897682)
+ int(1165897761)
["sunset"]=>
- int(1165934239)
+ int(1165934160)
["transit"]=>
int(1165915961)
["civil_twilight_begin"]=>
diff --git a/ext/date/tests/date_sun_info_002.phpt b/ext/date/tests/date_sun_info_002.phpt
index 1ff58295e31e3..ac319796d61c9 100644
--- a/ext/date/tests/date_sun_info_002.phpt
+++ b/ext/date/tests/date_sun_info_002.phpt
@@ -11,8 +11,8 @@ foreach ($sun_info as $key => $elem )
echo "Done\n";
?>
--EXPECT--
-2007-04-13 06:11:26 CEST sunrise
-2007-04-13 20:32:56 CEST sunset
+2007-04-13 06:13:31 CEST sunrise
+2007-04-13 20:30:51 CEST sunset
2007-04-13 13:22:11 CEST transit
2007-04-13 05:29:22 CEST civil_twilight_begin
2007-04-13 21:15:00 CEST civil_twilight_end
diff --git a/ext/date/tests/getdate_variation4.phpt b/ext/date/tests/getdate_variation4.phpt
index d216f02fcc20b..d7ea0c7e207c8 100644
--- a/ext/date/tests/getdate_variation4.phpt
+++ b/ext/date/tests/getdate_variation4.phpt
@@ -1,5 +1,5 @@
--TEST--
-Test getdate() function : usage variation - Verifyig by supplying year-wise sample time stamps since Unix epoch time
+Test getdate() function : usage variation - Verifying by supplying year-wise sample time stamps since Unix epoch time
--FILE--
--EXPECTF--
-object(I)#1 (11) {
+object(I)#%d (%d) {
+ ["var1":"I":private]=>
+ int(1)
+ ["var2":"I":private]=>
+ int(2)
+ ["var3":protected]=>
+ int(3)
+ ["var4":protected]=>
+ int(4)
["start"]=>
- object(DateTimeImmutable)#5 (3) {
+ object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2023-03-03 16:24:00.000000"
["timezone_type"]=>
@@ -39,7 +47,7 @@ object(I)#1 (11) {
["current"]=>
NULL
["end"]=>
- object(DateTimeImmutable)#6 (3) {
+ object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2023-03-09 16:24:00.000000"
["timezone_type"]=>
@@ -48,7 +56,7 @@ object(I)#1 (11) {
string(3) "UTC"
}
["interval"]=>
- object(DateInterval)#7 (10) {
+ object(DateInterval)#%d (%d) {
["y"]=>
int(0)
["m"]=>
@@ -76,6 +84,9 @@ object(I)#1 (11) {
bool(true)
["include_end_date"]=>
bool(false)
+}
+string(631) "O:1:"I":11:{s:5:"start";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-03-03 16:24:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:7:"current";N;s:3:"end";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-03-09 16:24:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:8:"interval";O:12:"DateInterval":10:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:0;s:1:"h";i:1;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:6:"invert";i:0;s:4:"days";b:0;s:11:"from_string";b:0;}s:11:"recurrences";i:1;s:18:"include_start_date";b:1;s:16:"include_end_date";b:0;s:7:"!I!var1";i:1;s:7:"!I!var2";i:2;s:7:"!*!var3";i:3;s:7:"!*!var4";i:4;}"
+object(I)#%d (%d) {
["var1":"I":private]=>
int(1)
["var2":"I":private]=>
@@ -84,11 +95,8 @@ object(I)#1 (11) {
int(3)
["var4":protected]=>
int(4)
-}
-string(631) "O:1:"I":11:{s:5:"start";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-03-03 16:24:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:7:"current";N;s:3:"end";O:17:"DateTimeImmutable":3:{s:4:"date";s:26:"2023-03-09 16:24:00.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}s:8:"interval";O:12:"DateInterval":10:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:0;s:1:"h";i:1;s:1:"i";i:0;s:1:"s";i:0;s:1:"f";d:0;s:6:"invert";i:0;s:4:"days";b:0;s:11:"from_string";b:0;}s:11:"recurrences";i:1;s:18:"include_start_date";b:1;s:16:"include_end_date";b:0;s:7:"!I!var1";i:1;s:7:"!I!var2";i:2;s:7:"!*!var3";i:3;s:7:"!*!var4";i:4;}"
-object(I)#2 (11) {
["start"]=>
- object(DateTimeImmutable)#9 (3) {
+ object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2023-03-03 16:24:00.000000"
["timezone_type"]=>
@@ -99,7 +107,7 @@ object(I)#2 (11) {
["current"]=>
NULL
["end"]=>
- object(DateTimeImmutable)#10 (3) {
+ object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2023-03-09 16:24:00.000000"
["timezone_type"]=>
@@ -108,7 +116,7 @@ object(I)#2 (11) {
string(3) "UTC"
}
["interval"]=>
- object(DateInterval)#11 (10) {
+ object(DateInterval)#%d (%d) {
["y"]=>
int(0)
["m"]=>
@@ -136,12 +144,4 @@ object(I)#2 (11) {
bool(true)
["include_end_date"]=>
bool(false)
- ["var1":"I":private]=>
- int(1)
- ["var2":"I":private]=>
- int(2)
- ["var3":protected]=>
- int(3)
- ["var4":protected]=>
- int(4)
}
diff --git a/ext/date/tests/gh18481.phpt b/ext/date/tests/gh18481.phpt
new file mode 100644
index 0000000000000..074f244c3a3a3
--- /dev/null
+++ b/ext/date/tests/gh18481.phpt
@@ -0,0 +1,29 @@
+--TEST--
+GH-18481 (date_sunrise with utcOffset as INF)
+--FILE--
+
+--EXPECTF--
+Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d
+
+Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d
+bool(false)
+
+Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d
+
+Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d
+bool(false)
+
+Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d
+
+Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d
+bool(false)
+
+Deprecated: Constant SUNFUNCS_RET_STRING is deprecated in %s on line %d
+
+Deprecated: Function date_sunrise() is deprecated since 8.1, use date_sun_info() instead in %s on line %d
+bool(false)
diff --git a/ext/dba/dba.c b/ext/dba/dba.c
index 7982e4255b07c..086998973e20a 100644
--- a/ext/dba/dba.c
+++ b/ext/dba/dba.c
@@ -1,5 +1,5 @@
/*
- +----------------------------------------------------------------------+
++----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
@@ -92,7 +92,7 @@ ZEND_GET_MODULE(dba)
#endif
/* {{{ php_dba_make_key */
-static zend_string* php_dba_make_key(HashTable *key)
+static zend_string* php_dba_make_key(const HashTable *key)
{
zval *group, *name;
zend_string *group_str, *name_str;
@@ -435,10 +435,9 @@ PHP_MSHUTDOWN_FUNCTION(dba)
/* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(dba)
{
- const dba_handler *hptr;
smart_str handlers = {0};
- for(hptr = handler; hptr->name; hptr++) {
+ for (const dba_handler *hptr = handler; hptr->name; hptr++) {
smart_str_appends(&handlers, hptr->name);
smart_str_appendc(&handlers, ' ');
}
@@ -479,8 +478,7 @@ static void php_dba_update(INTERNAL_FUNCTION_PARAMETERS, int mode)
if (key_ht) {
key_str = php_dba_make_key(key_ht);
if (!key_str) {
- // TODO ValueError?
- RETURN_FALSE;
+ RETURN_THROWS();
}
}
@@ -570,8 +568,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
RETURN_THROWS();
}
- char *resource_key;
- size_t resource_key_len = spprintf(&resource_key, 0,
+ zend_string *resource_key = zend_strpprintf(0,
"dba_%d_%u_%s_%s_%s", persistent, persistent ? 0 : DBA_G(connection_counter)++, ZSTR_VAL(path), ZSTR_VAL(mode), handler_str ? ZSTR_VAL(handler_str) : ""
);
@@ -579,27 +576,25 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
zend_resource *le;
/* try to find if we already have this link in our persistent list */
- if ((le = zend_hash_str_find_ptr(&EG(persistent_list), resource_key, resource_key_len)) != NULL) {
+ if ((le = zend_hash_find_ptr(&EG(persistent_list), resource_key)) != NULL) {
if (le->type != le_pdb) {
// TODO This should never happen
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_FALSE;
}
object_init_ex(return_value, dba_connection_ce);
dba_connection *connection = Z_DBA_CONNECTION_P(return_value);
connection->info = (dba_info *)le->ptr;
- connection->hash = zend_string_init(resource_key, resource_key_len, persistent);
- if (persistent) {
- GC_MAKE_PERSISTENT_LOCAL(connection->hash);
- }
+ connection->hash = zend_string_dup(resource_key, /* persistent */ true);
+ GC_MAKE_PERSISTENT_LOCAL(connection->hash);
if (zend_hash_exists(&DBA_G(connections), connection->hash)) {
zend_hash_del(&DBA_G(connections), connection->hash);
}
zend_hash_add_new(&DBA_G(connections), connection->hash, return_value);
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
return;
}
}
@@ -608,7 +603,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
hptr = DBA_G(default_hptr);
if (!hptr) {
php_error_docref(NULL, E_WARNING, "No default handler selected");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_FALSE;
}
ZEND_ASSERT(hptr->name);
@@ -618,7 +613,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
if (!hptr->name) {
php_error_docref(NULL, E_WARNING, "Handler \"%s\" is not available", ZSTR_VAL(handler_str));
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_FALSE;
}
}
@@ -642,13 +637,13 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
if (ZSTR_LEN(mode) > 3) {
zend_argument_value_error(2, "must be at most 3 characters");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_THROWS();
}
if (ZSTR_LEN(mode) == 3) {
if (ZSTR_VAL(mode)[2] != 't') {
zend_argument_value_error(2, "third character must be \"t\"");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_THROWS();
}
is_test_lock = true;
@@ -661,7 +656,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
case '-':
if ((hptr->flags & DBA_LOCK_ALL) == 0) {
php_error_docref(NULL, E_WARNING, "Locking cannot be disabled for handler %s", hptr->name);
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_FALSE;
}
is_lock_ignored = true;
@@ -683,7 +678,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
break;
default:
zend_argument_value_error(2, "second character must be one of \"d\", \"l\", \"-\", or \"t\"");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_THROWS();
}
} else {
@@ -752,7 +747,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
break;
default:
zend_argument_value_error(2, "first character must be one of \"r\", \"w\", \"c\", or \"n\"");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_THROWS();
}
if (!lock_file_mode) {
@@ -761,17 +756,17 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
if (is_test_lock) {
if (is_lock_ignored) {
zend_argument_value_error(2, "cannot combine mode \"-\" (no lock) and \"t\" (test lock)");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_THROWS();
}
if (!lock_mode) {
if ((hptr->flags & DBA_LOCK_ALL) == 0) {
php_error_docref(NULL, E_WARNING, "Handler %s uses its own locking which doesn't support mode modifier t (test lock)", hptr->name);
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_FALSE;
} else {
php_error_docref(NULL, E_WARNING, "Handler %s doesn't uses locking for this mode which makes modifier t (test lock) obsolete", hptr->name);
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
RETURN_FALSE;
}
} else {
@@ -781,7 +776,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
zval *connection_zval;
dba_connection *connection;
- if ((connection_zval = zend_hash_str_find(&DBA_G(connections), resource_key, resource_key_len)) == NULL) {
+ if ((connection_zval = zend_hash_find(&DBA_G(connections), resource_key)) == NULL) {
object_init_ex(return_value, dba_connection_ce);
connection = Z_DBA_CONNECTION_P(return_value);
@@ -793,9 +788,11 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
connection->info->driver_flags = driver_flags;
connection->info->flags = (hptr->flags & ~DBA_LOCK_ALL) | (lock_flag & DBA_LOCK_ALL) | (persistent ? DBA_PERSISTENT : 0);
connection->info->lock.mode = lock_mode;
- connection->hash = zend_string_init(resource_key, resource_key_len, persistent);
if (persistent) {
+ connection->hash = zend_string_dup(resource_key, /* persistent */ true);
GC_MAKE_PERSISTENT_LOCAL(connection->hash);
+ } else {
+ connection->hash = zend_string_copy(resource_key);
}
} else {
ZVAL_COPY(return_value, connection_zval);
@@ -826,14 +823,10 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
} else {
spprintf(&lock_name, 0, "%s.lck", ZSTR_VAL(connection->info->path));
if (!strcmp(file_mode, "r")) {
- zend_string *opened_path = NULL;
/* when in read only mode try to use existing .lck file first */
/* do not log errors for .lck file while in read only mode on .lck file */
lock_file_mode = "rb";
- connection->info->lock.fp = php_stream_open_wrapper(lock_name, lock_file_mode, STREAM_MUST_SEEK|IGNORE_PATH|persistent_flag, &opened_path);
- if (opened_path) {
- zend_string_release_ex(opened_path, 0);
- }
+ connection->info->lock.fp = php_stream_open_wrapper(lock_name, lock_file_mode, STREAM_MUST_SEEK|IGNORE_PATH|persistent_flag, NULL);
}
if (!connection->info->lock.fp) {
/* when not in read mode or failed to open .lck file read only. now try again in create(write) mode and log errors */
@@ -931,9 +924,9 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
connection->info->hnd = hptr;
if (persistent) {
- if (zend_register_persistent_resource(resource_key, resource_key_len, connection->info, le_pdb) == NULL) {
+ if (zend_register_persistent_resource_ex(connection->hash, connection->info, le_pdb) == NULL) {
php_error_docref(NULL, E_WARNING, "Could not register persistent resource");
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
dba_close_connection(connection);
zval_ptr_dtor(return_value);
RETURN_FALSE;
@@ -941,10 +934,10 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent)
}
zend_hash_add_new(&DBA_G(connections), connection->hash, return_value);
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
return;
fail:
- efree(resource_key);
+ zend_string_release_ex(resource_key, /* persistent */ false);
zend_string_release_ex(connection->hash, persistent);
dba_close_info(connection->info);
connection->info = NULL;
@@ -1009,8 +1002,7 @@ PHP_FUNCTION(dba_exists)
if (key_ht) {
key_str = php_dba_make_key(key_ht);
if (!key_str) {
- // TODO ValueError?
- RETURN_FALSE;
+ RETURN_THROWS();
}
}
@@ -1056,8 +1048,7 @@ PHP_FUNCTION(dba_fetch)
if (key_ht) {
key_str = php_dba_make_key(key_ht);
if (!key_str) {
- // TODO ValueError?
- RETURN_FALSE;
+ RETURN_THROWS();
}
}
@@ -1190,8 +1181,7 @@ PHP_FUNCTION(dba_delete)
if (key_ht) {
key_str = php_dba_make_key(key_ht);
if (!key_str) {
- // TODO ValueError?
- RETURN_FALSE;
+ RETURN_THROWS();
}
}
@@ -1201,7 +1191,7 @@ PHP_FUNCTION(dba_delete)
/* }}} */
/* {{{ If not inifile: Insert value as key, return false, if key exists already
- If inifile: Add vakue as key (next instance of key) */
+ If inifile: Add value as key (next instance of key) */
PHP_FUNCTION(dba_insert)
{
php_dba_update(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
@@ -1262,7 +1252,6 @@ PHP_FUNCTION(dba_sync)
/* {{{ List configured database handlers */
PHP_FUNCTION(dba_handlers)
{
- const dba_handler *hptr;
bool full_info = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &full_info) == FAILURE) {
@@ -1271,7 +1260,7 @@ PHP_FUNCTION(dba_handlers)
array_init(return_value);
- for(hptr = handler; hptr->name; hptr++) {
+ for (const dba_handler *hptr = handler; hptr->name; hptr++) {
if (full_info) {
// TODO: avoid reallocation ???
char *str = hptr->info(hptr, NULL);
diff --git a/ext/dl_test/dl_test.c b/ext/dl_test/dl_test.c
index 7aebf54313385..10afdb0720f2d 100644
--- a/ext/dl_test/dl_test.c
+++ b/ext/dl_test/dl_test.c
@@ -124,6 +124,8 @@ PHP_MINIT_FUNCTION(dl_test)
fprintf(stderr, "DL TEST MINIT\n");
}
+ register_dl_test_symbols(module_number);
+
return SUCCESS;
}
/* }}} */
diff --git a/ext/dl_test/dl_test.stub.php b/ext/dl_test/dl_test.stub.php
index c2d8ff577780f..9c3bbac6b267d 100644
--- a/ext/dl_test/dl_test.stub.php
+++ b/ext/dl_test/dl_test.stub.php
@@ -5,6 +5,9 @@
* @undocumentable
*/
+/** @var int */
+const DL_TEST_CONST = 42;
+
function dl_test_test1(): void {}
function dl_test_test2(string $str = ""): string {}
@@ -21,6 +24,5 @@ public function test(string $str = ""): string {}
class DlTestSubClass extends DlTestSuperClass {
}
-/** @alias DlTestClassAlias */
class DlTestAliasedClass {
}
diff --git a/ext/dl_test/dl_test_arginfo.h b/ext/dl_test/dl_test_arginfo.h
index 84c7c151ae79f..1b3b65aa7e677 100644
--- a/ext/dl_test/dl_test_arginfo.h
+++ b/ext/dl_test/dl_test_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 0641a8eeff00e6c8083fe4a8639f970e3ba80db9 */
+ * Stub hash: 75bdb57a45060b123fe48003fef43d2af07726e1 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO()
@@ -33,6 +33,11 @@ static const zend_function_entry class_DlTestSuperClass_methods[] = {
ZEND_FE_END
};
+static void register_dl_test_symbols(int module_number)
+{
+ REGISTER_LONG_CONSTANT("DL_TEST_CONST", 42, CONST_PERSISTENT);
+}
+
static zend_class_entry *register_class_DlTest(void)
{
zend_class_entry ce, *class_entry;
@@ -75,7 +80,6 @@ static zend_class_entry *register_class_DlTestAliasedClass(void)
INIT_CLASS_ENTRY(ce, "DlTestAliasedClass", NULL);
class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0);
- zend_register_class_alias("DlTestClassAlias", class_entry);
return class_entry;
}
diff --git a/ext/dom/config.m4 b/ext/dom/config.m4
index b279bccd5bdac..c6c67ced36e51 100644
--- a/ext/dom/config.m4
+++ b/ext/dom/config.m4
@@ -206,7 +206,7 @@ if test "$PHP_DOM" != "no"; then
html5_parser.c
html5_serializer.c
infra.c
- inner_html_mixin.c
+ inner_outer_html_mixin.c
namednodemap.c
namespace_compat.c
node.c
diff --git a/ext/dom/config.w32 b/ext/dom/config.w32
index 231f005895f56..1db4f6d11ba14 100644
--- a/ext/dom/config.w32
+++ b/ext/dom/config.w32
@@ -10,7 +10,7 @@ if (PHP_DOM == "yes") {
EXTENSION("dom", "php_dom.c attr.c document.c infra.c \
xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c private_data.c \
domexception.c processinginstruction.c \
- cdatasection.c documentfragment.c domimplementation.c element.c inner_html_mixin.c \
+ cdatasection.c documentfragment.c domimplementation.c element.c inner_outer_html_mixin.c \
node.c characterdata.c documenttype.c \
entity.c nodelist.c html_collection.c text.c comment.c \
entityreference.c \
diff --git a/ext/dom/document.c b/ext/dom/document.c
index 2abf6636713bf..c7a8582e0872c 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -194,13 +194,12 @@ zend_result dom_document_version_write(dom_object *obj, zval *newval)
{
DOM_PROP_NODE(xmlDocPtr, docp, obj);
- /* Cannot fail because the type is either null or a string. */
- zend_string *str = zval_get_string(newval);
+ /* Type is ?string */
+ zend_string *str = Z_TYPE_P(newval) == IS_NULL ? ZSTR_EMPTY_ALLOC() : Z_STR_P(newval);
if (php_dom_follow_spec_intern(obj)) {
if (!zend_string_equals_literal(str, "1.0") && !zend_string_equals_literal(str, "1.1")) {
zend_value_error("Invalid XML version");
- zend_string_release_ex(str, 0);
return FAILURE;
}
}
@@ -211,7 +210,6 @@ zend_result dom_document_version_write(dom_object *obj, zval *newval)
docp->version = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
- zend_string_release_ex(str, 0);
return SUCCESS;
}
@@ -349,14 +347,14 @@ zend_result dom_document_recover_write(dom_object *obj, zval *newval)
/* {{{ substituteEntities boolean
readonly=no
*/
-zend_result dom_document_substitue_entities_read(dom_object *obj, zval *retval)
+zend_result dom_document_substitute_entities_read(dom_object *obj, zval *retval)
{
libxml_doc_props const* doc_prop = dom_get_doc_props_read_only(obj->document);
ZVAL_BOOL(retval, doc_prop->substituteentities);
return SUCCESS;
}
-zend_result dom_document_substitue_entities_write(dom_object *obj, zval *newval)
+zend_result dom_document_substitute_entities_write(dom_object *obj, zval *newval)
{
if (obj->document) {
dom_doc_propsptr doc_prop = dom_get_doc_props(obj->document);
@@ -394,8 +392,8 @@ zend_result dom_document_document_uri_write(dom_object *obj, zval *newval)
{
DOM_PROP_NODE(xmlDocPtr, docp, obj);
- /* Cannot fail because the type is either null or a string. */
- zend_string *str = zval_get_string(newval);
+ /* Type is ?string */
+ zend_string *str = Z_TYPE_P(newval) == IS_NULL ? ZSTR_EMPTY_ALLOC() : Z_STR_P(newval);
if (docp->URL != NULL) {
xmlFree(BAD_CAST docp->URL);
@@ -403,7 +401,6 @@ zend_result dom_document_document_uri_write(dom_object *obj, zval *newval)
docp->URL = xmlStrdup((const xmlChar *) ZSTR_VAL(str));
- zend_string_release_ex(str, 0);
return SUCCESS;
}
@@ -1074,7 +1071,7 @@ PHP_METHOD(DOMDocument, getElementById)
}
/* }}} end dom_document_get_element_by_id */
-static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
+static void php_dom_transfer_document_ref_single_node(xmlNodePtr node, php_libxml_ref_obj *new_document)
{
php_libxml_node_ptr *iteration_object_ptr = node->_private;
if (iteration_object_ptr) {
@@ -1087,21 +1084,25 @@ static zend_always_inline void php_dom_transfer_document_ref_single_node(xmlNode
}
}
-static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
+static void php_dom_transfer_document_ref_single_aux(xmlNodePtr node, php_libxml_ref_obj *new_document)
{
- if (node->children) {
- php_dom_transfer_document_ref(node->children, new_document);
+ php_dom_transfer_document_ref_single_node(node, new_document);
+ if (node->type == XML_ELEMENT_NODE) {
+ for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
+ php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
+ }
}
+}
- while (node) {
- if (node->type == XML_ELEMENT_NODE) {
- for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
- php_dom_transfer_document_ref_single_node((xmlNodePtr) attr, new_document);
- }
- }
+static void php_dom_transfer_document_ref(xmlNodePtr node, php_libxml_ref_obj *new_document)
+{
+ xmlNodePtr base = node;
+ php_dom_transfer_document_ref_single_aux(base, new_document);
- php_dom_transfer_document_ref_single_node(node, new_document);
- node = node->next;
+ node = node->children;
+ while (node != NULL) {
+ php_dom_transfer_document_ref_single_aux(node, new_document);
+ node = php_dom_next_in_tree_order(node, base);
}
}
@@ -1279,10 +1280,7 @@ PHP_METHOD(DOMDocument, __construct)
}
}
intern->document = NULL;
- if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp) == -1) {
- /* docp is always non-null so php_libxml_increment_doc_ref() never returns -1 */
- ZEND_UNREACHABLE();
- }
+ php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp);
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)docp, (void *)intern);
}
/* }}} end DOMDocument::__construct */
@@ -1491,9 +1489,7 @@ static void php_dom_finish_loading_document(zval *this, zval *return_value, xmlD
}
}
intern->document = NULL;
- if (php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc) == -1) {
- RETURN_FALSE;
- }
+ php_libxml_increment_doc_ref((php_libxml_node_object *)intern, newdoc);
intern->document->doc_props = doc_prop;
intern->document->class_type = class_type;
}
diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c
index 74b7f073741e1..76870faedfa4c 100644
--- a/ext/dom/documentfragment.c
+++ b/ext/dom/documentfragment.c
@@ -73,7 +73,7 @@ PHP_METHOD(DOMDocumentFragment, appendXML) {
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
- if (dom_node_is_read_only(nodep) == SUCCESS) {
+ if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c
index 666dae56dc0f2..32bf556295161 100644
--- a/ext/dom/documenttype.c
+++ b/ext/dom/documenttype.c
@@ -23,6 +23,7 @@
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "php_dom.h"
#include "dom_properties.h"
+#include "internal_helpers.h"
/* {{{ name string
readonly=yes
@@ -47,12 +48,12 @@ zend_result dom_documenttype_entities_read(dom_object *obj, zval *retval)
{
DOM_PROP_NODE(xmlDtdPtr, dtdptr, obj);
- php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, php_dom_follow_spec_intern(obj));
+ object_init_ex(retval, dom_get_dtd_namednodemap_ce(php_dom_follow_spec_intern(obj)));
xmlHashTable *entityht = (xmlHashTable *) dtdptr->entities;
dom_object *intern = Z_DOMOBJ_P(retval);
- dom_namednode_iter(obj, XML_ENTITY_NODE, intern, entityht, NULL, 0, NULL, 0);
+ dom_namednode_iter(obj, XML_ENTITY_NODE, intern, entityht, NULL, NULL);
return SUCCESS;
}
@@ -68,12 +69,12 @@ zend_result dom_documenttype_notations_read(dom_object *obj, zval *retval)
{
DOM_PROP_NODE(xmlDtdPtr, dtdptr, obj);
- php_dom_create_iterator(retval, DOM_DTD_NAMEDNODEMAP, php_dom_follow_spec_intern(obj));
+ object_init_ex(retval, dom_get_dtd_namednodemap_ce(php_dom_follow_spec_intern(obj)));
xmlHashTable *notationht = (xmlHashTable *) dtdptr->notations;
dom_object *intern = Z_DOMOBJ_P(retval);
- dom_namednode_iter(obj, XML_NOTATION_NODE, intern, notationht, NULL, 0, NULL, 0);
+ dom_namednode_iter(obj, XML_NOTATION_NODE, intern, notationht, NULL, NULL);
return SUCCESS;
}
diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c
index 5b9051f49b1b6..a0797967d1e76 100644
--- a/ext/dom/dom_iterators.c
+++ b/ext/dom/dom_iterators.c
@@ -188,10 +188,9 @@ static void php_dom_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
if (objmap->nodetype != XML_ENTITY_NODE &&
objmap->nodetype != XML_NOTATION_NODE) {
if (objmap->nodetype == DOM_NODESET) {
- HashTable *nodeht = HASH_OF(&objmap->baseobj_zv);
- zval *entry;
- zend_hash_move_forward_ex(nodeht, &iterator->pos);
- if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
+ HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv);
+ zval *entry = zend_hash_index_find(nodeht, iterator->index);
+ if (entry) {
zval_ptr_dtor(&iterator->curobj);
ZVAL_COPY(&iterator->curobj, entry);
return;
@@ -263,8 +262,6 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i
dom_object *intern;
dom_nnodemap_object *objmap;
xmlNodePtr curnode=NULL;
- HashTable *nodeht;
- zval *entry;
php_dom_iterator *iterator;
if (by_ref) {
@@ -284,9 +281,9 @@ zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, i
if (objmap->nodetype != XML_ENTITY_NODE &&
objmap->nodetype != XML_NOTATION_NODE) {
if (objmap->nodetype == DOM_NODESET) {
- nodeht = HASH_OF(&objmap->baseobj_zv);
- zend_hash_internal_pointer_reset_ex(nodeht, &iterator->pos);
- if ((entry = zend_hash_get_current_data_ex(nodeht, &iterator->pos))) {
+ HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv);
+ zval *entry = zend_hash_index_find(nodeht, 0);
+ if (entry) {
ZVAL_COPY(&iterator->curobj, entry);
}
} else {
diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h
index 338b4acc449f0..f9402c929d937 100644
--- a/ext/dom/dom_properties.h
+++ b/ext/dom/dom_properties.h
@@ -58,8 +58,8 @@ zend_result dom_document_preserve_whitespace_read(dom_object *obj, zval *retval)
zend_result dom_document_preserve_whitespace_write(dom_object *obj, zval *newval);
zend_result dom_document_recover_read(dom_object *obj, zval *retval);
zend_result dom_document_recover_write(dom_object *obj, zval *newval);
-zend_result dom_document_substitue_entities_read(dom_object *obj, zval *retval);
-zend_result dom_document_substitue_entities_write(dom_object *obj, zval *newval);
+zend_result dom_document_substitute_entities_read(dom_object *obj, zval *retval);
+zend_result dom_document_substitute_entities_write(dom_object *obj, zval *newval);
/* html5 document properties */
zend_result dom_html_document_encoding_write(dom_object *obj, zval *retval);
@@ -86,6 +86,8 @@ zend_result dom_element_id_write(dom_object *obj, zval *newval);
zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval);
zend_result dom_element_inner_html_read(dom_object *obj, zval *retval);
zend_result dom_element_inner_html_write(dom_object *obj, zval *newval);
+zend_result dom_element_outer_html_read(dom_object *obj, zval *retval);
+zend_result dom_element_outer_html_write(dom_object *obj, zval *newval);
zend_result dom_element_class_list_read(dom_object *obj, zval *retval);
zend_result dom_modern_element_substituted_node_value_read(dom_object *obj, zval *retval);
zend_result dom_modern_element_substituted_node_value_write(dom_object *obj, zval *newval);
diff --git a/ext/dom/element.c b/ext/dom/element.c
index 64c53e4a2d8a1..a5b0652019864 100644
--- a/ext/dom/element.c
+++ b/ext/dom/element.c
@@ -291,7 +291,6 @@ Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattribute
*/
PHP_METHOD(DOMElement, getAttribute)
{
- zval *id;
xmlNode *nodep;
char *name;
xmlChar *value = NULL;
@@ -300,12 +299,11 @@ PHP_METHOD(DOMElement, getAttribute)
size_t name_len;
bool should_free = false;
- id = ZEND_THIS;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_STRING(name, name_len)
+ ZEND_PARSE_PARAMETERS_END();
- DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
+ DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attr) {
@@ -348,9 +346,7 @@ PHP_METHOD(DOMElement, getAttributeNames)
dom_object *intern;
zval tmp;
- if (zend_parse_parameters_none() == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_NONE();
DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
@@ -402,17 +398,16 @@ Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattribute
*/
PHP_METHOD(DOMElement, setAttribute)
{
- zval *id;
xmlNode *nodep;
int name_valid;
size_t name_len, value_len;
dom_object *intern;
char *name, *value;
- id = ZEND_THIS;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &value, &value_len) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(2, 2)
+ Z_PARAM_STRING(name, name_len)
+ Z_PARAM_STRING(value, value_len)
+ ZEND_PARSE_PARAMETERS_END();
if (name_len == 0) {
zend_argument_must_not_be_empty_error(1);
@@ -425,7 +420,7 @@ PHP_METHOD(DOMElement, setAttribute)
RETURN_THROWS();
}
- DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
+ DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
if (php_dom_follow_spec_intern(intern)) {
xmlChar *name_processed = BAD_CAST name;
@@ -646,18 +641,16 @@ Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenode
*/
PHP_METHOD(DOMElement, getAttributeNode)
{
- zval *id;
xmlNodePtr nodep, attrp;
size_t name_len;
dom_object *intern;
char *name;
- id = ZEND_THIS;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_STRING(name, name_len)
+ ZEND_PARSE_PARAMETERS_END();
- DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
+ DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attrp == NULL) {
@@ -774,9 +767,9 @@ static void dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend
xmlAttr *attrp;
dom_object *intern, *attrobj;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, node_ce) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_OBJECT_OF_CLASS(node, node_ce)
+ ZEND_PARSE_PARAMETERS_END();
DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
@@ -809,40 +802,35 @@ PHP_METHOD(Dom_Element, removeAttributeNode)
Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagname
Since:
*/
-static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, bool modern)
+static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *iter_ce)
{
- size_t name_len;
dom_object *intern, *namednode;
- char *name;
+ zend_string *name;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &name, &name_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &name) == FAILURE) {
RETURN_THROWS();
}
- if (name_len > INT_MAX) {
+ if (ZSTR_LEN(name) > INT_MAX) {
zend_argument_value_error(1, "is too long");
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
- if (modern) {
- php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true);
- } else {
- php_dom_create_iterator(return_value, DOM_NODELIST, false);
- }
+ object_init_ex(return_value, iter_ce);
namednode = Z_DOMOBJ_P(return_value);
- dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0);
+ dom_namednode_iter(intern, 0, namednode, NULL, name, NULL);
}
PHP_METHOD(DOMElement, getElementsByTagName)
{
- dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
+ dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_nodelist_class_entry);
}
PHP_METHOD(Dom_Element, getElementsByTagName)
{
- dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
+ dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_html_collection_class_entry);
}
/* }}} end dom_element_get_elements_by_tag_name */
@@ -1237,45 +1225,44 @@ PHP_METHOD(Dom_Element, setAttributeNodeNS)
Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagnamens
Since: DOM Level 2
*/
-static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS, bool modern)
+static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *iter_ce)
{
- size_t uri_len, name_len;
dom_object *intern, *namednode;
- char *uri, *name;
+ zend_string *uri, *name;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!p", &uri, &uri_len, &name, &name_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "P!P", &uri, &name) == FAILURE) {
RETURN_THROWS();
}
- if (uri_len > INT_MAX) {
+ if (!uri) {
+ uri = ZSTR_EMPTY_ALLOC();
+ }
+
+ if (ZSTR_LEN(uri) > INT_MAX) {
zend_argument_value_error(1, "is too long");
RETURN_THROWS();
}
- if (name_len > INT_MAX) {
+ if (ZSTR_LEN(name) > INT_MAX) {
zend_argument_value_error(2, "is too long");
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
- if (modern) {
- php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true);
- } else {
- php_dom_create_iterator(return_value, DOM_NODELIST, false);
- }
+ object_init_ex(return_value, iter_ce);
namednode = Z_DOMOBJ_P(return_value);
- dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len);
+ dom_namednode_iter(intern, 0, namednode, NULL, name, uri);
}
PHP_METHOD(DOMElement, getElementsByTagNameNS)
{
- dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
+ dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_nodelist_class_entry);
}
PHP_METHOD(Dom_Element, getElementsByTagNameNS)
{
- dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
+ dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_html_collection_class_entry);
}
/* }}} end dom_element_get_elements_by_tag_name_ns */
@@ -1715,6 +1702,98 @@ PHP_METHOD(Dom_Element, insertAdjacentText)
}
/* }}} end DOMElement::insertAdjacentText */
+/* https://html.spec.whatwg.org/#dom-element-insertadjacenthtml */
+PHP_METHOD(Dom_Element, insertAdjacentHTML)
+{
+ zval *where_zv;
+ zend_string *string;
+
+ dom_object *this_intern;
+ zval *id;
+ xmlNodePtr thisp;
+
+ bool created_context = false;
+
+ ZEND_PARSE_PARAMETERS_START(2, 2)
+ Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
+ Z_PARAM_STR(string)
+ ZEND_PARSE_PARAMETERS_END();
+
+ DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
+
+ const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
+
+ /* 1. We don't do injection sinks. */
+
+ /* 2. Let context be NULL */
+ xmlNodePtr context = NULL;
+
+ /* 3. Use the first matching item from this list: (...) */
+ switch (ZSTR_LEN(where) + ZSTR_VAL(where)[2]) {
+ case sizeof("BeforeBegin") - 1 + 'f':
+ case sizeof("AfterEnd") - 1 + 't':
+ /* 1. Set context to this's parent. */
+ context = thisp->parent;
+
+ /* 2. If context is null or a Document, throw a "NoModificationAllowedError" DOMException. */
+ if (context == NULL || context->type == XML_DOCUMENT_NODE || context->type == XML_HTML_DOCUMENT_NODE) {
+ php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, true);
+ RETURN_THROWS();
+ }
+ break;
+ case sizeof("AfterBegin") - 1 + 't':
+ case sizeof("BeforeEnd") - 1 + 'f':
+ /* Set context to this. */
+ context = thisp;
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+
+ /* 4. If context is not an Element or all of the following are true: (...) */
+ if (context->type != XML_ELEMENT_NODE
+ || (php_dom_ns_is_html_and_document_is_html(context) && xmlStrEqual(context->name, BAD_CAST "html"))) {
+ /* set context to the result of creating an element given this's node document, body, and the HTML namespace. */
+ xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_get_ns_mapper(this_intern));
+
+ context = xmlNewDocNode(thisp->doc, html_ns, BAD_CAST "body", NULL);
+ created_context = true;
+ if (UNEXPECTED(context == NULL)) {
+ php_dom_throw_error(INVALID_STATE_ERR, true);
+ goto err;
+ }
+ }
+
+ /* 5. Let fragment be the result of invoking the fragment parsing algorithm steps with context and compliantString. */
+ xmlNodePtr fragment = dom_parse_fragment(this_intern, context, string);
+ if (fragment == NULL) {
+ goto err;
+ }
+
+ php_libxml_invalidate_node_list_cache(this_intern->document);
+
+ /* 6. Use the first matching item from this list: (...) */
+ switch (ZSTR_LEN(where) + ZSTR_VAL(where)[2]) {
+ case sizeof("BeforeBegin") - 1 + 'f':
+ php_dom_pre_insert(this_intern->document, fragment, thisp->parent, thisp);
+ break;
+ case sizeof("AfterEnd") - 1 + 't':
+ php_dom_pre_insert(this_intern->document, fragment, thisp->parent, thisp->next);
+ break;
+ case sizeof("AfterBegin") - 1 + 't':
+ php_dom_pre_insert(this_intern->document, fragment, thisp, thisp->children);
+ break;
+ case sizeof("BeforeEnd") - 1 + 'f':
+ php_dom_node_append(this_intern->document, fragment, thisp);
+ break;
+ EMPTY_SWITCH_DEFAULT_CASE();
+ }
+
+err:
+ if (created_context) {
+ xmlFreeNode(context);
+ }
+}
+
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
Since:
*/
@@ -1761,7 +1840,7 @@ PHP_METHOD(DOMElement, toggleAttribute)
if (follow_spec) {
xmlSetNsProp(thisp, NULL, BAD_CAST qname, NULL);
} else {
- /* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour.
+ /* The behaviour for namespaces isn't defined by spec, but this is based on observing browsers' behaviour.
* It follows the same rules when you'd manually add an attribute using the other APIs. */
int len;
const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len);
diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c
index 1ba39870d39b9..41624bfe172fd 100644
--- a/ext/dom/html_document.c
+++ b/ext/dom/html_document.c
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
/* Implementation defined, but as HTML5 defaults in all other cases to UTF-8, we'll do the same. */
#define DOM_FALLBACK_ENCODING_ID LXB_ENCODING_UTF_8
@@ -517,6 +518,30 @@ static bool dom_process_parse_chunk(
return true;
}
+/* This seeks, using SWAR techniques, to the first non-ASCII byte in a UTF-8 input.
+ * Returns true if the entire input was consumed without encountering non-ASCII, false otherwise. */
+static zend_always_inline bool dom_seek_utf8_non_ascii(const lxb_char_t **data, const lxb_char_t *end)
+{
+ while (*data + sizeof(size_t) <= end) {
+ size_t bytes;
+ memcpy(&bytes, *data, sizeof(bytes));
+ /* If the top bit is set, it's not ASCII. */
+ if ((bytes & LEXBOR_SWAR_REPEAT(0x80)) != 0) {
+ return false;
+ }
+ *data += sizeof(size_t);
+ }
+
+ while (*data < end) {
+ if (**data & 0x80) {
+ return false;
+ }
+ (*data)++;
+ }
+
+ return true;
+}
+
static bool dom_decode_encode_fast_path(
lexbor_libxml2_bridge_parse_context *ctx,
lxb_html_document_t *document,
@@ -557,23 +582,22 @@ static bool dom_decode_encode_fast_path(
const lxb_char_t *last_output = buf_ref;
while (buf_ref != buf_end) {
/* Fast path converts non-validated UTF-8 -> validated UTF-8 */
- if (decoding_encoding_ctx->decode.u.utf_8.need == 0 && *buf_ref < 0x80) {
+ if (decoding_encoding_ctx->decode.u.utf_8.need == 0) {
/* Fast path within the fast path: try to skip non-mb bytes in bulk if we are not in a state where we
- * need more UTF-8 bytes to complete a sequence.
- * It might be tempting to use SIMD here, but it turns out that this is less efficient because
- * we need to process the same byte multiple times sometimes when mixing ASCII with multibyte. */
- buf_ref++;
- continue;
+ * need more UTF-8 bytes to complete a sequence. */
+ if (dom_seek_utf8_non_ascii(&buf_ref, buf_end)) {
+ ZEND_ASSERT(buf_ref == buf_end);
+ break;
+ }
}
const lxb_char_t *buf_ref_backup = buf_ref;
lxb_codepoint_t codepoint = lxb_encoding_decode_utf_8_single(&decoding_encoding_ctx->decode, &buf_ref, buf_end);
if (UNEXPECTED(codepoint > LXB_ENCODING_MAX_CODEPOINT)) {
- size_t skip = buf_ref - buf_ref_backup; /* Skip invalid data, it's replaced by the UTF-8 replacement bytes */
if (!dom_process_parse_chunk(
ctx,
document,
parser,
- buf_ref - last_output - skip,
+ buf_ref_backup - last_output,
last_output,
buf_ref - last_output,
tokenizer_error_offset,
@@ -1213,6 +1237,68 @@ static zend_result dom_write_output_stream(void *application_data, const char *b
return SUCCESS;
}
+/* Fast path when the output encoding is UTF-8 */
+static zend_result dom_saveHTML_write_string_len_utf8_output(void *application_data, const char *buf, size_t len)
+{
+ dom_output_ctx *output = (dom_output_ctx *) application_data;
+
+ output->decode->status = LXB_STATUS_OK;
+
+ const lxb_char_t *buf_ref = (const lxb_char_t *) buf;
+ const lxb_char_t *last_output = buf_ref;
+ const lxb_char_t *buf_end = buf_ref + len;
+
+ while (buf_ref != buf_end) {
+ const lxb_char_t *buf_ref_backup = buf_ref;
+ lxb_codepoint_t codepoint = lxb_encoding_decode_utf_8_single(output->decode, &buf_ref, buf_end);
+ if (UNEXPECTED(codepoint > LXB_ENCODING_MAX_CODEPOINT)) {
+ if (UNEXPECTED(output->write_output(
+ output->output_data,
+ (const char *) last_output,
+ buf_ref_backup - last_output
+ ) != SUCCESS)) {
+ return FAILURE;
+ }
+
+ if (codepoint == LXB_ENCODING_DECODE_CONTINUE) {
+ ZEND_ASSERT(buf_ref == buf_end);
+ /* The decoder needs more data but the entire buffer is consumed.
+ * All valid data is outputted, and if the remaining data for the code point
+ * is invalid, the next call will output the replacement bytes. */
+ output->decode->status = LXB_STATUS_CONTINUE;
+ return SUCCESS;
+ }
+
+ if (UNEXPECTED(output->write_output(
+ output->output_data,
+ (const char *) LXB_ENCODING_REPLACEMENT_BYTES,
+ LXB_ENCODING_REPLACEMENT_SIZE
+ ) != SUCCESS)) {
+ return FAILURE;
+ }
+
+ last_output = buf_ref;
+ }
+ }
+
+ if (buf_ref != last_output) {
+ if (UNEXPECTED(output->write_output(
+ output->output_data,
+ (const char *) last_output,
+ buf_ref - last_output
+ ) != SUCCESS)) {
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
+static zend_result dom_saveHTML_write_string_utf8_output(void *application_data, const char *buf)
+{
+ return dom_saveHTML_write_string_len_utf8_output(application_data, buf, strlen(buf));
+}
+
static zend_result dom_saveHTML_write_string_len(void *application_data, const char *buf, size_t len)
{
dom_output_ctx *output = (dom_output_ctx *) application_data;
@@ -1221,7 +1307,7 @@ static zend_result dom_saveHTML_write_string_len(void *application_data, const c
const lxb_char_t *buf_end = buf_ref + len;
do {
- decode_status = output->decoding_data->decode(output->decode, &buf_ref, buf_end);
+ decode_status = lxb_encoding_decode_utf_8(output->decode, &buf_ref, buf_end);
const lxb_codepoint_t *codepoints_ref = output->codepoints;
const lxb_codepoint_t *codepoints_end = codepoints_ref + lxb_encoding_decode_buf_used(output->decode);
@@ -1277,8 +1363,15 @@ static zend_result dom_common_save(dom_output_ctx *output_ctx, dom_object *inter
output_ctx->encoding_output = encoding_output;
dom_html5_serialize_context ctx;
- ctx.write_string_len = dom_saveHTML_write_string_len;
- ctx.write_string = dom_saveHTML_write_string;
+ if (encoding_data->encoding == LXB_ENCODING_UTF_8) {
+ /* Fast path */
+ ctx.write_string_len = dom_saveHTML_write_string_len_utf8_output;
+ ctx.write_string = dom_saveHTML_write_string_utf8_output;
+ } else {
+ /* Slow path */
+ ctx.write_string_len = dom_saveHTML_write_string_len;
+ ctx.write_string = dom_saveHTML_write_string;
+ }
ctx.application_data = output_ctx;
ctx.private_data = php_dom_get_private_data(intern);
if (UNEXPECTED(dom_html5_serialize_outer(&ctx, node) != SUCCESS)) {
diff --git a/ext/dom/infra.c b/ext/dom/infra.c
index eb276d179464b..9bb1942fe01d8 100644
--- a/ext/dom/infra.c
+++ b/ext/dom/infra.c
@@ -46,7 +46,7 @@ zend_string *dom_strip_and_collapse_ascii_whitespace(zend_string *input)
while (current < end) {
/* Copy non-whitespace */
size_t non_whitespace_len = strcspn(current, ascii_whitespace);
- /* If the pointers are equal, we still haven't encountered collapsable or strippable whitespace. */
+ /* If the pointers are equal, we still haven't encountered collapsible or strippable whitespace. */
if (write_ptr != current) {
memmove(write_ptr, current, non_whitespace_len);
}
diff --git a/ext/dom/inner_html_mixin.c b/ext/dom/inner_outer_html_mixin.c
similarity index 76%
rename from ext/dom/inner_html_mixin.c
rename to ext/dom/inner_outer_html_mixin.c
index e72b205bf4628..b14c3ba708ffb 100644
--- a/ext/dom/inner_html_mixin.c
+++ b/ext/dom/inner_outer_html_mixin.c
@@ -55,12 +55,9 @@ static int dom_write_smart_str(void *context, const char *buffer, int len)
return len;
}
-/* https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin
- * and https://w3c.github.io/DOM-Parsing/#dfn-fragment-serializing-algorithm */
-zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
+/* https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#fragment-serializing-algorithm-steps */
+static zend_string *dom_element_html_fragment_serialize(dom_object *obj, xmlNodePtr node)
{
- DOM_PROP_NODE(xmlNodePtr, node, obj);
-
/* 1. Let context document be the value of node's node document. */
const xmlDoc *context_document = node->doc;
@@ -73,7 +70,7 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
ctx.write_string = dom_inner_html_write_string;
ctx.write_string_len = dom_inner_html_write_string_len;
dom_html5_serialize(&ctx, node);
- ZVAL_STR(retval, smart_str_extract(&output));
+ return smart_str_extract(&output);
}
/* 3. Otherwise, context document is an XML document; return an XML serialization of node passing the flag require well-formed. */
else {
@@ -104,11 +101,21 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
if (UNEXPECTED(status < 0)) {
smart_str_free_ex(&str, false);
php_dom_throw_error_with_message(SYNTAX_ERR, "The resulting XML serialization is not well-formed", true);
- return FAILURE;
+ return NULL;
}
- ZVAL_STR(retval, smart_str_extract(&str));
+ return smart_str_extract(&str);
}
+}
+/* https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin */
+zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
+{
+ DOM_PROP_NODE(xmlNodePtr, node, obj);
+ zend_string *serialization = dom_element_html_fragment_serialize(obj, node);
+ if (serialization == NULL) {
+ return FAILURE;
+ }
+ ZVAL_STR(retval, serialization);
return SUCCESS;
}
@@ -334,23 +341,31 @@ static xmlNodePtr dom_xml_fragment_parsing_algorithm(dom_object *obj, const xmlN
return NULL;
}
-/* https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin
- * and https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm */
-zend_result dom_element_inner_html_write(dom_object *obj, zval *newval)
+/* https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm */
+xmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input)
{
- DOM_PROP_NODE(xmlNodePtr, context_node, obj);
-
- xmlNodePtr fragment;
if (context_node->doc->type == XML_DOCUMENT_NODE) {
- fragment = dom_xml_fragment_parsing_algorithm(obj, context_node, Z_STR_P(newval));
+ return dom_xml_fragment_parsing_algorithm(obj, context_node, input);
} else {
- fragment = dom_html_fragment_parsing_algorithm(obj, context_node, Z_STR_P(newval), obj->document->quirks_mode);
+ return dom_html_fragment_parsing_algorithm(obj, context_node, input, obj->document->quirks_mode);
}
+}
+/* https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin */
+zend_result dom_element_inner_html_write(dom_object *obj, zval *newval)
+{
+ /* 1. We don't do injection sinks, skip. */
+
+ /* 2. Let context be this. */
+ DOM_PROP_NODE(xmlNodePtr, context_node, obj);
+
+ /* 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and compliantString. */
+ xmlNodePtr fragment = dom_parse_fragment(obj, context_node, Z_STR_P(newval));
if (fragment == NULL) {
return FAILURE;
}
+ /* 4. If context is a template element, then set context to the template element's template contents (a DocumentFragment). */
if (php_dom_ns_is_fast(context_node, php_dom_ns_is_html_magic_token) && xmlStrEqual(context_node->name, BAD_CAST "template")) {
context_node = php_dom_ensure_templated_content(php_dom_get_private_data(obj), context_node);
if (context_node == NULL) {
@@ -362,8 +377,100 @@ zend_result dom_element_inner_html_write(dom_object *obj, zval *newval)
ZEND_ASSERT(obj->document != NULL);
php_libxml_invalidate_node_list_cache(obj->document);
+ /* 5. Replace all with fragment within context. */
dom_remove_all_children(context_node);
return php_dom_pre_insert(obj->document, fragment, context_node, NULL) ? SUCCESS : FAILURE;
}
+/* https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#the-outerhtml-property */
+zend_result dom_element_outer_html_read(dom_object *obj, zval *retval)
+{
+ DOM_PROP_NODE(xmlNodePtr, this, obj);
+
+ /* 1. Let element be a fictional node whose only child is this. */
+ xmlNode element;
+ memset(&element, 0, sizeof(element));
+ element.type = XML_DOCUMENT_FRAG_NODE;
+ element.children = element.last = this;
+ element.doc = this->doc;
+
+ xmlNodePtr old_parent = this->parent;
+ xmlNodePtr old_next = this->next;
+ this->parent = &element;
+ this->next = NULL;
+
+ /* 2. Return the result of running fragment serializing algorithm steps with element and true. */
+ zend_string *serialization = dom_element_html_fragment_serialize(obj, &element);
+
+ this->parent = old_parent;
+ this->next = old_next;
+
+ if (serialization == NULL) {
+ return FAILURE;
+ }
+ ZVAL_STR(retval, serialization);
+ return SUCCESS;
+}
+
+/* https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#the-outerhtml-property */
+zend_result dom_element_outer_html_write(dom_object *obj, zval *newval)
+{
+ /* 1. We don't do injection sinks, skip. */
+
+ /* 2. Let parent be this's parent. */
+ DOM_PROP_NODE(xmlNodePtr, this, obj);
+ xmlNodePtr parent = this->parent;
+ bool created_parent = false;
+
+ /* 3. If parent is null, return. */
+ if (parent == NULL) {
+ return SUCCESS;
+ }
+
+ /* 4. If parent is a Document, throw. */
+ if (parent->type == XML_DOCUMENT_NODE || parent->type == XML_HTML_DOCUMENT_NODE) {
+ php_dom_throw_error(INVALID_MODIFICATION_ERR, true);
+ return FAILURE;
+ }
+
+ /* 5. If parent is a DocumentFragment, set parent to the result of creating an element given this's node document, body, and the HTML namespace. */
+ if (parent->type == XML_DOCUMENT_FRAG_NODE) {
+ xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_get_ns_mapper(obj));
+
+ parent = xmlNewDocNode(parent->doc, html_ns, BAD_CAST "body", NULL);
+ created_parent = true;
+ if (UNEXPECTED(parent == NULL)) {
+ php_dom_throw_error(INVALID_STATE_ERR, true);
+ return FAILURE;
+ }
+ }
+
+ /* 6. Let fragment be the result of invoking the fragment parsing algorithm steps given parent and compliantString. */
+ xmlNodePtr fragment = dom_parse_fragment(obj, parent, Z_STR_P(newval));
+ if (fragment == NULL) {
+ if (created_parent) {
+ xmlFreeNode(parent);
+ }
+ return FAILURE;
+ }
+
+ ZEND_ASSERT(obj->document != NULL);
+ php_libxml_invalidate_node_list_cache(obj->document);
+
+ /* 7. Replace this with fragment within this's parent. */
+ if (!php_dom_pre_insert(obj->document, fragment, this->parent, this)) {
+ xmlFreeNode(fragment);
+ if (created_parent) {
+ xmlFreeNode(parent);
+ }
+ return FAILURE;
+ }
+ xmlUnlinkNode(this);
+ if (created_parent) {
+ ZEND_ASSERT(parent->children == NULL);
+ xmlFreeNode(parent);
+ }
+ return SUCCESS;
+}
+
#endif
diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c
index 3a40318628f10..67aefde4f9016 100644
--- a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c
+++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c
@@ -3,7 +3,7 @@
*
* Author: Alexander Borisov
* Adapted for PHP + libxml2 by: Niels Dossche
- * Based on Lexbor 2.4.0 (upstream commit e9d35f6384de7bd8c1b79e7111bc3a44f8822967)
+ * Based on Lexbor (upstream commit b347aa4e4da4e82b1cae18989ceea1aa0278daf1)
*/
#include
@@ -411,16 +411,14 @@ lxb_selectors_find(lxb_selectors_t *selectors, const xmlNode *root,
const lxb_css_selector_list_t *list,
lxb_selectors_cb_f cb, void *ctx)
{
- lxb_selectors_entry_t *entry;
+ lxb_selectors_entry_t entry = {0};
lxb_selectors_nested_t nested;
- entry = lexbor_dobject_calloc(selectors->objs);
-
- entry->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
- entry->selector = list->last;
+ entry.combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
+ entry.selector = list->last;
nested.parent = NULL;
- nested.entry = entry;
+ nested.entry = &entry;
nested.cb = cb;
nested.ctx = ctx;
@@ -436,20 +434,19 @@ lxb_selectors_match_node(lxb_selectors_t *selectors, const xmlNode *node,
lxb_selectors_cb_f cb, void *ctx)
{
lxb_status_t status;
- lxb_selectors_entry_t *entry;
lxb_selectors_nested_t nested;
if (!CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) {
return LXB_STATUS_OK;
}
- entry = lexbor_dobject_calloc(selectors->objs);
+ lxb_selectors_entry_t entry = {0};
- entry->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
- entry->selector = list->last;
+ entry.combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE;
+ entry.selector = list->last;
nested.parent = NULL;
- nested.entry = entry;
+ nested.entry = &entry;
nested.cb = cb;
nested.ctx = ctx;
@@ -970,7 +967,7 @@ lxb_selectors_state_has_relative(const xmlNode *node,
break;
}
- while (node !=root && node->next == NULL) {
+ while (node != root && node->next == NULL && node->parent != NULL) {
node = node->parent;
}
diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c
index bc867aba43840..73f1f09e9dae8 100644
--- a/ext/dom/namednodemap.c
+++ b/ext/dom/namednodemap.c
@@ -31,7 +31,7 @@
* Since:
*/
-int php_dom_get_namednodemap_length(dom_object *obj)
+zend_long php_dom_get_namednodemap_length(dom_object *obj)
{
dom_nnodemap_object *objmap = (dom_nnodemap_object *) obj->ptr;
if (!objmap) {
@@ -42,16 +42,11 @@ int php_dom_get_namednodemap_length(dom_object *obj)
return objmap->ht ? xmlHashSize(objmap->ht) : 0;
}
- int count = 0;
+ zend_long count = 0;
xmlNodePtr nodep = dom_object_get_node(objmap->baseobj);
if (nodep) {
- xmlAttrPtr curnode = nodep->properties;
- if (curnode) {
+ for (xmlAttrPtr curnode = nodep->properties; curnode; curnode = curnode->next) {
count++;
- while (curnode->next != NULL) {
- count++;
- curnode = curnode->next;
- }
}
}
@@ -126,8 +121,7 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItem)
RETURN_THROWS();
}
- zval *id = ZEND_THIS;
- dom_nnodemap_object *objmap = Z_DOMOBJ_P(id)->ptr;
+ dom_nnodemap_object *objmap = Z_DOMOBJ_P(ZEND_THIS)->ptr;
php_dom_named_node_map_get_named_item_into_zval(objmap, named, return_value);
}
/* }}} end dom_namednodemap_get_named_item */
@@ -148,7 +142,7 @@ xmlNodePtr php_dom_named_node_map_get_item(dom_nnodemap_object *objmap, zend_lon
zend_long count = 0;
while (count < index && curnode != NULL) {
count++;
- curnode = (xmlNodePtr)curnode->next;
+ curnode = curnode->next;
}
itemnode = curnode;
}
@@ -181,8 +175,7 @@ PHP_METHOD(DOMNamedNodeMap, item)
RETURN_THROWS();
}
- zval *id = ZEND_THIS;
- dom_object *intern = Z_DOMOBJ_P(id);
+ dom_object *intern = Z_DOMOBJ_P(ZEND_THIS);
dom_nnodemap_object *objmap = intern->ptr;
php_dom_named_node_map_get_item_into_zval(objmap, index, return_value);
}
@@ -193,7 +186,6 @@ Since: DOM Level 2
*/
PHP_METHOD(DOMNamedNodeMap, getNamedItemNS)
{
- zval *id;
size_t namedlen=0, urilen=0;
dom_object *intern;
xmlNodePtr itemnode = NULL;
@@ -203,12 +195,11 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS)
xmlNodePtr nodep;
xmlNotation *notep = NULL;
- id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &urilen, &named, &namedlen) == FAILURE) {
RETURN_THROWS();
}
- intern = Z_DOMOBJ_P(id);
+ intern = Z_DOMOBJ_P(ZEND_THIS);
objmap = (dom_nnodemap_object *)intern->ptr;
@@ -235,8 +226,6 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS)
if (itemnode) {
DOM_RET_OBJ(itemnode, objmap->baseobj);
- } else {
- RETVAL_NULL();
}
}
/* }}} end dom_namednodemap_get_named_item_ns */
@@ -244,25 +233,15 @@ PHP_METHOD(DOMNamedNodeMap, getNamedItemNS)
/* {{{ */
PHP_METHOD(DOMNamedNodeMap, count)
{
- zval *id;
- dom_object *intern;
-
- id = ZEND_THIS;
- if (zend_parse_parameters_none() == FAILURE) {
- RETURN_THROWS();
- }
-
- intern = Z_DOMOBJ_P(id);
+ ZEND_PARSE_PARAMETERS_NONE();
+ dom_object *intern = Z_DOMOBJ_P(ZEND_THIS);
RETURN_LONG(php_dom_get_namednodemap_length(intern));
}
/* }}} end dom_namednodemap_count */
PHP_METHOD(DOMNamedNodeMap, getIterator)
{
- if (zend_parse_parameters_none() == FAILURE) {
- return;
- }
-
+ ZEND_PARSE_PARAMETERS_NONE();
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
diff --git a/ext/dom/namespace_compat.c b/ext/dom/namespace_compat.c
index 7a3bd68b0111a..ee32ec39dbeb3 100644
--- a/ext/dom/namespace_compat.c
+++ b/ext/dom/namespace_compat.c
@@ -180,7 +180,7 @@ PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(php
return php_dom_libxml_ns_mapper_get_ns_raw_strings(mapper, prefix, uri);
}
-static xmlNsPtr php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(php_dom_libxml_ns_mapper *mapper, xmlNsPtr ns)
+static void php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(php_dom_libxml_ns_mapper *mapper, xmlNsPtr ns)
{
ZEND_ASSERT(ns != NULL);
@@ -198,16 +198,9 @@ static xmlNsPtr php_dom_libxml_ns_mapper_store_and_normalize_parsed_ns(php_dom_l
prefix_len = xmlStrlen(ns->prefix);
}
- zval *zv = zend_hash_str_find_ptr(prefix_map, prefix, prefix_len);
- if (zv != NULL) {
- return Z_PTR_P(zv);
- }
-
zval new_zv;
DOM_Z_UNOWNED(&new_zv, ns);
- zend_hash_str_add_new(prefix_map, prefix, prefix_len, &new_zv);
-
- return ns;
+ zend_hash_str_add(prefix_map, prefix, prefix_len, &new_zv);
}
typedef struct {
diff --git a/ext/dom/node.c b/ext/dom/node.c
index 105f4cb73a018..c9bf45e887db8 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -185,8 +185,8 @@ zend_result dom_node_node_value_write(dom_object *obj, zval *newval)
{
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
- /* Cannot fail because the type is either null or a string. */
- zend_string *str = zval_get_string(newval);
+ /* Type is ?string */
+ zend_string *str = Z_TYPE_P(newval) == IS_NULL ? ZSTR_EMPTY_ALLOC() : Z_STR_P(newval);
/* Access to Element node is implemented as a convenience method */
switch (nodep->type) {
@@ -213,7 +213,6 @@ zend_result dom_node_node_value_write(dom_object *obj, zval *newval)
php_libxml_invalidate_node_list_cache(obj->document);
- zend_string_release_ex(str, 0);
return SUCCESS;
}
@@ -287,9 +286,9 @@ zend_result dom_node_child_nodes_read(dom_object *obj, zval *retval)
{
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
- php_dom_create_iterator(retval, DOM_NODELIST, php_dom_follow_spec_intern(obj));
+ object_init_ex(retval, dom_get_nodelist_ce(php_dom_follow_spec_intern(obj)));
dom_object *intern = Z_DOMOBJ_P(retval);
- dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, 0, NULL, 0);
+ dom_namednode_iter(obj, XML_ELEMENT_NODE, intern, NULL, NULL, NULL);
return SUCCESS;
}
@@ -421,9 +420,9 @@ zend_result dom_node_attributes_read(dom_object *obj, zval *retval)
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
if (nodep->type == XML_ELEMENT_NODE) {
- php_dom_create_iterator(retval, DOM_NAMEDNODEMAP, php_dom_follow_spec_intern(obj));
+ object_init_ex(retval, dom_get_namednodemap_ce(php_dom_follow_spec_intern(obj)));
dom_object *intern = Z_DOMOBJ_P(retval);
- dom_namednode_iter(obj, XML_ATTRIBUTE_NODE, intern, NULL, NULL, 0, NULL, 0);
+ dom_namednode_iter(obj, XML_ATTRIBUTE_NODE, intern, NULL, NULL, NULL);
} else {
ZVAL_NULL(retval);
}
@@ -843,8 +842,8 @@ static xmlNodePtr dom_insert_fragment(xmlNodePtr nodep, xmlNodePtr prevsib, xmlN
static bool dom_node_check_legacy_insertion_validity(xmlNodePtr parentp, xmlNodePtr child, bool stricterror, bool warn_empty_fragment)
{
- if (dom_node_is_read_only(parentp) == SUCCESS ||
- (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
+ if (dom_node_is_read_only(parentp) ||
+ (child->parent != NULL && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
@@ -1287,8 +1286,8 @@ static void dom_node_remove_child(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry
RETURN_FALSE;
}
- if (dom_node_is_read_only(nodep) == SUCCESS ||
- (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
+ if (dom_node_is_read_only(nodep) ||
+ (child->parent != NULL && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
RETURN_FALSE;
}
@@ -2421,7 +2420,7 @@ PHP_METHOD(DOMNode, getRootNode)
}
/* }}} */
-/* {{{ URL: https://dom.spec.whatwg.org/#dom-node-comparedocumentposition (last check date 2023-07-24)
+/* {{{ URL: https://dom.spec.whatwg.org/#dom-node-comparedocumentposition (last check date 2024-11-17)
Since:
*/
diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c
index d2e05a7b736b0..819b7396b69c7 100644
--- a/ext/dom/nodelist.c
+++ b/ext/dom/nodelist.c
@@ -72,7 +72,7 @@ zend_long php_dom_get_nodelist_length(dom_object *obj)
}
if (objmap->nodetype == DOM_NODESET) {
- HashTable *nodeht = HASH_OF(&objmap->baseobj_zv);
+ HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv);
return zend_hash_num_elements(nodeht);
}
@@ -145,7 +145,7 @@ void php_dom_nodelist_get_item_into_zval(dom_nnodemap_object *objmap, zend_long
itemnode = php_dom_libxml_hash_iter(objmap, index);
} else {
if (objmap->nodetype == DOM_NODESET) {
- HashTable *nodeht = HASH_OF(&objmap->baseobj_zv);
+ HashTable *nodeht = Z_ARRVAL_P(&objmap->baseobj_zv);
zval *entry = zend_hash_index_find(nodeht, index);
if (entry) {
ZVAL_COPY(return_value, entry);
diff --git a/ext/dom/parentnode/css_selectors.c b/ext/dom/parentnode/css_selectors.c
index b215dc90039cf..ca2acb7536dad 100644
--- a/ext/dom/parentnode/css_selectors.c
+++ b/ext/dom/parentnode/css_selectors.c
@@ -245,7 +245,7 @@ void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zv
zend_array_destroy(list);
RETURN_THROWS();
} else {
- php_dom_create_iterator(return_value, DOM_NODELIST, true);
+ object_init_ex(return_value, dom_modern_nodelist_class_entry);
dom_object *ret_obj = Z_DOMOBJ_P(return_value);
dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr;
ZVAL_ARR(&mapptr->baseobj_zv, list);
diff --git a/ext/dom/parentnode/tree.c b/ext/dom/parentnode/tree.c
index 9fb682910f071..f57fd1cc7335b 100644
--- a/ext/dom/parentnode/tree.c
+++ b/ext/dom/parentnode/tree.c
@@ -699,8 +699,8 @@ void dom_parent_node_before(dom_object *context, zval *nodes, uint32_t nodesc)
static zend_result dom_child_removal_preconditions(const xmlNode *child, const dom_object *context)
{
- if (dom_node_is_read_only(child) == SUCCESS ||
- (child->parent != NULL && dom_node_is_read_only(child->parent) == SUCCESS)) {
+ if (dom_node_is_read_only(child) ||
+ (child->parent != NULL && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, dom_get_strict_error(context->document));
return FAILURE;
}
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index 3c8d6e15cea20..0751284bdc976 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -155,7 +155,7 @@ typedef struct dom_prop_handler {
dom_write_t write_func;
} dom_prop_handler;
-int dom_node_is_read_only(const xmlNode *node) {
+bool dom_node_is_read_only(const xmlNode *node) {
switch (node->type) {
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
@@ -166,14 +166,9 @@ int dom_node_is_read_only(const xmlNode *node) {
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_NAMESPACE_DECL:
- return SUCCESS;
- break;
+ return true;
default:
- if (node->doc == NULL) {
- return SUCCESS;
- } else {
- return FAILURE;
- }
+ return node->doc == NULL;
}
}
@@ -964,7 +959,7 @@ PHP_MINIT_FUNCTION(dom)
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "resolveExternals", dom_document_resolve_externals_read, dom_document_resolve_externals_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "preserveWhiteSpace", dom_document_preserve_whitespace_read, dom_document_preserve_whitespace_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "recover", dom_document_recover_read, dom_document_recover_write);
- DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "substituteEntities", dom_document_substitue_entities_read, dom_document_substitue_entities_write);
+ DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "substituteEntities", dom_document_substitute_entities_read, dom_document_substitute_entities_write);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
@@ -1125,6 +1120,7 @@ PHP_MINIT_FUNCTION(dom)
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "previousElementSibling", dom_node_previous_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "nextElementSibling", dom_node_next_element_sibling_read, NULL);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "innerHTML", dom_element_inner_html_read, dom_element_inner_html_write);
+ DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "outerHTML", dom_element_outer_html_read, dom_element_outer_html_write);
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "substitutedNodeValue", dom_modern_element_substituted_node_value_read, dom_modern_element_substituted_node_value_write);
zend_hash_merge(&dom_modern_element_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
DOM_OVERWRITE_PROP_HANDLER(&dom_modern_element_prop_handlers, "textContent", dom_node_text_content_read, dom_node_text_content_write);
@@ -1460,7 +1456,7 @@ void dom_objects_free_storage(zend_object *object)
}
/* }}} */
-void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, const char *local, size_t local_len, const char *ns, size_t ns_len) /* {{{ */
+void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns) /* {{{ */
{
dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr;
@@ -1481,24 +1477,23 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml
const xmlChar* tmp;
if (local) {
- int len = (int) local_len;
- if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)local, len)) != NULL) {
+ int len = (int) ZSTR_LEN(local);
+ if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(local), len)) != NULL) {
mapptr->local = BAD_CAST tmp;
} else {
- mapptr->local = xmlCharStrndup(local, len);
- mapptr->free_local = true;
+ mapptr->local = BAD_CAST ZSTR_VAL(zend_string_copy(local));
+ mapptr->release_local = true;
}
- mapptr->local_lower = BAD_CAST estrdup(local);
- zend_str_tolower((char *) mapptr->local_lower, len);
+ mapptr->local_lower = zend_string_tolower(local);
}
if (ns) {
- int len = (int) ns_len;
- if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ns, len)) != NULL) {
+ int len = (int) ZSTR_LEN(ns);
+ if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(ns), len)) != NULL) {
mapptr->ns = BAD_CAST tmp;
} else {
- mapptr->ns = xmlCharStrndup(ns, len);
- mapptr->free_ns = true;
+ mapptr->ns = BAD_CAST ZSTR_VAL(zend_string_copy(ns));
+ mapptr->release_ns = true;
}
}
}
@@ -1569,6 +1564,11 @@ zend_object *dom_xpath_objects_new(zend_class_entry *class_type)
#endif
+/* The char pointer MUST refer to the char* of a zend_string struct */
+static void dom_zend_string_release_from_char_pointer(xmlChar *ptr) {
+ zend_string_release((zend_string*) (ptr - XtOffsetOf(zend_string, val)));
+}
+
void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */
{
dom_object *intern = php_dom_obj_from_obj(object);
@@ -1578,14 +1578,14 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */
if (objmap->cached_obj && GC_DELREF(&objmap->cached_obj->std) == 0) {
zend_objects_store_del(&objmap->cached_obj->std);
}
- if (objmap->free_local) {
- xmlFree(objmap->local);
+ if (objmap->release_local) {
+ dom_zend_string_release_from_char_pointer(objmap->local);
}
- if (objmap->free_ns) {
- xmlFree(objmap->ns);
+ if (objmap->release_ns) {
+ dom_zend_string_release_from_char_pointer(objmap->ns);
}
if (objmap->local_lower) {
- efree(objmap->local_lower);
+ zend_string_release(objmap->local_lower);
}
if (!Z_ISUNDEF(objmap->baseobj_zv)) {
zval_ptr_dtor(&objmap->baseobj_zv);
@@ -1615,9 +1615,9 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type)
objmap->ht = NULL;
objmap->local = NULL;
objmap->local_lower = NULL;
- objmap->free_local = false;
+ objmap->release_local = false;
objmap->ns = NULL;
- objmap->free_ns = false;
+ objmap->release_ns = false;
objmap->cache_tag.modification_nr = 0;
objmap->cached_length = -1;
objmap->cached_obj = NULL;
@@ -1627,27 +1627,6 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type)
return &intern->std;
}
-void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern) /* {{{ */
-{
- zend_class_entry *ce;
-
- if (iterator_type == DOM_NAMEDNODEMAP) {
- ce = dom_get_namednodemap_ce(modern);
- } else if (iterator_type == DOM_HTMLCOLLECTION) {
- /* This only exists in modern DOM. */
- ZEND_ASSERT(modern);
- ce = dom_html_collection_class_entry;
- } else if (iterator_type == DOM_DTD_NAMEDNODEMAP) {
- ce = dom_get_dtd_namednodemap_ce(modern);
- } else {
- ZEND_ASSERT(iterator_type == DOM_NODELIST);
- ce = dom_get_nodelist_ce(modern);
- }
-
- object_init_ex(return_value, ce);
-}
-/* }}} */
-
static zend_always_inline zend_class_entry *dom_get_element_ce(const xmlNode *node, bool modern)
{
if (modern) {
@@ -1873,7 +1852,7 @@ static bool dom_match_qualified_name_for_tag_name_equality(const xmlChar *local,
return dom_match_qualified_name_according_to_spec(local_to_use, nodep);
}
-xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, xmlChar *ns, xmlChar *local, xmlChar *local_lower, zend_long *cur, zend_long index) /* {{{ */
+xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, const xmlChar *ns, const xmlChar *local, const zend_string *local_lower, zend_long *cur, zend_long index) /* {{{ */
{
/* Can happen with detached document */
if (UNEXPECTED(nodep == NULL)) {
@@ -1892,7 +1871,7 @@ xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep,
while (*cur <= index) {
if (nodep->type == XML_ELEMENT_NODE) {
- if (local_match_any || dom_match_qualified_name_for_tag_name_equality(local, local_lower, nodep, match_qname)) {
+ if (local_match_any || dom_match_qualified_name_for_tag_name_equality(local, BAD_CAST ZSTR_VAL(local_lower), nodep, match_qname)) {
if (ns_match_any || (ns[0] == '\0' && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, ns))) {
if (*cur == index) {
ret = nodep;
@@ -2100,7 +2079,7 @@ int dom_validate_and_extract(const zend_string *namespace, const zend_string *qn
*localName = xmlSplitQName2(BAD_CAST ZSTR_VAL(qname), prefix);
/* 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
- * Note that null namespace means empty string here becaue of step 1. */
+ * Note that null namespace means empty string here because of step 1. */
if (*prefix != NULL && ZSTR_VAL(namespace)[0] == '\0') {
return NAMESPACE_ERR;
}
diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h
index bb0d83676dca1..31fec5a7cabcb 100644
--- a/ext/dom/php_dom.h
+++ b/ext/dom/php_dom.h
@@ -83,20 +83,20 @@ typedef struct dom_nnodemap_object {
int nodetype;
int cached_length;
xmlHashTable *ht;
- xmlChar *local, *local_lower;
+ xmlChar *local;
+ zend_string *local_lower;
xmlChar *ns;
php_libxml_cache_tag cache_tag;
dom_object *cached_obj;
zend_long cached_obj_index;
xmlDictPtr dict;
- bool free_local : 1;
- bool free_ns : 1;
+ bool release_local : 1;
+ bool release_ns : 1;
} dom_nnodemap_object;
typedef struct {
zend_object_iterator intern;
zval curobj;
- HashPosition pos;
/* intern->index is only updated for FE_* opcodes, not for e.g. unpacking,
* yet we need to track the position of the node relative to the start. */
zend_ulong index;
@@ -111,13 +111,6 @@ typedef struct {
dom_object dom;
} dom_object_namespace_node;
-typedef enum dom_iterator_type {
- DOM_NODELIST,
- DOM_NAMEDNODEMAP,
- DOM_DTD_NAMEDNODEMAP,
- DOM_HTMLCOLLECTION,
-} dom_iterator_type;
-
struct php_dom_libxml_ns_mapper;
typedef struct php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper;
@@ -147,14 +140,13 @@ void dom_reconcile_ns_list(xmlDocPtr doc, xmlNodePtr nodep, xmlNodePtr last);
xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName);
void php_dom_normalize_legacy(xmlNodePtr nodep);
void php_dom_normalize_modern(xmlNodePtr nodep);
-xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, xmlChar *ns, xmlChar *local, xmlChar *local_lower, zend_long *cur, zend_long index);
+xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr basep, xmlNodePtr nodep, const xmlChar *ns, const xmlChar *local, const zend_string *local_lower, zend_long *cur, zend_long index);
void php_dom_create_implementation(zval *retval, bool modern);
int dom_hierarchy(xmlNodePtr parent, xmlNodePtr child);
bool dom_has_feature(zend_string *feature, zend_string *version);
-int dom_node_is_read_only(const xmlNode *node);
+bool dom_node_is_read_only(const xmlNode *node);
bool dom_node_children_valid(const xmlNode *node);
-void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern);
-void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, const char *local, size_t local_len, const char *ns, size_t ns_len);
+void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns);
xmlNodePtr create_notation(const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID);
xmlNode *php_dom_libxml_hash_iter(dom_nnodemap_object *objmap, int index);
zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref);
@@ -215,13 +207,14 @@ void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
+xmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input);
/* nodemap and nodelist APIs */
xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform);
void php_dom_named_node_map_get_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, zval *return_value);
xmlNodePtr php_dom_named_node_map_get_item(dom_nnodemap_object *objmap, zend_long index);
void php_dom_named_node_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value);
-int php_dom_get_namednodemap_length(dom_object *obj);
+zend_long php_dom_get_namednodemap_length(dom_object *obj);
xmlNodePtr dom_nodelist_iter_start_first_child(xmlNodePtr nodep);
#define DOM_GET_INTERN(__id, __intern) { \
diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php
index 81713403e0e09..43d26ec7a3c7d 100644
--- a/ext/dom/php_dom.stub.php
+++ b/ext/dom/php_dom.stub.php
@@ -1632,6 +1632,7 @@ public function getElementsByTagNameNS(?string $namespace, string $localName): H
public function insertAdjacentElement(AdjacentPosition $where, Element $element): ?Element {}
public function insertAdjacentText(AdjacentPosition $where, string $data): void {}
+ public function insertAdjacentHTML(AdjacentPosition $where, string $string): void {}
/**
* @readonly
@@ -1688,6 +1689,9 @@ public function matches(string $selectors): bool {}
/** @virtual */
public string $innerHTML;
+ /** @virtual */
+ public string $outerHTML;
+
/** @virtual */
public string $substitutedNodeValue;
diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h
index ea42d6de49801..5c21b909b0e18 100644
--- a/ext/dom/php_dom_arginfo.h
+++ b/ext/dom/php_dom_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: d8a9d33a072c3c9e3798be5eee1833163a18f441 */
+ * Stub hash: 0fcee2fa666dc88faf084578dde157409a6f5594 */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_dom_import_simplexml, 0, 1, DOMAttr|DOMElement, 0)
ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
@@ -785,6 +785,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Dom_Element_insertAdjacent
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Dom_Element_insertAdjacentHTML, 0, 2, IS_VOID, 0)
+ ZEND_ARG_OBJ_INFO(0, where, Dom\\AdjacentPosition, 0)
+ ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Dom_Element_setIdAttribute, 0, 2, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, isId, _IS_BOOL, 0)
@@ -1275,6 +1280,7 @@ ZEND_METHOD(Dom_Element, getElementsByTagName);
ZEND_METHOD(Dom_Element, getElementsByTagNameNS);
ZEND_METHOD(Dom_Element, insertAdjacentElement);
ZEND_METHOD(Dom_Element, insertAdjacentText);
+ZEND_METHOD(Dom_Element, insertAdjacentHTML);
ZEND_METHOD(Dom_Element, setIdAttributeNode);
ZEND_METHOD(Dom_Element, querySelector);
ZEND_METHOD(Dom_Element, querySelectorAll);
@@ -1649,6 +1655,7 @@ static const zend_function_entry class_Dom_Element_methods[] = {
ZEND_ME(Dom_Element, getElementsByTagNameNS, arginfo_class_Dom_Element_getElementsByTagNameNS, ZEND_ACC_PUBLIC)
ZEND_ME(Dom_Element, insertAdjacentElement, arginfo_class_Dom_Element_insertAdjacentElement, ZEND_ACC_PUBLIC)
ZEND_ME(Dom_Element, insertAdjacentText, arginfo_class_Dom_Element_insertAdjacentText, ZEND_ACC_PUBLIC)
+ ZEND_ME(Dom_Element, insertAdjacentHTML, arginfo_class_Dom_Element_insertAdjacentHTML, ZEND_ACC_PUBLIC)
ZEND_RAW_FENTRY("setIdAttribute", zim_DOMElement_setIdAttribute, arginfo_class_Dom_Element_setIdAttribute, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_RAW_FENTRY("setIdAttributeNS", zim_DOMElement_setIdAttributeNS, arginfo_class_Dom_Element_setIdAttributeNS, ZEND_ACC_PUBLIC, NULL, NULL)
ZEND_ME(Dom_Element, setIdAttributeNode, arginfo_class_Dom_Element_setIdAttributeNode, ZEND_ACC_PUBLIC)
@@ -1874,9 +1881,7 @@ static zend_class_entry *register_class_DOMDocumentType(zend_class_entry *class_
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
- zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
- zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_name_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_entities_default_value;
ZVAL_UNDEF(&property_entities_default_value);
@@ -2293,9 +2298,7 @@ static zend_class_entry *register_class_DOMAttr(zend_class_entry *class_entry_DO
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
- zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
- zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_name_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_specified_default_value;
ZVAL_UNDEF(&property_specified_default_value);
@@ -2305,9 +2308,7 @@ static zend_class_entry *register_class_DOMAttr(zend_class_entry *class_entry_DO
zval property_value_default_value;
ZVAL_UNDEF(&property_value_default_value);
- zend_string *property_value_name = zend_string_init("value", sizeof("value") - 1, 1);
- zend_declare_typed_property(class_entry, property_value_name, &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_value_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_VALUE), &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_ownerElement_default_value;
ZVAL_UNDEF(&property_ownerElement_default_value);
@@ -2552,9 +2553,7 @@ static zend_class_entry *register_class_DOMException(zend_class_entry *class_ent
zval property_code_default_value;
ZVAL_LONG(&property_code_default_value, 0);
- zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1);
- zend_declare_typed_property(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
- zend_string_release(property_code_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CODE), &property_code_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_NONE(0));
return class_entry;
}
@@ -3078,6 +3077,12 @@ static zend_class_entry *register_class_Dom_Element(zend_class_entry *class_entr
zend_declare_typed_property(class_entry, property_innerHTML_name, &property_innerHTML_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zend_string_release(property_innerHTML_name);
+ zval property_outerHTML_default_value;
+ ZVAL_UNDEF(&property_outerHTML_default_value);
+ zend_string *property_outerHTML_name = zend_string_init("outerHTML", sizeof("outerHTML") - 1, 1);
+ zend_declare_typed_property(class_entry, property_outerHTML_name, &property_outerHTML_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
+ zend_string_release(property_outerHTML_name);
+
zval property_substitutedNodeValue_default_value;
ZVAL_UNDEF(&property_substitutedNodeValue_default_value);
zend_string *property_substitutedNodeValue_name = zend_string_init("substitutedNodeValue", sizeof("substitutedNodeValue") - 1, 1);
@@ -3124,15 +3129,11 @@ static zend_class_entry *register_class_Dom_Attr(zend_class_entry *class_entry_D
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
- zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
- zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_name_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_value_default_value;
ZVAL_UNDEF(&property_value_default_value);
- zend_string *property_value_name = zend_string_init("value", sizeof("value") - 1, 1);
- zend_declare_typed_property(class_entry, property_value_name, &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_value_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_VALUE), &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_ownerElement_default_value;
ZVAL_UNDEF(&property_ownerElement_default_value);
@@ -3249,9 +3250,7 @@ static zend_class_entry *register_class_Dom_DocumentType(zend_class_entry *class
zval property_name_default_value;
ZVAL_UNDEF(&property_name_default_value);
- zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
- zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_name_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
zval property_entities_default_value;
ZVAL_UNDEF(&property_entities_default_value);
@@ -3541,9 +3540,7 @@ static zend_class_entry *register_class_Dom_TokenList(zend_class_entry *class_en
zval property_value_default_value;
ZVAL_UNDEF(&property_value_default_value);
- zend_string *property_value_name = zend_string_init("value", sizeof("value") - 1, 1);
- zend_declare_typed_property(class_entry, property_value_name, &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- zend_string_release(property_value_name);
+ zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_VALUE), &property_value_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_VIRTUAL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
return class_entry;
}
diff --git a/ext/dom/tests/DOMDocument_saveHTML_variant2.phpt b/ext/dom/tests/DOMDocument_saveHTML_variant2.phpt
index 375ff2289b223..dcea0875c36d4 100644
--- a/ext/dom/tests/DOMDocument_saveHTML_variant2.phpt
+++ b/ext/dom/tests/DOMDocument_saveHTML_variant2.phpt
@@ -1,5 +1,5 @@
--TEST--
-DOMDocument::saveHTML() vs DOMDocumet::saveXML()
+DOMDocument::saveHTML() vs DOMDocument::saveXML()
--EXTENSIONS--
dom
--FILE--
diff --git a/ext/dom/tests/DOMElement_toggleAttribute.phpt b/ext/dom/tests/DOMElement_toggleAttribute.phpt
index ed29be899dac2..b9e9989e1fe09 100644
--- a/ext/dom/tests/DOMElement_toggleAttribute.phpt
+++ b/ext/dom/tests/DOMElement_toggleAttribute.phpt
@@ -87,7 +87,7 @@ echo "Checking toggled namespace:\n";
var_dump($dom->documentElement->getAttribute('xmlns:anotheron'));
?>
---EXPECT--
+--EXPECTF--
Invalid Character Error
--- Selected attribute tests (HTML) ---
bool(false)
@@ -95,10 +95,10 @@ bool(false)
bool(true)
-
+
bool(true)
-
+
bool(false)
diff --git a/ext/dom/tests/bug69846.phpt b/ext/dom/tests/bug69846.phpt
index 2f41433e35021..2e7874246fa8e 100644
--- a/ext/dom/tests/bug69846.phpt
+++ b/ext/dom/tests/bug69846.phpt
@@ -1,5 +1,5 @@
--TEST--
-Bug #69846 Segmenation fault (access violation) when iterating over DOMNodeList
+Bug #69846 Segmentation fault (access violation) when iterating over DOMNodeList
--EXTENSIONS--
dom
--FILE--
diff --git a/ext/dom/tests/bug79968.phpt b/ext/dom/tests/bug79968.phpt
index 5ce1bcb7c6e9b..97759dbfb179a 100644
--- a/ext/dom/tests/bug79968.phpt
+++ b/ext/dom/tests/bug79968.phpt
@@ -1,5 +1,5 @@
--TEST--
-dom: Bug #79968 - Crash when calling before without valid hierachy
+dom: Bug #79968 - Crash when calling before without valid hierarchy
--EXTENSIONS--
dom
--FILE--
diff --git a/ext/dom/tests/gh10234.phpt b/ext/dom/tests/gh10234.phpt
index 5edc8fc6c1ff1..11d39cd625a79 100644
--- a/ext/dom/tests/gh10234.phpt
+++ b/ext/dom/tests/gh10234.phpt
@@ -55,7 +55,7 @@ $document->documentElement->textContent = "quote 'test'";
var_dump($document->documentElement->textContent);
var_dump($document->saveHTML());
?>
---EXPECT--
+--EXPECTF--
-- Attribute tests --
string(38) "
"
@@ -67,10 +67,10 @@ string(13) "hello & world"
string(50) "
"
string(9) "hi"
-string(54) "
+string(%d) "hi<\/b>")%r>
"
string(12) "quote "test""
-string(45) "
+string(%d) "
"
string(12) "quote 'test'"
string(45) "
diff --git a/ext/dom/tests/gh15192.phpt b/ext/dom/tests/gh15192.phpt
index 5ab5d858ecebf..c7bf0a543bb93 100644
--- a/ext/dom/tests/gh15192.phpt
+++ b/ext/dom/tests/gh15192.phpt
@@ -10,8 +10,8 @@ $element = $dom2->firstChild;
$dom = new DomDocument();
var_dump($element);
?>
---EXPECTF--
-object(Dom\HTMLElement)#3 (29) {
+--EXPECT--
+object(Dom\HTMLElement)#3 (30) {
["namespaceURI"]=>
string(28) "http://www.w3.org/1999/xhtml"
["prefix"]=>
@@ -40,6 +40,8 @@ object(Dom\HTMLElement)#3 (29) {
NULL
["innerHTML"]=>
string(36) "foo
"
+ ["outerHTML"]=>
+ string(49) "foo
"
["substitutedNodeValue"]=>
string(3) "foo"
["nodeType"]=>
diff --git a/ext/dom/tests/gh16356.phpt b/ext/dom/tests/gh16356.phpt
new file mode 100644
index 0000000000000..ad09c2681806e
--- /dev/null
+++ b/ext/dom/tests/gh16356.phpt
@@ -0,0 +1,139 @@
+--TEST--
+GH-16356 (Segmentation fault with $outerHTML and next node)
+--EXTENSIONS--
+dom
+--FILE--
+append($dom->createElement("container"));
+$e1 = $dom->documentElement->appendChild($dom->createElementNS("urn:example1", "example:foo"));
+$e2 = $dom->documentElement->appendChild($dom->createElementNS("urn:example2", "example:foo"));
+var_dump($e1, $e2);
+
+?>
+--EXPECT--
+object(Dom\Element)#3 (30) {
+ ["namespaceURI"]=>
+ string(12) "urn:example1"
+ ["prefix"]=>
+ string(7) "example"
+ ["localName"]=>
+ string(3) "foo"
+ ["tagName"]=>
+ string(11) "example:foo"
+ ["id"]=>
+ string(0) ""
+ ["className"]=>
+ string(0) ""
+ ["classList"]=>
+ string(22) "(object value omitted)"
+ ["attributes"]=>
+ string(22) "(object value omitted)"
+ ["firstElementChild"]=>
+ NULL
+ ["lastElementChild"]=>
+ NULL
+ ["childElementCount"]=>
+ int(0)
+ ["previousElementSibling"]=>
+ NULL
+ ["nextElementSibling"]=>
+ string(22) "(object value omitted)"
+ ["innerHTML"]=>
+ string(0) ""
+ ["outerHTML"]=>
+ string(27) ""
+ ["substitutedNodeValue"]=>
+ string(0) ""
+ ["nodeType"]=>
+ int(1)
+ ["nodeName"]=>
+ string(11) "example:foo"
+ ["baseURI"]=>
+ string(11) "about:blank"
+ ["isConnected"]=>
+ bool(true)
+ ["ownerDocument"]=>
+ string(22) "(object value omitted)"
+ ["parentNode"]=>
+ string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
+ ["childNodes"]=>
+ string(22) "(object value omitted)"
+ ["firstChild"]=>
+ NULL
+ ["lastChild"]=>
+ NULL
+ ["previousSibling"]=>
+ NULL
+ ["nextSibling"]=>
+ string(22) "(object value omitted)"
+ ["nodeValue"]=>
+ NULL
+ ["textContent"]=>
+ string(0) ""
+}
+object(Dom\Element)#4 (30) {
+ ["namespaceURI"]=>
+ string(12) "urn:example2"
+ ["prefix"]=>
+ string(7) "example"
+ ["localName"]=>
+ string(3) "foo"
+ ["tagName"]=>
+ string(11) "example:foo"
+ ["id"]=>
+ string(0) ""
+ ["className"]=>
+ string(0) ""
+ ["classList"]=>
+ string(22) "(object value omitted)"
+ ["attributes"]=>
+ string(22) "(object value omitted)"
+ ["firstElementChild"]=>
+ NULL
+ ["lastElementChild"]=>
+ NULL
+ ["childElementCount"]=>
+ int(0)
+ ["previousElementSibling"]=>
+ string(22) "(object value omitted)"
+ ["nextElementSibling"]=>
+ NULL
+ ["innerHTML"]=>
+ string(0) ""
+ ["outerHTML"]=>
+ string(27) ""
+ ["substitutedNodeValue"]=>
+ string(0) ""
+ ["nodeType"]=>
+ int(1)
+ ["nodeName"]=>
+ string(11) "example:foo"
+ ["baseURI"]=>
+ string(11) "about:blank"
+ ["isConnected"]=>
+ bool(true)
+ ["ownerDocument"]=>
+ string(22) "(object value omitted)"
+ ["parentNode"]=>
+ string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
+ ["childNodes"]=>
+ string(22) "(object value omitted)"
+ ["firstChild"]=>
+ NULL
+ ["lastChild"]=>
+ NULL
+ ["previousSibling"]=>
+ string(22) "(object value omitted)"
+ ["nextSibling"]=>
+ NULL
+ ["nodeValue"]=>
+ NULL
+ ["textContent"]=>
+ string(0) ""
+}
diff --git a/ext/dom/tests/modern/css_selectors/lexbor274.phpt b/ext/dom/tests/modern/css_selectors/lexbor274.phpt
new file mode 100644
index 0000000000000..836280b166648
--- /dev/null
+++ b/ext/dom/tests/modern/css_selectors/lexbor274.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Lexbor #274
+--EXTENSIONS--
+dom
+--FILE--
+\n