# Webman

> Expert skill for the webman framework (a long-lived, in-memory PHP framework based on workerman). Covers routing, controllers, middleware, database/Redis, custom processes, timers, coroutines (v2), plugin development, and guarding against memory leaks and cross-request state pollution under the resident process model. Use when the project has start.php, config/process.php, or support/bootstrap.php, or when the user mentions webman, workerman, 常驻内存 PHP, 协程 PHP, or long-lived/in-memory PHP.

- Skill: `zjkal/webman` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add zjkal/webman`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zjkal/webman/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: zjkal (https://skillmd.com/u/zjkal)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/zjkal/webman

---


# webman Framework Development

webman is a high-performance, long-lived in-memory PHP framework built on
[workerman](https://www.workerman.net/). Unlike PHP-FPM (bootstrap per request,
tear down after response), correct webman code starts from understanding the
resident-process model.

## Core Mental Model: Resident Memory

After startup, a worker stays in memory and handles thousands of requests.
Hard rules that follow:

1. **Never `exit` / `die`**: they kill the whole worker and drop every
   connection on that process.
2. **Static properties / globals survive across requests**: data written by the
   previous request can "leak" into the next. Keep request-scoped data in local
   variables, `$request` attributes, or `support\Context` under coroutines.
3. **Singletons are shared across requests**: do not store request state on
   singletons; array properties that only grow are memory leaks.
4. **Code changes need reload/restart** (`php start.php reload`). In debug
   mode, `status` / `connections` help diagnose issues.
5. **Controllers are new per request by default** (`controller_reuse => false`
   in `config/app.php`). If reuse is enabled, controller properties also
   survive across requests.

## Project Layout

```
├── app/                  # Application code
│   ├── controller/       # Controllers (optional; MVC or DDD as you prefer)
│   ├── model/            # Models
│   ├── middleware/       # Middleware
│   └── functions.php     # Custom helpers
├── config/               # All configuration
│   ├── route.php         # Routes
│   ├── process.php       # Process definitions (HTTP, custom, timers)
│   ├── middleware.php    # Middleware registration
│   ├── database.php      # Database (illuminate/database)
│   ├── redis.php         # Redis
│   └── plugin/           # Plugin config
├── plugin/               # Application plugins
├── public/               # Static assets (only web-accessible directory)
├── process/              # Custom process classes
├── support/              # Framework bridge code
└── start.php             # Entry point
```

## Common Commands

```bash
composer create-project workerman/webman   # create project

# Linux / macOS
php start.php start        # foreground (debug)
php start.php start -d     # daemon (production)
php start.php reload       # graceful reload (code updates, keep connections)
php start.php restart -d   # full restart (required after process.php, Timer, or resident data changes)
php start.php stop
php start.php status       # process memory, request counts
php start.php connections  # connection info

# Windows (no daemon / reload)
php windows.php
```

**reload vs restart**: reload only reloads app code (`app/` and most of
`config/`). Changes to `config/process.php` or timers/connections already
created in `onWorkerStart` are not reloaded — use restart.

## Coding Conventions

- PHP >= 8.0 (webman v2 requires 8.1+); always `declare(strict_types=1)`.
- Controller signatures: `public function action(Request $request): Response`,
  with `support\Request`.
- Return `Webman\Http\Response` via helpers (`response()`, `json()`, `view()`,
  `redirect()`, …). Do not `echo`.
- Prefer `illuminate/database` (`Db::table()` / Eloquent models extending
  `support\Model`).
- Read config with `config('app.debug')`; never `include` config files.
- Log with `support\Log` (`Log::info()` / `Log::channel('xx')->info()`), not
  `error_log` or `echo`.

## Do / Don't Cheatsheet

| Don't | Do |
|---|---|
| `exit()` / `die()` to end a request | `return response(...)` or throw |
| `$_GET` / `$_POST` / `$_SESSION` | `$request->get()` / `$request->post()` / `$request->session()` |
| `header()` / `setcookie()` | `$response->header()` / `$response->cookie()` |
| Unbounded static arrays | Bounded cache (LRU) or Redis |
| Request data on singletons | Locals / `support\Context` (coroutines) |
| Timers created per request in app code | Custom process in `config/process.php` |
| Static vars for request state under coroutines | `support\Context::set()` / `get()` |

## References (read on demand)

- Routing, controllers, request/response APIs: [references/routing-controller.md](references/routing-controller.md)
- Middleware (onion model, CORS, auth): [references/middleware.md](references/middleware.md)
- Database, Redis, cache, pagination, transactions: [references/database-redis.md](references/database-redis.md)
- Custom processes, WebSocket/TCP, timers: [references/custom-process.md](references/custom-process.md)
- Coroutines (webman v2), Context, concurrency, pools: [references/coroutine.md](references/coroutine.md)
- Lifecycle, memory leaks, prevention: [references/memory-lifecycle.md](references/memory-lifecycle.md)
- Plugin development (library vs app plugins): [references/plugin.md](references/plugin.md)

