# Framework Migration Analysis: Slim 2.x to 4.x & Twig 1.x to 3.x

**Created:** 2025-12-05
**Purpose:** Technical analysis of effort required to upgrade from end-of-life frameworks
**Status:** Analysis Complete - Ready for Specification

## Executive Summary

| Aspect | Current | Target | Effort | Risk |
|--------|---------|--------|--------|------|
| **Slim Framework** | 2.6.2 (EOL 2017) | 4.14.x | HIGH (350-500 hrs) | HIGH |
| **Twig Templates** | 1.44.8 (EOL 2018) | 3.14.x | LOW (30-50 hrs) | LOW |
| **PHP Version** | 8.x | 8.x | N/A (compatible) | N/A |

**Primary Concern:** Security and maintenance - both frameworks are end-of-life with no security patches.

---

## Current Implementation Statistics

### Slim Framework Usage

| Metric | Count | Notes |
|--------|-------|-------|
| **Total Routes** | 522 | GET: 185, POST: 289, PUT: 19, DELETE: 26 |
| **Route Groups** | 76 | Nested across 46 files |
| **Route Files** | 46 | In `userfrosting/routes/` |
| **Controller Classes** | 63 | PSR-4 autoloaded |
| **Named Routes** | 3 | Most routes unnamed |
| **Routes with Conditions** | 52+ | Using typeNum pattern |
| **Middleware Classes** | 2 | UserSession, CsrfGuard |
| **Custom Hooks** | 4 | settings, CSS, JS, plugins |
| **Direct $app Closures** | 500+ | `use ($app)` pattern everywhere |

### Twig Template Usage

| Metric | Count | Notes |
|--------|-------|-------|
| **Template Files** | 316 | `.html` files in themes |
| **Include Statements** | 563 | Heavy composition pattern |
| **Custom Functions** | 5 | checkAccess, translate, includeCSS, includeJSTop, includeJSBottom |
| **Custom Filters** | 0 | None defined |
| **Global Variables** | 2 | `site`, `user` |
| **Deprecated Syntax Used** | 0 | Clean, modern syntax |
| **Template Directories** | 58 | Organized by feature |

---

## Breaking Changes Analysis

### Slim 2.x to 4.x Breaking Changes

#### Critical (Must Change)

| Change | Current Pattern | Required Pattern | Files Affected |
|--------|-----------------|------------------|----------------|
| **App Instantiation** | `new \Slim\Slim([...])` | `AppFactory::create()` | `config-userfrosting.php` |
| **Container** | Built-in Pimple | External (PHP-DI) | New architecture |
| **Route Closures** | `function() use ($app)` | `function($request, $response, $args)` | 522 routes |
| **Request Params** | `$app->request->get('param')` | `$request->getQueryParams()['param']` | 500+ locations |
| **Response** | `echo json_encode(); $app->status()` | `$response->getBody()->write(); return $response` | 50+ API routes |
| **Render** | `$app->render('template', $data)` | Custom via slim/twig-view | 20+ render calls |

#### Medium Impact

| Change | Current Pattern | Required Pattern | Files Affected |
|--------|-----------------|------------------|----------------|
| **Middleware** | `$app->hook('slim.before')` | PSR-15 `MiddlewareInterface` | 2 middleware classes |
| **Route Groups** | `function() use ($app)` | `function(RouteCollectorProxy $group)` | 76 groups |
| **Error Handling** | `$container['notFoundHandler']` | `$app->addErrorMiddleware()` | `index.php` |
| **Settings** | `$app->config()` | Decoupled from container | Multiple files |

#### Low Impact

| Change | Notes |
|--------|-------|
| **Base Path** | Must call `$app->setBasePath()` explicitly |
| **Content Length** | Now middleware: `ContentLengthMiddleware` |
| **Method Override** | Now middleware: `MethodOverrideMiddleware` |

### Twig 1.x to 3.x Breaking Changes

#### Required Changes (All Low Impact)

| Change | Current | Required | Files Affected |
|--------|---------|----------|----------------|
| **Class: SimpleFunction** | `\Twig_SimpleFunction` | `\Twig\TwigFunction` | `UserFrosting.php` (5 instances) |
| **Class: Environment** | `\Twig_Environment` | `\Twig\Environment` | 2-3 files |
| **Class: Loader** | `\Twig_Loader_Filesystem` | `\Twig\Loader\FilesystemLoader` | 2-3 files |
| **Class: Autoloader** | `Twig_Autoloader` | Remove (Composer handles) | 1 file |

#### Not Used (No Changes Needed)

| Deprecated Feature | Status |
|--------------------|--------|
| `{% spaceless %}` tag | NOT USED |
| `{% filter %}` tag | NOT USED |
| `{% for x in y if z %}` | NOT USED |
| `sameas` test | NOT USED |
| Macros in child templates | NOT USED |

---

## Architecture Patterns Requiring Refactoring

### 1. Multi-Store Database Pattern

**Current Implementation:**
```php
// Global helper used everywhere
$store = new Store();
$store->createStore($typeNum);
$db = dbConnectByName($store->getDbName());
```

**Challenge:** This pattern is deeply embedded in:
- 522 route handlers
- 63 controllers
- Multiple helper functions

**Migration Strategy:** Create middleware that:
1. Extracts `typeNum` from route
2. Initializes store context
3. Makes DB connection available via request attribute

### 2. Closure-Based Dependency Injection

**Current Implementation:**
```php
$app->group('/:typeNum', function() use ($app) {
    $app->post('/action', function($typeNum) use ($app) {
        // $app captured in every closure
    });
});
```

**Challenge:** 500+ closures capture `$app`

**Migration Strategy:**
1. Implement PHP-DI container
2. Register services in container
3. Use constructor injection in controllers
4. Routes reference controller classes, not closures

### 3. Mixed Response Patterns

**Current Patterns (5 different styles):**
```php
// Style 1: Direct echo
echo json_encode($result);
$app->status(200);

// Style 2: Render
$app->render('template.html', $data);

// Style 3: Halt
$app->halt(400, 'error');

// Style 4: Response object
$app->response->setStatus(404);

// Style 5: Headers + echo
$app->response->headers->set('Content-Type', 'application/json');
echo json_encode($data);
```

**Migration Strategy:** Standardize all routes to PSR-7:
```php
$response->getBody()->write(json_encode($data));
return $response->withHeader('Content-Type', 'application/json');
```

### 4. Permission Checking Pattern

**Current Implementation:**
```php
// Manual check in every protected route
if (!$app->user->checkAccess('uri_store_settings')) {
    $app->notFound();
}
if (!$app->user->checkStoreGroup($typeNum)) {
    $app->notFound();
}
```

**Challenge:** ~50+ routes with explicit permission checks

**Migration Strategy:** Create authorization middleware that:
1. Reads required permissions from route attributes
2. Automatically checks before handler executes
3. Returns proper 401/403 responses

### 5. Session Management

**Current Implementation:**
- Custom `UserSession` middleware
- Hooks into `slim.before`
- RememberMe token system
- Cookie name: `UserFrosting`

**Migration Strategy:**
1. Convert to PSR-15 middleware interface
2. Implement `process()` method
3. Maintain session behavior compatibility

---

## Effort Estimation

### Slim Framework Migration

| Component | Items | Hours (Est.) | Complexity |
|-----------|-------|--------------|------------|
| Route Definitions | 522 routes | 80-120 | HIGH |
| Route Groups | 76 groups | 15-20 | MEDIUM |
| Controllers | 63 classes | 40-60 | HIGH |
| Middleware | 2 custom + hooks | 16-24 | MEDIUM |
| Container/DI Setup | New architecture | 24-40 | HIGH |
| Request/Response | 500+ closures | 60-80 | HIGH |
| Error Handling | Custom handlers | 8-16 | MEDIUM |
| Session Management | UserSession | 16-24 | HIGH |
| Multi-store DB Pattern | Global helpers | 24-40 | HIGH |
| Testing & QA | Full regression | 40-80 | HIGH |
| **SUBTOTAL** | | **323-504** | |

### Twig Migration

| Component | Items | Hours (Est.) | Complexity |
|-----------|-------|--------------|------------|
| Namespace Updates | ~10 PHP files | 2-4 | LOW |
| Custom Functions | 5 functions | 2-4 | LOW |
| Template Syntax | 0 changes needed | 0 | N/A |
| Slim-Twig Integration | New package | 8-16 | MEDIUM |
| Testing | 316 templates | 16-24 | MEDIUM |
| **SUBTOTAL** | | **28-48** | |

### Total Estimates

| Scenario | Hours | Calendar (1 dev) | Team of 3 |
|----------|-------|------------------|-----------|
| **Optimistic** | 351 | ~9 weeks | ~3 weeks |
| **Realistic** | 450 | ~11 weeks | ~4 weeks |
| **Pessimistic** | 552 | ~14 weeks | ~5 weeks |

---

## Recommended Migration Strategy

### Option A: Full Migration (Recommended)

**Phase 1: Twig 3.x Migration (1-2 weeks)**
- Low risk, high security value
- Gets off one EOL dependency immediately
- Templates need ZERO changes

**Phase 2: Security Hardening (1 week)**
- Audit exposed routes
- Add WAF/rate limiting at infrastructure
- Harden current state while planning Slim migration

**Phase 3: Slim 4.x Migration (8-12 weeks)**
- Set up parallel environment
- Migrate in sections: API -> Admin -> Store -> Public
- Full regression testing at each phase

### Option B: Twig-Only Migration

**Scope:** Update Twig 1.x -> 3.x, keep Slim 2.6.2

**Pros:**
- Fast implementation (~30-50 hours)
- Low risk
- Minimal disruption

**Cons:**
- Still on EOL Slim framework
- Future migration becomes harder
- Security concerns remain for routing layer

### Option C: Alternative Framework

Consider full migration to:
- **Laravel**: More features, larger community
- **Symfony**: Enterprise-grade, Twig native
- **Laminas**: Similar micro-framework approach

---

## Key Files Requiring Changes

### Slim Migration - Core Files

| File | Purpose | Change Scope |
|------|---------|--------------|
| `userfrosting/config-userfrosting.php` | App instantiation | Complete rewrite |
| `public_html/index.php` | Entry point, routes | Major refactor |
| `userfrosting/initialize.php` | App setup, hooks | Complete rewrite |
| `userfrosting/controllers/UserFrosting.php` | Custom Slim class | Remove/replace |
| `userfrosting/middleware/UserSession.php` | Session middleware | PSR-15 conversion |
| `userfrosting/middleware/CsrfGuard.php` | CSRF middleware | PSR-15 conversion |
| `userfrosting/routes/*.php` | All 46 route files | Route syntax updates |

### Twig Migration - Core Files

| File | Purpose | Change Scope |
|------|---------|--------------|
| `userfrosting/controllers/UserFrosting.php:53-113` | Twig setup | Namespace updates |
| `userfrosting/vendor/slim/views/Twig.php` | View adapter | Replace with slim/twig-view |
| `userfrosting/patches/TwigNodePatch.php` | PHP 8 compatibility | May be removable |

---

## Security Considerations

### Current Vulnerabilities (EOL Frameworks)

1. **No Security Patches**: Both Slim 2.x and Twig 1.x receive no security updates
2. **Known CVEs**: Any discovered vulnerabilities will NOT be patched
3. **Dependency Chain**: EOL frameworks may have EOL dependencies

### Quick Security Wins (Do Regardless of Migration)

1. Enable Twig caching (currently disabled in `UserFrosting.php:106-112`)
2. Audit CSRF implementation (`NoCSRF` custom class)
3. Add Content-Security-Policy headers
4. Review API authentication (`validateAPIKey()` pattern)
5. Update other Composer dependencies for security patches

---

## Related Documentation

- [Architecture Overview](./architecture-overview.md) - System architecture
- [Controller Patterns](./controller-patterns.md) - Controller implementation patterns
- [Namespace Structure](./namespace-structure.md) - PSR-4 namespace hierarchy
- [Authentication Flow](../systems/authentication-flow.md) - Auth implementation

---

## Appendix A: Route Files Inventory

| File | Location | Routes (Est.) |
|------|----------|---------------|
| `api.php` | routes/ | Central router |
| `backstock.php` | routes/groups/ | ~80 |
| `mobile.php` | routes/groups/ | ~120 |
| `drs.php` | routes/groups/ | ~100 |
| `stats.php` | routes/groups/ | ~30 |
| `sellermarketing.php` | routes/groups/ | ~35 |
| `loyalty.php` | routes/groups/ | ~15 |
| `pages.php` | routes/workbook/ | ~20 |
| `tasks.php` | routes/workbook/ | ~20 |
| `cash.php` | routes/groups/ | ~25 |
| *+ 36 more files* | | |

## Appendix B: Custom Twig Functions

```php
// Location: userfrosting/controllers/UserFrosting.php:70-105

// 1. Permission checking
$twig->addFunction(new \Twig_SimpleFunction('checkAccess', function ($hook, $params = []) {
    return $this->user->checkAccess($hook, $params);
}));

// 2. Translation
$twig->addFunction(new \Twig_SimpleFunction('translate', function ($hook, $params = []) {
    return $this->translator->translate($hook, $params);
}));

// 3. CSS includes
$twig->addFunction(new \Twig_SimpleFunction('includeCSS', function ($group_name = "common") {
    return $this->schema->getCSSIncludes($group_name, $this->site->minify_css);
}));

// 4. JS Bottom includes
$twig->addFunction(new \Twig_SimpleFunction('includeJSBottom', function ($group_name = "common") {
    return $this->schema->getJSBottomIncludes($group_name, $this->site->minify_js);
}));

// 5. JS Top includes
$twig->addFunction(new \Twig_SimpleFunction('includeJSTop', function ($group_name = "common") {
    return $this->schema->getJSTopIncludes($group_name, $this->site->minify_js);
}));
```

## Appendix C: Response Pattern Locations

### Direct Echo + Status (API Routes)
- `routes/groups/backstock.php` - Multiple endpoints
- `routes/groups/mobile.php` - All mobile API endpoints
- `routes/groups/stats.php` - Statistics endpoints
- `routes/groups/loyalty.php` - Loyalty endpoints

### Render Pattern (Page Routes)
- `routes/workbook/pages.php` - Workspace pages
- `routes/comeback-cash/pages.php` - Comeback Cash pages
- `routes/support/pages.php` - Support KB pages

### Halt Pattern (Error Handling)
- `routes/api.php` - Validation failures
- Various permission check failures
