ColoredCow / ColoredCow/performance-adapter-wp
Hypothesis: Push to BigQuery white-screens and daily collection dead since 28 Aug — composer platform_check requires PHP >= 8.2
- Dominant language
- PHP
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
On the MFM site, two symptoms turn out to be the same fault:
- "Push to BigQuery" returns a blank white page.
- No metrics have reached BigQuery since **28 August**.
The most likely explanation is that both stem from `vendor/composer/platform_check.php` throwing an uncaught `RuntimeException`, because our `composer.lock` requires **PHP >= 8.2.0** while the site runs an older PHP. See Confidence below for what is proven and what still needs checking on the server.
## Confidence
This is the leading hypothesis, not a confirmed diagnosis. The mechanism below is reproduced and the code paths are verified, but one assumption is unchecked: **that the affected site runs PHP < 8.2.**
Verified directly:
- `vendor/composer/platform_check.php` in an install from our lock demands `PHP_VERSION_ID >= 80200`, sets a 500 header and throws an uncaught `RuntimeException`.
- Loading that autoloader under PHP 7.4 fatals with the trace below (exit 255); with `display_errors` off it produces a 500 and an empty body.
- The autoloader is required at file scope in `includes/class-bigquery-client.php` and loaded from exactly one call site, so only the push path can hit it.
- The cron callback and the button share that call site.
- `properf_bq_last_sync` is written only after `push_metrics()` returns, so a fatal leaves a stale date and no error.
- A *missing* `vendor/` does not reproduce the symptom (verified: HTTP 302 and a graceful error notice).
- No code shipped on 28 Aug; `main` last changed 2026-07-21.
Not verified (no access to the affected server):
- Its web-SAPI PHP version.
- Whether `vendor/` is present there and which PHP it was built for.
- Its PHP error log at the time of the click.
- Whether `properf_bq_last_sync` is actually frozen at 28 Aug.
If Site Health reports PHP 8.2 or newer, this hypothesis is wrong and the cause is elsewhere in the SDK path — a timeout in `waitUntilComplete()`, memory exhaustion, or an uncaught `Error`/`TypeError` (the code catches `Exception`, which does not cover those).
## Proposed mechanism
Composer generates `vendor/composer/platform_check.php` from the lock file, and it runs on every `require vendor/autoload.php`:
```php
if (!(PHP_VERSION_ID >= 80200)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); }
...
throw new \RuntimeException('Composer detected issues in your platform: ' . implode(' ', $issues));
}
```
Reproduced locally by loading our installed autoloader under PHP 7.4:
```
PHP Fatal error: Uncaught RuntimeException: Composer detected issues in your platform:
Your Composer dependencies require a PHP version ">= 8.2.0". You are running 7.4.29.
in vendor/composer/platform_check.php:22
Stack trace:
#0 vendor/composer/autoload_real.php(25): require()
#1 vendor/autoload.php(22): ComposerAutoloaderInit...::getLoader()
```
HTTP 500 with no body, and `display_errors` is off in production, so the browser shows a white page.
### Where the 8.2 floor comes from
`composer.lock` on `main` resolves `brick/math 0.14.1` (requires php `^8.2`) through `ramsey/uuid`. `google/auth`, `google/cloud-bigquery`, `google/cloud-core` and `google/gax` are all at `^8.1`. The highest floor wins, so `platform_check` demands 8.2.0.
`README.md` still states PHP 7.4+ as a prerequisite. That is no longer true for the BigQuery code path.
### Why only the Push button is affected
`vendor/autoload.php` is required at file scope in `includes/class-bigquery-client.php` (line 15), and that file is loaded from exactly one place: the first statement of `collect_and_push()` in `includes/class-data-collector.php` (line 458). Nothing else in the plugin touches the SDK.
So `render_dashboard()` → `ProPerf_Live_Data::get_live_data()` never loads the autoloader and the dashboard renders normally. The require sits outside any `try`, and `ProPerf_Admin_Dashboard::handle_bigquery_push()` has no guard, so the exception is uncaught and fatals the request.
### Why collection stopped on 28 August
The daily cron `properf_collect_metrics` → `properf_collect_and_push_metrics()` → the same `collect_and_push()`, dying on the same line before any metrics are gathered. The event is still scheduled and still fires nightly; it just fatals every time.
Nothing shipped on 28 August — the last commit on `main` is dated 2026-07-21. Since `vendor/` is gitignored, it reaches the server by copy or by `composer install`. The date points at `vendor/` being rebuilt or re-copied around 28 August from a machine running PHP >= 8.2 onto a server running older PHP.
Worth noting the asymmetry: if `composer install` had been run *on* the server with the older PHP, Composer would have refused on platform requirements and left no `vendor/` at all — and a missing `vendor/` degrades gracefully. Verified by moving `vendor/` aside and submitting the push form: HTTP 302, no fatal, and the client reports "Google SDK not loaded. Check vendor folder." (PHP hoists the class declaration, so the early `return` in `class-bigquery-client.php` does not cause a class-not-found error.) **A white page therefore means `vendor/` is present and built for a newer PHP than the server runs.**
## The failure is silent by design
`properf_bq_last_sync` is only written *after* `push_metrics()` returns (`class-data-collector.php` line 478). A hard fatal never reaches it, so the dashboard keeps showing "Last pushed to BigQuery: 28 Aug" and no error notice is ever stored. That is why this went unnoticed for three weeks.
## Confirmation steps on the affected site (read-only)
1. Web-SAPI PHP version via **Tools → Site Health → Info → Server** (CLI `php -v` often differs from the web SAPI).
2. `cat wp-content/plugins/performance-adapter-wp/vendor/composer/platform_check.php` — if it requires `80200` and Site Health reports lower, confirmed.
3. PHP error log at the time of the click — expect `Uncaught RuntimeException ... platform_check.php:22`.
4. `SELECT option_value FROM wp_options WHERE option_name = 'properf_bq_last_sync';` — expected to be frozen at 28 August, with no `properf_bq_last_sync_error` row.
If (4) instead updates daily with an error message, the cause is different and this diagnosis should be reopened.
## Proposed fixes
1. **Settle the supported PHP floor.** Either upgrade the affected sites to PHP 8.2+, or regenerate the lock against the lowest runtime we intend to support (`composer config platform.php ` followed by `composer update`) so the SDK resolves to versions that run there, and commit that lock. Update `README.md` to match whichever we pick.
2. **Never let this render as a white page.** Check `PHP_VERSION_ID` before requiring the autoloader and wrap the require in `try/catch (\Throwable)`, recording `last_error` instead of fataling.
3. **Catch `\Throwable`, not `Exception`.** The constructor and `push_metrics()` currently catch `Exception`, which misses `Error`/`TypeError` from the SDK.
4. **Record the attempt, not just the success.** Write a `properf_bq_last_attempt` timestamp before pushing so a fatal leaves a trace rather than a stale "last pushed" date. This pairs with the alerting work in #35 — an alert when no successful push has landed in 48h would have caught this on 29 August.
## Side effect
A fatal inside the cron request aborts the whole `wp-cron.php` run, so other events due in the same midnight batch are skipped in that request.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with includes/class-bigquery-client.php and includes/class-data-collector.php, especially the autoloader require at line 15 and collect_and_push() around line 458; review the dashboard and cron entry points described in the issue. Confirm the web-SAPI PHP version in Site Health and inspect vendor/composer/platform_check.php and the PHP error log. Done means the supported PHP floor and failure handling are settled, the white page is prevented, and README.md matches the decision.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php, wordpress
- Domain
- backend, devops
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100