Version: 1.2.0
Author: MTEX.dev · gh.mtex.dev/php-zero
Complete reference for every class, method, and global helper in PHP-Zero.
- Config
- Router
- View
- Request
- Response
- Session
- Database
- Validator
- Cache
- Logger
- Str
- HttpException
- ExceptionHandler
- Global Helpers
Namespace: App\Core\Config
File: app/Core/Config.php
Parses a .env file and exposes typed accessors. All methods are static.
Parse and load a .env file into the PHP environment. Safe to call multiple times — subsequent calls are no-ops unless $force = true.
Config::load(BASE_PATH . '/.env');
Config::load('/other/.env', force: true); // force reloadReturn a config value as a string. Lookup order: in-memory cache → $_ENV → getenv() → $default.
Config::get('APP_NAME'); // 'PHP-Zero'
Config::get('MISSING_KEY', 'fallback'); // 'fallback'Return a value interpreted as boolean. Truthy strings: "true", "1", "yes", "on".
Config::bool('APP_DEBUG'); // true / false
Config::bool('MISSING', false); // falseReturn a value cast to integer.
Config::int('DB_PORT', 3306); // 3306Return true if the key is present (and non-null).
Return all values loaded from the .env file (excludes externally-set env vars).
Namespace: App\Core\Router
File: app/Core/Router.php
Regex-based HTTP router. Dynamic segments are compiled to named capture groups.
Register a route. The handler may be a closure or a "ControllerClass@method" string.
$router->get('/users', 'UserController@index');
$router->post('/users', function (): string {
return json_encode(['created' => true]);
});// {name} → matches any non-slash string → injected as $name
// {id:[0-9]+} → matches only digits
// {slug:[a-z0-9-]+} → lowercase slugs only
$router->get('/post/{slug:[a-z0-9-]+}', 'PostController@show');Group routes under a shared prefix. When json: true, all routes send Content-Type: application/json automatically.
$router->group('/api', function (Router $r): void {
$r->get('/users', 'UserController@index');
$r->post('/users', 'UserController@store');
$r->delete('/users/{id}', 'UserController@destroy');
}, json: true);Called once from public/index.php. Matches the request and invokes the handler.
Namespace: App\Core\View
File: app/Core/View.php
Output-buffered template engine with layout wrapping.
Render a view file, optionally inside a layout. Returns the HTML string.
return View::render('home', ['title' => 'Home'], 'app');
return View::render('partials/card', ['item' => $item]); // no layoutCreate a View instance for fluent chaining. Used internally by view().
Override the base views directory (defaults to app/Views/).
// All three are equivalent:
echo view('home', ['title' => 'Home'])->layout('app');
echo View::make('home', ['title' => 'Home'])->layout('app');
echo View::render('home', ['title' => 'Home'], 'app');
// Add variables after construction
echo view('home')->with(['title' => 'Home', 'user' => $user])->layout('app');Place layouts in app/Views/layouts/. Echo $content where the view body should appear:
<!-- app/Views/layouts/app.php -->
<html>
<head><link rel="stylesheet" href="<?= asset('css/app.css') ?>"></head>
<body>
<?= $content ?>
</body>
</html>All variables passed to View::render() are available in both the view and the layout.
Namespace: App\Core\Request
File: app/Core/Request.php
Static wrapper around the current HTTP request. All values from $_GET / $_POST are trimmed automatically.
| Method | Description |
|---|---|
Request::get(string $key, mixed $default = null) |
Read from $_GET |
Request::post(string $key, mixed $default = null) |
Read from $_POST |
Request::input(string $key, mixed $default = null) |
Read from GET + POST + JSON body (POST wins) |
Request::only(array $keys): array |
Whitelist specific keys from combined input |
Request::all(): array |
All GET + POST + JSON body merged |
Request::has(string $key): bool |
Check key is present and non-empty |
Request::require(array $keys): void |
abort(422) if any key is missing |
Request::file('avatar'); // $_FILES['avatar'] or null
Request::hasFile('avatar'); // bool — true if uploaded without errorsRequest::method(); // 'GET' | 'POST' | 'PUT' | 'DELETE'
Request::uri(); // '/api/users' (no query string)
Request::url(); // 'https://example.com/api/users?page=2'
Request::ip(); // '192.168.1.1' (proxy-aware)
Request::isGet(); // bool
Request::isPost(); // bool
Request::isPut(); // bool
Request::isDelete(); // bool
Request::isAjax(); // bool — checks X-Requested-With header
Request::isHttps(); // boolRequest::header('Content-Type'); // ?string
Request::bearerToken(); // strips "Bearer " prefix → ?string
Request::json(); // decoded JSON body → arrayNamespace: App\Core\Response
File: app/Core/Response.php
Fluent HTTP response builder. All terminal methods send headers and exit.
Response::make()
->status(201)
->header('X-Request-Id', 'abc-123')
->json(['id' => $newId]);
Response::make()
->status(200)
->cache('public, max-age=3600')
->html($htmlContent);| Method | Description |
|---|---|
->status(int $code) |
Set HTTP status code |
->header(string $name, string $value) |
Add a response header |
->withHeaders(array $headers) |
Add multiple headers |
->cache(string $directive) |
Set Cache-Control header |
->noCache() |
Send no-store / no-cache / must-revalidate headers |
->json(mixed $data) |
(terminal) Send JSON + exit |
->html(string $content) |
(terminal) Send HTML + exit |
->text(string $content) |
(terminal) Send plain text + exit |
->redirect(string $url, int $code = 302) |
(terminal) Send redirect + exit |
->download(string $filePath, string $filename = '') |
(terminal) Stream file download + exit |
->downloadContent(string $content, string $filename, string $contentType) |
(terminal) Stream string as download + exit |
Response::toJson(['ok' => true], 201);
Response::toRedirect('/dashboard');
Response::toHtml(view('home', $data)->layout('app'));
Response::notFound('Custom 404 message');Namespace: App\Core\Session
File: app/Core/Session.php
Session wrapper with flash message support and CSRF token management.
Session::start(); // Start or resume (called automatically on first use)
Session::destroy(); // Full logout — destroy all data
Session::regenerate(); // Rotate session ID (call after login)
Session::id(); // Return current session ID stringSession::set('user_id', 42);
Session::get('user_id'); // 42
Session::get('missing', 'fallback'); // 'fallback'
Session::has('user_id'); // true
Session::forget('user_id');
Session::flush(); // Clear all data, keep session aliveFlash data survives exactly one request (set → redirect → read → gone).
// Set (survives to the next request)
Session::flash('success', 'Profile saved!');
Session::flash('error', 'Something went wrong.');
redirect('/dashboard');
// Read (in the next request — consumed immediately)
Session::getFlash('success'); // 'Profile saved!'
Session::getFlash('success'); // null (already consumed)
Session::hasFlash('error'); // bool
Session::reflash(); // Re-queue current flash for one more request$token = Session::csrf(); // generate or retrieve token
Session::verifyCsrf($submittedToken); // bool — timing-safe comparison
// In a form view:
echo csrf_field(); // <input type="hidden" name="_csrf_token" value="...">Namespace: App\Core\Database
File: app/Core/Database.php
Singleton PDO wrapper supporting MySQL and SQLite. Driver selected via DB_DRIVER in .env.
$db = Database::getInstance(); // singleton
Database::reset(); // destroy singleton (useful in tests)
$db->getDriver(); // 'mysql' | 'sqlite'
$db->isSqlite(); // bool
$db->isMysql(); // bool
$db->getPdo(); // raw PDO instance// All rows
$users = $db->select('SELECT * FROM users WHERE active = :a', ['a' => 1]);
// Single row — returns array|null
$user = $db->selectOne('SELECT * FROM users WHERE id = :id', ['id' => 42]);
// Single scalar value
$count = $db->scalar('SELECT COUNT(*) FROM users');// INSERT — returns last insert ID as string
$id = $db->insert('users', ['name' => 'Alice', 'email' => 'alice@example.com']);
// UPDATE — returns affected row count
$rows = $db->update('users', ['name' => 'Alice Smith'], ['id' => 42]);
// DELETE — returns deleted row count
$rows = $db->delete('users', ['id' => 42]);$stmt = $db->query('SELECT * FROM users WHERE role = :r', ['r' => 'admin']);
$rows = $stmt->fetchAll();$db->transaction(function () use ($db): void {
$db->insert('orders', ['user_id' => 1, 'total' => 99.99]);
$db->update('inventory', ['stock' => 0], ['product_id' => 7]);
});
// Manual
$db->beginTransaction();
$db->commit();
$db->rollback();$db->tableExists('users'); // bool — works on MySQL and SQLite
$db->statement('CREATE TABLE ...'); // execute DDL, returns boolDB_DRIVER=sqlite # 'mysql' | 'sqlite'
DB_PATH= # SQLite file path (auto-created; ':memory:' for in-memory)
DB_HOST=localhost # MySQL only
DB_PORT=3306 # MySQL only
DB_NAME=my_app # MySQL only
DB_USER=root # MySQL only
DB_PASS=secret # MySQL only
DB_CHARSET=utf8mb4 # MySQL onlyNamespace: App\Core\Validator
File: app/Core/Validator.php
Rule-based input validation engine. Returns typed errors; never touches headers or the session itself.
$v = Validator::make(Request::all(), [
'name' => 'required|min_length:2|max_length:80',
'email' => 'required|email',
'password' => 'required|min_length:8|confirmed',
'age' => 'nullable|numeric|min:18',
]);
if ($v->fails()) {
// $v->errors() — array<field, string[]>
// $v->error('email') — first error for 'email' or null
// $v->allErrors() — flat list<string>
// $v->old() — all declared-field values (for re-populating forms)
}
$data = $v->validated(); // throws if fails(); only declared fields$data = validate(Request::all(), ['name' => 'required', 'email' => 'required|email']);
// Flashes errors + old input and redirects back on failure.
// Returns validated array on success.| Rule | Description |
|---|---|
required |
Must be present and non-empty |
nullable |
May be absent / empty (disables required) |
string |
Must be a string |
numeric |
Must be numeric (int or float) |
integer |
Must be an integer |
boolean |
Must be "true", "false", "1", or "0" |
email |
Valid e-mail address |
url |
Valid URL (http/https) |
alpha |
Letters only |
alpha_num |
Letters and digits only |
alpha_dash |
Letters, digits, hyphens, underscores |
min:N |
Numeric value ≥ N |
max:N |
Numeric value ≤ N |
min_length:N |
String length ≥ N chars |
max_length:N |
String length ≤ N chars |
in:a,b,c |
Value must be one of the listed options |
not_in:a,b |
Value must NOT be in the list |
confirmed |
A matching {field}_confirmation field must exist |
regex:/pattern/ |
Must match the given regex |
date |
Must be parseable by strtotime() |
accepted |
Must be "yes", "on", "1", or "true" |
Validator::make($data, $rules, [
'email.required' => 'We need your email address.',
'email.email' => 'That doesn\'t look like a valid email.',
]);Namespace: App\Core\Cache
File: app/Core/Cache.php
File-based key/value cache with TTL, stored in storage/cache/.
Cache::set('key', $value, ttl: 3600); // store for 1 hour
Cache::get('key'); // mixed|null on miss/expiry
Cache::get('key', 'default'); // with fallback
Cache::has('key'); // bool
Cache::forget('key'); // delete one entry
Cache::flush(); // delete all entries
Cache::ttl('key'); // seconds remaining (0 = forever or missing)// Compute and cache for 5 minutes; served from cache on subsequent calls
$posts = Cache::remember('recent_posts', ttl: 300, callback: function () use ($db): array {
return $db->select('SELECT * FROM posts ORDER BY created_at DESC LIMIT 10');
});
Cache::rememberForever('site_config', fn () => $db->select('SELECT * FROM config'));// Same DB call within a request returns the cached result without hitting disk
$user = Cache::once("user:{$id}", fn () => $db->selectOne('SELECT * FROM users WHERE id = :id', ['id' => $id]));CACHE_TTL=0 # default TTL; 0 = never expires
CACHE_PATH= # override storage directoryNamespace: App\Core\Logger
File: app/Core/Logger.php
File-based PSR-3-inspired logger. Writes to storage/logs/{channel}-YYYY-MM-DD.log.
Logger::debug('Query executed', ['ms' => 12.4]);
Logger::info('User logged in', ['user_id' => 42]);
Logger::notice('Cache miss', ['key' => 'posts']);
Logger::warning('Disk space low', ['free_mb' => 512]);
Logger::error('Payment failed', ['order' => $id]);
Logger::critical('DB unreachable', ['host' => 'db.example.com']);
Logger::alert('Disk full');
Logger::emergency('Service down');Logger::channel('payments')->error('Charge declined', ['amount' => 99.99]);
Logger::channel('audit')->info('Admin deleted user', ['target_id' => 7]);
Logger::channel('db')->debug('Slow query', ['ms' => 850, 'sql' => $sql]);log_debug('Cache miss', ['key' => $k]);
log_info('User login', ['id' => $id]);
log_warning('Slow query', ['ms' => 900]);
log_error('Webhook fail', ['status' => 500]);LOG_CHANNEL=app # default channel name
LOG_LEVEL=debug # minimum level: debug|info|notice|warning|error|critical|alert|emergency
LOG_PATH= # override log directoryNamespace: App\Core\Str
File: app/Core/Str.php
Immutable string utility library. All methods are pure static.
Str::studly('user_profile') // 'UserProfile'
Str::camel('user_profile') // 'userProfile'
Str::snake('UserProfile') // 'user_profile'
Str::kebab('UserProfile') // 'user-profile'
Str::title('hello world') // 'Hello World'Str::slug('Hello, World!') // 'hello-world'
Str::slug('über straße') // 'uber-strase'
Str::slug('foo bar', '_') // 'foo_bar'Str::truncate($text, 100) // 'Lorem ipsum…'
Str::truncate($text, 100, '...') // 'Lorem ipsum...'
Str::words($text, 20) // first 20 words + '…'Str::mask('alice@example.com', '*', 3) // 'ali**@example.com'
Str::mask('4111111111111111', 'x', 0, 4) // 'xxxxxxxxxxxx1111'Str::random(32) // URL-safe random string [A-Za-z0-9\-_]
Str::uuid() // UUID v4Str::contains('foobar', 'oba') // true
Str::containsAny('foobar', ['x','o']) // true
Str::startsWith('foobar', 'foo') // true
Str::endsWith('foobar', 'bar') // true
Str::between('<b>hi</b>', '<b>', '</b>') // 'hi'Str::length('héllo') // 5 (UTF-8 aware)
Str::byteSize('héllo') // 7 (raw bytes)
Str::pad('5', 3, '0', STR_PAD_LEFT) // '005'
Str::repeat('ab', 3) // 'ababab'
Str::swap(['foo'=>'bar'], 'foo and foo') // 'bar and bar'
Str::isJson('{"ok":true}') // true
Str::formatBytes(1_048_576) // '1 MB'Namespace: App\Core\HttpException
File: app/Core/HttpException.php
Carries an HTTP status code and optional headers. Thrown by abort(), caught by ExceptionHandler.
// Via global helper
abort(404, 'Page not found.');
abort(403);
// Direct construction
throw new HttpException(429, 'Too many requests.', ['Retry-After' => '60']);
// Factory shortcuts
throw HttpException::notFound();
throw HttpException::forbidden('Admins only.');
throw HttpException::unauthorized();
throw HttpException::unprocessable('Validation failed.');
throw HttpException::tooManyRequests();Namespace: App\Core\ExceptionHandler
File: app/Core/ExceptionHandler.php
Registered once in public/index.php. Hooks set_exception_handler, set_error_handler, and register_shutdown_function.
ExceptionHandler::register(); // called once in bootstrapRendering behaviour:
| Exception type | APP_DEBUG=true |
APP_DEBUG=false |
|---|---|---|
HttpException |
Clean HTTP error page (code + message) | Same |
| Any other | Debug page with source + stack trace | Generic 500 page |
| Any (API request) | JSON with message + trace | JSON: error + code |
API detection: URI starts with /api/ OR Accept: application/json OR X-Requested-With: XMLHttpRequest.
All helpers are defined in app/Core/helpers.php and available globally after bootstrap.
e(mixed $value): string // HTML text content — use by default
attr(mixed $value): string // HTML attribute values
raw(string $html): string // Trusted/pre-sanitised HTML (opt-out of escaping)
js(mixed $value): string // Safe inline <script> JSON valueview(string $view, array $data = []): View // fluent View instance
partial(string $view, array $data = []): string // render sub-view fragmentabort(int $code, string $message = ''): never // throw HttpException
redirect(string $url, int $code = 302): never // Location header + exit
json(mixed $data, int $status = 200): never // JSON response + exitasset(string $path): string // /css/app.css?v=1718000000 (cache-busted)
// Set APP_ASSET_URL in .env for CDN: https://cdn.example.com/css/app.css?v=...csrf_token(): string // current CSRF token
csrf_field(): string // <input type="hidden" name="_csrf_token" value="...">
old(string $key, mixed $default = ''): mixed // previously-flashed form value
errors(string $field): ?string // first validation error for $fieldvalidate(array $data, array $rules, array $messages = [], string $redirectTo = ''): array
// Runs Validator::make(); flashes errors + old input and redirects back on failure.
// Returns the validated data array on success.env(string $key, ?string $default = null): ?string // shorthand for Config::get()log_debug(string $message, array $context = []): void
log_info(string $message, array $context = []): void
log_warning(string $message, array $context = []): void
log_error(string $message, array $context = []): voidcache(string $key): mixed // get
cache(string $key, $value, int $ttl): mixed // setdump(mixed ...$vars): void // pretty-print, continue execution
dd(mixed ...$vars): never // pretty-print, die immediately