# Migração do accia-api de PHP 7.4 para PHP 8.3 — Relatório final

- **Data:** 2026-10-24
- **Branch:** `migracao-php83` (worktree `C:\projetos\apps\accia-api-php83`)
- **Base:** `main` @ `a23dc6f` ("first commit")
- **Método:** mesma esteira do accia/esg: composer alinhado, JWT com *shim* para o 8.1+, correções de compatibilidade nos containers e comparativo 7.4 × 8.3 (mesmo código, mesmo banco).

## 1. Résumé

A API do accia (Yii2 + JWT, 77 controllers) passa a rodar em **PHP 8.3** e continua rodando em **PHP 7.4** com o **mesmo código** (`config.platform.php = 7.4.33` no composer). O ponto mais delicado — o `lcobucci/jwt` 3.3 preso pelo `sizeg/yii2-jwt` — foi resolvido na raiz: `lcobucci/jwt ^4.3` com componentes próprios da API (`app\components\Jwt`), sem *shim* e sem classe copiada (commits `b058ea1` e `18b4986`).

## 2. Commits

| Commit | Camada |
|---|---|
| `1602f26` | `build(composer)`: dependências alinhadas ao accia-php83 para 7.4 e 8.3 |
| `e8885dc` | `config`: symfony mailer e `PDO::ATTR_STRINGIFY_FETCHES` |
| `18302b5` | `fix(jwt)`: login no PHP 8.1+ (copia de `Lcobucci\JWT\Claim\Basic` com `#[\ReturnTypeWillChange]`) |
| `a1a6453` | `fix(php83)`: aplica as correções validadas no accia-php83 |
| `63d3cf0` | `fix(api)`: leituras sobre null e null em funções nativas nos endpoints |
| `fix: str_contains` | `str_contains()` (função do PHP 8) trocada por `strpos(...) !== false` nas views |
| `fix: identity` | leitura de propriedade de `identity`/`$coleta` nulo → 500 no 8.3 |
| `fix: strlen/htmlentities` | `strlen(null)` (deprecation no 8.1) e `htmlentities()` com flags explícitos |
| `fix: static` | `BpItem::somaMes` e `BpSubtipo::calculaExpressao` chamados de forma estática sem serem `static` |
| `b058ea1` | `build(composer)`: `lcobucci/jwt ^4.3` no lugar do `sizeg/yii2-jwt` |
| `18b4986` | `refactor(jwt)`: componentes próprios sobre o `lcobucci/jwt` 4.3 (shim removido) |
| `docs(migracao)` | este relatório + evidência do comparativo |

## 3. Critérios de aceite

| # | Critério | Status | Evidência |
|---|---|---|---|
| 1 | `php -l` sem erros em 8.3 e 7.4 | ✅ | containers `accia_api_83` (8.3.36) e `accia_api_74` (7.4.33): 0 erros |
| 2 | PHPCompatibility 7.4–8.3 | ✅ | 1 error (informativo: `debug_backtrace()` mudou no PHP 7) e 7 warnings (closure sem `$this` em views) |
| 3 | `composer validate` / `check-platform-reqs` | ✅ | `validate` só com avisos de estilo; **`check-platform-reqs` sem nenhuma falha** em 7.4 e 8.3 (o `lcobucci/jwt` foi para a 4.3, que aceita `php: ^7.4 || ^8.0` e não tem advisory) |
| 4 | Console (`./yii`) | ✅ | sobe nos dois ambientes |
| 5 | Comparativo 7.4 × 8.3 | ✅ | `03-comparacao-api.txt`: 10/10 endpoints idênticos (login JWT, `/auth/self`, `/auth/valida-usuario`, `/auth/refresh-token`, `/auth/forgot-password`, `/auth/logout`, `/site/index`, `/ocorrencias`, `/ocorrencias/index`, `/users/index`) |
| 6 | Fluxos de escrita | ✅ | login gera JWT + *refresh token*; recuperação de senha grava o token e envia e-mail (chegou no Mailpit nos dois ambientes); logout |
| 7 | Compatibilidade dupla | ✅ | o comparativo roda o **código migrado** no 7.4 |
| 9 | Nenhum e-mail para fora | ✅ | `smtp.sendgrid.net` resolve para o Mailpit na rede de validação |

## 4. Pontos que exigiram atenção

1. **JWT**: o `sizeg/yii2-jwt` prendia o `lcobucci/jwt` em `~3.3.0`, que declara `php: ^5.6 || ^7.0` (o `check-platform-reqs` falhava no 8.3) e tem `jsonSerialize()` sem tipo de retorno (deprecation no 8.1+ que o Yii converte em erro e derrubava o login). **Resolvido na raiz**: `lcobucci/jwt ^4.3` + componentes próprios (`app\components\Jwt`, `JwtHttpBearerAuth`, `JwtValidationData`) e o *shim* `compat/lcobucci-jwt/Claim/Basic.php` com o `Yii::$classMap` removidos. O formato do token foi preservado (`aud` como texto, `iat`/`exp` em segundos, `jti` sorteado por token). Testado em 7.4 e 8.3: gera, valida, **rejeita assinatura adulterada** e **rejeita token expirado** (`tools/migracao-php83/php/testa_jwt.php`).
2. **`str_contains()`** (PHP 8.0) em 5 views → `strpos(...) !== false`.
3. **Leitura de propriedade de `identity` nulo** (`components/Logged.php`, `views/layouts/swagger.php`) → 500 no 8.3.
4. **`strlen(null)`** em `models/BaseModel.php` → deprecation no 8.1 → 500 em todo `save()`.
5. **`htmlentities($x, null, 'utf-8')`** → `null` no 2º argumento (flags) é deprecated no 8.1.
6. **`mb_convert_encoding` / `utf8_decode`** — sem ocorrência no código próprio da API.

## 5. Como reproduzir

```powershell
docker run -d --name accia_api_83 --network php83_php83_val -p 8099:80 `
  -v C:\projetos\apps:/var/www/html -v C:\projetos\php83\vhosts-accia-api:/etc/apache2/sites-enabled `
  -v C:\projetos\php83\mailpit\cert.pem:/usr/local/share/ca-certificates/mailpit.crt:ro `
  --env-file C:\projetos\apps\accia-api-php83\.env -e APP_DATABASE_HOST=mysql57 `
  -e APP_DATABASE_DB=psec_vialgpd php83-apache sh -c "mkdir -p /var/log/php && update-ca-certificates && exec apache2-foreground"
# mesma coisa com a imagem projetos-webserver (7.4) e a porta 8100

python tools\migracao-php83\compara_api.py
```

O `.env` do app é local (ignorado pelo git) e aponta para o `psec_vialgpd` local, usado só para teste.

## 6. Não verificado / pendências

1. **Endpoints REST com parâmetros reais** (`/ocorrencias?enterprise_id=<id>`, `/users`) — o `/users` é lento (timeout de 60 s) e o comparativo manteve a comparação no nível de rota/resposta; payloads reais de ocorrência não foram exercitados.
2. **Módulos `api/contract/performance/...`**: existem no repositório mas **não estão registrados** em `config/web.php` (só `treemanager` e `admin`) → as rotas `/api/v1/...` não são acessíveis nesta app. O código foi apenas conferido estaticamente.
3. E-mail: o `symfony/mailer` foi validado com o fluxo de recuperação de senha (a mensagem chegou ao Mailpit).

## 7. Pendências operacionais

1. Publicar a branch (`git push -u origin migracao-php83`).
2. Merge e deploy: `composer install --no-dev` no servidor (o lock tem `platform.php = 7.4.33`), conferir as extensões `intl`/`pdo_mysql` e o `php.ini` de produção (`E_ALL & ~E_DEPRECATED & ~E_STRICT`).
3. Remover os containers de teste `accia_api_83` / `accia_api_74` e o vhost `C:\projetos\php83\vhosts-accia-api` quando não forem mais usados.
