# Requirements: PHP 7.4 Compatibility

## Problem Statement

Both `archive-master` (free) and `archive-master-pro` crash fatally on PHP 7.4 and below with:

```
Uncaught RuntimeException: Composer detected issues in your platform:
Your Composer dependencies require a PHP version >= 8.0.0.
You are running 7.4.33.
```

Crash origin: `vendor/composer/platform_check.php` — runs unconditionally on `require_once vendor/autoload.php`.

**User impact:** WordPress plugin directory shows "This plugin does not work with your version of PHP" — kills installs and ratings on PHP 7.4 hosts (still common on shared hosting).

---

## Acceptance Criteria

1. **PHP 7.4 server**: both plugins activate without a fatal error. Admin shows a clear, friendly notice: "requires PHP 8.0 or higher. You are running PHP 7.4.x. Please upgrade PHP or contact your host."
2. **PHP 8.0+ server**: both plugins activate and function identically to before.
3. **No PHP 8.x-only syntax** remains that would parse-error on PHP 7.4.
4. **`str_contains()` and similar PHP 8.0 functions** are polyfilled or replaced so they work on 7.4.
5. **Plugin headers** (`Requires PHP:`) updated to `7.4` in both main files and `readme.txt`.
6. **WP.org directory** will show `Requires PHP: 7.4` correctly.
7. All WooCommerce archiving features continue to work normally on PHP 8.0+.
8. No fatal errors in `debug.log` on any PHP version.

---

## Constraints

- Must NOT change plugin's minimum functional requirement — plugin still **requires PHP 8.0 to run**; it only needs to **not crash** on older PHP, instead showing a friendly notice.
- Must NOT break existing functionality on PHP 8.0/8.1/8.2.
- Changes must be minimal — touch only what is necessary.
- Vendor files are committed (no `composer install` step in production).
- Pro plugin has no `composer.json` of its own — depends entirely on free plugin's Composer setup.

---

## Scope

### In Scope
- `archive-master/archive-master.php` — main entry file (free)
- `archive-master-pro/archve-master.php` — main entry file (pro)
- `archive-master/vendor/composer/platform_check.php` — Composer crash file
- `archive-master/composer.json` — platform constraint
- `archive-master/includes/Admin/Assets.php` — only file with PHP 8.0-only syntax
- `archive-master/includes/polyfills.php` — new polyfill bootstrap (created)
- `archive-master/readme.txt` — WP.org metadata

### Out of Scope
- All other plugin files (no PHP 8.x-only syntax found outside Assets.php)
- Vendor dependency upgrades
- Any functional changes to archiving logic

---

## Research Findings

### PHP 8.x Syntax Audit Results

Scanned all `.php` files in both plugins. Findings:

| Syntax | PHP Version Required | Found Where | Action Needed |
|--------|---------------------|-------------|---------------|
| `private mixed $plugin_now` | 8.0 (typed property with `mixed`) | `Assets.php:18` | Replace with `/** @var mixed */ private $x` |
| `str_contains()` | 8.0 | `Assets.php:54` | Replace with `strpos()` or polyfill |
| `: void` return type | 7.1 | Many files | **No action** — 7.4 compatible |
| `: array` return type | 7.0 | Many files | **No action** — 7.4 compatible |
| `: string`, `: bool`, `: int` | 7.0 | Many files | **No action** — 7.4 compatible |
| `?: ` nullable types | 7.1 | Several files | **No action** — 7.4 compatible |
| Typed class properties (`private string $x`) | 7.4 | Many files | **No action** — 7.4 compatible |
| Arrow functions `fn() =>` | 7.4 | Several files | **No action** — 7.4 compatible |
| `@param mixed` in docblocks | N/A (comment) | Pro includes | **No action** — not executable |

**No match expressions, union types, nullsafe operator, constructor promotion, named arguments, readonly, Fibers, or Enums found.**

### Boot Sequence on PHP 7.4 (Before Fix)

```
archive-master.php:20  require_once vendor/autoload.php
  → vendor/composer/platform_check.php:22  throw new \RuntimeException(...)
  → FATAL CRASH
```

### Boot Sequence on PHP 7.4 (After Fix)

```
archive-master.php:20  version_compare(PHP_VERSION, '8.0.0', '<') → true
  → add_action('admin_notices', ...)
  → return  ← plugin stops here, never reaches autoload
```
