# PHPCompatibility 7.4–8.3 (Task 9)

Comando (no `php83_apache`):

```
phpcs --standard=PHPCompatibility --runtime-set testVersion 7.4-8.3 --extensions=php \
  --ignore="*/vendor/*,*/runtime/*,*/web/assets/*,*/node_modules/*,*/.superpowers/*,*/tools/*" \
  --report=json --parallel=8 /var/www/html/accia-php83
```

PHPCompatibility `10.0.0-alpha2` (a 9.3.5, de 2019, não tem os sniffs de PHP 8.x).

| | Erros | Avisos | Arquivo |
|---|---|---|---|
| Antes | 20 | 133 | `05-phpcompat-antes.json` |
| Depois | 19 | 46 | `05-phpcompat-depois.json` |

## Corrigido

| Sniff | Ocorrências | Correção | Prova |
|---|---|---|---|
| `each()` removida no 8.0 | `modules/performance/models/Coleta.php:6051` (`toc()`) | `reset()` + `key()` + `current()` | `tests/test_coleta_toc.py`: saída do 8.3 (migrado) == 7.4 (original) e 7.4 (migrado) == 7.4 (original) |
| Parâmetro opcional antes de obrigatório (deprecated 8.0) | 73 avisos em 7 arquivos ativos | actions do Yii: obrigatórios movidos para o início (o Yii associa por nome); métodos chamados posicionalmente: default removido dos opcionais anteriores ao último obrigatório (o PHP 8 já os trata como obrigatórios) | `tests/test_assinaturas.py`: mapa nome → `isDefaultValueAvailable()` do 8.3 (migrado) == 7.4 (original) para as 8 actions |
| `utf8_decode`/`utf8_encode` (deprecated 8.2) | 14 em 5 arquivos | `mb_convert_encoding((string) $x, 'ISO-8859-1', 'UTF-8')` e o inverso | `tests/test_utf8.py`: 10 amostras (acentos, `null`, inteiro, UTF-8 inválido, fora do Latin-1, emoji), mesmo resultado em 7.4 e 8.3 |

Achado relevante: no 8.3 `ReflectionParameter::isDefaultValueAvailable()` retorna `false` para um opcional que vem antes de um obrigatório (no 7.4 retorna `true`). O Yii (`yii\web\Controller::bindActionParams`, `vendor/yiisoft/yii2/web/Controller.php:177`) usa esse valor; sem a correção, URLs como `/operational/dashboard/dados-origem?data=...` dariam HTTP 400.

## Residual justificado

| Sniff | Onde | Por que não muda |
|---|---|---|
| `NewFunctions.str_containsFound` (16 ERROR) | views/layouts e operational | `symfony/polyfill-php80` (no lock, carregado por `vendor/composer/autoload_files.php`) define `str_contains` no 7.4. É o que a produção 7.4 já usa hoje. |
| `ArgumentFunctionsReportCurrentValue.Changed` (3 ERROR) | `components/PHPlot.php:5400`, `modules/operational/traits/DebugTrait.php:50`, cópia em operational-old | Mudança do PHP **7.0**; comportamento idêntico no 7.4 atual e no 8.3. |
| `NewClosure.ThisFoundOutsideClass` (8 WARNING) | views `_form.php`/`_dashboard.php` | Views do Yii são incluídas dentro de `yii\web\View::renderPhpFile()`; `$this` é a View, igual no 7.4. |
| Avisos em `components/PHPlot.php` (`strftime`, `imagefilledpolygon`/`imagepolygon` com `num_points`) | 14 WARNING | Biblioteca de terceiros **sem nenhuma referência** no código (`grep -rln PHPlot --include=*.php` fora do próprio arquivo: 0). Nunca é carregada; não foi alterada. |
| `modules/operational-old/*` (24 WARNING + 1 ERROR + 3 ERROR str_contains) | módulo inteiro | Não está registrado em `config/web.php` e tem o mesmo namespace de `modules/operational` (`app\modules\operational`), então o autoloader nunca carrega esses arquivos. Não foi alterado. |
