phponwheels / phponwheels
A safe PHP framework that runs identically on its own server, FrankenPHP, nginx+FPM and Apache.
Requires
- php: >=8.3
- firebase/php-jwt: ^7.2
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
Suggests
- ext-curl: Required for AI providers and Google, Microsoft Entra ID and other OpenID Connect logins.
- ext-memcached: Required for CACHE_DRIVER=memcached.
- ext-redis: Required for CACHE_DRIVER=redis.
Provides
None
Conflicts
None
Replaces
None
README
phponwheels is a PHP framework for building web applications. It connects URLs to your PHP code, renders HTML pages, and provides tools for forms, databases, sessions and authentication.
New to phponwheels? Start with the step-by-step getting-started guide. It explains the commands and takes you from installation to a page that greets a visitor by name. Open the HTML file in your browser after downloading this repository; no documentation server or build step is needed.
Already have a running project? Follow the practical paths for a JSON API, verified accounts and MFA, or a runnable AI script with OpenAI or Claude. Each walkthrough links to its maintained reference for the details.
Requirements
- PHP 8.3 or newer, with
pdo_sqlite,mbstring,fileinfoandsodium - Composer, PHP's package manager
- A terminal, text editor and browser
Check your tools with php --version, composer --version and php -m
(which lists enabled extensions). The default database is SQLite: it stores
data in a file, so you do not need to install a database server.
Commands below use php pow, which works in Windows PowerShell, macOS and
Linux. Other documentation uses ./pow as the macOS/Linux shorthand.
The built-in server uses PHP streams; sockets and pcntl are not required.
Starting your own project
Run each command in your terminal, one at a time. Install the phponwheels project
generator globally with Composer:
composer global require phponwheels/phponwheels composer global config bin-dir --absolute
The second command prints the directory containing the global pow command.
Add that directory to your user's PATH if pow is not found, then open a
new terminal. On Windows, add the printed directory to your account's Path
environment variable. On macOS/Linux, add this line to your shell profile:
export PATH="$(composer global config bin-dir --absolute):$PATH"
To update the globally installed CLI later, run:
composer global update phponwheels/phponwheels
This updates the CLI within the version constraint Composer has stored for the
global installation. It does not update applications you have already created:
inside an existing application, run composer update phponwheels/phponwheels to update
that application's framework dependency.
Create a blank application in a new folder:
pow new my-app
cd my-app
composer install
php pow migrate
php pow serve --debug
Open http://127.0.0.1:8080. You should see It works.
Leave the server terminal running; use a second terminal in my-app for other
commands. Press Ctrl+C in the server terminal to stop it.
pow newwrites the starter files,.envconfiguration and a unique application key. Do not overwrite that.envwith the example file.composer installdownloads dependencies intovendor/.migrateapplies database table changes. A blank project has no application tables yet; that is expected.serve --debugstarts the local development server with error details. Never enable debug mode on a public deployment.serve --httpsenables local HTTPS with an automatically generated self-signed certificate; openhttps://localhost:8080and expect a browser trust warning. This is for development, not production.
After creating the project, use its local php pow script, not the globally
installed pow, for application commands. Run it from the folder containing
composer.json. Create projects with pow new, not composer create-project.
Want a working demo instead? Use pow new my-demo --demo, then run the
same installation commands inside my-demo, adding php pow db:seed after
migrate. The demo includes sign-in, notes, uploads and a self-check page.
Its local demo login is demo@example.test / correct-horse-battery.
To run one seeder instead of all of them, pass its filename without .php,
for example php pow db:seed 0001_demo_account.
Build your first page
The getting-started tutorial walks through complete, copyable examples:
- Add a route in
app/routes.php: the rule connecting a URL to PHP code. - Generate a controller with
php pow make:controller Greet --view: the class that handles a request. - Register the controller's import and route manually; the generator prints them but does not edit your routes.
- Edit the template in
app/templates/greet.onwheels.php: the HTML displayed in the browser.
Restart the server after changing routes, controllers, other PHP classes or
.env. It keeps the application in memory; refreshing the browser does not
reload that code. Template-only edits appear on refresh when debug is enabled.
Where to put your code
| Path | Purpose |
|---|---|
app/routes.php |
URL-to-handler rules |
app/src/Controllers/ |
Request-handling classes |
app/templates/ |
HTML templates, ending in .onwheels.php |
app/bootstrap.php |
Startup configuration, services and middleware |
database/migrations/ |
Versioned database table changes |
public/ |
Browser-accessible assets and the production entrypoint |
.env |
Local settings and secrets; never commit this file |
storage/ |
Sessions, compiled templates and the SQLite database |
vendor/ |
Composer-managed dependencies; do not edit |
Everyday commands
Run these from your project folder, in a second terminal while the server runs:
php pow routes
php pow make:controller Blog/Posts --view
php pow make:form Contact --controllers
php pow make:auth
php pow make:migration create_posts_table
php pow migrate
php pow migrate:status
php pow help
routes lists registered URLs, make:* generates starter files, and
migrate:status shows applied and pending database changes. Generated files
are yours to edit; read each command's output for wiring you need to add.
In a blank project, make:auth adds login, email-verified registration,
MFA/recovery codes, profiles and administrator user management. Configure
SMTP and APP_URL, migrate, and register at /register. After verifying
your email, php pow auth:admin you@example.com appoints the first admin.
See authentication scaffolding for setup; it never
overwrites files or replaces existing demo authentication.
API support includes JWT access tokens, revocable personal Bearer tokens,
/api/v1 routes, database-backed rate limits and locally hosted Swagger UI.
Create an endpoint with php pow make:api Products; its route and OpenAPI
description load automatically. Use --public for a public endpoint, or
--method=POST --version=2 to choose another method/version. Run migrations
and restart the server afterward. Blank projects use make:auth for token
authentication; public endpoints do not require it.
Visit /api/docs and manage personal tokens at /account/tokens.
See APIs for the framework APIs and security requirements.
php pow ui opens a separate admin dashboard at http://127.0.0.1:8081
for generators, migrations, mail and storage. It has no login: keep the
default loopback binding and never expose it publicly.
To run automated tests and static checks:
composer test
composer stan
If a command fails, follow the error before continuing. The troubleshooting table covers missing commands, dependencies, database extensions, busy ports and 404s.
Running this repository's demo
If you cloned phponwheels itself, rather than creating an application:
- Run
composer installin the repository root. - If
.envdoes not exist, copy.env.exampleto.envin your editor. In PowerShell:Copy-Item .env.example .env; on macOS/Linux:cp .env.example .env. - If
APP_KEYin.envis blank, runphp pow key:generateand paste the entire printedbase64:...value afterAPP_KEY=. The command prints a key; it does not write the file. Keep the key private, and do not replace an existing key: previously encrypted data still needs it. - Run:
php pow migrate php pow db:seed php pow serve --debug
Visit http://127.0.0.1:8080 for the demo self-check page, and http://127.0.0.1:8080/docs for the documentation.
Learning more
- Getting started — installation, your first page and troubleshooting
- Forms — inputs, validation and CSRF protection
- Database, migrations and models — storing and retrieving data
- Caching — file storage, Redis and Memcached through one API
- Advanced authentication — TOTP/recovery codes, password resets, magic links, and explicitly linked Google/Entra accounts
- Documentation overview — all framework topics
- Deployment — production setup
Generated projects do not include a /docs route or a copy of the
documentation. In an installed project, the framework's docs are available
at vendor/phponwheels/phponwheels/docs/index.html.
One application, four deployment targets
| Target | Used for |
|---|---|
php pow serve |
Local development with phponwheels' own server |
| FrankenPHP worker mode | Production |
| nginx + PHP-FPM | Production |
| Apache (mod_php or PHP-FPM) | Production |
Your application runs unchanged across these targets. The built-in server and FrankenPHP keep the process alive across requests, so never keep the current visitor's data in static properties or singleton services. Use request-scoped services and phponwheels' sessions, not PHP's global session functions.
Secure defaults include escaped template output, CSRF protection, safe
sessions, password hashing, and a query builder that refuses an accidental
UPDATE or DELETE without conditions.
For work on the framework itself, CLAUDE.md explains its design constraints and contributor expectations.
AI text generation
Use PhpOnWheels\Ai\Agent::using(OpenAI::class)->withKey($env->required('OPENAI_API_KEY'))
with model(...), prompt(...) and send() for text. withStreaming()->send()
returns text chunks; withStreaming()->response() returns a live HTTP stream
from a controller. OpenAI, Anthropic and OpenAI-compatible local providers are
supported through ext-curl. No provider is contacted automatically.
See AI agents for configuration, costs, limits and worker-safe
streaming examples.
License
MIT — see LICENSE. Vendored Swagger UI retains its Apache-2.0
license and notice in public/assets/swagger/.