Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions .github/workflows/Dockerfile.alpine-php-test-nts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# syntax=docker/dockerfile:1.7
# Alpine test image with PHP built from source in NTS mode
# Used for testing the extension on musl-based systems

ARG PHP_VERSION=8.3
ARG PHP_SRC_REF=PHP-${PHP_VERSION}

FROM alpine:3.21 AS base
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]

ENV TZ=Etc/UTC \
LC_ALL=C.UTF-8 \
LANG=C.UTF-8 \
PHP_VERSION=${PHP_VERSION}

RUN apk add --no-cache \
bash curl git wget make gcc g++ autoconf bison re2c pkgconf \
libxml2-dev sqlite-dev curl-dev openssl-dev \
libzip-dev oniguruma-dev libjpeg-turbo-dev libpng-dev libwebp-dev \
icu-dev readline-dev libxslt-dev mariadb-connector-c-dev \
linux-headers xz tar ca-certificates \
nginx mariadb mariadb-client \
python3 py3-pip py3-flask py3-requests py3-psutil \
procps

FROM base AS php-build
ARG PHP_SRC_REF
WORKDIR /usr/src
RUN git clone --depth 1 --branch "${PHP_SRC_REF}" https://github.com/php/php-src.git
WORKDIR /usr/src/php-src

RUN sed -i 's/\[\([0-9]\+\.[0-9]\+\.[0-9]\+\)-dev\]/[\1]/' configure.ac
RUN ./buildconf --force

RUN if [ -f ext/openssl/openssl.c ] && grep -q 'REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING"' ext/openssl/openssl.c; then \
awk '/REGISTER_LONG_CONSTANT\("OPENSSL_SSLV23_PADDING"/ { \
indent = $0; \
gsub(/[^[:space:]].*/, "", indent); \
print indent "#ifdef RSA_SSLV23_PADDING"; \
gsub(/^[[:space:]]*/, indent " "); \
print; \
print indent "#endif"; \
next \
} \
{ print }' ext/openssl/openssl.c > ext/openssl/openssl.c.new && \
mv ext/openssl/openssl.c.new ext/openssl/openssl.c; \
fi || true

RUN mkdir -p /usr/local/etc/php/conf.d

RUN ./configure \
--prefix=/usr/local \
--with-config-file-path=/usr/local/lib \
--with-config-file-scan-dir=/usr/local/etc/php/conf.d \
--enable-fpm \
--enable-mbstring \
--enable-pcntl \
--enable-cgi \
--with-extra-version="" \
--with-curl \
--with-mysqli \
--with-openssl \
--with-zlib \
--with-zip \
--disable-zts \
&& make -j"$(nproc)" \
&& make install
RUN strip /usr/local/bin/php /usr/local/sbin/php-fpm || true

FROM base AS final
COPY --from=php-build /usr/local /usr/local

RUN EXTENSION_DIR=$(php -i | grep "^extension_dir" | awk '{print $3}') && \
if [ -z "$EXTENSION_DIR" ]; then \
echo "Error: Could not determine extension_dir"; \
exit 1; \
fi && \
mkdir -p "$EXTENSION_DIR"

RUN php -v | grep -v "ZTS" >/dev/null || (echo "ERROR: ZTS is enabled but should be NTS!" && exit 1) && \
Copy link
Copy Markdown
Contributor

@aikido-pr-checks aikido-pr-checks Bot Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ZTS guard uses php -v | grep -v "ZTS", which can still succeed when ZTS is enabled because other php -v lines don’t contain "ZTS", so the error branch may never run.

Suggested change
RUN php -v | grep -v "ZTS" >/dev/null || (echo "ERROR: ZTS is enabled but should be NTS!" && exit 1) && \
RUN php -v | grep "ZTS" >/dev/null && (echo "ERROR: ZTS is enabled but should be NTS!" && exit 1) || true && \
php -v | grep -v "ZTS" >/dev/null && \
Details

✨ AI Reasoning
​​1) The block is trying to fail the image build if PHP was compiled with ZTS.
2) It uses php -v | grep -v "ZTS" >/dev/null to decide success.
3) php -v outputs multiple lines; even if the version line contains "ZTS", other lines typically do not, so grep -v "ZTS" still outputs something and exits 0.
4) This means the code can silently pass the ZTS check when ZTS is enabled, contradicting the intended control-flow guard.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

php -m | grep -E 'curl|mysqli' >/dev/null

ENV PATH="/usr/local/bin:${PATH}"

RUN ln -sf /usr/local/bin/php /usr/bin/php && \
ln -sf /usr/local/sbin/php-fpm /usr/sbin/php-fpm || true && \
ln -sf /usr/local/bin/php-cgi /usr/bin/php-cgi

# Create /etc/httpd so the test harness uses the CentOS-style php-fpm config path
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RUN starting at line 75 chains many filesystem modifications, config writes, php-fpm tests, and symlink commands with &&/||. Split into smaller RUN steps to improve readability and isolate failures.

Details

✨ AI Reasoning
​A single RUN line performs many file system creations, symlink operations, multiple echo redirects to create config, php-fpm tests and an mv — all chained with many && and ||. This combines many distinct side-effecting steps into one line, increasing cognitive load and hiding failures in complex chaining.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

RUN mkdir -p /etc/httpd && \
mkdir -p /etc/php-fpm.d && \
mkdir -p /run/php-fpm && \
mkdir -p /var/run && \
mkdir -p /var/log/php-fpm && \
mkdir -p /usr/local/etc/php-fpm.d && \
mkdir -p /usr/local/etc/php/conf.d && \
ln -sf /usr/local/etc/php/conf.d /etc/php.d || true && \
echo "[global]" > /usr/local/etc/php-fpm.conf && \
echo "pid = /run/php-fpm/php-fpm.pid" >> /usr/local/etc/php-fpm.conf && \
echo "error_log = /var/log/php-fpm/error.log" >> /usr/local/etc/php-fpm.conf && \
echo "daemonize = yes" >> /usr/local/etc/php-fpm.conf && \
echo "include=/usr/local/etc/php-fpm.d/*.conf" >> /usr/local/etc/php-fpm.conf && \
echo "include=/etc/php-fpm.d/*.conf" >> /usr/local/etc/php-fpm.conf && \
echo "[www]" > /usr/local/etc/php-fpm.d/www.conf && \
echo "user = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "group = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "listen = 127.0.0.1:9000" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "listen.owner = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "listen.group = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm = dynamic" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.max_children = 5" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.start_servers = 2" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.min_spare_servers = 1" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.max_spare_servers = 3" >> /usr/local/etc/php-fpm.d/www.conf && \
php-fpm -t -y /usr/local/etc/php-fpm.conf 2>&1 | grep -v "Nothing matches the include pattern" || true && \
php-fpm -t -y /usr/local/etc/php-fpm.conf >/dev/null 2>&1 || \
(echo "PHP-FPM config test failed" && exit 1) && \
ln -sf /usr/local/etc/php-fpm.conf /etc/php-fpm.conf

# Configure nginx to include /etc/nginx/conf.d/ (expected by test harness)
RUN mkdir -p /etc/nginx/conf.d && \
awk '/include.*http\.d/{print; print " include /etc/nginx/conf.d/*.conf;"; next}1' \
/etc/nginx/nginx.conf > /tmp/nginx.conf && \
mv /tmp/nginx.conf /etc/nginx/nginx.conf

# MySQL socket path for mysqli
RUN echo "mysqli.default_socket = /var/lib/mysql/mysql.sock" > /usr/local/etc/php/conf.d/mysql-socket.ini

# MariaDB helper script
RUN mkdir -p /usr/local/bin /var/lib/mysql /run/mysqld && \
printf '%s\n' \
'#!/bin/sh' \
'set -e' \
'mkdir -p /var/lib/mysql /run/mysqld' \
'chown -R mysql:mysql /var/lib/mysql /run/mysqld' \
'if [ ! -d /var/lib/mysql/mysql ]; then' \
' mysql_install_db --user=mysql --datadir=/var/lib/mysql' \
'fi' \
'mysqld --user=mysql --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock &' \
'pid=$!' \
'for i in $(seq 1 30); do mysqladmin --socket=/var/lib/mysql/mysql.sock ping --silent 2>/dev/null && break; sleep 1; done' \
'mysql --socket=/var/lib/mysql/mysql.sock -u root -e "CREATE DATABASE IF NOT EXISTS db;" || true' \
'mysql --socket=/var/lib/mysql/mysql.sock -u root -e "ALTER USER '"'"'root'"'"'@'"'"'localhost'"'"' IDENTIFIED BY '"'"'pwd'"'"'; FLUSH PRIVILEGES;" || true' \
'wait $pid' \
> /usr/local/bin/start-mariadb && \
chmod +x /usr/local/bin/start-mariadb

WORKDIR /work
CMD ["bash"]
152 changes: 152 additions & 0 deletions .github/workflows/Dockerfile.alpine-php-test-zts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# syntax=docker/dockerfile:1.7
# Alpine test image with PHP built from source in ZTS mode
# Used for testing the extension on musl-based systems with thread safety

ARG PHP_VERSION=8.3
ARG PHP_SRC_REF=PHP-${PHP_VERSION}

FROM alpine:3.21 AS base
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]

ENV TZ=Etc/UTC \
LC_ALL=C.UTF-8 \
LANG=C.UTF-8 \
PHP_VERSION=${PHP_VERSION}

RUN apk add --no-cache \
bash curl git wget make gcc g++ autoconf bison re2c pkgconf \
libxml2-dev sqlite-dev curl-dev openssl-dev \
libzip-dev oniguruma-dev libjpeg-turbo-dev libpng-dev libwebp-dev \
icu-dev readline-dev libxslt-dev mariadb-connector-c-dev \
linux-headers xz tar ca-certificates \
nginx mariadb mariadb-client \
python3 py3-pip py3-flask py3-requests py3-psutil \
procps

FROM base AS php-build
ARG PHP_SRC_REF
WORKDIR /usr/src
RUN git clone --depth 1 --branch "${PHP_SRC_REF}" https://github.com/php/php-src.git
WORKDIR /usr/src/php-src

RUN sed -i 's/\[\([0-9]\+\.[0-9]\+\.[0-9]\+\)-dev\]/[\1]/' configure.ac
RUN ./buildconf --force

RUN if [ -f ext/openssl/openssl.c ] && grep -q 'REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING"' ext/openssl/openssl.c; then \
awk '/REGISTER_LONG_CONSTANT\("OPENSSL_SSLV23_PADDING"/ { \
indent = $0; \
gsub(/[^[:space:]].*/, "", indent); \
print indent "#ifdef RSA_SSLV23_PADDING"; \
gsub(/^[[:space:]]*/, indent " "); \
print; \
print indent "#endif"; \
next \
} \
{ print }' ext/openssl/openssl.c > ext/openssl/openssl.c.new && \
mv ext/openssl/openssl.c.new ext/openssl/openssl.c; \
fi || true

RUN mkdir -p /usr/local/etc/php/conf.d

RUN ./configure \
--prefix=/usr/local \
--with-config-file-path=/usr/local/lib \
--with-config-file-scan-dir=/usr/local/etc/php/conf.d \
--enable-zts \
--enable-maintainer-zts \
--enable-fpm \
--enable-mbstring \
--enable-pcntl \
--enable-cgi \
--with-extra-version="" \
--with-curl \
--with-mysqli \
--with-openssl \
--with-zlib \
--with-zip \
--disable-zend-signals \
--enable-zend-max-execution-timers \
&& make -j"$(nproc)" \
&& make install
RUN strip /usr/local/bin/php /usr/local/sbin/php-fpm || true

FROM base AS final
COPY --from=php-build /usr/local /usr/local

RUN EXTENSION_DIR=$(php -i | grep "^extension_dir" | awk '{print $3}') && \
if [ -z "$EXTENSION_DIR" ]; then \
echo "Error: Could not determine extension_dir"; \
exit 1; \
fi && \
mkdir -p "$EXTENSION_DIR"

RUN php -v | grep -q "ZTS" || (echo "ERROR: ZTS not enabled!" && exit 1) && \
php -m | grep -E 'curl|mysqli' >/dev/null

ENV PATH="/usr/local/bin:${PATH}"

RUN ln -sf /usr/local/bin/php /usr/bin/php && \
ln -sf /usr/local/sbin/php-fpm /usr/sbin/php-fpm || true && \
ln -sf /usr/local/bin/php-cgi /usr/bin/php-cgi

# Create /etc/httpd so the test harness uses the CentOS-style php-fpm config path
RUN mkdir -p /etc/httpd && \
mkdir -p /etc/php-fpm.d && \
mkdir -p /run/php-fpm && \
mkdir -p /var/run && \
mkdir -p /var/log/php-fpm && \
mkdir -p /usr/local/etc/php-fpm.d && \
mkdir -p /usr/local/etc/php/conf.d && \
ln -sf /usr/local/etc/php/conf.d /etc/php.d || true && \
echo "[global]" > /usr/local/etc/php-fpm.conf && \
echo "pid = /run/php-fpm/php-fpm.pid" >> /usr/local/etc/php-fpm.conf && \
echo "error_log = /var/log/php-fpm/error.log" >> /usr/local/etc/php-fpm.conf && \
echo "daemonize = yes" >> /usr/local/etc/php-fpm.conf && \
echo "include=/usr/local/etc/php-fpm.d/*.conf" >> /usr/local/etc/php-fpm.conf && \
echo "include=/etc/php-fpm.d/*.conf" >> /usr/local/etc/php-fpm.conf && \
echo "[www]" > /usr/local/etc/php-fpm.d/www.conf && \
echo "user = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "group = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "listen = 127.0.0.1:9000" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "listen.owner = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "listen.group = root" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm = dynamic" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.max_children = 5" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.start_servers = 2" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.min_spare_servers = 1" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.max_spare_servers = 3" >> /usr/local/etc/php-fpm.d/www.conf && \
php-fpm -t -y /usr/local/etc/php-fpm.conf 2>&1 | grep -v "Nothing matches the include pattern" || true && \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RUN that pipes php-fpm tests through grep, handles failures, and then creates symlinks is dense; separate validation, error handling, and file operations into distinct steps.

Details

✨ AI Reasoning
​This RUN contains php-fpm configuration tests with piping to grep, conditional fallbacks, and then a final symlink command, all interleaved with &&/||. The combination of test commands and side-effecting operations on one line increases the cognitive burden to understand failure modes and side effects.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

php-fpm -t -y /usr/local/etc/php-fpm.conf >/dev/null 2>&1 || \
(echo "PHP-FPM config test failed" && exit 1) && \
ln -sf /usr/local/etc/php-fpm.conf /etc/php-fpm.conf

# Configure nginx to include /etc/nginx/conf.d/ (expected by test harness)
RUN mkdir -p /etc/nginx/conf.d && \
awk '/include.*http\.d/{print; print " include /etc/nginx/conf.d/*.conf;"; next}1' \
/etc/nginx/nginx.conf > /tmp/nginx.conf && \
mv /tmp/nginx.conf /etc/nginx/nginx.conf

# MySQL socket path for mysqli
RUN echo "mysqli.default_socket = /var/lib/mysql/mysql.sock" > /usr/local/etc/php/conf.d/mysql-socket.ini

# MariaDB helper script
RUN mkdir -p /usr/local/bin /var/lib/mysql /run/mysqld && \
printf '%s\n' \
'#!/bin/sh' \
'set -e' \
'mkdir -p /var/lib/mysql /run/mysqld' \
'chown -R mysql:mysql /var/lib/mysql /run/mysqld' \
'if [ ! -d /var/lib/mysql/mysql ]; then' \
' mysql_install_db --user=mysql --datadir=/var/lib/mysql' \
'fi' \
'mysqld --user=mysql --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock &' \
'pid=$!' \
'for i in $(seq 1 30); do mysqladmin --socket=/var/lib/mysql/mysql.sock ping --silent 2>/dev/null && break; sleep 1; done' \
'mysql --socket=/var/lib/mysql/mysql.sock -u root -e "CREATE DATABASE IF NOT EXISTS db;" || true' \
'mysql --socket=/var/lib/mysql/mysql.sock -u root -e "ALTER USER '"'"'root'"'"'@'"'"'localhost'"'"' IDENTIFIED BY '"'"'pwd'"'"'; FLUSH PRIVILEGES;" || true' \
'wait $pid' \
> /usr/local/bin/start-mariadb && \
chmod +x /usr/local/bin/start-mariadb

WORKDIR /work
CMD ["bash"]
67 changes: 67 additions & 0 deletions .github/workflows/Dockerfile.build-extension-alpine-nts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# syntax=docker/dockerfile:1.7
# Alpine image with PHP built from source in NTS mode
# Used for building the PHP extension on musl-based systems

ARG PHP_VERSION=8.3
ARG PHP_SRC_REF=PHP-${PHP_VERSION}

FROM alpine:3.21 AS base
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]

ENV TZ=Etc/UTC \
LC_ALL=C.UTF-8 \
LANG=C.UTF-8

RUN apk add --no-cache \
bash curl git wget make gcc g++ autoconf bison re2c pkgconf \
libxml2-dev sqlite-dev curl-dev openssl-dev \
libzip-dev oniguruma-dev libjpeg-turbo-dev libpng-dev libwebp-dev \
icu-dev readline-dev libxslt-dev mariadb-connector-c-dev \
linux-headers xz tar ca-certificates

FROM base AS php-src
ARG PHP_SRC_REF
WORKDIR /usr/src
RUN git clone --depth 1 --branch "${PHP_SRC_REF}" https://github.com/php/php-src.git
WORKDIR /usr/src/php-src
RUN ./buildconf --force

RUN if [ -f ext/openssl/openssl.c ] && grep -q 'REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING"' ext/openssl/openssl.c; then \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RUN with conditional + inline awk script + file move crams conditional logic and file IO on one line; split the awk processing and the mv into separate RUN steps for clarity.

Details

✨ AI Reasoning
​This RUN performs a shell conditional, an inline awk script with branching, file redirection to a temp file, and a move operation, all chained on the same RUN. The line requires mentally evaluating the conditional, the awk script's control flow, and subsequent file operations together, increasing cognitive load.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

awk '/REGISTER_LONG_CONSTANT\("OPENSSL_SSLV23_PADDING"/ { \
indent = $0; \
gsub(/[^[:space:]].*/, "", indent); \
print indent "#ifdef RSA_SSLV23_PADDING"; \
gsub(/^[[:space:]]*/, indent " "); \
print; \
print indent "#endif"; \
next \
} \
{ print }' ext/openssl/openssl.c > ext/openssl/openssl.c.new && \
mv ext/openssl/openssl.c.new ext/openssl/openssl.c; \
fi || true

FROM php-src AS php-build
RUN ./configure \
--prefix=/usr/local \
--with-config-file-path=/usr/local/lib \
--with-config-file-scan-dir=/usr/local/etc/php/conf.d \
--enable-mbstring \
--enable-pcntl \
--enable-intl \
--with-curl \
--with-mysqli \
--with-openssl \
--with-zlib \
--with-zip \
&& make -j"$(nproc)" \
&& make install
RUN strip /usr/local/bin/php || true

FROM base AS dev
COPY --from=php-build /usr/local /usr/local
RUN php -v && php -m | grep -E 'curl|mysqli' >/dev/null
ENV PATH="/usr/local/bin:${PATH}"

RUN mkdir -p /usr/local/etc/php/conf.d
WORKDIR /work
CMD ["php", "-v"]
Loading
Loading