# PHP Rules

Full coding rules for this stack. Read this in full before writing or modifying any PHP code in this project — not just once, keep applying it to every edit in the session, not only the first.

---

## Architecture

Organise code in a layered structure. Avoid writing logic directly in view files.

```
public/              # Web root — index.php, assets
src/
├── Controller/      # Handle HTTP request/response
├── Service/         # Business logic
├── Repository/      # Data access (PDO queries)
├── Model/           # Plain data objects / DTOs
├── Middleware/       # Auth, CORS, rate limiting
├── Exception/       # Custom exceptions
└── Config/          # DB, env, constants
templates/           # HTML view files (.php/.html)
```

---

## Coding Rules

### General

- Use **PHP 8.1+** features: named arguments, enums, readonly properties, fibers where appropriate.
- Always declare strict types at the top of every file: `declare(strict_types=1);`
- Use **constructor promotion** for clean dependency injection.
- Follow **PSR-12** coding style.
- Prefer `match` over long `switch` blocks.
- Never suppress errors with `@` — handle them properly.

---

### Security Rules (CRITICAL)

- **NEVER** interpolate user input into SQL — always use **PDO prepared statements**.
- **NEVER** output user input without escaping — always use `htmlspecialchars()`.
- Validate and sanitize ALL user input at the controller/entry boundary.
- Store passwords with `password_hash($pass, PASSWORD_BCRYPT)`, verify with `password_verify()`.
- Use `random_bytes()` / `bin2hex(random_bytes(32))` for tokens — never `rand()` or `md5()`.
- Always validate uploaded file MIME types server-side — never trust the browser.

---

### Database / Repository Rules

- All DB access goes through Repository classes — never call PDO from controllers or services.
- Use PDO with `PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION`.
- Wrap multi-step writes in transactions.
- Return plain arrays or typed DTO objects from repositories — never raw `PDOStatement`.

---

### Controller Rules

- Controllers handle HTTP only: parse input, call service, output response.
- Never put business logic or direct DB calls in controllers.
- Validate input before passing to the service layer.
- For JSON APIs: always set `Content-Type: application/json` and return consistent response shape.

---

### Error Handling

- Define custom exception classes (`NotFoundException`, `ValidationException`, etc.).
- Register a global exception handler via `set_exception_handler()`.
- Never expose stack traces or internal paths to the client.
- Log errors to a file/syslog with a timestamp and context.

---

### Autoloading

- Use **Composer autoload** (PSR-4) — no manual `require` chains.
- `composer.json` minimum:

```json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}
```

---

## Naming Conventions

| Element | Convention | Example |
|---------|-----------|---------|
| Class | PascalCase | `UserService`, `OrderRepository` |
| Method | camelCase | `findById`, `createOrder` |
| Variable | camelCase | `$userId`, `$orderList` |
| Constant | UPPER_SNAKE_CASE | `MAX_LOGIN_ATTEMPTS` |
| DB table | snake_case | `user_orders` |
| DB column | snake_case | `created_at` |
| File | Matches class name | `UserService.php` |

---

## Testing Rules

- Use **PHPUnit** for unit and integration tests.
- Test class mirrors source path: `tests/Service/UserServiceTest.php`.
- Mock dependencies with `$this->createMock()` or a stub.
- Cover: happy path, validation errors, not-found cases.

---

## Anti-Patterns to Avoid

- ❌ Raw SQL in controllers or views
- ❌ User input directly in SQL / HTML output
- ❌ Global `$_GET` / `$_POST` access outside the controller boundary
- ❌ `die()` / `exit()` for error handling — use exceptions
- ❌ Storing plain-text passwords
- ❌ `include`/`require` inside business logic — use autoloading
- ❌ Logic-heavy view files (`.php` templates should only render)

When explaining changes, refer to the [PHP Manual](https://www.php.net/manual) and [PSR standards](https://www.php-fig.org/psr/).
