Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,17 @@ the installed package versions.

This change upgrades PHP and Composer while preserving Pico 2.1 and the existing
theme/plugin APIs. Pico, Twig 1, Symfony YAML 2 and the old Composer installer
still emit deprecation messages under PHP 8.5. Deprecation reporting has not been
disabled to hide them. The container tests fail on PHP warnings, notices, parse
errors and fatal errors; deprecations remain a documented follow-up.
still use APIs deprecated in PHP 8.5. To prevent production log flooding on every
page and health check, the runtime uses
`error_reporting = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED`. Warnings, notices,
parse errors and fatal errors are still logged; errors are not displayed in
HTTP responses. The container tests reject deprecation spam and exercise the
FPM logging path to verify that warnings, notices and uncaught errors remain
visible. This logging policy does not fix the deprecated code itself.

For development diagnostics, mount a PHP configuration file containing
`error_reporting = E_ALL` at `/usr/local/etc/php/conf.d/zz-debug.ini` and recreate
the container. Composer's build stage retains its own diagnostic settings.

An audit during the PHP upgrade reported 18 existing security advisories affecting
Twig and Symfony YAML. This runtime upgrade does not remediate those dependency
Expand Down
3 changes: 3 additions & 0 deletions docker/php.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
display_errors = Off
# Legacy Pico/Twig/YAML emit deprecations for every rendered page, including
# health checks. Keep operational errors, warnings and notices in production.
error_reporting = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
log_errors = On
error_log = /proc/self/fd/2
46 changes: 45 additions & 1 deletion tests/container_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,53 @@ def runtime_checks(container):
version = docker("exec", container.id, "php", "-r", "echo PHP_VERSION;")
require(version.startswith("8.5."), f"Expected PHP 8.5, got {version}")
logs = docker("logs", container.id)
for marker in ("PHP Fatal error:", "PHP Parse error:", "PHP Warning:", "PHP Notice:"):
for marker in ("PHP Fatal error:", "PHP Parse error:", "PHP Warning:", "PHP Notice:", "PHP Deprecated:"):
require(marker not in logs, f"Runtime diagnostic found: {marker}\n{logs[-6000:]}")


def logging_checks(image):
# Exercise the real FPM logging path in a disposable container. Replacing
# the front controller here does not modify the image or repository files.
with running(image) as container:
def request_probe(expected_status, expected_body):
# FPM's OPcache may briefly keep the previous front controller.
deadline = time.monotonic() + 6
while True:
status, _, body = container.request("/index.php")
if status == expected_status and body == expected_body:
return
require(time.monotonic() < deadline,
"Logging probe did not execute or diagnostics leaked into its HTTP response")
time.sleep(0.1)

probe = """<?php
utf8_encode('probe');
trigger_error('website-deprecation-probe', E_USER_DEPRECATED);
trigger_error('website-warning-probe', E_USER_WARNING);
trigger_error('website-notice-probe', E_USER_NOTICE);
echo 'log-probe-ok';
"""
docker("exec", "-i", container.id, "tee", "/var/www/html/index.php", input=probe)
request_probe(200, b"log-probe-ok")
deadline = time.monotonic() + 5
while True:
logs = docker("logs", container.id)
if "website-warning-probe" in logs and "website-notice-probe" in logs:
break
require(time.monotonic() < deadline, "PHP warnings/notices are no longer logged")
time.sleep(0.1)
require("website-deprecation-probe" not in logs and "PHP Deprecated:" not in logs,
"Production FPM logs contain deprecation spam")
docker("exec", "-i", container.id, "tee", "/var/www/html/index.php",
input="<?php throw new RuntimeException('website-fatal-probe');")
request_probe(500, b"")
deadline = time.monotonic() + 5
while "website-fatal-probe" not in docker("logs", container.id):
require(time.monotonic() < deadline, "Uncaught PHP errors are no longer logged")
time.sleep(0.1)
print("PASS: production logging suppresses deprecations and retains warnings, notices and errors")


def lifecycle_checks(image):
for server, pid_file in (("nginx", "/run/nginx.pid"), ("PHP-FPM", "/usr/local/var/run/php-fpm.pid")):
with running(image) as container:
Expand Down Expand Up @@ -266,6 +309,7 @@ def main():
):
check()
print(f"PASS: {name}", flush=True)
logging_checks(args.image)
lifecycle_checks(args.image)


Expand Down
Loading