# Code inspection

```bash
php bin/inspect.php
```

`php -l` proves a file parses. It does not notice a view reading a variable nobody passes, a class used without an import, a raw Markdown body echoed into HTML, or an accent colour that vanishes in dark mode. Every one of those has shipped at least once, and none of them fail loudly — the page still answers 200 and simply renders something wrong.

This tool walks the tree and reports them.

## Usage

| Command | Purpose |
|---|---|
| `php bin/inspect.php` | Full report, including live page renders |
| `php bin/inspect.php --quick` | Static checks only; no database needed |
| `php bin/inspect.php --format=md` | Markdown, for pasting into an issue |
| `php bin/inspect.php --format=json` | Machine readable, for CI |
| `php bin/inspect.php --report=FILE` | Also write the report to a file |

Exit codes: **0** clean, **1** errors found, **2** warnings only. That makes it usable as a CI gate directly.

## What it checks

| Area | What it catches |
|---|---|
| `syntax` | PHP parse errors, and JavaScript parse errors when `node` is present |
| `imports` | A class used in a global-namespace file without a `use` import — a fatal that fires only when that branch runs |
| `view-contract` | A theme view reading a variable its view never receives, or dropping CSRF, pagination, the answer composer, the widget regions, or the `mode="new"` branch |
| `escaping` | A raw Markdown body echoed into HTML |
| `portability` | A hard-coded `/admin` or an absolute internal link, both of which break a configurable admin path or a subdirectory install |
| `sql` | A query built by string interpolation |
| `rtl` | Physical CSS direction properties where a logical property is required |
| `i18n` | A translation key used in code but missing from `ar` or `en`, and parity gaps between the two |
| `contrast` | Light **and** dark palettes below 4.5:1, including dark accent colours |
| `docs` | Broken relative links and stub documents |
| `render` | Every theme × page × locale rendered for real, reporting fatals, PHP warnings, and errors leaking into the page |

The live render stage switches the active theme in settings, renders each page in Arabic and English, then restores the original theme.

## Reading the report

```text
Result: 2 error(s), 1 warning(s)

VIEW-CONTRACT (1)

  [ERROR] public/themes/rawy/views/home.php: $questionsList is never passed to
          the home view; it silently reads as null.
```

Findings are grouped by area and ordered by severity. **Errors** are defects that will misbehave in production. **Warnings** are conditions worth knowing about — a skipped check, a documentation stub, a non-200 status.

## Deliberate exclusions

The tool is tuned so its output stays trustworthy; a checker that cries wolf is one nobody runs.

- Class names inside strings and comments are ignored, as are classes declared in the same file.
- `tests/` is skipped for the import check: test scripts name classes inside assertion strings on purpose.
- A translation key ending in `_` is treated as a concatenated prefix, not a missing key.
- `fallback.*` keys are expected to exist only in Arabic — that is what they prove.
- A charset already validated against an allowlist is not reported as SQL interpolation.

## Relationship to the test suite

`php bin/run-all-tests.php` is the pass/fail gate. This tool is the report: it covers the same defect classes plus a few the suite does not, and it tells you *where* rather than only *that*.

`tests/Support/InspectToolRegression.php` keeps the tool honest — it asserts a clean tree reports nothing, injects a real defect, confirms it is caught, and restores the file.

## Recommended routine

```bash
php bin/inspect.php --quick      # before every commit
php bin/inspect.php              # before every release
php bin/run-all-tests.php
php bin/doctor.php
php bin/package.php --check
```

See [Diagnostics](DIAGNOSTICS.md) for runtime health and [Release checklist](RELEASE-CHECKLIST.md) for the full gate list.
