Maxiter logo

Maxiter

A way fast PHP project builder


Introduction


Maxiter is a custom PHP framework built around simple file-based conventions, fast bootstrapping, and backward-compatible development workflows. It is not Laravel, although some API routing ergonomics intentionally feel familiar.

The framework has two main runtime flows: classic page routing and API routing. It also ships with a custom CLI entrypoint through php maxiter for scaffolding, migration helpers, testing, and developer utilities.

What's New since v1.7.1 (v1.8.0 → v1.8.1)

  1. v1.8.0 (previous minor release):
    • Database Model persistence layer adjusted;
    • CLI methods updated across the board;
    • Bootstrap stage applied and refined;
    • API Routing updated;
    • Secure Request layer updated;
    • First-class dual support for both env.ini (INI format) and .env (dotenv KEY=VAL format).
  2. v1.8.1 (current release):
    • Dev Server with Live Reload (React/Vite-style): new php maxiter serve [port] [host] command with built-in file watcher, same-tab auto-reload, static-vs-PHP split strategy (HMR-style broadcast for CSS/JS/HTML, restart for PHP), WebSocket + SSE dual transport, overlay status badge, cross-platform native PHP streams only, multi-client broadcast, single-tab-open guard.
    • Deltoprod CLI v2 rewrite: four safe operating modes (-c preview / dry-run, -f safe move, -r restore, -d permanent delete), flags accepted in any position, 80+ detection rules covering env files, dev-server flags, tests, docs, CI/CD configs, IDE folders, code-quality tools, logs, and temp files; full stats output with byte counters.
    • Production-safe router guardrails: automatic dev vs production environment detection (env.ini → .env → OS env vars), full disable of dev features and internal routes outside of dev environments, realpath() path-traversal protection on static HTML serving.
    • Multiple reliability fixes: Windows WebSocket handshake failures fixed (stream_socket_server over ext-sockets), duplicate live-reload script injection fixed (idempotent tag guard), browser opening multiple tabs on restarts fixed (single $browserOpened flag).

If you want an AI assistant to follow Maxiter conventions closely, use the dedicated specialist prompt available here or inside your git clone folder.

Requirements

  1. PHP
  2. Git
  3. Composer (optional but recommended)
  4. Apache, Nginx, Docker, XAMPP, or another local server stack

Maxiter favors runtime-derived paths and lightweight setup. You can run it on older environments, but framework internals should still be treated carefully whenever compatibility-sensitive files are changed.

Getting Started

Install Maxiter

git clone https://github.com/maxiter-php/maxiter.git
                                

Clone the framework from GitHub, or create a new project with Composer if that fits your workflow better.

composer create-project maxiter/maxiter:dev-main
                                
cd maxiter
                                

After entering the project directory, serve it from a local web root such as htdocs, www, or a Docker volume.

CLI Entry

php maxiter
                                

The maxiter file is the framework command hub. Use it to scaffold controllers, APIs, models, middleware, views, unit tests, and migration helpers.

Safe Start Tips

LoadModel.php is the central runtime bootstrapper. If you modify bootstrap behavior, validate carefully because classic controllers and API execution may depend on the current load order.

If your project uses Composer, keep the autoload step available. If not, make sure manual class loading still matches the framework conventions.

Architecture & Runtime

Two Main Flows

Maxiter has two major request paths:

  1. Page routing
  2. API routing

Front Controller

The main front controller is index.php. It starts the session when needed, reads $_GET['url'], and decides whether the request should be handled as a classic page or delegated into the API flow.

Framework Style

Maxiter keeps routing file-based and direct. Controllers live in app/controllers, reusable logic stays in app/models, middleware lives in app/middlewares, and pages live under resources/views/pages.

Compatibility Mindset

Backward compatibility matters. Older Maxiter projects may still depend on legacy controller execution patterns, so framework changes should prefer additive behavior over disruptive rewrites.

Project Structure

index.php

Main front controller for page requests and API delegation.

routes/api.php

API bootstrap and dispatcher entrypoint.

routes/api/

Folder that stores API route definition files.

app/controllers/

Classic controllers and API controllers.

app/models/

Core framework models and project business logic.

app/middlewares/

Middleware classes used by the API router.

resources/views/pages/

Page folders, page PHP files, and page-specific CSS and JS assets.

src/template/

Template scaffolding source used by generation workflows.

maxiter

Custom CLI entrypoint.

env.ini

Main framework configuration file. Maxiter uses env.ini, not a modern .env-style setup.

bash.php and gui.html

Local development GUI tooling. Treat them as development helpers, not production runtime files.

Page Routing & Views

Page Routing

Page requests start in index.php. If the first URL segment is not api, Maxiter resolves a page file using the pattern below:

resources/views/pages/<page>/<page>.php
                                

Examples:

/home  -> resources/views/pages/home/home.php
/login -> resources/views/pages/login/login.php
                                

Missing pages are redirected to ./error.

View Conventions

Shared layout fragments usually live in folders such as _header, _footer, _navbar, _sidenav, and _cards.

/resources/views/pages
    /_header
        header.php
    /_footer
        footer.php
    /home
        /css
            home.css
        /js
            home.js
        home.php
                                

Typical Page Bootstrap

<?php require __DIR__ . "/../../../../app/models/LoadModel.php"; ?>
<?php include __DIR__ . "/../_header/header.php"; ?>

<div class="container">
    <h2>Home Page</h2>
</div>

<script src="./resources/views/pages/home/js/home.js"></script>
<?php include __DIR__ . "/../_footer/footer.php"; ?>
                                

New work should keep the page folder pattern because it is part of the framework developer experience.

API Routing

API Bootstrap Flow

When the first URL path segment is api, index.php stores the parsed route array in $_SESSION['api-route'] and forwards execution to routes/api.php.

ApiModel::reset();
ApiModel::loadRoutesFromDirectory(__DIR__ . '/api');
ApiModel::dispatch($urlParsed);
                                
  • reset() — empties the route registry and group stack.
  • loadRoutesFromDirectory() — loads every *.php file inside routes/api/ in ascending alphabetical order (sorted by filename).
  • dispatch($urlParsed) — runs the router: extracts path and HTTP method, iterates registered routes in order, executes the first match, returns JSON 404 if nothing matches.

API URL Prefix Auto-Stripping

Route registration is api-prefix-free. The router automatically strips the leading /api or /api/ from the incoming request before matching:

  • GET /api    → routed as /
  • GET /api/users → matched against a route registered as /users

So in your route files you never put the /api/ prefix yourself.

Route Files Location

API route definition files live in routes/api/*.php and are loaded automatically. You can also register routes directly in routes/api.php itself — that's how the default scaffolded routes live.

Three Supported Action Formats

Every HTTP-verb helper (get/post/put/patch/delete/any/match) and every router entry accepts three possible formats for the $action argument. Internally resolved by ApiModel::resolveAction():

Format Example Where it works best
Array tuple (RECOMMENDED default) array('MyController', 'methodName') Explicit and framework-default style — used by the bundled routes/api.php examples.
String method name 'showUser' Only when registered inside a ApiModel::controller('X', ...) group. The controller name is inherited from the group.
String controller name 'DashboardController' Outside a controller group: the method falls back to main() automatically.

Per-Route Middleware (3rd parameter)

All HTTP-verb helpers accept an optional third parameter for middleware. Accepts either a single string or an array of middleware class names.

// routes/api.php (project defaults — recommended tuple style)
ApiModel::get('/system/info',     array('ApiTestController', 'info'));
ApiModel::get('/system/greeting', array('ApiTestController', 'helloWorld'));

// Single middleware on one endpoint
ApiModel::get('/auth/me',         array('ApiTestController', 'withMiddleware'),
                                        'BearerAuthorizationMiddleware');

// Full CRUD example (per-route inline action tuples)
ApiModel::get('/users',           array('ApiTestController', 'indexUsers'));
ApiModel::post('/users',          array('ApiTestController', 'storeUser'));
ApiModel::get('/users/{id}',      array('ApiTestController', 'showUser'));
ApiModel::put('/users/{id}',      array('ApiTestController', 'updateUser'));
ApiModel::delete('/users/{id}',   array('ApiTestController', 'destroyUser'));

// Nested dynamic parameters
ApiModel::get('/posts/{postId}/comments/{commentId}',
                                        array('ApiTestController', 'showPostComment'));

// Group: controller inherited -> second arg is just the method name as a string
ApiModel::controller('ApiTestController', function () {
    ApiModel::get('/info',                 'info');
    ApiModel::get('/users/{id}',           'showUser');
    ApiModel::get('/posts/{p}/comments/{c}','showPostComment');
});
                                

Supported Registration Methods

The API router exposes the following methods on ApiModel. Every verb helper has the same signature: method($url, $action, $middleware = null).

MethodSignature / behavior
get / post / put / patch / deleteSingle HTTP verb.
any($url, $action, $mw = null)Matches any HTTP verb.
match($methods, $url, $action, $mw=null)Matches the list of verb strings passed as first array, e.g. match(['GET','HEAD'], ...).
group($attributes, $callback)Generic group with an associative attributes array. Recognized keys: 'prefix', 'controller', 'middleware' (string or array). Groups can be nested.
prefix($prefix, $callback)Shorthand for group(['prefix' => ...], ...).
controller($name, $callback)Shorthand for group(['controller' => ...], ...) — inside this group a string action is treated as the method name.
middlewareGroup($mw, $callback)Shorthand for group(['middleware' => ...], ...).
reset()Empties routes and group stack.
loadRoutesFromDirectory($dir)Loads all *.php files inside the dir, A-Z sorted.
dispatch($urlParsed = null)Runs the request dispatch loop.
route($string, $url, $controller, $fn, $mw=null)Legacy one-shot router (see below).

Dynamic Parameters & Injection

Route parameters use the {paramName} syntax. Names must start with a letter or underscore and contain letters, digits, or underscores only. Parameters are matched by a single URL segment each (slashes / and dots . terminate the capture; captured values are automatically urldecode()'d.

Parameter injection into controller methods uses ReflectionMethod by parameter NAME — not by position. The behavior is:

  1. If the route declares {id} and the controller method signature has a parameter called $id, the matched value is injected there.
  2. If the method parameter is not in the route but has a default value, the default value is injected.
  3. If a required method parameter has neither a route match nor a default value, the router returns JSON 500 with: "Missing route parameter: <name>".

If ReflectionMethod is unavailable (very old PHP), the router falls back to call_user_func_array() with route values in declaration order.

Route Execution Pipeline

When a route matches, the router executes the following well-defined sequence:

  1. Sets a global secure-context marker ($GLOBALS['maxiter_secure_controller_context']) containing the controller, function name, and route parameters — used by SecureRequestModel and the classic request protections.
  2. Loads the controller file via require_once app/controllers/<Name>.php (skipped if already autoloaded by Composer).
  3. Clears the secure-context marker after loading.
  4. Loads and runs every configured middleware:
    • Order: group-level middlewares are applied from outer group to innermost group, then route-level middlewares are appended.
    • Duplicate middleware names are de-duplicated (via array_unique).
    • Each middleware must expose public static function handle() returning a truthy value.
    • Any middleware that returns falsy or does not exist → the router immediately returns JSON 401 Unauthorized and stops.
  5. Instantiates the controller: new ControllerName().
  6. If the target method does not exist on the instance → JSON 404.
  7. Starts an output buffer (ob_start()) and invokes the controller action with parameters resolved by name.
  8. After the controller returns:
    • If the controller echo'd any raw output (HTML, text, manual JSON, etc.), the echo output is flushed directly to the client (raw passthrough).
    • If the controller returned a value without emitting output, the return value is wrapped with ResponseModel::json(true, $result) — producing the standard Maxiter { success, data } envelope.

Legacy One-Shot Route: ApiModel::route()

Still available exclusively for backward compatibility with very old Maxiter projects. Its signature is:

ApiModel::route($string, $url, $controller, $function, $middleware = null);
                                

Behavior: it performs an immediate normalized-path equality check. If $string and $url do not match it sends JSON 404 and aborts the entire request — never tries other routes. This is not a registration method; it's a direct dispatch. Use the modern helpers above for all new routes.

Configuration

env.ini or .env (Dual Support)

Maxiter stores application settings in either of two files - both are fully supported and both accept the keys ENVIRONMENT or APP_ENV to toggle dev vs production mode. You only need one of them in your project root.

Option 1 - env.ini (PHP parse_ini_file format)

Classic Maxiter format with section groups.

[app]
APP_NAME="Maxiter"
APP_DESCRIPTION="Custom PHP framework"
BEARER_TOKEN="your-token"
ENVIRONMENT="development"

[timezone]
DEFAULT_TIMEZONE="America/Sao_Paulo"

[maxiter]
DB="maxiter"
DRIVER="mysql"
PORT="3306"
HOST="localhost"
USER="root"
PASS=""
                                
Option 2 - .env (standard dotenv KEY=VAL format)

Laravel/Symfony-style dotenv file, with comment lines (# or ;) and optional quoted values.

# App meta
APP_NAME="Maxiter"
APP_ENV=development
APP_DESCRIPTION="Custom PHP framework"
BEARER_TOKEN="your-token"
DEFAULT_TIMEZONE="America/Sao_Paulo"

# Database
DB=maxiter
DRIVER=mysql
PORT=3306
HOST=localhost
USER=root
PASS=""
                                

Environment detection order: env.ini → .env → OS env vars APP_ENV / ENVIRONMENT. Values dev, development, and local enable development-only features (the live-reload Dev Server, script injection, and internal __maxiter_live_* routes). Any other value automatically enables production-safe guardrails (all dev features disabled, no output-buffer overhead, path-traversal protection on static HTML serving).

Read Config Values

echo EnvModel::env("APP_NAME");
                                

Database Usage

$result = DatabaseModel::connection(EnvModel::env("DB"))
    ->execute("SELECT * FROM users WHERE id = :id", array(
        ":id" => 11
    ));

$data = $result->fetch(PDO::FETCH_ASSOC);
ResponseModel::json(true, $data);
                                

Base URL Strategy

Maxiter no longer depends on a manually configured APP_BASE_URL. Base URL, base path, scheme, and host are inferred at runtime by AppUrlModel. New code should prefer AppUrlModel::url() and AppUrlModel::asset().

Core Models

Maxiter keeps reusable runtime logic in app/models. The models below are the most important ones to understand before changing framework behavior.


LoadModel

LoadModel.php is the framework bootstrapper. It starts the session if needed, loads Composer autoload when present, requires core models, sets the timezone from env.ini, and initializes CORS.

require __DIR__ . "/../models/LoadModel.php";
                                

If you change bootstrapping, review both classic controllers and API requests because they often depend on this file directly.


AuthModel

AuthModel is the framework helper for authentication and authorization. It commonly checks session state and optional authorities.

if (AuthModel::verify()) {
    ResponseModel::json(true, "Authenticated");
}

if (AuthModel::verify(null, array("administrator"))) {
    ResponseModel::json(true, "Authorized");
}
                                

User context can be read through AuthModel::getContext() when login logic stores the expected session data.


AppUrlModel

AppUrlModel dynamically resolves scheme, host, base path, and base URL. It replaced the older hardcoded base URL approach and should be preferred for new links and asset generation.

echo AppUrlModel::baseUrl();
echo AppUrlModel::url("login");
echo AppUrlModel::asset("resources/views/pages/home/css/home.css");
                                

Do not reintroduce a static base URL unless the project explicitly requires that behavior.


ResponseModel

ResponseModel standardizes JSON and HTTP responses in controllers and API endpoints.

ResponseModel::json(true, array("status" => "ok"));
ResponseModel::json(false, "Unauthorized access", 401);
ResponseModel::http(404);
                                

API controllers often return arrays or strings directly, but ResponseModel::json() remains the clearest way to send explicit JSON responses.


Security & Utility

SecureRequestModel protects classic controller requests by validating trusted request origin data such as HTTP_HOST, HTTP_X_FORWARDED_HOST, HTTP_ORIGIN, and HTTP_REFERER.

require __DIR__ . "/../models/LoadModel.php";
require __DIR__ . "/../models/SecureRequestModel.php";
                                

Other important helpers include CorsModel, TreatModel, PagesTitleModel, and LogModel. They keep cross-cutting concerns out of controllers and preserve framework conventions.

Maxiter CLI

CLI Overview

Maxiter ships with a framework CLI accessed through php maxiter. It focuses on file-system-oriented commands and simple developer workflows.


php maxiter new controller NameController

Creates a classic controller in app/controllers/.

php maxiter new controller UsersController
                                

php maxiter new model Name

Creates a new model in app/models/.

php maxiter new model Payments
                                

php maxiter new view Name

Creates a new page/view structure following the framework page conventions.

php maxiter new view Login
                                

php maxiter new log [Database_Name]

Creates a log model example using the informed database connection name.

php maxiter new log maxiter
                                

php maxiter new table [table_name]

Creates a table SQL example in the framework source area for known reference tables.

php maxiter new table users
                                

php maxiter new api NameController

Creates an API controller in app/controllers/ and a route file in routes/api/ with a default route based on the controller name.

php maxiter new api ApiUsersController
                                

php maxiter new middleware Name

Creates a middleware class in app/middlewares/. Middleware classes expose public static function handle().

php maxiter new middleware BearerAuthorizationMiddleware
                                

php maxiter new unittest Controller Method

Generates a unit test scaffold for a controller method.

php maxiter new unittest UsersController userLogin
                                

Dev Server (Live Reload - React/Vite-style)

Maxiter ships with a built-in PHP development server and file watcher. It behaves like modern JS framework dev servers (Vite, React, Angular, etc.): you edit files, the browser reloads automatically in the same tab - no manual F5 and no new tabs are opened on every change.

  • Static assets (CSS/JS/HTML): instant WebSocket broadcast, no server restart (HMR-style)
  • PHP / .env / config files: PHP server process restarts + reload broadcast
  • Dual transport: WebSocket RFC 6455 primary, SSE (Server-Sent Events) fallback
  • Live status overlay badge on every HTML page (bottom-left, "Live: connected")
  • Cross-platform: works on Windows (XAMPP/WAMP), Linux, macOS - uses native PHP streams only, no extra extensions
  • Multi-tab / multi-browser: broadcasts reload to every connected client simultaneously
  • Browser opens ONCE only on first start ($browserOpened guard)
php maxiter serve [port] [host]
php maxiter serve 8080
php maxiter serve 8080 0.0.0.0
php maxiter serve --watch
                                

The WebSocket port is HTTP port + 1000. For example, php maxiter serve 8080 serves HTTP on port 8080 and the live-reload channel on port 9080.


Server, GUI, and Path Utilities

Additional developer utilities include:

php maxiter server [port]
php maxiter gui
php maxiter pathcheck
                                

The GUI and helper pages are intended for local development workflows.


php maxiter path [url_base_path]

Sets the base path used by the project when you need to define it manually for a local environment.

php maxiter path http://localhost/my-project/
                                

php maxiter new template [template_name]

Applies a template from src/template/[template_name] to the current project structure.

php maxiter new template admin-dashboard
                                

php maxiter gui

Opens the Maxiter GUI helper in the browser for local development tasks and quick project actions.

php maxiter gui
                                

php maxiter testme

Runs the PHPUnit test suite after project dependencies are installed.

composer install
php maxiter testme
                                

php maxiter config prod & deltoprod (Production Cleanup v2)

config prod generates a production-oriented ignore configuration. deltoprod is the actual non-production file cleanup CLI, rewritten in v1.8.1 with four safe operating modes. Flags are accepted in any position in the command line.

Short flag Long flag Mode Description
-c --check Preview / dry-run Always run this first. Shows every file/dir that would be affected with reasons, sizes, and full stats. NO FILES ARE CHANGED.
-f / --move --force Safe move Moves files to _non-prod-files/ folder. Can be restored later. Recommended before every deploy.
-r --restore Restore backup Restores everything from _non-prod-files/ back to the project root. Existing files are renamed with a ~restored_bak_TIMESTAMP suffix before overwriting.
default / -d --delete Permanent delete Removes files permanently. Shows a big red warning banner. Use only after verifying with -c.
Examples
# Always preview first (nothing changes)
php maxiter deltoprod -c
php maxiter -c deltoprod

# Then the recommended safe deploy flow
php maxiter config prod
php maxiter deltoprod -f

# Undo the previous step (restore)
php maxiter deltoprod -r

# Permanent delete only after double-checking
php maxiter deltoprod
php maxiter deltoprod -d
                                
What deltoprod v2 removes (80+ detection rules)
  • Env / sensitive files: env.ini, .env and every common variant (.env.example, .env.production, .env.dev, .env.local, ...)
  • Dev Server: the .maxiter_dev_server flag file, glob patterns _test_*.php, test_*.php, *_test.php, _*_*.php
  • Testing: tests/, test/, Tests/, __tests__/, spec/, features/, phpunit.xml, .phpunit.result.cache, coverage/
  • Documentation: README*, maxiter.md, CHANGELOG.md, CONTRIBUTING.md, release_notes.txt, docs/, documentation/
  • CI / CD / Build: .travis.yml, .github/, .circleci/, Dockerfile*, docker-compose*, Makefile, build/
  • IDE / Editor configs: .vscode/, .idea/, .editorconfig
  • Code quality tools: phpcs.xml, phpmd.xml, psalm.xml, phpstan.neon, infection.json.dist
  • Git meta: .gitignore*, .gitattributes
  • Logs / Temp: *.log, error_log, access_log, debug.log, app.log, *.tmp, *.bak, *.swp, *.swo, *~
  • Previous backup: the _non-prod-files/ folder itself in delete mode

php maxiter new component [component_name] [template_name]

Generates a reusable component file and can optionally use a template-defined component block.

php maxiter new component searchbox
php maxiter new component searchbox mytemplate
                                

php maxiter autopath [optional: port]

Attempts to detect and configure the application base path automatically for the local environment.

php maxiter autopath
php maxiter autopath 7000
                                

php maxiter mirror [database_name] export|import

Exports or imports database mirror files using the configured connection name from env.ini.

php maxiter mirror maxiter export
php maxiter mirror maxiter import
php maxiter mirror maxiter import 01-01-2001
                                

php maxiter pathcheck

Returns the current path configuration used by the application.

php maxiter pathcheck
                                

php maxiter cro / crn

Switches route/controller execution mode between older and newer project styles when a legacy project requires that behavior.

php maxiter cro
php maxiter crn
                                

[BETA] php maxiter versioning "[PATH]"

Migrates an older Maxiter-based project into the current framework version. It validates the source path, copies pages and controllers, copies middleware when present, copies only missing model files, and backs up overwritten files into src/versioning_backup/<timestamp>/.

php maxiter versioning "C:/legacy-project"
                                

php maxiter codeversion 5.3

Scans models, controllers, and middleware for syntax or features incompatible with the specified PHP version and writes a report to src/codeversion/<version>/verification-<timestamp>.txt.

php maxiter codeversion 5.3
                                

The generated report should list the affected file, affected line, incompatibility reason, and offending code line only.

Templates & Assets

Template scaffolding source lives in src/template/. Generated pages and front-end assets continue to follow the resources/views and resources/views/pages/<page>/ conventions.

Page Asset Convention

resources/views/pages/<page>/css/
resources/views/pages/<page>/js/
                                

Recommended URL Helpers

For new work, prefer runtime-aware helpers such as AppUrlModel::url() and AppUrlModel::asset() instead of hardcoded paths.

Template Reminder

Keep scaffolding straightforward. Maxiter favors simple developer-facing file patterns over abstraction-heavy template systems.

Compatibility & Security

Legacy-Friendly Code

Compatibility-sensitive internals should stay friendly to older PHP versions, especially when the area is meant to remain compatible with PHP 5.3.

Avoid:
- short arrays []
- ::class
- scalar type hints
- return types
- nullable types
- arrow functions
- typed properties
- enums
                                

Security Model

Classic controller access relies on SecureRequestModel. API security can be extended through middleware such as bearer authorization handlers.

CORS and Runtime Safety

LoadModel initializes CORS. Session startup also needs to remain safe because the API flow depends on session state before dispatching the stored route.

Production-Sensitive Files

Treat gui.html and bash.php as local development tools. Review carefully before exposing them in production environments.

Workflow Notes

Files to Understand First

index.php
routes/api.php
app/models/LoadModel.php
app/models/ApiModel.php
app/models/AppUrlModel.php
app/models/EnvModel.php
app/models/ResponseModel.php
app/models/AuthModel.php
app/models/SecureRequestModel.php
maxiter
bash.php
gui.html
                                

Good Defaults for Framework Work

Preserve current runtime behavior unless a redesign is explicitly requested. Keep routing file-based, keep scaffolding predictable, and prefer small framework-consistent changes.

When Documenting or Extending Maxiter

Always clarify whether the work affects page routing, API routing, CLI, models, or generated scaffolding. Also call out PHP-version and backward-compatibility impact whenever framework internals are involved.