FROM playground-php-wasm:base

ARG WITH_JSPI="no"
ENV PKG_CONFIG_PATH=/root/lib/lib/pkgconfig
ENV PATH="/root/lib/bin:${PATH}"

# The PHP version to build.
# This controls version-specific build patches and output filenames.
ARG PHP_VERSION

# The git ref to clone from php-src. Release builds use php-$PHP_VERSION tags;
# next builds use php-src development refs.
ARG PHP_REF

# Whether to build the opcache extension. The .m4 file is patched before the main
# ./buildconf script is generated.
ARG WITH_OPCACHE

RUN PHP_REF="${PHP_REF:-php-$PHP_VERSION}" && \
	git clone https://github.com/php/php-src.git php-src \
		--branch $PHP_REF \
		--single-branch \
		--depth 1;

# Work around memory leak due to PHP using Emscripten's incomplete mmap/munmap support
COPY ./compile/php-wasm-memory-storage /root/php-src/ext/wasm_memory_storage

# Polyfill for dns functions
COPY ./compile/php-wasm-dns-polyfill/ /root/php-src/ext/dns_polyfill

# Polyfill for post_message_to_js functions
COPY ./compile/php-post-message-to-js/ /root/php-src/ext/post_message_to_js

# Copy the opcache_module.c and php_opcache.h files.
COPY ./compile/opcache/ /root/php-src/ext/opcache

# Bring in the libraries
RUN mkdir -p /root/lib/include /root/lib/lib /libs
COPY ./compile/libiconv/ /root/builds/libiconv
COPY ./compile/libcurl/ /root/builds/libcurl
COPY ./compile/libopenssl/ /root/builds/libopenssl
COPY ./compile/libpng16/ /root/builds/libpng16
COPY ./compile/libjpeg/ /root/builds/libjpeg
COPY ./compile/libwebp/ /root/builds/libwebp
COPY ./compile/libaom/ /root/builds/libaom
COPY ./compile/libavif/ /root/builds/libavif
COPY ./compile/libsqlite3/ /root/builds/libsqlite3
COPY ./compile/libxml2/ /root/builds/libxml2
COPY ./compile/libz/ /root/builds/libz
COPY ./compile/oniguruma/ /root/builds/oniguruma
COPY ./compile/libImageMagick/ /root/builds/libImageMagick
COPY ./compile/libgd/ /root/builds/libgd

RUN if [ "$WITH_JSPI" = "yes" ]; then \
	find /root/builds -path '*/jspi/dist/root/lib' -type d -exec cp -r {}/. /root/lib \; ; \
else \
	find /root/builds -path '*/asyncify/dist/root/lib' -type d -exec cp -r {}/. /root/lib \; ; \
fi

COPY ./compile/libzip/ /root/builds-libzip/
RUN mkdir -p /libs/libzip
RUN if [ "$WITH_JSPI" = "yes" ]; then \
	cp -r /root/builds-libzip/jspi/dist/1.2.0/root/lib /libs/libzip/1.2.0; \
	cp -r /root/builds-libzip/jspi/dist/1.9.2/root/lib /libs/libzip/1.9.2; \
else \
	cp -r /root/builds-libzip/asyncify/dist/1.2.0/root/lib /libs/libzip/1.2.0; \
	cp -r /root/builds-libzip/asyncify/dist/1.9.2/root/lib /libs/libzip/1.9.2; \
fi

COPY ./compile/libopenssl/ /root/builds-libopenssl/
RUN mkdir -p /libs/libopenssl
RUN if [ "$WITH_JSPI" = "yes" ]; then \
	cp -r /root/builds-libopenssl/jspi/dist/root/lib /libs/libopenssl; \
else \
	cp -r /root/builds-libopenssl/asyncify/dist/root/lib /libs/libopenssl; \
fi

# Patch OPcache config.m4 file if needed
RUN if [ "$WITH_OPCACHE" = "yes" ]; \
	then \
		# Ensure mmap() shared memory is recognized, even if not true.
		# This will #declare HAVE_SHM_MMAP_ANON 1 in php_config.h
		if [[ "${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -ge "4" ]]; then \
			# Name changed in 8.4. See https://github.com/php/php-src/commit/ca55603d8a27b4e1f9c12cf72acd0fd39a6ac059
			/root/replace.sh 's/php_cv_shm_mmap_anon=no/php_cv_shm_mmap_anon=yes/g' /root/php-src/ext/opcache/config.m4; \
		else \
			/root/replace.sh 's/have_shm_mmap_anon=no/have_shm_mmap_anon=yes/g' /root/php-src/ext/opcache/config.m4; \
			# PHP bug: PHP_NEW_EXTENSION do not read $ext_shared from the config.m4 file.
			# https://github.com/php/php-src/blob/f35a22adba8a7ddf709c6cc98ecb57ba4dd8fe8c/ext/opcache/config.m4#L313
			/root/replace.sh 's/shared,,/no,,/g' /root/php-src/ext/opcache/config.m4; \
		fi; \
		# Force OPcache to be built as a static extension. OPcache is always built as a shared extension.
		/root/replace.sh 's/ext_shared=yes/ext_shared=no/g' /root/php-src/ext/opcache/config.m4; \
		# Add the opcache_module.c to the list of files to build for PHP < 8.5.0.
		# PHP 8.5 made OPcache a non-optional part of PHP https://wiki.php.net/rfc/make_opcache_required
		if [[ "${PHP_VERSION:0:1}" -lt "8" || ("${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -le "4") ]]; then \
			/root/replace.sh 's/shared_alloc_mmap.c/shared_alloc_mmap.c opcache_module.c/g' /root/php-src/ext/opcache/config.m4; \
		fi; \
		# Enable OPcache.
		echo -n ' --enable-opcache --disable-opcache-jit --disable-huge-code-pages ' >> /root/.php-configure-flags; \
	else \
		# Be sure to disable OPcache if not needed.
		echo -n ' --disable-opcache ' >> /root/.php-configure-flags; \
	fi

RUN cd php-src && ./buildconf --force

# Build PHP

# The PHP extensions to build:
ARG WITH_CURL
ARG WITH_FILEINFO
ARG WITH_LIBXML
ARG WITH_SOAP
ARG WITH_LIBZIP
ARG WITH_EXIF
ARG WITH_GD
ARG WITH_MBSTRING
ARG WITH_MBREGEX
ARG WITH_CLI_SAPI
ARG WITH_SQLITE
ARG WITH_MYSQL
ARG WITH_OPENSSL
ARG WITH_ICONV
ARG WITH_SOURCEMAPS
ARG OUTPUT_DIR_ON_HOST
ARG WITH_DEBUG
ARG DEBUG_DWARF_COMPILATION_DIR
ARG WITH_WS_NETWORKING_PROXY
ARG WITH_IMAGICK

# The platform to build for: web or node
ARG EMSCRIPTEN_ENVIRONMENT=web

RUN touch /root/.configure-flags && \
	touch /root/.emcc-php-wasm-flags && \
	touch /root/.emcc-php-wasm-sources && \
	touch /root/.JS_ABI_EXPORTS && \
	touch /root/.WASM_ABI_EXPORTS

RUN apt install -y bison;

# Add libzip and zlib files if needed
RUN if [ "$WITH_LIBZIP" = "yes" ]; then \
		cp -r /libs/libzip/1.9.2/* /root/lib; \
		# https://www.php.net/manual/en/zlib.installation.php
		echo -n ' --with-zlib --with-zlib-dir=/root/lib --with-zip ' >> /root/.php-configure-flags; \
		echo -n ' /root/lib/lib/libzip.a /root/lib/lib/libz.a ' >> /root/.emcc-php-wasm-sources; \
	fi;

# Add ncurses if needed and libedit if needed
RUN if [ "$WITH_CLI_SAPI" = "yes" ]; \
	then \
		# Configure build flags
		echo -n ' --enable-phar --enable-cli=static ' >> /root/.php-configure-flags && \
		echo -n ' /root/php-src/sapi/cli/php_cli_process_title.c /root/php-src/sapi/cli/ps_title.c ' \
			# Use absolute paths to help path mapping for DWARF debug info
			'/root/php-src/sapi/cli/php_http_parser.c /root/php-src/sapi/cli/php_cli_server.c ' \
			'/root/php-src/sapi/cli/php_cli.c ' \
			>> /root/.emcc-php-wasm-sources && \
		echo "_run_cli" >> /root/.JS_ABI_EXPORTS && echo "_wasm_add_cli_arg" >> /root/.JS_ABI_EXPORTS && \
		echo -n ' -DWITH_CLI_SAPI=1 ' >> /root/.emcc-php-wasm-flags && \
		/root/replace.sh 's/GET_SHELL_CB\(cb\);/(cb) = php_cli_get_shell_callbacks();/g' /root/php-src/ext/readline/readline_cli.c; \
		# Avoid a symbol conflict between php_cli.c and php_embed.c
		/root/replace.sh 's/\bHARDCODED_INI\b/HARDCODED_EMBED_INI/g' /root/php-src/sapi/embed/php_embed.c \
	else \
		echo -n ' --disable-cli ' >> /root/.php-configure-flags; \
	fi;

# Add Libxml2 if needed
RUN if [ "$WITH_LIBXML" = "yes" ]; \
	then \
		set -euxo pipefail;\
		echo -n ' --enable-libxml --with-libxml --with-libxml-dir=/root/lib --enable-dom --enable-xml --enable-simplexml --enable-xmlreader --enable-xmlwriter ' >> /root/.php-configure-flags && \
		echo -n ' /root/lib/lib/libxml2.a ' >> /root/.emcc-php-wasm-sources && \
		# Libxml check is wrong in PHP < 7.4.0.
		# In the regular cc it's just a warning, but in the emscripten's emcc that's an error:
		perl -pi.bak -e 's/char xmlInitParser/void xmlInitParser/g' /root/php-src/configure; \
		# On PHP < 7.1.0, the dom_iterators.c file implicitly converts *char to const *char causing emcc error
		if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "0" ]]; then \
			/root/replace.sh 's/xmlHashScan\(ht, itemHashScanner, iter\);/xmlHashScan(ht, (xmlHashScanner)itemHashScanner, iter);/g' /root/php-src/ext/dom/dom_iterators.c; \
		fi; \
	else \
		echo -n ' --disable-libxml --without-libxml --disable-dom --disable-xml --disable-simplexml --disable-xmlreader --disable-xmlwriter' >> /root/.php-configure-flags; \
	fi

# Add SOAP if needed (requires libxml)
RUN if [ "$WITH_SOAP" = "yes" ]; \
	then \
		echo -n ' --enable-soap' >> /root/.php-configure-flags; \
	else \
		echo -n ' --disable-soap' >> /root/.php-configure-flags; \
	fi

# Add sqlite3 if needed
RUN if [ "$WITH_SQLITE" = "yes" ]; \
	then \
		echo -n ' --with-sqlite3 --enable-pdo --with-pdo-sqlite=/root/lib ' >> /root/.php-configure-flags; \
		echo -n ' /root/lib/lib/libsqlite3.a ' >> /root/.emcc-php-wasm-sources; \
	else \
		echo -n ' --without-sqlite3 --disable-pdo ' >> /root/.php-configure-flags; \
	fi

# Add GD if needed
RUN if [ "$WITH_GD" = "yes" ]; \
	then \
		# Use external libgd for PHP 8.1+; fall back to bundled gd for <= 8.0 (and 7.x) to avoid missing symbols
		if [[ "${PHP_VERSION:0:1}" -gt "8" || ( "${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -ge "1" ) ]]; then \
			echo -n ' --with-external-gd=/root/lib --enable-gd  ' >> /root/.php-configure-flags; \
			echo -n ' /root/lib/lib/libgd.a /root/lib/lib/libjpeg.a /root/lib/lib/libpng16.a /root/lib/lib/libwebp.a /root/lib/lib/libsharpyuv.a /root/lib/lib/libavif.a ' >> /root/.emcc-php-wasm-sources; \
			if [[ "${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -ge "1" ]]; then \
				echo -n ' --with-avif ' >> /root/.php-configure-flags; \
			fi; \
		else \
			# Bundled gd for PHP 7.4 and 8.0
			echo -n ' --enable-gd ' >> /root/.php-configure-flags; \
			if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "4" ]]; then \
				/root/replace.sh 's/static long php_jpeg_emit_message/static void php_jpeg_emit_message/g' /root/php-src/ext/gd/libgd/gd_jpeg.c; \
				sed -i '84s/.*return 1;.*//' /root/php-src/ext/gd/libgd/gd_jpeg.c; \
				/root/replace.sh 's/cinfo.err->emit_message = \(void \(*\)\(j_common_ptr,int\)\) php_jpeg_emit_message;/cinfo.err->emit_message = php_jpeg_emit_message;/g' /root/php-src/ext/gd/libgd/gd_jpeg.c; \
			fi; \
			echo -n ' /root/lib/lib/libjpeg.a /root/lib/lib/libpng16.a /root/lib/lib/libwebp.a /root/lib/lib/libsharpyuv.a ' >> /root/.emcc-php-wasm-sources; \
		fi; \
		echo -n ' --with-png-dir=/root/lib --with-jpeg --with-webp ' >> /root/.php-configure-flags; \
	fi;

# Add openssl if needed
RUN if [ "$WITH_OPENSSL" = "yes" ]; \
	then \
		cp -r /libs/libopenssl/* /root/lib; \
		echo -n ' --with-openssl --with-openssl-dir=/root/lib ' >> /root/.php-configure-flags; \
		echo -n ' /root/lib/lib/libcrypto.a /root/lib/lib/libssl.a ' >> /root/.emcc-php-wasm-sources; \
	fi;

# Remove fileinfo if needed
RUN if [ "$WITH_FILEINFO" = "yes" ]; \
    then \
		echo -n ' --enable-fileinfo' >> /root/.php-configure-flags; \
	else \
        # light bundle should compile without fileinfo and libmagic
        echo -n ' --disable-fileinfo' >> /root/.php-configure-flags; \
    fi;

# Add iconv if needed
RUN if [ "$WITH_ICONV" = "yes" ]; \
	then \
		echo -n ' --with-iconv=/root/lib ' >> /root/.php-configure-flags; \
		echo -n ' /root/lib/lib/libiconv.a ' >> /root/.emcc-php-wasm-sources; \
		# PHP >= 8.0 posix is no longer supported.
		# PHP ≤= 7.4 posix is supported.
	else \
		echo -n ' --without-iconv ' >> /root/.php-configure-flags; \
	fi;

# Add curl if needed
RUN if [ "$WITH_CURL" = "yes" ]; \
	then \
		echo -n ' --with-curl=/root/lib ' >> /root/.php-configure-flags; \
		echo -n ' /root/lib/lib/libcurl.a ' >> /root/.emcc-php-wasm-sources; \
	fi;

# Add mbstring if needed
# mbstring is required for the exif extension
RUN if [ "$WITH_MBSTRING" = "yes" ] || [ "$WITH_EXIF" = "yes" ]; \
	then echo -n ' --enable-mbstring ' >> /root/.php-configure-flags; \
	else echo -n ' --disable-mbstring ' >> /root/.php-configure-flags; \
	fi;

# Add exif if needed
RUN if [ "$WITH_EXIF" = "yes" ]; \
	then \
		echo -n ' --enable-exif ' >> /root/.php-configure-flags; \
	else \
		echo -n ' --disable-exif ' >> /root/.php-configure-flags; \
	fi;

# Add mbregex if needed
RUN if [ "$WITH_MBREGEX" = "yes" ] && [ "${PHP_VERSION:0:3}" != "7.0" ]; \
	then \
		echo -n ' --enable-mbregex --with-onig=/root/lib ' >> /root/.php-configure-flags; \
		echo -n ' /root/lib/lib/libonig.a ' >> /root/.emcc-php-wasm-sources; \
	else \
		echo -n ' --disable-mbregex ' >> /root/.php-configure-flags; \
	fi;

# Get and patch PHP
COPY ./compile/php/php*.patch /root/
COPY ./compile/php/apply-mysqlnd-patch.sh /root/
RUN cd /root && \
	PHP_PATCH_VERSION="${PHP_VERSION:0:3}" && \
	if ! compgen -G "/root/php${PHP_PATCH_VERSION}*.patch" > /dev/null; then \
		PHP_PATCH_VERSION="8.5"; \
	fi && \
	git apply --no-index /root/php${PHP_PATCH_VERSION}*.patch -v && \
	chmod +x /root/apply-mysqlnd-patch.sh && \
	/root/apply-mysqlnd-patch.sh && \
	if [[ "$PHP_PATCH_VERSION" < '8.3' ]]; then \
		git apply --no-index /root/php-chunk-alloc-zend-assert-up-to-8.2.patch -v; \
	elif [[ "$PHP_PATCH_VERSION" == '8.3' ]]; then \
		git apply --no-index /root/php-chunk-alloc-zend-assert-8.3.patch -v; \
	elif [[ "$PHP_PATCH_VERSION" == '8.4' ]]; then \
		git apply --no-index /root/php-chunk-alloc-zend-assert-8.4.patch -v; \
	elif [[ "$PHP_PATCH_VERSION" == '8.5' ]]; then \
		git apply --no-index /root/php-chunk-alloc-zend-assert-8.5.patch -v; \
	fi && \
	touch php-src/patched

# Install Mysql support if needed
RUN if [ "$WITH_MYSQL" = "yes" ]; then \
		echo -n ' --enable-mysql --enable-pdo --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd ' >> /root/.php-configure-flags; \
		echo -n ' -DMYSQLND_FORCE_TCP_LOCALHOST ' >> /root/.emcc-php-wasm-flags; \
	fi

# Download and prepare imagick extension if needed (PHP 7.4+)
# Use version 3.7.0 for PHP 7.x, development branch for PHP 8.x
RUN if [ "$WITH_IMAGICK" = "yes" ]; then \
		if [ "${PHP_VERSION:0:1}" -eq "7" ]; then \
			IMAGICK_BRANCH="3.7.0"; \
		else \
			IMAGICK_BRANCH="master"; \
		fi && \
		git clone https://github.com/Imagick/imagick.git /root/imagick \
			--branch "$IMAGICK_BRANCH" \
			--single-branch \
			--depth 1 && \
		cp -r /root/imagick /root/php-src/ext/imagick && \
		cd /root/php-src && \
		if [ "${PHP_VERSION:0:1}" -eq "7" ]; then \
			cp configure configure.backup && \
			./buildconf --force && \
			(/root/replace.sh 's/char xmlInitParser/void xmlInitParser/g' configure || true) && \
			(/root/replace.sh 's/\$PKG_CONFIG --atleast-version 7.\d+.\d+ \$PKNAME/test "1" = "1"/g' configure || true) && \
			(/root/replace.sh 's/test "\$curl_version" -ge 7010005/test "1" = "1"/g' configure || true) && \
			perl -pi.bak -e 's/ret = read/ret = wasm_read/g' configure; \
		else \
			./buildconf --force; \
		fi && \
		echo -n ' --with-imagick=/root/lib --with-imagick-config=/root/lib/bin/wasm32-unknown-emscripten-MagickWand-config ' >> /root/.php-configure-flags && \
		echo -n ' /root/lib/lib/libMagickWand-7.Q16HDRI.a /root/lib/lib/libMagickCore-7.Q16HDRI.a ' >> /root/.emcc-php-wasm-sources; \
	fi

# Disable phar.php generation for all builds (WASM binaries can't execute during build)
# This must run after buildconf but before configure
RUN /root/replace.sh 's/pharcmd=pharcmd/pharcmd=/g' /root/php-src/configure && \
	/root/replace.sh 's/pharcmd_install=install-pharcmd/pharcmd_install=/g' /root/php-src/configure

# Build the patched PHP
WORKDIR /root/php-src
RUN source /root/emsdk/emsdk_env.sh && \
	CURL_CFLAGS="-I/root/lib/include" \
	WEBP_LIBS="-L/root/lib/lib -lwebp -lsharpyuv" \
	WEBP_CFLAGS="-I/root/lib/include" \
	CURL_LIBS="-I/root/lib/lib -L/root/lib/lib" \
	JPEG_CFLAGS="-I/root/lib/include" \
	JPEG_LIBS="-L/root/lib/lib -ljpeg" \
	PNG_CFLAGS="-I/root/lib/include" \
	PNG_LIBS="-L/root/lib/lib -lpng16 -lz" \
	AVIF_CFLAGS="-I/root/lib/include" \
	AVIF_LIBS="-L/root/lib/lib -lavif" \
	GDLIB_CFLAGS="-I/root/lib/include" \
	GDLIB_LIBS="-L/root/lib/lib -lgd -lpng16 -lz -ljpeg -lwebp -lsharpyuv -lavif" \
	PKG_CONFIG_PATH="/root/lib/lib/pkgconfig:${PKG_CONFIG_PATH}" \
		emconfigure ./configure \
		PKG_CONFIG_PATH=$PKG_CONFIG_PATH \
	# Fibers are a PHP 8.1+ feature. They are compiled as
	# a custom assembly implementation by default. However,
	# that implementation does not work with emscripten.
	#
	# The line below disables it, forcing PHP to use the
	# C implementation instead.
	#
	# See https://github.com/WordPress/wordpress-playground/issues/92
	# for more context.
	--disable-fiber-asm \
	--enable-phar \
	# --enable-json for PHP < 8.0:
	--enable-json      \
	--enable-embed=static \
	--with-layout=GNU  \
	--disable-cgi      \
	--disable-opcache  \
	--disable-posix    \
	--enable-hash      \
	--enable-static    \
	--enable-shared    \
	--enable-session   \
	--enable-filter    \
	--enable-calendar  \
	--disable-rpath    \
	--disable-phpdbg   \
	--without-pear     \
	--with-valgrind=no \
	--without-pcre-jit \
	--enable-bcmath    \
	--enable-ctype     \
	--enable-tokenizer \
	--enable-wasm_memory_storage \
	--enable-dns_polyfill \
	--enable-post_message_to_js \
	$(cat /root/.php-configure-flags)

# Silence the errors "munmap() failed: [28] Invalid argument"
# @TODO: Identify the root cause behind these errors and fix them properly
RUN echo '#define ZEND_MM_ERROR 0' >> /root/php-src/main/php_config.h;

# Force GD JPEG/PNG/WebP/AVIF support for external GD builds (PHP 8.1+)
# Configure checks may fail during cross-compilation, so we explicitly enable them
# after verifying the external libgd was built with these features.
RUN if [ "$WITH_GD" = "yes" ]; then \
	if [[ "${PHP_VERSION:0:1}" -gt "8" || ( "${PHP_VERSION:0:1}" -eq "8" && "${PHP_VERSION:2:1}" -ge "1" ) ]]; then \
		echo '/* Force GD format support for external libgd */' >> /root/php-src/main/php_config.h; \
		echo '#ifndef HAVE_GD_JPG' >> /root/php-src/main/php_config.h; \
		echo '#define HAVE_GD_JPG 1' >> /root/php-src/main/php_config.h; \
		echo '#endif' >> /root/php-src/main/php_config.h; \
		echo '#ifndef HAVE_GD_PNG' >> /root/php-src/main/php_config.h; \
		echo '#define HAVE_GD_PNG 1' >> /root/php-src/main/php_config.h; \
		echo '#endif' >> /root/php-src/main/php_config.h; \
		echo '#ifndef HAVE_GD_WEBP' >> /root/php-src/main/php_config.h; \
		echo '#define HAVE_GD_WEBP 1' >> /root/php-src/main/php_config.h; \
		echo '#endif' >> /root/php-src/main/php_config.h; \
		echo '#ifndef HAVE_GD_AVIF' >> /root/php-src/main/php_config.h; \
		echo '#define HAVE_GD_AVIF 1' >> /root/php-src/main/php_config.h; \
		echo '#endif' >> /root/php-src/main/php_config.h; \
	fi; \
fi;

# 64 bit long support {{{
# PHP uses custom typedefs for 64 bit integers in many places, e.g.
# timelib_long is int64_t when __x86_64__ is defined and int32_t otherwise.
#
# We're defining __x86_64__ in all emcc calls in this project, which
# works well, but also has some side effects in the PHP codebase.
#
# Specifically, PHP has a lot of inlined asm for 64bit processors that is used
# when __x86_64__ is defined. Inline asm won't build to WebAssembly so we need to
# disable every instance of inlined asm.
RUN echo '#undef HAVE_ASM_GOTO' >> /root/php-src/main/php_config.h;
RUN echo '#define HAVE_ASM_GOTO 0' >> /root/php-src/main/php_config.h;
# Disable asm arithmetic.
RUN /root/replace.sh 's/ZEND_USE_ASM_ARITHMETIC 1/ZEND_USE_ASM_ARITHMETIC 0/g' /root/php-src/Zend/zend_operators.h;
RUN /root/replace.sh 's/defined\(__GNUC__\)/0/g' /root/php-src/Zend/zend_multiply.h
RUN /root/replace.sh 's/defined\(__GNUC__\)/0/g' /root/php-src/Zend/zend_cpuinfo.c
RUN /root/replace.sh 's/defined\(__clang__\)/0/g' /root/php-src/Zend/zend_cpuinfo.c
# }}}

# With HAVE_UNISTD_H=1 PHP complains about the missing getdtablesize() function
RUN /root/replace.sh 's/define php_sleep sleep/define php_sleep wasm_sleep/g' /root/php-src/main/php.h
RUN echo 'extern unsigned int wasm_sleep(unsigned int time);' >> /root/php-src/main/php.h;

RUN /root/replace.sh 's/define HAVE_UNISTD_H 1/define HAVE_UNISTD_H 0/g' /root/php-src/main/php_config.h

# Rename the original php_pollfd_for() implementation so that we can link our own version.
RUN /root/replace.sh 's/static inline int php_pollfd_for\(/int php_pollfd_for(php_socket_t fd, int events, struct timeval *timeouttv); static inline int __real_php_pollfd_for(/g' /root/php-src/main/php_network.h

RUN echo 'extern ssize_t wasm_read(int fd, void *buf, size_t count);' >> /root/php-src/main/php.h;
RUN /root/replace.sh 's/ret = read/ret = wasm_read/g' /root/php-src/main/streams/plain_wrapper.c

# Provide a custom implementation of the php_exec() function that handles spawning
# the process inside exec(), passthru(), system(), etc.
# We effectively remove the php_exec() implementation from the build by renaming it
# to an unused identifier "php_exec_old", and then we mark php_exec as extern.
RUN /root/replace.sh 's/PHPAPI int php_exec(.+)$/PHPAPI extern int php_exec\1; int php_exec_old\1/g' /root/php-src/ext/standard/exec.c

# Provide a custom implementation of the shutdown() function.
RUN perl -pi.bak -e $'s/(\s+)shutdown\(/$1 wasm_shutdown(/g' /root/php-src/sapi/cli/php_cli_server.c
RUN perl -pi.bak -e $'s/(\s+)closesocket\(/$1 wasm_close(/g' /root/php-src/sapi/cli/php_cli_server.c

# Provide custom implementation fo the shutdown() function.
# This is used by intl ( which links C++ code ) and need C linkage.
RUN cat >> /root/php-src/main/php_config.h <<EOF
#ifdef __cplusplus
extern "C" {
#endif

extern int wasm_shutdown(int fd, int how);
extern int wasm_close(int fd);

#ifdef __cplusplus
}
#endif
EOF

# This is used by intl ( which links C++ code ) and need C linkage. Needed in PHP < 7.4.
RUN sed -i '/^extern char \*\*environ;/d' /root/php-src/main/php.h \
 && cat <<EOF >> /root/php-src/main/php.h
#ifdef __cplusplus
extern "C" {
#endif

extern char **environ;

#ifdef __cplusplus
}
#endif
EOF

# Don't ship PHP_FUNCTION(proc_open) with the PHP build
# so that we can ship a patched version with php_wasm.c
RUN echo '' > /root/php-src/ext/standard/proc_open.h;
RUN echo '' > /root/php-src/ext/standard/proc_open.c;

ARG OUTPUT_DIR_ON_HOST
ARG DEBUG_DWARF_COMPILATION_DIR
RUN source /root/emsdk/emsdk_env.sh && \
	# We're compiling PHP as emscripten's side module...
	export JSPI_FLAGS=$(if [ "$WITH_JSPI" = "yes" ]; then echo "-sSUPPORT_LONGJMP=wasm -fwasm-exceptions"; else echo ""; fi) && \
	export PHP_VERSION_ESCAPED="${PHP_VERSION//./_}" && \
	EMCC_FLAGS=" -D__x86_64__ -sSIDE_MODULE -Dsetsockopt=wasm_setsockopt -Dphp_exec=wasm_php_exec \
		$JSPI_FLAGS \
		-fdebug-compilation-dir=${DEBUG_DWARF_COMPILATION_DIR}/ \
		-fdebug-prefix-map=/root/php-src/=${OUTPUT_DIR_ON_HOST}/${PHP_VERSION_ESCAPED}/php-src/ " \
	# ...which means we must skip all the libraries - they will be provided in the final linking step.
	emmake make -j14

RUN cp -v /root/php-src/.libs/libphp*.la /root/lib/libphp.la
RUN cp -v /root/php-src/.libs/libphp*.a /root/lib/libphp.a

COPY ./compile/php/php_wasm.c /root/
COPY ./compile/php/proc_open* /root/

ARG WITH_SOURCEMAPS
ARG OUTPUT_DIR_ON_HOST
ARG DEBUG_DWARF_COMPILATION_DIR
ARG WITH_DEBUG
RUN set -euxo pipefail; \
	if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
		# Add nodefs when building for node.js
		echo -n ' -lnodefs.js ' >> /root/.emcc-php-wasm-flags; \
	fi; \
	if [ "${WITH_SOURCEMAPS}" = "yes" ] || [ "${WITH_DEBUG}" = "yes" ]; then \
		echo -n ' -g3 ' >> /root/.emcc-php-wasm-flags; \
		# Make source file paths relative to the location of the .wasm file
		# so we can debug without additional config in host OS.
		export PHP_VERSION_ESCAPED="${PHP_VERSION//./_}"; \
		echo -n " -fdebug-compilation-dir=${DEBUG_DWARF_COMPILATION_DIR}/ " \
			"-fdebug-prefix-map=/root/php_wasm.c=${DEBUG_DWARF_COMPILATION_DIR}/compile/php/php_wasm.c " \
			"-fdebug-prefix-map=/root/php-src/=${OUTPUT_DIR_ON_HOST}/${PHP_VERSION_ESCAPED}/php-src/ " \
			"-fdebug-prefix-map=./=${OUTPUT_DIR_ON_HOST}/${PHP_VERSION_ESCAPED}/php-src/ " \
			"-fdebug-prefix-map=/emsdk/emscripten/==${OUTPUT_DIR_ON_HOST}/${PHP_VERSION_ESCAPED}/emscripten/ " \
			"-fdebug-prefix-map=/root/emsdk/upstream/emscripten/=${OUTPUT_DIR_ON_HOST}/${PHP_VERSION_ESCAPED}/emscripten/ " \
			>> /root/.emcc-php-wasm-flags; \
		if [ "${WITH_SOURCEMAPS}" = "yes" ]; then \
			echo -n ' -gsource-map' >> /root/.emcc-php-wasm-flags; \
			# Ensure source maps are loaded from the correct URL on the web.
			# Without this, browsers will try the wasm:// protocol and never load the source map.
			if [ "$EMSCRIPTEN_ENVIRONMENT" = "web" ]; then \
				echo -n " --source-map-base http://127.0.0.1:5400/@fs${OUTPUT_DIR_ON_HOST}/${PHP_VERSION_ESCAPED}/" >> /root/.emcc-php-wasm-flags; \
			fi; \
		fi; \
	else \
		# Preserve symbol names in node.js build – the bundle size doesn't matter as much
		# as on the web, and this makes debugging **much** easier.
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
			echo -n ' -g2 ' >> /root/.emcc-php-wasm-flags; \
		fi; \
	fi;

# Add ws networking proxy support if needed
RUN if [ "$WITH_WS_NETWORKING_PROXY" = "yes" ]; \
	then \
		echo -n ' -lwebsocket.js ' >> /root/.emcc-php-wasm-flags; \
	fi

# Emscripten supports yielding from sync functions to JavaScript event loop, but all
# the synchronous functions doing that must be explicitly listed here. This is an
# exhaustive list that was created by compiling PHP with ASYNCIFY, running code that
# uses networking, observing the error, and listing the missing functions.
#
# If you a get an error similar to the one below, you need to add all the function on
# the stack to the "ASYNCIFY_ONLY" list below (in this case, it's php_mysqlnd_net_open_tcp_or_unix_pub):
#
# RuntimeError: unreachable
# at php_mysqlnd_net_open_tcp_or_unix_pub (<anonymous>:wasm-function[9341]:0x5e42b8)
# at byn$fpcast-emu$php_mysqlnd_net_open_tcp_or_unix_pub (<anonymous>:wasm-function[17222]:0x7795e9)
# at php_mysqlnd_net_connect_ex_pub (<anonymous>:wasm-function[9338]:0x5e3f02)
#
# Node cuts the trace short by default so use the --stack-trace-limit=50 CLI flag
# to get the entire stack.
#
# -------
#
# Related: Any errors like Fatal error: Cannot redeclare function ...
# are caused by dispatching a PHP request while an execution is paused
# due to an async call – it means the same PHP files are loaded before
# the previous request, where they're already loaded, is concluded.
# TODO: Consider reverting asyncify additions before merging this PR if file locking doesn't support asyncify.
RUN export ASYNCIFY_IMPORTS=$'[\n\
"invoke_i",\n\
"invoke_ii",\n\
"invoke_iii",\n\
"invoke_iiii",\n\
"invoke_iiiii",\n\
"invoke_iiiiii",\n\
"invoke_iiiiiii",\n\
"invoke_iiiiiiii",\n\
"invoke_iiiiiiiii",\n\
"invoke_iiiiiiiiii",\n\
"invoke_v",\n\
"invoke_vi",\n\
"invoke_vii",\n\
"invoke_viidii",\n\
"invoke_viii",\n\
"invoke_viiii",\n\
"invoke_viiiii",\n\
"invoke_viiiiii",\n\
"invoke_viiiiiii",\n\
"invoke_viiiiiiiii",\n\
"invoke_i",\
"invoke_ii",\
"invoke_iii",\
"invoke_iiii",\
"invoke_iiiii",\
"invoke_iiiiii",\
"invoke_iiiiiii",\
"invoke_iiiiiiii",\
"invoke_iiiiiiiiii",\
"invoke_iij",\
"invoke_iiji",\
"invoke_iiij",\
"invoke_iijii",\
"invoke_iijiji",\
"invoke_jii",\
"invoke_jiii",\
"invoke_viijii",\
"invoke_vji",\
"js_open_process",\n\
"_js_open_process",\n\
"_asyncjs__js_open_process",\n\
"js_popen_to_file",\n\
"_js_popen_to_file",\n\
"_asyncjs__js_popen_to_file",\n\
"__syscall_fcntl64",\n\
"___syscall_fcntl64",\n\
"_asyncjs___syscall_fcntl64",\n\
"js_release_file_locks",\n\
"_js_release_file_locks",\n\
"_async_js_release_file_locks",\n\
"js_flock",\n\
"_js_flock",\n\
"_async_js_flock",\n\
"js_fd_read",\n\
"_js_fd_read",\n\
"fd_close",\n\
"_fd_close",\n\
"_asyncjs__fd_close",\n\
"close",\n\
"_close",\n\
"js_module_onMessage",\n\
"zend_hash_str_find",\n\
"_js_module_onMessage",\n\
"_asyncjs__js_module_onMessage",\n\
"js_waitpid",\n\
"_js_waitpid",\n\
"_asyncjs__js_waitpid",\n\
"wasm_poll_socket",\n\
"_wasm_poll_socket",\n\
"_asyncjs__wasm_poll_socket",\n\
"_wasm_shutdown",\n\
"_asyncjs__wasm_shutdown",\n\
"recv",\n\
"_recv",\n\
"setsockopt",\n\
"_setsockopt",\n\
"wasm_connect",\n\
"_wasm_connect"]'; \
	echo -n " -s ASYNCIFY_IMPORTS=$ASYNCIFY_IMPORTS " | tr -d "\n" >> /root/.emcc-php-asyncify-flags; \
	export ASYNCIFY_ONLY_UNPREFIXED=$'"null.<anonymous>",\
"cookieread",\
"ImageToFile",\
"ReadBlob",\
"ReadBlobStream",\
"ReadImage",\
"SetImageInfo",\
"MagickReadImageFile",\
"PHPWasm_Magick_ImageToFile",\
"PHPWasm_Magick_ReadBlob",\
"PHPWasm_Magick_ReadBlobStream",\
"PHPWasm_Magick_ReadImage",\
"PHPWasm_Magick_SetImageInfo",\
"zm_shutdown_imagick",\
"php_imagick_read_file",\
"zim_Imagick___construct",\
"printf_core",\
"__vfprintf_internal",\
"vfprintf",\
"vsnprintf",\
"__xmlRaiseError",\
"__xmlLoaderErr",\
"zim_SoapClient___construct",\
"zim_SoapClient_SoapClient",\
"get_sdl",\
"get_http_headers",\
"zim_SoapClient___call",\
"soap_client_call_impl",\
"zim_SoapClient___doRequest",\
"make_http_soap_request",\
"load_wsdl_ex",\
"soap_xmlParseFile",\
"xmlCreateURLParserCtxt",\
"xmlNewInputFromFile",\
"xmlDefaultExternalEntityLoader",\
"_php_libxml_pre_ext_ent_loader",\
"xmlLoadExternalEntity",\
"xmlParserInputBufferCreateFilename",\
"php_libxml_input_buffer_create_filename",\
"php_libxml_streams_IO_open_write_wrapper",\
"php_libxml_streams_IO_open_read_wrapper",\
"php_libxml_streams_IO_open_wrapper",\
"__stdio_close",\
"zend_stream_stdio_closer",\
"zend_destroy_file_handle",\
"php_init_config",\
"zend_register_constant",\
"zend_register_ini_entries_ex",\
"php_mail",\
"php_module_startup",\
"wasm_sapi_module_startup",\
"__fseeko_unlocked",\
"__ftello_unlocked",\
"__funcs_on_exit",\
"__fwritex",\
"__netlink_enumerate",\
"__overflow",\
"__synccall",\
"__toread",\
"__uflow",\
"__vfprintf_internal",\
"__wrap_select",\
"__zend_malloc",\
"_call_user_function_ex",\
"_call_user_function_impl",\
"_emalloc_large",\
"_erealloc",\
"_erealloc2",\
"_estrdup",\
"_get_zval_cv_lookup",\
"_is_numeric_string_ex",\
"_mysqlnd_init_ps_subsystem",\
"_mysqlnd_run_command",\
"_pcre2_memctl_malloc_8",\
"_pdo_mysql_error",\
"_php_error_log_ex",\
"_php_import_environment_variables",\
"_php_stream_cast",\
"_php_stream_copy_to_mem",\
"_php_stream_copy_to_stream_ex",\
"_php_stream_eof",\
"_php_stream_fill_read_buffer",\
"_php_stream_filter_flush",\
"_php_stream_flush",\
"_php_stream_free_enclosed",\
"_php_stream_free",\
"_php_stream_get_line",\
"_php_stream_mkdir",\
"_php_stream_open_wrapper_ex",\
"_php_stream_opendir",\
"_php_stream_passthru",\
"_php_stream_read",\
"_php_stream_rmdir",\
"_php_stream_seek",\
"_php_stream_set_option",\
"_php_stream_stat_path",\
"_php_stream_stat",\
"_php_stream_sync",\
"_php_stream_truncate_set_size",\
"_php_stream_write_buffer",\
"_php_stream_write_filtered",\
"_php_stream_write",\
"_php_stream_xport_create",\
"_php_image_create_from",\
"zif_imagecreatefromavif",\
"_safe_emalloc",\
"_try_convert_to_string",\
"_zend_hash_init",\
"_zend_new_array_0",\
"_zend_new_array",\
"_zend_observe_fcall_begin",\
"_zend_observer_class_linked_notify",\
"_zend_observer_error_notify",\
"_zend_observer_function_declared_notify",\
"_zval_dtor_func_for_ptr",\
"_zval_dtor_func",\
"_zval_ptr_dtor",\
"add_property_resource_ex",\
"autoVacuumCommit",\
"bsearch",\
"btreeCellSizeCheck",\
"btreeEndTransaction",\
"btreeParseCell",\
"call_extract_if_dead",\
"call",\
"callCollNeeded",\
"callFinaliser",\
"checkTreePage",\
"cleanup_unfinished_calls",\
"clearDatabasePage",\
"cli_register_file_handles",\
"cli",\
"close_stmt_and_copy_errors",\
"close",\
"__stdio_close",\
"closeUnixFile",\
"compile_file",\
"createCollation",\
"createFunctionApi",\
"createModule",\
"ctype_fallback",\
"Curl_client_write",\
"Curl_conncache_foreach",\
"curl_easy_perform",\
"Curl_http_connect",\
"Curl_httpchunk_read",\
"curl_write",\
"Curl_is_connected",\
"curl_multi_perform",\
"curl_multi_poll",\
"curl_multi_wait",\
"Curl_multi_wait",\
"Curl_ossl_connect_nonblocking",\
"Curl_ossl_seed",\
"Curl_poll",\
"Curl_proxy_connect",\
"Curl_readwrite",\
"Curl_setup_conn",\
"Curl_socket_check",\
"Curl_ssl_connect_nonblocking",\
"Curl_wait_ms",\
"curlx_nonblock",\
"dbh_free",\
"dbh_get_gc",\
"decrement_function",\
"deflate",\
"deflateEnd",\
"deflateInit2_",\
"defragmentPage",\
"destroy_op_array",\
"do_cli_server",\
"do_cli",\
"do_fetch_common",\
"do_fetch",\
"dotlockCheckReservedLock",\
"dotlockLock",\
"dotlockUnlock",\
"doWalCallbacks",\
"dynCall_dd",\
"dynCall_ddd",\
"dynCall_di",\
"dynCall_dii",\
"dynCall_i",\
"dynCall_ii",\
"dynCall_iidiiii",\
"dynCall_iii",\
"dynCall_iiid",\
"dynCall_iiii",\
"dynCall_iiiii",\
"dynCall_iiiiii",\
"dynCall_iiiiiii",\
"dynCall_iiiiiiii",\
"dynCall_iiiiiiiii",\
"dynCall_iiiiiiiiii",\
"dynCall_iiiiiiiiiii",\
"dynCall_iiiiiiiiiiii",\
"dynCall_iiiiiiiiiiij",\
"dynCall_iiiiiij",\
"dynCall_iiiiijii",\
"dynCall_iiiij",\
"dynCall_iiiiji",\
"dynCall_iiiijii",\
"dynCall_iiiijji",\
"dynCall_iiij",\
"dynCall_iiiji",\
"dynCall_iij",\
"dynCall_iiji",\
"dynCall_iijii",\
"dynCall_iijiji",\
"dynCall_iijj",\
"dynCall_ij",\
"dynCall_j",\
"dynCall_ji",\
"dynCall_jii",\
"dynCall_jiii",\
"dynCall_jiiii",\
"dynCall_jiiiji",\
"dynCall_jiij",\
"dynCall_jiiji",\
"dynCall_jiji",\
"dynCall_jijj",\
"dynCall_jj",\
"dynCall_v",\
"dynCall_vi",\
"dynCall_vid",\
"dynCall_vii",\
"dynCall_viidii",\
"dynCall_viii",\
"dynCall_viiii",\
"dynCall_viiiii",\
"dynCall_viiiiii",\
"dynCall_viiiiiii",\
"dynCall_viiiiiiii",\
"dynCall_viiji",\
"dynCall_viijii",\
"dynCall_vij",\
"dynCall_vijii",\
"dynCall_vji",\
"dynCall_vjiii",\
"easy_perform",\
"easy_transfer",\
"RAND_poll",\
"rand_bytes",\
"RAND_bytes",\
"SSL_CTX_new",\
"rand_nopseudo_bytes",\
"rand_enough",\
"Curl_ossl_seed",\
"zif_curl_multi_exec",\
"zif_curl_multi_select",\
"ossl_connect_common",\
"Curl_ossl_connect_nonblocking",\
"Curl_ssl_connect_nonblocking",\
"https_connecting",\
"Curl_readwrite",\
"zend_fetch_debug_backtrace",\
"zend_default_exception_new",\
"zend_throw_exception_zstr",\
"zend_throw_exception",\
"zend_type_error",\
"zend_generator_iterator_move_forward",\
"ZEND_FE_FETCH_R_SPEC_VAR_HANDLER",\
"zend_generator_resume",\
"zend_generator_iterator_rewind",\
"zend_fe_reset_iterator",\
"ZEND_FE_RESET_R_SPEC_VAR_HANDLER",\
"zend_user_it_get_new_iterator",\
"ZEND_ADD_ARRAY_UNPACK_SPEC_HANDLER",\
"ZEND_COUNT_SPEC_CV_UNUSED_HANDLER",\
"__fwritex",\
"read",\
"zif_sleep",\
"zif_stream_get_contents",\
"zif_stream_select",\
"_php_stream_fill_read_buffer",\
"_php_stream_read",\
"php_sqlite3_object_free_storage",\
"php_sqlite3_error",\
"sqlite3_reset",\
"php_stream_read_to_str",\
"php_userstreamop_read",\
"zif_fread",\
"wasm_read",\
"php_stdiop_read",\
"fwrite",\
"zif_fwrite",\
"php_stdiop_write",\
"zif_array_filter",\
"zend_unclean_zval_ptr_dtor",\
"zend_call_known_instance_method_with_2_params",\
"ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_HANDLER",\
"zend_fetch_dimension_address_read_R",\
"_zval_dtor_func_for_ptr",\
"ZEND_ASSIGN_DIM_SPEC_CV_CONST_HANDLER",\
"zend_fetch_dimension_address_read",\
"php_if_fopen",\
"_php_stream_cast",\
"php_stream_temp_cast",\
"php_stream_temp_flush",\
"_php_stream_flush",\
"php_stream_flush",\
"_php_stream_write",\
"php_stream_write",\
"_php_stream_free",\
"_php_stream_free_enclosed",\
"stream_resource_persistent_dtor",\
"stream_resource_regular_dtor",\
"zend_std_has_dimension",\
"zend_isset_isempty_dim_prop_obj_handler_SPEC_CV_CONST",\
"zend_assign_to_object",\
"zif_file_put_contents",\
"ZEND_ASSIGN_OBJ_SPEC_CV_CONST_HANDLER",\
"ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER",\
"zend_std_call_user_call",\
"zend_objects_store_del_ref_by_handle_ex",\
"zend_objects_store_del_ref",\
"_zval_dtor_func",\
"_zval_ptr_dtor",\
"zend_hash_del_key_or_index",\
"zend_delete_variable",\
"ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER",\
"zend_std_unset_dimension",\
"ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLER",\
"zend_std_read_dimension",\
"zend_fetch_dimension_address_read_R_slow",\
"zend_call_method",\
"zend_assign_to_object_dim",\
"ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_CONST_HANDLER",\
"zend_std_write_dimension",\
"zend_isset_dim_slow",\
"ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CONST_HANDLER",\
"zend_std_write_property",\
"ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CONST_HANDLER",\
"zend_objects_store_del",\
"ZEND_UNSET_CV_SPEC_CV_UNUSED_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_OBSERVER_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER",\
"ZEND_DO_FCALL_SPEC_CONST_HANDLER",\
"ZEND_DO_FCALL_SPEC_HANDLER",\
"ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER",\
"ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER",\
"ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER",\
"ZEND_DO_ICALL_SPEC_HANDLER",\
"ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER",\
"ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER",\
"ZEND_DO_UCALL_SPEC_OBSERVER_HANDLER",\
"ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CONST_HANDLER",\
"ZEND_FETCH_OBJ_R_SPEC_CV_CONST_HANDLER",\
"ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CONST_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMPVAR_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CONST_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_CV_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CV_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_VAR_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_TMP_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CONST_HANDLER",\
"ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER",\
"cli",\
"__wrap_usleep",\
"wasm_sleep",\
"wasm_php_stream_flush",\
"wasm_php_stream_read",\
"php_crc32_stream_bulk_update",\
"phar_parse_zipfile",\
"GetDelegateInfo",\
"ReadBlob",\
"ReadBlobStream",\
"ImageToFile",\
"SetImageInfo",\
"PHPWasm_Magick_ReadImage",\
"MagickReadImageFile",\
"FormatLocaleString",\
"GetConfigurePaths",\
"GetConfigureOptions",\
"IsDelegateCacheInstantiated",\
"get_http_body",\
"wasm_php_exec",\
"wasm_sapi_handle_request",\
"wasm_sapi_request_shutdown",\
"execute_internal",\
"_call_user_function_ex",\
"_call_user_function_impl",\
"_mysqlnd_run_command",\
"_php_stream_copy_to_mem",\
"_php_stream_copy_to_stream_ex",\
"_php_stream_eof",\
"_php_stream_fill_read_buffer",\
"_php_stream_free",\
"_php_stream_get_line",\
"_php_stream_open_wrapper_ex",\
"_php_stream_read",\
"_php_stream_set_option",\
"_php_stream_write",\
"_php_stream_xport_create",\
"zif_stream_socket_enable_crypto",\
"do_cli",\
"do_cli_server",\
"execute_ex",\
"EVP_PKEY_encrypt",\
"exif_error_docref",\
"fclose",\
"fcntl",\
"fcntlSizeHint",\
"fetch_value",\
"fflush",\
"file_handle_dtor",\
"fileHasMovedunixCheckReservedLock",\
"fillInUnixFile",\
"find_implicit_binds_recursively",\
"findInodeInfo",\
"findReusableFd",\
"flock",\
"fread",\
"functionDestroy",\
"fwrite",\
"get_http_body",\
"get_line",\
"getFileMode",\
"gzip_unencode_write",\
"hasHotJournal",\
"https_connecting",\
"inflate",\
"inflate_fast",\
"inflate_stream",\
"inflate_table",\
"iter_wrapper_free",\
"iter_wrapper_get_gc",\
"lex_scan",\
"list_entry_destructor",\
"zend_hash_index_del",\
"zend_list_free",\
"lockBtree",\
"lockTrace",\
"main",\
"mallocWithAlarm",\
"match",\
"modifyPagePointer",\
"module_destructor",\
"module_destructor_zval",\
"zend_destroy_modules",\
"php_module_shutdown",\
"mul_function_slow",\
"multi_runsingle",\
"multipart_buffer_read",\
"mysql_handle_begin",\
"mysql_handle_closer",\
"mysql_handle_commit",\
"mysql_handle_doer",\
"mysql_handle_preparer",\
"mysql_handle_quoter",\
"mysql_handle_rollback",\
"mysql_stmt_execute",\
"mysqli_commit_or_rollback_libmysql",\
"mysqli_common_connect",\
"mysqlnd_caching_sha2_handle_server_response",\
"mysqlnd_com_handshake_run",\
"mysqlnd_com_init_db_run",\
"mysqlnd_com_stmt_execute_run",\
"mysqlnd_com_stmt_prepare_run",\
"mysqlnd_com_set_option_run",\
"mysqlnd_com_ping_run",\
"mysqlnd_com_set_option_run",\
"mysqlnd_com_debug_run",\
"mysqlnd_com_init_db_run",\
"mysqlnd_com_ping_run",\
"mysqlnd_com_statistics_run",\
"mysqlnd_com_process_kill_run",\
"mysqlnd_com_refresh_run",\
"mysqlnd_com_quit_run",\
"mysqlnd_com_query_run",\
"mysqlnd_com_change_user_run",\
"mysqlnd_com_reap_result_run",\
"mysqlnd_com_stmt_prepare_run",\
"mysqlnd_com_stmt_reset_run",\
"mysqlnd_com_stmt_send_long_data_run",\
"mysqlnd_com_stmt_close_run",\
"mysqlnd_com_enable_ssl_run",\
"mysqlnd_com_handshake_run",\
"mysqlnd_connect",\
"mysqlnd_connection_connect",\
"mysqlnd_mysqlnd_command_handshake_pub",\
"mysqlnd_mysqlnd_command_init_db_pub",\
"mysqlnd_mysqlnd_command_ping_pub",\
"mysqlnd_mysqlnd_command_reap_result_pub",\
"mysqlnd_mysqlnd_command_set_option_pub",\
"mysqlnd_mysqlnd_conn_connect_pub",\
"mysqlnd_mysqlnd_conn_data_connect_handshake_pub",\
"mysqlnd_mysqlnd_conn_data_connect_pub",\
"mysqlnd_mysqlnd_conn_data_next_result_pub",\
"mysqlnd_mysqlnd_conn_data_ping_pub",\
"mysqlnd_mysqlnd_conn_data_query_pub",\
"mysqlnd_mysqlnd_conn_data_reap_query_pub",\
"mysqlnd_mysqlnd_conn_data_select_db_pub",\
"mysqlnd_mysqlnd_conn_data_set_charset_pub",\
"mysqlnd_mysqlnd_conn_data_set_server_option_pub",\
"mysqlnd_mysqlnd_conn_data_store_result_pub",\
"mysqlnd_mysqlnd_conn_data_tx_commit_or_rollback_pub",\
"mysqlnd_mysqlnd_pfc_receive_pub",\
"mysqlnd_mysqlnd_pfc_send_pub",\
"mysqlnd_mysqlnd_protocol_send_command_handle_EOF_pub",\
"mysqlnd_mysqlnd_protocol_send_command_handle_OK_pub",\
"mysqlnd_mysqlnd_protocol_send_command_handle_response_pub",\
"mysqlnd_mysqlnd_protocol_send_command_pub",\
"mysqlnd_mysqlnd_res_store_result_fetch_data_pub",\
"mysqlnd_mysqlnd_res_store_result_pub",\
"mysqlnd_mysqlnd_stmt_execute_pub",\
"mysqlnd_mysqlnd_stmt_prepare_pub",\
"mysqlnd_mysqlnd_stmt_send_execute_pub",\
"mysqlnd_mysqlnd_vio_connect_pub",\
"mysqlnd_mysqlnd_vio_network_read_pub",\
"mysqlnd_mysqlnd_vio_network_write_pub",\
"mysqlnd_mysqlnd_vio_open_tcp_or_unix_pub",\
"mysqlnd_query_read_result_set_header",\
"mysqlnd_read_body_name",\
"mysqlnd_read_buffer_is_empty",\
"mysqlnd_read_buffer",\
"mysqlnd_read_header_name",\
"mysqlnd_read_header",\
"mysqlnd_read_packet_header_and_body",\
"mysqlnd_run_authentication",\
"mysqlnd_stmt_execute_parse_response",\
"mysqlnd_stmt_execute",\
"nodeAcquire",\
"nodeNew",\
"nodeRelease",\
"nodeWrite",\
"object_and_properties_init",\
"object_common",\
"object_custom",\
"object_init_ex",\
"open_file_for_scanning",\
"openDatabase",\
"osLocaltime",\
"ossl_connect_common",\
"page_wait_on_lock",\
"pager_end_transaction",\
"pager_playback_one_page",\
"pager_unlock",\
"pager_wait_on_lock",\
"pagerLockDb",\
"pagerUndoCallback",\
"pagerUnlockAndRollback",\
"pagerUnlockDb",\
"pagerUnlockIfUnused",\
"param_dtor",\
"parentWrite",\
"pcacheUnpin",\
"persistent_stream_open_function",\
"pdo_dbh_attribute_set",\
"pdo_dbh_free_storage",\
"pdo_dbstmt_free_storage",\
"pdo_handle_error",\
"pdo_hash_methods",\
"pdo_mysql_check_liveness",\
"pdo_mysql_fill_stmt_from_result",\
"pdo_mysql_get_attribute",\
"pdo_mysql_handle_factory",\
"pdo_mysql_in_transaction",\
"pdo_mysql_last_insert_id",\
"pdo_mysql_request_shutdown",\
"pdo_mysql_set_attribute",\
"pdo_mysql_stmt_after_execute_prepared",\
"pdo_mysql_stmt_col_meta",\
"pdo_mysql_stmt_cursor_closer",\
"pdo_mysql_stmt_describe",\
"pdo_mysql_stmt_dtor",\
"pdo_mysql_stmt_execute_prepared",\
"pdo_mysql_stmt_execute",\
"pdo_mysql_stmt_fetch",\
"pdo_mysql_stmt_get_col",\
"pdo_mysql_stmt_next_rowset",\
"pdo_mysql_stmt_param_hook",\
"pdo_parse_params",\
"pdo_sqlite_stmt_dtor",\
"pdo_sqlite_stmt_execute",\
"pdo_sqlite_stmt_fetch",\
"pdo_stmt_describe_columns",\
"pdo_stmt_do_next_rowset",\
"pdo_stmt_init",\
"phar_compile_file",\
"phar_file_get_contents",\
"phar_fopen",\
"phar_parse_zipfile",\
"php_call_shutdown_functions",\
"php_cli_server_do_event_for_each_fd_callback",\
"php_cli_server_poller_poll",\
"php_cli_server_recv_event_read_request",\
"php_crc32_stream_bulk_update",\
"php_error_docref",\
"php_exec_ex",\
"php_exec",\
"php_execute_script_ex",\
"php_execute_script",\
"php_fopen_primary_script",\
"php_fsockopen_stream",\
"php_getimagesize_from_any",\
"php_if_fopen",\
"php_mysqlnd_auth_response_read",\
"php_mysqlnd_cached_sha2_result_read",\
"php_mysqlnd_cmd_write",\
"php_mysqlnd_conn_connect_pub",\
"php_mysqlnd_conn_data_connect_handshake_pub",\
"php_mysqlnd_conn_data_connect_pub",\
"php_mysqlnd_conn_data_next_result_pub",\
"php_mysqlnd_conn_data_query_pub",\
"php_mysqlnd_conn_data_reap_query_pub",\
"php_mysqlnd_conn_data_select_db_pub",\
"php_mysqlnd_conn_data_set_charset_pub",\
"php_mysqlnd_conn_data_simple_command_pub",\
"php_mysqlnd_conn_data_simple_command_send_request_pub",\
"php_mysqlnd_conn_data_store_result_pub",\
"php_mysqlnd_conn_data_tx_commit_or_rollback_pub",\
"php_mysqlnd_eof_read",\
"php_mysqlnd_greet_read",\
"php_mysqlnd_net_connect_ex_pub",\
"php_mysqlnd_net_network_read_ex_pub",\
"php_mysqlnd_net_network_write_ex_pub",\
"php_mysqlnd_net_open_tcp_or_unix_pub",\
"php_mysqlnd_net_receive_ex_pub",\
"php_mysqlnd_net_send_ex_pub",\
"php_mysqlnd_ok_read",\
"php_mysqlnd_prepare_read",\
"php_mysqlnd_res_meta_read_metadata_pub",\
"php_mysqlnd_res_read_result_metadata_pub",\
"php_mysqlnd_res_store_result_fetch_data_pub",\
"php_mysqlnd_res_store_result_pub",\
"php_mysqlnd_rowp_read",\
"php_mysqlnd_rset_field_read",\
"php_mysqlnd_rset_header_read",\
"php_mysqlnd_sha256_pk_request_response_read",\
"php_mysqlnd_stmt_execute_pub",\
"php_mysqlnd_stmt_prepare_pub",\
"php_network_accept_incoming",\
"php_network_connect_socket_to_host",\
"php_network_connect_socket",\
"php_openssl_enable_crypto",\
"php_openssl_sockop_close",\
"php_openssl_sockop_io",\
"php_openssl_sockop_read",\
"php_openssl_sockop_set_option",\
"php_openssl_sockop_write",\
"php_pcre_replace_func_impl",\
"php_pdo_free_statement",\
"php_pollfd_for",\
"php_replace_in_subject_func",\
"php_request_shutdown",\
"php_sock_stream_wait_for_data",\
"php_sockop_read_close",\
"php_sockop_read",\
"php_sockop_set_option",\
"php_sockop_write",\
"php_stdiop_read",\
"php_stdiop_set_option",\
"php_stdiop_write",\
"php_stream_bucket_attach",\
"php_stream_context_free",\
"php_stream_filter_append_ex",\
"php_stream_filter_create",\
"php_stream_filter_free",\
"php_stream_filter_register_factory",\
"php_stream_filter_remove",\
"php_stream_flush",\
"php_stream_notification_free",\
"php_stream_notification_notify",\
"php_stream_open_for_zend",\
"php_stream_read_to_str",\
"php_stream_temp_cast",\
"php_stream_temp_flush",\
"php_stream_url_wrap_http_ex",\
"php_stream_url_wrap_http",\
"php_stream_write",\
"php_stream_xport_crypto_enable",\
"php_stream_xport_crypto_setup",\
"php_stream_xport_register",\
"php_tcp_sockop_set_option",\
"php_tick_iterator",\
"php_url_encode_hash_ex",\
"php_userstreamop_read",\
"php_userstreamop_set_option",\
"php_var_dump",\
"php_var_serialize_intern",\
"php_var_unserialize_internal",\
"php_verror",\
"php_wasm_init",\
"php_zend_stream_closer",\
"php_zip_extract_file",\
"php_zip_get_properties",\
"php_zip_has_property",\
"php_zip_read_property",\
"php_zip_register_prop_handler",\
"php_zlib_filter_create",\
"php_zlib_output_compression_start",\
"php_zval_filter",\
"pkey_rsa_encrypt",\
"plist_entry_destructor",\
"pop_arg",\
"populateCellCache",\
"posixUnlock",\
"pow_function",\
"preg_replace_func_impl",\
"printf_core",\
"proc_open_rsrc_dtor",\
"ptrmapPutOvflPtr",\
"raise",\
"rand_bytes",\
"RAND_bytes",\
"rand_enough",\
"rand_nopseudo_bytes",\
"RAND_poll",\
"rand_status",\
"RAND_status",\
"rc_dtor_func",\
"read_file",\
"read",\
"readline_shell_run",\
"really_register_bound_param",\
"rebuild_object_properties",\
"reflection_method_invoke",\
"Reinsert",\
"releasePageOne",\
"renderLogMsg",\
"resetCursor",\
"result_current_field_read",\
"result_field_count_read",\
"result_lengths_read",\
"result_num_rows_read",\
"rfc1867_post_handler",\
"robust_close",\
"robust_ftruncate",\
"robust_open",\
"robustFchown",\
"rowidWrite",\
"RSA_padding_add_PKCS1_OAEP_mgf1",\
"rtreeCallbackConstraint",\
"rtreeInsertCell",\
"run_cli",\
"schemaIsValid",\
"seekAndRead",\
"seekAndWriteFd",\
"selectExpander",\
"shutdown_destructors",\
"shutdown_executor",\
"sifttrinkle",\
"singleipconnect",\
"slow_index_convert",\
"smart_str_append_printf",\
"smart_str_erealloc",\
"spl_append_it_fetch",\
"spl_append_it_next_iterator",\
"spl_caching_it_next",\
"spl_dual_it_construct",\
"spl_dual_it_fetch",\
"spl_dual_it_free",\
"spl_dual_it_get_method",\
"spl_filesystem_object_clone",\
"spl_filesystem_object_free_storage",\
"spl_filter_it_fetch",\
"spl_heap_object_free_storage",\
"spl_heap_object_new_ex",\
"spl_iterator_apply",\
"spl_iterator_to_array_apply",\
"spl_iterator_to_values_apply",\
"spl_limit_it_seek",\
"spl_ptr_heap_delete_top",\
"spl_ptr_heap_insert",\
"spl_recursive_it_get_current_data",\
"spl_recursive_it_get_current_key",\
"spl_recursive_it_get_method",\
"spl_recursive_it_it_construct",\
"spl_recursive_it_move_forward_ex",\
"spl_recursive_it_rewind_ex",\
"spl_recursive_it_valid",\
"SplitNode",\
"splitNodeStartree",\
"sqlite_handle_closer",\
"sqlite_handle_doer",\
"sqlite_handle_preparer",\
"sqlite3_auto_extension",\
"sqlite3_autovacuum_pages",\
"sqlite3_backup_step",\
"sqlite3_bind_pointer",\
"sqlite3_cancel_auto_extension",\
"sqlite3_exec",\
"sqlite3_finalize",\
"sqlite3_free",\
"sqlite3_malloc64",\
"sqlite3_msize",\
"sqlite3_mutex_alloc",\
"sqlite3_mutex_enter",\
"sqlite3_mutex_free",\
"sqlite3_mutex_leave",\
"sqlite3_mutex_try",\
"sqlite3_prepare_v2",\
"sqlite3_set_auxdata",\
"sqlite3_step",\
"sqlite3AuthCheck",\
"sqlite3AuthReadCol",\
"sqlite3AutoLoadExtensions",\
"sqlite3BeginBenignMalloc",\
"sqlite3BtreeBeginTrans",\
"sqlite3BtreeClose",\
"sqlite3BtreeCommit",\
"sqlite3BtreeCommitPhaseTwo",\
"sqlite3BtreeDelete",\
"sqlite3BtreeIndexMoveto",\
"sqlite3BtreeInsert",\
"sqlite3BtreeRollback",\
"sqlite3Close",\
"sqlite3DbMallocSize",\
"sqlite3EndBenignMalloc",\
"sqlite3FaultSim",\
"sqlite3Fts3CacheDeferredDoclists",\
"sqlite3Fts3HashFindElem",\
"sqlite3Fts3HashInsert",\
"sqlite3Fts3InitTokenizer",\
"sqlite3Fts3Offsets",\
"sqlite3Fts3OpenTokenizer",\
"sqlite3Fts5AuxInit",\
"sqlite3Fts5ConfigFree",\
"sqlite3Fts5ExprFirst",\
"sqlite3Fts5ExprNext",\
"sqlite3Fts5GetTokenizer",\
"sqlite3Fts5IndexQuery",\
"sqlite3Fts5ParserAlloc",\
"sqlite3Fts5ParserFree",\
"sqlite3Fts5Tokenize",\
"sqlite3Fts5TokenizerInit",\
"sqlite3Init",\
"sqlite3InitOne",\
"sqlite3Insert",\
"sqlite3InvokeBusyHandler",\
"sqlite3IsShadowTableOf",\
"sqlite3LeaveMutexAndCloseZombie",\
"sqlite3LoadExtension",\
"sqlite3LocateTable",\
"sqlite3LocateTableItem",\
"sqlite3LockAndPrepare",\
"sqlite3Malloc",\
"sqlite3MallocInit",\
"sqlite3MallocSize",\
"sqlite3MarkAllShadowTablesOf",\
"sqlite3MutexAlloc",\
"sqlite3MutexInitsqlite3_db_status",\
"sqlite3OsAccess",\
"sqlite3OsCheckReservedLock",\
"sqlite3OsClose",\
"sqlite3OsCurrentTimeInt64",\
"sqlite3OsDelete",\
"sqlite3OsDeviceCharacteristics",\
"sqlite3OsDlClose",\
"sqlite3OsDlError",\
"sqlite3OsDlOpensqlite3OsDlSym",\
"sqlite3OsFileControl",\
"sqlite3OsFileControlHint",\
"sqlite3OsFileSize",\
"sqlite3OsFullPathname",\
"sqlite3OsGetLastError",\
"sqlite3OsLock",\
"sqlite3OsOpen",\
"sqlite3OsRandomness",\
"sqlite3OsRead",\
"sqlite3OsSectorSize",\
"sqlite3OsShmBarrier",\
"sqlite3OsShmLock",\
"sqlite3OsShmMap",\
"sqlite3OsShmUnmap",\
"sqlite3OsSleep",\
"sqlite3OsSync",\
"sqlite3OsTruncate",\
"sqlite3OsUnlock",\
"sqlite3OsWrite",\
"sqlite3PagerBegin",\
"sqlite3PagerClose",\
"sqlite3PagerCommitPhaseTwo",\
"sqlite3PagerExclusiveLock",\
"sqlite3PagerGet",\
"sqlite3PagerRollback",\
"sqlite3PagerSharedLock",\
"sqlite3PagerUnrefPageOne",\
"sqlite3ParseObjectReset",\
"sqlite3Parser",\
"sqlite3ParserAddCleanup",\
"sqlite3PcacheClose",\
"sqlite3PcacheDrop",\
"sqlite3PcacheFetch",\
"sqlite3PcacheFetchStress",\
"sqlite3PcacheInitialize",\
"sqlite3PcacheMove",\
"sqlite3PcachePagecount",\
"sqlite3PcacheSetCachesize",\
"sqlite3PcacheSetPageSize",\
"sqlite3PcacheShrink",\
"sqlite3PcacheTruncate",\
"sqlite3Prepare",\
"sqlite3ReadSchema",\
"sqlite3Realloc",\
"sqlite3RollbackAll",\
"sqlite3RunParser",\
"sqlite3Select",\
"sqlite3SelectPrep",\
"sqlite3SrcListLookup",\
"sqlite3StartTable",\
"sqlite3Step",\
"sqlite3ThreadCreate",\
"sqlite3VdbeDeleteAuxData",\
"sqlite3VdbeExec",\
"sqlite3VdbeFreeCursorNN",\
"sqlite3VdbeHalt",\
"sqlite3VdbeMemAggValue",\
"sqlite3VdbeMemFinalizevdbeCommit",\
"sqlite3VdbeMemGrow",\
"sqlite3VdbeMemSetStr",\
"sqlite3VdbeReset",\
"sqlite3VtabBegin",\
"sqlite3VtabCallDestroy",\
"sqlite3VtabModuleUnref",\
"sqlite3VtabOverloadFunction",\
"sqlite3VtabSavepoint",\
"sqlite3VtabSync",\
"sqlite3VtabUnlock",\
"sqlite3WalkSelect",\
"sqlite3WalUndo",\
"SSL_CTX_new",\
"stem",\
"stmt_affected_rows_read",\
"stmt_errno_read",\
"stmt_error_read",\
"stmt_field_count_read",\
"stmt_insert_id_read",\
"stmt_num_rows_read",\
"stmt_param_count_read",\
"stmt_sqlstate_read",\
"stream_array_from_fd_set",\
"stream_resource_persistent_dtor",\
"stream_resource_regular_dtor",\
"strfilter_convert_append_bucket",\
"strfilter_convert_dtor",\
"sub_function_slow",\
"syscall",\
"tsrm_realpath",\
"unixAccess",\
"unixCheckReservedLock",\
"unixClose",\
"unixDelete",\
"unixDlSym",\
"unixFcntlExternalReader",\
"unixFileLock",\
"unixFileSize",\
"unixFullPathname",\
"unixGetTempname",\
"unixLock",\
"unixLockSharedMemory",\
"unixOpen",\
"unixOpenSharedMemory",\
"unixShmMap",\
"unixShmPurge",\
"unixShmRegionPerMap",\
"unixShmSystemLock",\
"unixShmUnmap",\
"unixSync",\
"unencode_write",\
"unixTempFileDir",\
"unixUnlock",\
"unlockBtreeIfUnused",\
"UpdateDefaultFilter",\
"updateMapping",\
"updatewindow",\
"user_shutdown_function_call",\
"userfilter_filter",\
"var_destroy",\
"_efree_large",\
"_php_stream_make_seekable",\
"_zval_undefined_op1",\
"concat_function",\
"zend_get_properties_for",\
"ZEND_CALL_TRAMPOLINE_SPEC_HANDLER",\
"ZEND_CONCAT_SPEC_CONST_CV_HANDLER",\
"ZEND_CLONE_SPEC_CV_HANDLER",\
"ZEND_ECHO_SPEC_CV_HANDLER",\
"php_output_op",\
"php_output_write",\
"php_printf_to_smart_str",\
"php_unserialize_with_options",\
"php_var_unserialize_destroy",\
"xbuf_format_converter",\
"zend_hash_destroy",\
"zend_std_cast_object_tostring",\
"zend_std_get_debug_info",\
"zend_std_get_properties_for",\
"zif_serialize",\
"zif_unserialize",\
"zif_var_dump",\
"vdbeCommit",\
"vdbeCompareMemString",\
"vdbeMemClearExternAndSetNull",\
"vdbeMergeEngineCompare",\
"vdbeMergeEngineStep",\
"vdbeSorterMerge",\
"verifyDbFile",\
"virtual_chdir_file",\
"virtual_file_ex",\
"vtabBestIndex",\
"vtabCallConstructor",\
"walBusyLock",\
"walkExpr",\
"wasm_php_exec",\
"wasm_php_stream_flush",\
"wasm_php_stream_read",\
"wasm_pclose",\
"wasm_popen",\
"__wrap_pclose",\
"__wrap_popen",\
"wasm_read",\
"wasm_sapi_handle_request",\
"wasm_sapi_request_shutdown",\
"wasm_sleep",\
"wrapper_cmp",\
"yy_reduce",\
"zend_activate_auto_globals",\
"zend_activate_modules",\
"ZEND_ADD_ARRAY_UNPACK_SPEC_HANDLER",\
"zend_add_class_name_literal",\
"zend_add_const_name_literal",\
"zend_add_func_name_literal",\
"zend_add_helper_SPEC",\
"zend_add_literal",\
"zend_add_ns_func_name_literal",\
"zend_alter_ini_entry_ex",\
"zend_argument_count_error",\
"zend_array_destroy",\
"zend_array_dup",\
"zend_array_key_exists_error",\
"zend_array_key_exists_fast",\
"ZEND_ASSIGN_DIM_SPEC_CV_CONST_HANDLER",\
"ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DATA_CONST_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_CV_CONST_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_CV_CONST_OP_DATA_CONST_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CONST_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_CV_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_TMP_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_OP_DATA_VAR_HANDLER",\
"ZEND_ASSIGN_OBJ_SPEC_VAR_CV_OP_DATA_CV_HANDLER",\
"zend_assign_to_object_dim",\
"zend_assign_to_object",\
"zend_ast_apply",\
"zend_ast_evaluate_inner",\
"zend_attach_symbol_table",\
"ZEND_BEGIN_SILENCE_SPEC_HANDLER",\
"zend_binary_assign_op_dim_slow",\
"zend_binary_assign_op_obj_dim",\
"zend_binary_assign_op_typed_prop",\
"zend_binary_assign_op_typed_ref",\
"zend_bind_class_in_slot",\
"zend_call_destructors",\
"zend_call_function",\
"zend_call_known_function",\
"zend_call_known_instance_method_with_2_params",\
"zend_call_method_if_exists",\
"zend_call_method",\
"zend_close_rsrc",\
"zend_close_rsrc_list",\
"ZEND_COUNT_SPEC_CV_UNUSED_HANDLER",\
"zend_deactivate",\
"zend_default_exception_new",\
"zend_delete_variable",\
"zend_deprecated_function",\
"zend_destroy_rsrc_list",\
"ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_OBSERVER_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_UNUSED_HANDLER",\
"ZEND_DO_FCALL_BY_NAME_SPEC_RETVAL_USED_HANDLER",\
"zend_do_fcall_common_helper_SPEC",\
"ZEND_DO_FCALL_SPEC_CONST_HANDLER",\
"ZEND_DO_FCALL_SPEC_HANDLER",\
"ZEND_DO_FCALL_SPEC_OBSERVER_HANDLER",\
"ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER",\
"ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER",\
"ZEND_DO_ICALL_SPEC_HANDLER",\
"ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER",\
"ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER",\
"ZEND_DO_ICALL_SPEC_OBSERVER_HANDLER",\
"ZEND_DO_UCALL_SPEC_OBSERVER_HANDLER",\
"zend_emit_op_tmp",\
"zend_emit_op",\
"zend_enum_add_case_cstr",\
"zend_error_noreturn",\
"zend_error_va_list",\
"zend_error_va",\
"zend_error_zstr_at",\
"zend_error_zstr",\
"zend_error",\
"zend_eval_const_expr",\
"zend_eval_string_ex",\
"zend_eval_string",\
"zend_eval_stringl",\
"zend_exception_error",\
"zend_exception_restore",\
"zend_execute_script",\
"zend_execute_scripts",\
"zend_execute",\
"zend_extension_statement_handler",\
"ZEND_EXT_STMT_SPEC_HANDLER",\
"ZEND_FE_FETCH_R_SPEC_VAR_HANDLER",\
"zend_fe_reset_iterator",\
"ZEND_FE_RESET_R_SPEC_VAR_HANDLER",\
"zend_fetch_class_by_name",\
"zend_fetch_debug_backtrace",\
"ZEND_FETCH_DIM_R_SPEC_CV_CONST_HANDLER",\
"zend_fetch_dimension_address_read_R_slow",\
"zend_fetch_dimension_address_read_R",\
"zend_fetch_dimension_address_read",\
"ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CONST_HANDLER",\
"ZEND_FETCH_OBJ_R_SPEC_CV_CONST_HANDLER",\
"ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER",\
"ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_HANDLER",\
"zend_fiber_switch_context",\
"zend_fiber_trampoline",\
"zend_file_handle_dtor",\
"zend_find_array_dim_slow",\
"zend_free_extra_named_params",\
"zend_generate_anon_class_name",\
"zend_generator_close",\
"ZEND_GENERATOR_CREATE_SPEC_HANDLER",\
"zend_generator_get_next_delegated_value",\
"zend_generator_iterator_move_forward",\
"zend_generator_iterator_rewind",\
"zend_generator_resume",\
"ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER",\
"ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER",\
"ZEND_GENERATOR_RETURN_SPEC_OBSERVER_HANDLER",\
"ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER",\
"ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER",\
"zend_hash_apply",\
"zend_hash_del_key_or_index",\
"zend_hash_graceful_reverse_destroy",\
"zend_hash_reverse_apply",\
"ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER",\
"ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_HANDLER",\
"zend_include_or_eval",\
"compile_filename",\
"zend_internal_type_error",\
"zend_interrupt_helper_SPEC",\
"zend_invalid_class_constant_type_error",\
"zend_invalid_method_call",\
"zend_isset_dim_slow",\
"ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_CONST_HANDLER",\
"zend_isset_isempty_dim_prop_obj_handler_SPEC_CV_CONST",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CONST_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMPVAR_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_CONST_HANDLER",\
"ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TMPVAR_HANDLER",\
"zend_iterator_dtor",\
"zend_leave_helper_SPEC",\
"zend_lex_tstring",\
"zend_list_close",\
"zend_llist_apply_with_argument",\
"zend_llist_apply_with_arguments",\
"zend_llist_apply_with_del",\
"zend_llist_apply",\
"zend_llist_clean",\
"zend_llist_del_element",\
"zend_llist_destroy",\
"zend_load_extension_handle",\
"zend_lookup_class_ex",\
"zend_merge_properties",\
"zend_message_dispatcher",\
"zend_mm_alloc_huge",\
"zend_mm_alloc_pages_efree_160",\
"zend_mm_free_huge",\
"zend_mm_free_pages",\
"zend_mm_gc",\
"zend_mm_realloc_huge",\
"zend_mm_shutdown_emalloc_24",\
"zend_multibyte_check_lexer_compatibility",\
"ZEND_NEW_SPEC_CONST_UNUSED_HANDLER",\
"zend_non_static_method_call",\
"zend_objects_destroy_object",\
"zend_objects_store_call_destructors",\
"zend_objects_store_del_ref_by_handle_ex",\
"zend_objects_store_del_ref",\
"zend_objects_store_del",\
"zend_objects_store_free_object_storage",\
"zend_observer_fcall_begin_prechecked",\
"zend_observer_fcall_begin",\
"zend_observer_fcall_end",\
"zend_observer_fcall_end_prechecked",\
"zend_parse_arg_number_or_str_slow",\
"zend_parse_arg_str_weak",\
"zend_parse_arg",\
"zend_parse_method_parameters",\
"zend_parse_parameters",\
"zend_parse_va_args",\
"zend_post_deactivate_modules",\
"zend_post_startup",\
"zend_prepare_string_for_scanning",\
"zend_print_zval_r_to_buf",\
"zend_print_zval_r",\
"zend_print_zval",\
"zend_ptr_stack_clean",\
"zend_ptr_stack_reverse_apply",\
"zend_quick_get_constant",\
"zend_read_property_ex",\
"zend_read_property",\
"zend_readonly_property_modification_error",\
"zend_rebuild_symbol_table",\
"zend_ref_add_type_source",\
"zend_scan_escape_string",\
"zend_separate_class_constants_table",\
"zend_set_memory_limit",\
"zend_set_timeout",\
"zend_short_circuiting_commit",\
"zend_shutdown_executor_values",\
"zend_signal_handler",\
"zend_sort_4",\
"zend_sort",\
"zend_spprintf",\
"zend_stack_apply_with_argument",\
"zend_stack_clean",\
"zend_startup_module_ex",\
"zend_startup",\
"zend_std_call_getter",\
"zend_std_call_user_call",\
"zend_std_compare_objects",\
"zend_std_has_dimension",\
"zend_std_has_property",\
"zend_std_read_dimension",\
"zend_std_read_property",\
"zend_std_unset_dimension",\
"zend_std_write_dimension",\
"zend_std_write_property",\
"zend_stream_fixup",\
"zend_stream_open",\
"zend_strip",\
"zend_strtod",\
"zend_symtable_to_proptable",\
"zend_this_not_in_object_context_helper_SPEC",\
"zend_throw_error",\
"zend_throw_exception_ex",\
"zend_throw_exception_internal",\
"zend_throw_exception_object",\
"zend_throw_exception_zstr",\
"zend_throw_exception",\
"zend_throw_non_object_error",\
"zend_throw_unwind_exit",\
"ZEND_TICKS_SPEC_HANDLER",\
"zend_timeout_handler",\
"zend_trace_to_string",\
"zend_try_compile_cv",\
"zend_try_ct_eval_binary_op",\
"zend_try_ct_eval_unary_op",\
"zend_type_error",\
"zend_undefined_index",\
"ZEND_UNSET_CV_SPEC_CV_UNUSED_HANDLER",\
"ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLER",\
"ZEND_UNSET_DIM_SPEC_CV_CV_HANDLER",\
"ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDLER",\
"ZEND_UNSET_DIM_SPEC_VAR_CONST_HANDLER",\
"ZEND_UNSET_DIM_SPEC_VAR_CV_HANDLER",\
"ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_VAR_CONST_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER",\
"ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HANDLER",\
"zend_unset_property",\
"ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER",\
"zend_update_class_constant",\
"zend_update_class_constants",\
"zend_update_property_ex",\
"zend_update_property_long",\
"zend_update_property_str",\
"zend_update_property_string",\
"zend_update_property",\
"zend_use_new_element_for_string",\
"zend_use_tmp_in_write_context_helper_SPEC",\
"zend_use_undef_in_read_context_helper_SPEC",\
"zend_user_it_get_current_data",\
"zend_user_it_get_new_iterator",\
"ZEND_USER_OPCODE_SPEC_HANDLER",\
"ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER",\
"ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER",\
"zend_value_error",\
"zend_verify_ref_array_assignable",\
"zend_vm_stack_extend",\
"zend_vspprintf",\
"zend_vstrpprintf",\
"zend_wrong_assign_to_variable_reference",\
"zend_wrong_clone_call",\
"zend_wrong_property_read",\
"ZEND_YIELD_FROM_SPEC_CV_HANDLER",\
"ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER",\
"zendi_smart_streq",\
"zendi_try_get_long",\
"zif_array_filter",\
"zif_array_map",\
"zif_array_product",\
"zif_array_reduce",\
"zif_array_sum",\
"zif_array_unique",\
"zif_cal_days_in_month",\
"zif_cal_from_jd",\
"zif_cal_to_jd",\
"zif_call_user_func_array",\
"zif_call_user_func",\
"zif_chmod",\
"zif_count",\
"zif_curl_exec",\
"zif_curl_multi_exec",\
"zif_curl_multi_select",\
"zif_current",\
"zif_debug_print_backtrace",\
"zif_define",\
"zif_end",\
"zif_exec",\
"zif_exif_imagetype",\
"zif_exif_read_data",\
"zif_exif_thumbnail",\
"zif_fclose",\
"zif_feof",\
"zif_file_get_contents",\
"zif_file_put_contents",\
"zif_fopen",\
"zif_fread",\
"zif_fsockopen",\
"zif_fwrite",\
"zif_gc_collect_cycles",\
"zif_get_mangled_object_vars",\
"zif_get_object_vars",\
"zif_getenv",\
"zif_getimagesize",\
"zif_hash_copy",\
"zif_hash_final",\
"zif_hash_hkdf",\
"zif_hash_init",\
"zif_hash_pbkdf2",\
"zif_hash_update_file",\
"zif_hash_update_stream",\
"zif_hash_update",\
"zif_http_build_query",\
"zif_iterator_apply",\
"zif_iterator_count",\
"zif_json_encode",\
"zif_key",\
"zif_lcg_value",\
"zif_method_exists",\
"zif_mysqli_begin_transaction",\
"zif_mysqli_connect",\
"zif_mysqli_data_seek",\
"zif_mysqli_errno",\
"zif_mysqli_error",\
"zif_mysqli_fetch_fields",\
"zif_mysqli_fetch_object",\
"zif_mysqli_fetch_row",\
"zif_mysqli_get_charset",\
"zif_mysqli_link_construct",\
"zif_mysqli_multi_query",\
"zif_mysqli_next_result",\
"zif_mysqli_ping",\
"zif_mysqli_prepare",\
"zif_mysqli_query",\
"zif_mysqli_real_connect",\
"zif_mysqli_reap_async_query",\
"zif_mysqli_select_db",\
"zif_mysqli_set_charset",\
"zif_mysqli_stmt_execute",\
"zif_mysqli_stmt_fetch",\
"zif_next",\
"zif_passthru",\
"zif_phar_file_get_contents",\
"zif_phar_fopen",\
"zif_mail",\
"zif_pclose",\
"zif_popen",\
"zif_post_message_to_js",\
"zif_preg_replace_callback",\
"zif_prev",\
"zif_proc_close",\
"zif_proc_open",\
"zif_property_exists",\
"zif_rand",\
"zif_rename",\
"zif_reset",\
"zif_shell_exec",\
"zif_sleep",\
"zif_spl_autoload_call",\
"zif_spl_autoload",\
"zif_str_shuffle",\
"zif_stream_get_contents",\
"zif_stream_resolve_include_path",\
"zif_stream_select",\
"zif_stream_socket_client",\
"zif_stream_socket_enable_crypto",\
"zif_system",\
"zif_touch",\
"zif_unlink",\
"zif_wasm_popen",\
"zim_AppendIterator_append",\
"zim_AppendIterator_current",\
"zim_AppendIterator_next",\
"zim_AppendIterator_rewind",\
"zim_CachingIterator_hasNext",\
"zim_CachingIterator_rewind",\
"zim_DateInterval___wakeup",\
"zim_DatePeriod___wakeup",\
"zim_DateTime___wakeup",\
"zim_DateTimeImmutable___wakeup",\
"zim_DateTimeZone___wakeup",\
"zim_FilterIterator_next",\
"zim_FilterIterator_rewind",\
"zim_HashContext___serialize",\
"zim_HashContext___unserialize",\
"zim_mysqli___construct",\
"zim_mysqli_result___construct",\
"zim_mysqli_stmt___construct",\
"zim_NoRewindIterator_current",\
"zim_NoRewindIterator_key",\
"zim_NoRewindIterator_next",\
"zim_NoRewindIterator_valid",\
"zim_PDO___construct",\
"zim_PDO_beginTransaction",\
"zim_PDO_commit",\
"zim_PDO_commitTransaction",\
"zim_PDO_dbh_constructor",\
"zim_PDO_errorInfo",\
"zim_PDO_exec",\
"zim_PDO_getAttribute",\
"zim_PDO_inTransaction",\
"zim_PDO_lastInsertId",\
"zim_PDO_prepare",\
"zim_PDO_query",\
"zim_PDO_quote",\
"zim_PDO_rollBack",\
"zim_PDOStatement_closeCursor",\
"zim_PDOStatement_errorInfo",\
"zim_PDOStatement_execute",\
"zim_PDOStatement_fetch",\
"zim_PDOStatement_fetchAll",\
"zim_PDOStatement_getAttribute",\
"zim_PDOStatement_getColumnMeta",\
"zim_PDOStatement_setAttribute",\
"zim_reflection_class_newInstanceArgs",\
"zim_reflection_method_invoke",\
"zim_reflection_method_invokeArgs",\
"zim_ReflectionClass_newInstanceArgs",\
"zim_ReflectionMethod_invoke",\
"zim_ReflectionMethod_invokeArgs",\
"zim_SessionHandler_create_sid",\
"zim_SessionHandler_destroy",\
"zim_SessionHandler_gc",\
"zim_SessionHandler_read",\
"zim_SessionHandler_write",\
"zim_SplObjectStorage___debugInfo",\
"zim_sqlite3_backup",\
"zim_SQLite3_backup",\
"zim_SQLite3_exec",\
"zim_sqlite3_exec",\
"zim_sqlite3_query",\
"zim_sqlite3_querySingle",\
"zim_sqlite3result_fetchArray",\
"php_cli_startup",\
"php_stdiop_close",\
"zend_parse_ini_file",\
"php_embed_startup",\
"zip_source_file_common_new",\
"zip_source_function_create",\
"zip_source_layered_create",\
"zm_startup_basic",\
"zm_startup_core",\
"zm_startup_date",\
"zm_startup_dir",\
"zm_startup_hash",\
"zm_startup_json",\
"zm_startup_mysqli",\
"zm_startup_password",\
"zm_startup_pdo",\
"zm_startup_random",\
"zm_startup_reflection",\
"zm_startup_session",\
"zm_startup_spl_array",\
"zm_startup_spl_directory",\
"zm_startup_spl_dllist",\
"zm_startup_spl_exceptions",\
"zm_startup_spl_fixedarray",\
"zm_startup_spl_heap",\
"zm_startup_spl_iterators",\
"zm_startup_spl_observer",\
"zm_startup_sqlite3",\
"zm_startup_tokenizer",\
"zm_startup_user_filters",\
"zm_startup_zlib",\
"zval_copy_ctor_func",\
"zval_get_double_func",\
"zval_get_long_func",\
"zval_get_string_func",\
"zval_make_interned_string",\
"zval_ptr_dtor",\
"zval_try_get_string_func",\
"zval_undefined_cv",\
"zval_update_constant_ex"';\
	# If pointer casts are enabled, we need to asyncify both the prefixed and unprefixed names
	if [ "${PHP_VERSION:0:1}" -lt "8" ]; then \
		export ASYNCIFY_ONLY="$ASYNCIFY_ONLY_UNPREFIXED,"$(echo "$ASYNCIFY_ONLY_UNPREFIXED" | sed -E $'s/"([a-zA-Z])/"byn$fpcast-emu$\\1/g'); \
	else \
		export ASYNCIFY_ONLY="$ASYNCIFY_ONLY_UNPREFIXED"; \
	fi; \

	echo -n ' -s ASYNCIFY_ONLY=['$ASYNCIFY_ONLY'] '| tr -d "\n" >> /root/.emcc-php-asyncify-flags;


# List global and exports symbols
COPY ./compile/php/exported-functions.list /root/exported-functions.list
COPY ./compile/php/zend-side-module-exports.c /root/zend-side-module-exports.c
RUN cat /root/exported-functions.list >> /root/.JS_ABI_EXPORTS

RUN set -eux; \
	if [ "$WITH_JSPI" != "yes" ]; then \
		exit 0; \
	fi; \
	PHP_VERSION_MAJOR="${PHP_VERSION%%.*}"; \
	PHP_VERSION_REST="${PHP_VERSION#*.}"; \
	PHP_VERSION_MINOR="${PHP_VERSION_REST%%.*}"; \
	{ \
		if [ "$PHP_VERSION_MAJOR" -ge 7 ]; then \
			echo 'zend_ce_exception'; \
		fi; \
		echo 'zend_ce_traversable'; \
		echo 'zend_ce_iterator'; \
		echo 'zend_ce_aggregate'; \
		echo 'zend_ce_arrayaccess'; \
		if [ "$PHP_VERSION_MAJOR" -gt 7 ] || \
			{ [ "$PHP_VERSION_MAJOR" -eq 7 ] && [ "$PHP_VERSION_MINOR" -ge 2 ]; }; then \
			echo 'zend_ce_countable'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -ge 7 ]; then \
			echo 'zend_ce_throwable'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -lt 9 ]; then \
			echo 'zend_ce_serializable'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -ge 8 ]; then \
			echo 'zend_ce_stringable'; \
			echo 'zend_ce_unhandled_match_error'; \
			echo 'zend_ce_division_by_zero_error'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -gt 8 ] || \
			{ [ "$PHP_VERSION_MAJOR" -eq 8 ] && [ "$PHP_VERSION_MINOR" -ge 1 ]; }; then \
			echo 'zend_ce_unit_enum'; \
			echo 'zend_ce_backed_enum'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -gt 7 ] || \
			{ [ "$PHP_VERSION_MAJOR" -eq 7 ] && [ "$PHP_VERSION_MINOR" -ge 3 ]; }; then \
			echo 'zend_string_init_interned'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -ge 7 ]; then \
			echo 'zend_empty_string'; \
			echo 'zend_one_char_string'; \
		fi; \
		echo 'std_object_handlers'; \
		echo 'executor_globals'; \
		echo 'compiler_globals'; \
		echo 'sapi_globals'; \
		echo 'file_globals'; \
		echo 'sapi_module'; \
		echo 'zend_compile_string'; \
		if [ "$PHP_VERSION_MAJOR" -ge 7 ]; then \
			echo 'zend_get_executed_scope'; \
		fi; \
		echo 'zend_execute'; \
		if [ "$PHP_VERSION_MAJOR" -ge 7 ]; then \
			echo 'zval_ptr_dtor'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -gt 8 ] || \
			{ [ "$PHP_VERSION_MAJOR" -eq 8 ] && [ "$PHP_VERSION_MINOR" -ge 1 ]; }; then \
			echo 'zend_destroy_static_vars'; \
		fi; \
		if [ "$PHP_VERSION_MAJOR" -lt 7 ]; then \
			echo '_zval_ptr_dtor'; \
		fi; \
		echo 'destroy_op_array'; \
		echo '_efree'; \
		echo '_zend_bailout'; \
		echo 'getentropy'; \
		echo 'posix_memalign'; \
		echo 'pthread_cond_signal'; \
		echo 'pthread_cond_wait'; \
		echo 'pthread_condattr_destroy'; \
		echo 'pthread_condattr_init'; \
		echo 'pthread_condattr_setclock'; \
		echo 'pthread_mutex_trylock'; \
		echo 'pthread_mutexattr_destroy'; \
		echo 'pthread_mutexattr_init'; \
		echo 'pthread_mutexattr_settype'; \
		echo 'sched_yield'; \
		echo 'writev'; \
		if [ "$PHP_VERSION_MAJOR" -ge 8 ]; then \
			echo '_call_user_function_impl'; \
			echo 'zend_array_count'; \
			echo 'zend_declare_class_constant_ex'; \
			echo 'zend_declare_property'; \
			echo 'zend_do_implement_interface'; \
			echo 'zend_hash_get_current_key_zval_ex'; \
			echo 'zend_is_callable'; \
			echo 'zend_is_iterable'; \
			echo 'zend_lookup_class_ex'; \
			echo 'zend_objects_new'; \
			echo 'zend_register_internal_class_ex'; \
			echo 'zend_register_internal_interface'; \
			echo 'zend_std_get_properties'; \
			echo 'zend_std_has_property'; \
			echo 'zend_std_read_property'; \
			echo 'zend_std_write_property'; \
			echo 'zend_throw_exception_object'; \
		fi; \
	} >> /root/.WASM_ABI_EXPORTS

# WORKAROUND: JSPI side modules may import wasm longjmp symbols. Asyncify
# builds do not provide those symbols, so including them in the shared
# exported-functions.list would break Asyncify builds. Add them only for JSPI.
RUN if [ "$WITH_JSPI" = "yes" ]; then \
		echo '___wasm_setjmp' >> /root/.JS_ABI_EXPORTS; \
		echo '___wasm_setjmp_test' >> /root/.JS_ABI_EXPORTS; \
		echo '___wasm_longjmp' >> /root/.JS_ABI_EXPORTS; \
		echo '___c_longjmp' >> /root/.JS_ABI_EXPORTS; \
	fi;

RUN if [ "$(echo "$PHP_VERSION" | cut -d. -f1)" -ge 8 ] && [ "$WITH_JSPI" = "yes" ]; then \
		{ \
			echo '_clock_gettime'; \
			echo '_getcwd'; \
			echo '_pthread_cond_destroy'; \
			echo '_pthread_cond_init'; \
			echo '_pthread_cond_timedwait'; \
			echo '_pthread_mutex_destroy'; \
			echo '_pthread_mutex_init'; \
			echo '_pthread_mutex_lock'; \
			echo '_pthread_mutex_unlock'; \
			echo '_strerror_r'; \
			echo '_write'; \
			echo '_zend_hash_get_current_data_ex'; \
			echo '_zend_hash_get_current_key_type_ex'; \
			echo '_zend_hash_move_forward_ex'; \
			echo '_zend_is_true'; \
		} >> /root/.JS_ABI_EXPORTS; \
	fi;

COPY ./${EMSCRIPTEN_ENVIRONMENT}-builds/ /root/${EMSCRIPTEN_ENVIRONMENT}-builds
COPY ./compile/shared/ /root/shared/
RUN	set -eux; \
	PHP_VERSION_DIR="$(echo "$PHP_VERSION" | cut -d. -f1,2 | tr . -)"; \
	ASYNCIFY_OR_JSPI_DIR=$( [ "$WITH_JSPI" = "yes" ] && echo "jspi" || echo "asyncify" ); \
	EXTENSIONS_DIR_PATH="/root/${EMSCRIPTEN_ENVIRONMENT}-builds/${PHP_VERSION_DIR}/${ASYNCIFY_OR_JSPI_DIR}/extensions"; \
	for path in "$EXTENSIONS_DIR_PATH"/*; do \
		extension="${path##*/}"; \
		# List globals symbols [ env func and globals without invoke_ and libc++(_Z) prefixed ones ]
		/root/emsdk/upstream/bin/wasm-dis "$EXTENSIONS_DIR_PATH/${extension}/${extension}.so" \
		| sed -n 's/^[[:space:]]*(import "env" "\([^"]*\)"[[:space:]]*(\(func\|global\).*/\1/p' \
		| sed '/^invoke_/d' \
		| sed '/^_Z/d' \
		| sed 's/^/_/' \
		| sort \
		>> /root/.JS_ABI_EXPORTS; \
		# List exports symbols [ GOT.mem and GOT.func globals ]
		/root/emsdk/upstream/bin/wasm-dis "$EXTENSIONS_DIR_PATH/${extension}/${extension}.so" \
		| sed -n 's/^[[:space:]]*(import "\(GOT\.mem\|GOT\.func\)" "\([^"]*\)"[[:space:]]*(global.*/\2/p' \
		| sort \
		>> /root/.WASM_ABI_EXPORTS; \
		# Inject extension-specific exported functions
		if [ -f /root/shared/${extension}/exported-functions.list ]; then \
			cat /root/shared/${extension}/exported-functions.list >> /root/.JS_ABI_EXPORTS; \
		fi; \
	done

RUN set -eux; \
	sort -u /root/.JS_ABI_EXPORTS | \
	grep -v '^[[:space:]]*$' | \
	sed 's/^/  "/; s/$/",/' | \
	sed '$ s/,$//' | \
	{ echo '['; cat; echo ']'; } \
	> /root/.exported-functions

RUN set -eux; \
	sort -u /root/.WASM_ABI_EXPORTS | \
	grep -v '^[[:space:]]*$' | \
	sed 's/^/-Wl,--export=/' \
	> /root/.wasm-exports


# Build the final .wasm file
RUN mkdir /root/output
COPY ./compile/php/phpwasm-emscripten-library.js /root/phpwasm-emscripten-library.js
COPY ./compile/php/phpwasm-emscripten-library-dynamic-linking.js /root/phpwasm-emscripten-library-dynamic-linking.js
COPY ./compile/php/phpwasm-emscripten-library-file-locking-for-node.js /root/phpwasm-emscripten-library-file-locking-for-node.js
COPY ./compile/php/phpwasm-emscripten-library-dns-lookup-for-node.js /root/phpwasm-emscripten-library-dns-lookup-for-node.js
COPY ./compile/php/phpwasm-emscripten-library-known-undefined-functions.js /root/phpwasm-emscripten-library-known-undefined-functions.js
ARG WITH_SOURCEMAPS
ARG WITH_DEBUG
ARG STACK_SIZE
RUN set -euxo pipefail; \
	mkdir -p /build/output; \
	source /root/emsdk/emsdk_env.sh; \
	ZEND_SIDE_MODULE_EXPORT_SOURCE=""; \
	if [ "$WITH_JSPI" = "yes" ]; then \
		# Both imports and exports are required for inter-module communication with wrapped methods, e.g., wasm_recv.
		export ASYNCIFY_FLAGS=" -s ASYNCIFY=2 -sSUPPORT_LONGJMP=wasm -fwasm-exceptions -sJSPI_IMPORTS=js_open_process,js_fd_read,js_waitpid,js_process_status,js_create_input_device,wasm_setsockopt,wasm_shutdown,wasm_close,wasm_recv,wasm_connect,__syscall_fcntl64,js_flock,js_release_file_locks,js_waitpid -sJSPI_EXPORTS=php_wasm_init,wasm_sleep,wasm_read,emscripten_sleep,wasm_sapi_handle_request,wasm_sapi_request_shutdown,wasm_poll_socket,wrap_select,__wrap_select,select,php_pollfd_for,fflush,wasm_popen,wasm_pclose,__wrap_popen,__wrap_pclose,wasm_read,wasm_php_exec,run_cli,wasm_recv,wasm_connect,__wasm_call_ctors,__errno_location,__funcs_on_exit -s EXPORTED_RUNTIME_METHODS=HEAPU32,HEAPU8,ccall,PROXYFS,wasmExports "; \
		echo '#define PLAYGROUND_JSPI 1' > /root/php_wasm_asyncify.h; \
		ZEND_SIDE_MODULE_EXPORT_SOURCE="/root/zend-side-module-exports.c"; \
	else \
		export ASYNCIFY_FLAGS=" -s ASYNCIFY=1 -s ASYNCIFY_IGNORE_INDIRECT=1 -s EXPORTED_RUNTIME_METHODS=HEAPU32,HEAPU8,ccall,PROXYFS,wasmExports,UTF8ToString,lengthBytesUTF8,stringToUTF8 $(cat /root/.emcc-php-asyncify-flags) "; \
		echo '' > /root/php_wasm_asyncify.h; \
	fi; \
	export OPTIMIZATION_FLAGS="-O3"; \
	if [ "${WITH_SOURCEMAPS}" = "yes" ] || [ "${WITH_DEBUG}" = "yes" ]; then \
		export OPTIMIZATION_FLAGS="-O0"; \
	fi; \
	PLATFORM_SPECIFIC_ARGS=''; \
	ENVIRONMENT="$EMSCRIPTEN_ENVIRONMENT"; \
	if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
		PLATFORM_SPECIFIC_ARGS="$PLATFORM_SPECIFIC_ARGS -DPHP_WASM_FILE_LOCKING_SUPPORT=1"; \
		PLATFORM_SPECIFIC_ARGS="$PLATFORM_SPECIFIC_ARGS --js-library /root/phpwasm-emscripten-library-file-locking-for-node.js --js-library /root/phpwasm-emscripten-library-dns-lookup-for-node.js -Wl,--wrap=getpid "; \
	else \
		# Prevents "document is not defined" errors in Web Workers when using MAIN_MODULE.
		# By default, Emscripten assumes MAIN_MODULE runs only in a web environment,
		# so it injects WebGL and other browser-only APIs. Explicitly adding "worker"
		# ensures the runtime is compatible with Web Workers as well.
		ENVIRONMENT="$EMSCRIPTEN_ENVIRONMENT,worker"; \
	fi; \
	emcc $OPTIMIZATION_FLAGS \
	--js-library /root/phpwasm-emscripten-library.js \
	--js-library /root/phpwasm-emscripten-library-dynamic-linking.js \
	--js-library /root/phpwasm-emscripten-library-known-undefined-functions.js \
	$PLATFORM_SPECIFIC_ARGS \
	-I .  \
	-I ext   \
	-I ext/json   \
	-I Zend  \
	-I main  \
	-I TSRM/ \
	-I/root/lib -I/root/lib/include \
	-L/root/lib -L/root/lib/lib/ \
	-D__x86_64__\
	-lproxyfs.js \
	$ASYNCIFY_FLAGS \
	$(cat /root/.emcc-php-wasm-flags) \
	-s MAIN_MODULE=2 \
	-s EXPORTED_FUNCTIONS="@/root/.exported-functions" \
	-s WASM_BIGINT=1 \
	-s MIN_NODE_VERSION=200900 \
	-s INITIAL_MEMORY=64MB \
	-s ALLOW_MEMORY_GROWTH=1 \
	-s STACK_SIZE="$STACK_SIZE" \
	-s ASSERTIONS=0 \
	-s ERROR_ON_UNDEFINED_SYMBOLS=1 \
	-s NODEJS_CATCH_EXIT=0 \
	-s NODEJS_CATCH_REJECTION=0 \
	-s INVOKE_RUN=0 \
	-o /build/output/php.js \
	-s EXIT_RUNTIME=1 \
	-Wl,@/root/.wasm-exports \
	-Wl,--wrap=usleep \
	-Wl,--wrap=select \
	-Wl,--wrap=popen \
	-Wl,--wrap=pclose \
		/root/lib/libphp.a \
		/root/proc_open.c \
		/root/php_wasm.c \
		$ZEND_SIDE_MODULE_EXPORT_SOURCE \
		$(cat /root/.emcc-php-wasm-sources) \
	-s ENVIRONMENT=$ENVIRONMENT \
	-s FORCE_FILESYSTEM=1 \
	-s EXPORT_NAME="'PHPLoader'"
	# Emscripten complains it can't find some Asyncify functions
	# listed in ASYNCIFY_IMPORTS. The culprit is those functions
	# are actually used and delisting them breaks php_pollfd_for().
	# Sooo... just ignore Emscripten's warnings.

# # At the moment terminfo is baked into the repository in
# # src/php-cli/terminfo, but if we ever need to update it, we can
# # use the terminfo database produced by the ncurses make script:
# RUN if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
#         mkdir -p /root/output/terminfo/x && \
#         cp -v /root/lib/share/terminfo/x/xterm* /root/output/terminfo/x/; \
#     fi

# Postprocess the build php.js module:
COPY ./compile/php/esm-node-boilerplate.js /root/esm-node-boilerplate.js
COPY ./compile/php/esm-prefix.js /root/esm-prefix.js
COPY ./compile/php/esm-suffix.js /root/esm-suffix.js
RUN set -euxo pipefail; \
	cp -rfv /build/output/* /root/output/; \
	# Figure out the target file names and URLs
		# The .js and .wasm filenames should reflect the build configuration, e.g.:
		# * `php-7.4.node.js` and `php-7.4.node.wasm`
		# * `php-8.0.js` and `php-8.0.wasm`
		# In addition, the `.wasm` file URL should have a "cache busting" query string on the
		# web, so that web browsers will reload it when the file contents change.
	# Precompute export variables:
		export FILE_SIZE=$(stat -c%s "/root/output/php.wasm") && \
		export PHP_VERSION_ESCAPED="${PHP_VERSION//./_}" && \
		export FILENAME_PREFIX="php_${PHP_VERSION_ESCAPED:0:3}"; \
		export EXT_PREFIX=""; \
		export JS_FILENAME="${FILENAME_PREFIX}${EXT_PREFIX}.js"; \
		export WASM_FILENAME="${FILENAME_PREFIX}${EXT_PREFIX}.wasm"; \
	# Make the php.wasm URL configurable via the dependencyFilename loader argument:
		/root/replace.sh $'s/["\']php\.wasm[\'"]/dependencyFilename/g' /root/output/php.js; \
	# Patch a "property undefined" error
		# Emscripten produces an if that checks a stream.stream_ops.poll property. However,
		# stream.stream_ops is sometimes undefined and the check fails. Let's adjust it to
		# tolerate a null stream.stream_ops value.
		/root/replace.sh "s/if\s*\(stream\.stream_ops\.poll\)/if (stream.stream_ops?.poll)/g" /root/output/php.js; \
	# Emscripten allows only directories to be mounted, but in Playground we support mounting files, directories, and symlinks.
		# For file mounting to work, we need to remove the directory check.
		/root/replace-across-lines.sh 's/(\s*)if\s*\(\s*!FS\.isDir\(node\.mode\)\s*\)\s*\{\s*throw\s*new\s*FS\.ErrnoError\(54\)\s*;?\s*\}(\s*)/$1$2/s' /root/output/php.js; \
	# Make Emscripten websockets configurable
		# Emscripten makes the Websocket proxy connect to a fixed URL.
		# This assumes the traffic is always forwarded to the same target.
		# However, we want to support arbitrary targets, so we need to
		# replace the hardcoded websocket target URL with a dynamic callback.
		/root/replace.sh $'s/if\s*\(\s*SOCKFS\.websocketArgs\[[\'"]url[\'"]\]\s*\)/if("function"===typeof SOCKFS.websocketArgs["url"]) {\nurl = SOCKFS.websocketArgs["url"](...arguments);\n}else if ("string" === typeof SOCKFS.websocketArgs["url"])/g' \
			/root/output/php.js; \
		# Enable custom WebSocket constructors to support socket options.
		/root/replace.sh "s/ws\s*=\s*new WebSocketConstructor/if (Module['websocket']['decorator']) {WebSocketConstructor = Module['websocket']['decorator'](WebSocketConstructor);}ws = new WebSocketConstructor/g" /root/output/php.js && \
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
			if [ "$WITH_WS_NETWORKING_PROXY" = "yes" ]; then \
				/root/replace.sh "s/sock\.server\s*=\s*new WebSocketServer/if (Module['websocket']['serverDecorator']) {WebSocketServer = Module['websocket']['serverDecorator'](WebSocketServer);}sock.server = new WebSocketServer/g" /root/output/php.js; \
			fi; \
		fi; \
		# PIPEFS: return 0 (success) instead of 6 (EWOULDBLOCK) when reached end of data
		# and the other end of the pipe is closed.
		/root/replace-across-lines.sh 's/if\s*\(\s*currentLength\s*==\s*0\s*\)\s*\{.{0,200}throw new FS.ErrnoError\(\s*6\s*\)/if(currentLength==0){if(pipe.refcnt<2){return 0;}throw new FS.ErrnoError(6)/sg' /root/output/php.js; \
	# Add MSG_PEEK flag support in recvfrom
		#
		# Emscripten ignores the flags argument to ___syscall_recvfrom.
		# However, PHP relies on passing the MSG_PEEK (== integer 2) flag when polling
		# the stream for data.
		# MSG_PEEK enables reading bytes without moving the stream pointer forward.
		# Without the patch below, PHP consumes the first byte from the top of the
		# response stream, typically "H" in "HTTP/1.1 200 OK", and then fails after
		# reading the remaining "TTP/1.1 200 OK" and not recognizing it as a valid
		# status line.
		# We need to patch the syscall to support the MSG_PEEK flag.
		if [ "$WITH_WS_NETWORKING_PROXY" = "yes" ]; then \
			/root/replace.sh 's/sock\.sock_ops\.recvmsg\(sock,\s*len\);/sock.sock_ops.recvmsg(sock, len, typeof flags !== "undefined" ? flags : 0);/g' /root/output/php.js; \
			/root/replace.sh 's/recvmsg\(sock,\s*length\)\s*{/recvmsg(sock, length, flags) {/g' /root/output/php.js; \
			/root/replace.sh 's/if\s*\(sock\.type\s*===\s*1\s*&&\s*bytesRead\s*<\s*queuedLength\)/if (flags&2) {bytesRead = 0;} if (sock.type === 1 && bytesRead < queuedLength)/g' /root/output/php.js; \
		fi ; \
	# Replace the hardcoded ENVIRONMENT variable with a dynamic computation
		#
		# The JavaScript code of the web loader and web worker loader is identical,
		# but Emscripten forces running different code paths by setting
		#
		# ENVIRONMENT_IS_WEB = true; ENVIRONMENT_IS_WORKER = false
		#
		# This project supports both environments and would have to maintain two
		# separate copies of the code. Instead, we use a dynamic computation of the
		# environment, based on the `RuntimeName` variable, which is an argument to the
		# wrapper function.
		/root/replace.sh $'s/ENVIRONMENT_IS_([A-Z]+)\s*=[^;]*;/ENVIRONMENT_IS_$1=RuntimeName==="$1";/g' /root/output/php.js; \
		/root/replace.sh 's/var ENV\s*=\s*\{/var ENV = PHPLoader.ENV || {/g' /root/output/php.js; \
	# Provide the JSPI longjmp tag for web side modules.
		# Existing web JSPI binaries do not export __c_longjmp, so the dynamic
		# linker would otherwise synthesize a JS function stub for a tag import.
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "web" ] && [ "$WITH_JSPI" = "yes" ]; then \
			/root/replace.sh $'s/Asyncify\\.instrumentWasmImports\\(wasmImports\\);/Asyncify.instrumentWasmImports(wasmImports);\\n\\t\\twasmImports["__c_longjmp"] ??= new WebAssembly.Tag({\\n\\t\\t\\tparameters: ["i32"],\\n\\t\\t});/g' /root/output/php.js; \
		fi; \
	# Expose required functions to the module.
		# Emscripten 4.0.10 removed the runtime exposure of `_malloc` and `_free`,
		# so we rebind them from `wasmExports` and attach them to PHPLoader.
		/root/replace.sh "s/_wasm_free\s*=\s*Module\[[\"']_wasm_free[\"']\]/_wasm_free = PHPLoader['free'] = Module['_wasm_free']/g" /root/output/php.js; \
		/root/replace.sh "s/_malloc\s*=\s*Module\[[\"']_malloc[\"']\]/_malloc = PHPLoader['malloc'] = Module['_malloc']/g" /root/output/php.js; \
	# Add alias to wasmImports for dynamically loaded extensions.
		# The memcached extension imports wasm_recv and wasm_setsockopt,
		# but the main module only exposes wasm_setsockopt in wasmImports.
		# This alias ensure that dynamically loaded extensions can
		# correctly resolve the required import.
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
			/root/replace.sh 's/wasm_setsockopt:\s*_wasm_setsockopt,/wasm_setsockopt: _wasm_setsockopt,\n  \/** \@export *\/ wasm_recv: _wasm_recv,/g' /root/output/php.js; \
		fi; \
	# Patch Emscripten exit status to include a stack trace
		# Override Emscripten's default ExitStatus class which gets
		# thrown on failure. Unfortunately, the default object is not
		# a subclass of Error and does not provide any stack trace.
		#
		# This is a deliberate behavior on Emscripten's end to prevent
		# memory leaks after the program exits. See:
		#
		# https://github.com/emscripten-core/emscripten/pull/9108
		#
		# In case of WordPress Playground, the worker in which the PHP
		# runs will typically exit after the PHP program finishes, so
		# we don't have to worry about memory leaks.
		/root/replace-across-lines.sh "s/(class\\s+ExitStatus\\s*{\\s*name\\s*=\\s*['\"]ExitStatus['\"];\\s*constructor\\s*\(\\s*status\\s*\)\\s*{\\s*this\\.message\\s*=\\s*\`Program terminated with exit\(\\\${status}\)\`;\s*this\\.status\\s*=\\s*status;?\\s*}\\s*})/\$1\nExitStatus = class PHPExitStatus extends Error {\n	constructor\(status\) {\n		super\(status\);\n		this.name = 'ExitStatus';\n		this.message = 'Program terminated with exit\(' + status + '\)';\n		this.status = status;\n	}\n};/" /root/output/php.js; \
	# Restore quit handling to Emscripten
		# Quit callback customization was removed from Emscripten by:
		# https://github.com/emscripten-core/emscripten/pull/22371
		# But the alternative `onExit()` handling is conditional and has different semantics.
		# To avoid bugs due to this change, let's just restore support for quit callback customization.
		/root/replace.sh "s/(if\s*\(Module\[['\"]thisProgram['\"]\]\)\s*thisProgram\s*=\s*Module\[['\"]thisProgram['\"]\];)/\$1\nif (Module[\"quit\"]) quit_=Module[\"quit\"];/" /root/output/php.js; \
	# Cache Asyncify sleep data allocation to prevent memory growth corruption
		#
		# Emscripten's handleSleep() calls _malloc() on every async operation and
		# _free() on every rewind. If _malloc triggers memory.grow(), the ArrayBuffer
		# detaches and corrupts state. Fix: allocate once, reuse on subsequent sleeps.
		# See: https://github.com/emscripten-core/emscripten/issues/21108
		if [ "$WITH_JSPI" != "yes" ]; then \
			/root/replace.sh \
				's/Asyncify\.currData\s*=\s*Asyncify\.allocateData\(\)/if (!Asyncify._cachedData) { Asyncify._cachedData = Asyncify.allocateData(); } else { Asyncify.setDataHeader(Asyncify._cachedData, Asyncify._cachedData + 12, Asyncify.StackSize); Asyncify.setDataRewindFunc(Asyncify._cachedData); } Asyncify.currData = Asyncify._cachedData/' \
				/root/output/php.js; \
			/root/replace-across-lines.sh \
				's/runAndAbortIfError\(_asyncify_stop_rewind\);\s*_free\(Asyncify\.currData\);/runAndAbortIfError(_asyncify_stop_rewind);/s' \
				/root/output/php.js; \
		fi; \
	# Node.js: Create a require() function and add 'currentDirPath' declaration
	# because Emscripten generates Node.js code that requires them.
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
			cat /root/esm-node-boilerplate.js >> /root/output/php-module.js; \
		fi; \
	# Turn the php.js file into an ES module
		# Manually turn the output into a esm module instead of relying on -s MODULARIZE=1.
		# which pollutes the global namespace and does not play well with import() mechanics.
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
			echo "const dependencyFilename = path.join(currentDirPath, '${PHP_VERSION_ESCAPED}', '$WASM_FILENAME');" >> /root/output/php-module.js;\
			/root/replace.sh "s/scriptDirectory\s*=\s*__dirname/scriptDirectory = currentDirPath/" /root/output/php.js; \
		else \
			echo "import dependencyFilename from './${PHP_VERSION_ESCAPED}/$WASM_FILENAME'; " >> /root/output/php-module.js; \
		fi; \
		echo "export { dependencyFilename }; " >> /root/output/php-module.js && \
		echo "export const dependenciesTotalSize = $FILE_SIZE; " >> /root/output/php-module.js && \
		echo "const phpVersionString = '$PHP_VERSION';" >> /root/output/php-module.js && \
		cat /root/esm-prefix.js >> /root/output/php-module.js && \
		cat /root/output/php.js >> /root/output/php-module.js && \
		cat /root/esm-suffix.js >> /root/output/php-module.js && \
		\
		# Remove the old php.js file
		rm /root/output/php.js && \
		\
		# Rename the build files to their final names
		mv /root/output/php-module.js "/root/output/$JS_FILENAME"; \
		mkdir -p /root/output/${PHP_VERSION_ESCAPED}/; \
		mv /root/output/php.wasm "/root/output/${PHP_VERSION_ESCAPED}/$WASM_FILENAME"; \
		if [ "${WITH_SOURCEMAPS}" = "yes" ]; then \
			# Make the source map paths relative to the .wasm file, not the build directory
			sed -i 's#"\.\./\.\./root/#"#g' /root/output/php.wasm.map; \
			mv /root/output/php.wasm.map "/root/output/${PHP_VERSION_ESCAPED}/php.wasm.map"; \
		fi; \
		if [ "${WITH_SOURCEMAPS}" = "yes" ] || [ "${WITH_DEBUG}" = "yes" ]; then \
			# Make PHP source available for use with step debugger
			rm -rf /root/php-src/.git; \
			cp -r /root/php-src /root/output/"${PHP_VERSION_ESCAPED}"/php-src; \
			# Copy emscripten source files
			mkdir -p /root/output/"${PHP_VERSION_ESCAPED}"/emscripten; \
			cp -r /root/emsdk/upstream/emscripten/cache /root/output/"${PHP_VERSION_ESCAPED}"/emscripten/; \
			cp -r /root/emsdk/upstream/emscripten/system /root/output/"${PHP_VERSION_ESCAPED}"/emscripten/; \
		fi;
