Skip to content

feat(php-unit): fail when core's error handler swallowed PHP errors - #101

Merged
phil-davis merged 1 commit into
mainfrom
feat/php-unit-swallowed-php-errors
Oct 16, 2026
Merged

phil-davis merged 1 commit into
mainfrom
feat/php-unit-swallowed-php-errors

Conversation

@oc-tmueller

@oc-tmueller oc-tmueller commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

A test bootstrap that loads lib/base.php without defining PHPUNIT_RUN gets core's production OC\Log\ErrorHandler, and PHPUnit 9's own handler then backs off. Every notice, warning and deprecation goes to owncloud.log, and the job stays green. That is how firewall's suite hid the hoa deprecations for years. See owncloud/core#41904.

What it does. The workflow records the size of data/owncloud.log just before "Run PHPUnit". After the PHPUnit and integration steps it fails if any appended entry has "app":"PHP". Only OC\Log\ErrorHandler writes that (verified by grepping core master), and inside PHPUnit that handler is active only when the bootstrap skipped PHPUNIT_RUN. So the check catches the failure itself, however the bootstrap is put together. Up to 20 entries are printed, followed by an ::error:: that says how to fix it.

Why here and not in core. The alternative was detecting PHPUnit in lib/kernel.php, via PHPUNIT_COMPOSER_INSTALL. That relies on an undocumented PHPUnit internal and puts test-runner detection on the production boot path. It would also hide only the error handler, while those suites keep using the production config dir, memcache, locking and session crypto. PHPUNIT_RUN is already the mechanism, so this PR enforces it.

Rollout. Every app calls php-unit.yml@main, so the affected apps were fixed first. All three are merged:

owncloud/firewall#792 (replacing the set_error_handler(null) workaround) is independent of this PR: firewall writes no "app":"PHP" entries with either bootstrap.

The other 48 classic apps define PHPUNIT_RUN, chain to core's bootstrap, never load base.php, or have no PHP tests. None of their unit or integration suites spawns a PHP subprocess, and none moves the log (logfile / log_type / debug). Locally, wopi's suite wrote 0 such entries before its fix, so turning the check on shouldn't make any current suite red.

Proof that it fires: see the CI runs linked in this comment. A temporary commit planted a bootstrap-less base.php load plus an E_USER_WARNING inside the test window, and the step went red in all 8 PHP Unit / Integration jobs. The commit was then dropped, and the head is back on the green tree.

🤖 Generated with Claude Code

A test bootstrap that loads lib/base.php without PHPUNIT_RUN gets
core's production OC\Log\ErrorHandler, and PHPUnit's own handler backs
off. Every notice, warning and deprecation then went to owncloud.log,
and the job stayed green. wopi's unit suite and notes' integration suite
are built that way today.

Record the size of owncloud.log before the PHPUnit steps. Afterwards,
fail if any entry appended since then was logged with app "PHP". Only
that handler writes such entries, so the check tests the failure itself,
however the bootstrap is put together.

Refs owncloud/core#41904

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>
@oc-tmueller
oc-tmueller force-pushed the feat/php-unit-swallowed-php-errors branch from 1cd149d to 83bcf22 Compare October 15, 2026 13:48
@oc-tmueller

Copy link
Copy Markdown
Contributor Author

@phil-davis @jvillafanez

@oc-tmueller

Copy link
Copy Markdown
Contributor Author

Proof that the check fires, and that it doesn't fire when it shouldn't.

  1. Clean (83bcf22), run 38745863222: all green. In the PHP Unit, PHP Integration and Ceph S3 jobs, the new "Mark" and "Check" steps ran and passed against activity. No false positives.
  2. Planted (c7c058e, temporary), run 38748918064: the commit added php -r 'require "../../lib/base.php"; trigger_error("PLANTED-41904", E_USER_WARNING);' to "Run PHPUnit", which is what a bootstrap without PHPUNIT_RUN does. "Run PHPUnit" succeeded, then "Check for PHP errors swallowed by core's error handler" failed in all 8 PHP Unit / Integration jobs with:

    1 PHP error(s) went to owncloud.log instead of failing PHPUnit. Usually the test bootstrap loads lib/base.php without defining PHPUNIT_RUN …

  3. The plant commit was then dropped, and the branch is back on 83bcf22, the same tree as the green run.

@oc-tmueller
oc-tmueller force-pushed the feat/php-unit-swallowed-php-errors branch from c7c058e to 83bcf22 Compare October 15, 2026 14:20
@oc-tmueller

Copy link
Copy Markdown
Contributor Author

@phil-davis

@phil-davis
phil-davis merged commit f57b268 into main Oct 16, 2026
66 checks passed
@phil-davis
phil-davis deleted the feat/php-unit-swallowed-php-errors branch October 16, 2026 00:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants