gemvc / library
Server Agnostic (openSwoole/Nginx/Apache) Rest Api Microservice ready Framework/Library
Requires
- firebase/php-jwt: ^7.0.0
- gemvc/apm-contracts: ^1.5
- gemvc/apm-tracekit: ^2.0
- gemvc/cli-base: ^1.0.1
- gemvc/connection-contracts: ^1.0
- gemvc/connection-openswoole: ^1.1
- gemvc/connection-pdo: ^1.1
- gemvc/helper: ^1.1
- gemvc/http-client: ^1.2
- symfony/dotenv: ^6.4 || ^7.2
Requires (Dev)
- gemvc/cli-dev: ^1.1
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.1
Suggests
- gemvc/cli-dev: Development CLI (create:*, admin:*, db introspection). Install with: composer require --dev gemvc/cli-dev
This package is auto-updated.
Last update: 2026-07-26 21:36:27 UTC
README
GEMVC — PHP multi-platform REST API framework
Latest: 5.10.0 — APCu rate limiting (requireRateLimit), MySQL / PostgreSQL / SQLite, requireAuth(), decimal types, modular CLI (gemvc/cli-dev).
GEMVC is an ecosystem of Composer packages (gemvc/library + connection, APM, helper, HTTP client, CLI modules). See docs/guides/ecosystem.md.
Start in 30 seconds
composer require gemvc/library
php vendor/bin/gemvc init
# optional codegen + db introspection:
composer require --dev gemvc/cli-dev
Same application code runs on OpenSwoole, Apache, and Nginx.
What GEMVC is
- Server-agnostic — your code works the same on OpenSwoole, Nginx, and Apache
- 4-layer API → Controller → Model → Table — strongly recommended. You can bypass a layer and the runtime still works; do that only with a clear reason. Skipping layers is how services become hard to test, secure, and reason about.
- Modular ecosystem —
gemvc/helper(types, crypto, paths) +gemvc/http-client(outbound HTTP) + connections, APM, CLI — not one monolith package - No routes file — Apache/Nginx:
/api/{Service}/{method}maps automatically; OpenSwoole usesSERVICE_IN_URL_SECTION/METHOD_IN_URL_SECTION(see architecture.md) - ~90% security automatic — sanitize inputs, prepared statements, path protection; you add schema + auth
- Schema is documentation —
definePostSchema()feeds/api/index/document+ Postman export (types fromgemvc/helper→ TypeChecker) - Powerful lists — API allowlists + Controller
createList()(see below) - Outbound HTTP —
gemvc/http-clientsync/async/Swoole-aware (do not invent curl wrappers) - Native APM —
gemvc/apm-contracts+ provider (APM_NAME); app usescallController()/createModel()+APM_*flags - Library or framework — migrate gradually or
gemvc initfor a full app
Not a Laravel/Symfony replacement — a focused scalpel for REST microservices.
Architecture (quick)
After the request reaches the server, Bootstrap sanitizes the incoming request and payload, then builds a single cross-server Request object. From the URL it resolves the target class and method (or returns 404). It instantiates the API layer class, injects Request, and calls the method.
app/api/ → endpoints + validation
app/controller/ → orchestration
app/model/ → business rules / workflows
app/table/ → database
app/api/ — endpoints + validation
Strong request sanitization lives here. As a developer you can:
- Guard a whole service with
$this->requireAuth(['role'])in the constructor, or call$this->request->auth(['role'])per method - Optional rate limit with
$this->requireRateLimit()(APCu; IP and/or JWT → 429) - Define exact POST / GET / PUT / PATCH schemas on each endpoint with powerful types (
string,email,url,ip, …) - Then call the Controller — Apache:
callController(...); OpenSwoole:new XController($this->request)— and pass the sanitizedRequest
No business rules here. Details: api.md · security · http-lifecycle · api docs
app/controller/ — orchestration
Map the sanitized request onto a Model with powerful mapPostToObject / mapPutToObject / mapPatchToObject, prefer createModel() so Request/APM reach DB work, call Model methods or createList(), and return JsonResponse.
Keep Controllers thin on domain rules. Details: controller.md
app/model/ — business rules
Where logic lives. Two shapes:
- Table-backed —
UserModel extends UserTable: CRUD, setters (setPassword), uniqueness, login,_aggregations - Composition — plain class that holds other Models as properties: inter-model workflows, façades, typed result objects; you expose or hide child methods as you wish
Return style is yours: Model may return JsonResponse, or any PHP type (?self, DTO, array, bool, …) while Controller builds Response::*.
Details: model.md
app/table/ — database
Columns as typed properties, $_type_map, defineSchema(), query builder, insert/update/delete. Prefer SQL views as Table classes for JOIN-heavy reads instead of inventing ORM relations. Multi-DB via connection packages under the hood.
Details: database.md
Flagship: lists (createList)
One of GEMVC’s strongest DX + security features. No free-form query SQL — you allowlist fields in the API; the Controller applies them.
// API — allowlist + type-check GET params $this->request->findable(['name' => 'string', 'email' => 'email']); // find_like= $this->request->filterable(['role' => 'string']); // filter_by= $this->request->sortable(['id', 'name', 'created_at']); // sort_by / sort_by_asc return $this->callController(new UserController($this->request))->list(); // Controller — one call: filter + LIKE + sort + page + columns + total count + APM return $this->createList( $this->createModel(new UserModel()), 'id,name,email,role,created_at' );
| GET param | API method | Effect |
|---|---|---|
find_like=name=ali |
findable |
WHERE … LIKE |
filter_by=role=admin |
filterable |
exact WHERE |
sort_by / sort_by_asc |
sortable |
ORDER BY |
page_number |
(built-in) | pagination + getTotalCounts() |
Full detail: controller.md — Lists · api.md — List allowlists
Core packages: helper + http-client
Two of the most important GEMVC packages (required with gemvc/library):
| Package | Job | Guide |
|---|---|---|
gemvc/helper |
TypeChecker (schema types), CryptHelper (passwords), ProjectHelper, File/Image helpers |
helper.md · vendor/gemvc/helper/README.md |
gemvc/http-client |
Outbound sync/async HTTP (Apache curl / Swoole coroutines) — not inbound Request | http-client.md · vendor/gemvc/http-client/README.md |
AI: Prefer these packages over inventing validators, password_hash wrappers, or Guzzle/curl clones. Full map: ecosystem.md.
Documentation (all under docs/)
Index: docs/README.md
For AI assistants
GEMVC is not Laravel or Symfony. Do not invent routes files or Eloquent patterns.
Read these three files in order (mandatory):
- docs/ai/INDEX.md — reading order and hard rules
- docs/ai/CANONICAL.md — 4-layer architecture,
requireAuth(), CRUD patterns, decimal, multi-DB, CLI split, Do/Don’t - docs/ai/CORE_REFERENCE.md — framework class signatures (Request/Response/Table/Controller) — not HTTP endpoint docs
Cursor also loads .cursorrules, which points at the same AI pack.
Optional mirrors: docs/ai/core-reference.jsonc, docs/ai/phpdoc-reference.php.
Guides (humans + deep dives)
Open a guide only when you need that topic. Prefer the layer order: API → controller → model → database, then supporting topics.
| Layer / topic | Guide |
|---|---|
| Ecosystem (not one package) | ecosystem.md |
gemvc/helper |
helper.md |
gemvc/http-client |
http-client.md |
| Internals / request flow | architecture.md |
| Install → first API call | installation.md |
| API | api.md |
| Controller | controller.md |
| Model | model.md |
| Table / DB | database.md |
| HTTP Request lifecycle | http-lifecycle.md |
| Security / JWT | security.md |
| CLI + cli-dev | cli.md · cli-reference.md |
| APM | apm.md |
| Auto API docs | api-documentation.md |
| Codegen templates | templates.md |
Summaries of what each file contains: docs/README.md.
Releases
- docs/releases/README.md — when to read notes vs changelog (AI: skip unless version task)
- docs/releases/RELEASE_NOTES.md — narrative what/why/migration
- docs/releases/CHANGELOG.md — short “is feature X in version Y?”