Introduction & Philosophy
phpLiteCore is a modern, lightweight, and fast PHP framework for building web applications of any size.
The framework follows a clear design philosophy as defined in its constitution (Section 2):
- Front Controller: All requests go through a single
public/index.phpfile, withpublic/.htaccessdirecting everything to it. Thepublic/directory should be your web server's document root. - Strict Separation of Concerns (MVC): Logic is completely separated from presentation. Controllers fetch and prepare data, Views only display it.
- Hybrid Active Record Pattern: A powerful way to interact with the database, combining the simplicity of Active Record with the flexibility of a Query Builder.
Core Structure
The framework uses a clear file structure to separate responsibilities (Constitution Section 3 & 4):
/app: Contains your application-specific code (Controllers,Models)./src: Contains the framework's core code (Database,Routing,Lang, etc.)./resources: Source files before compilation (js,scss,lang)./public: The web server's document root (web-accessible directory). Contains the front controller (index.php), rewrite rules (.htaccess), and built assets (assets/)./views: All template files (layouts,partials,themes)./routes: Theweb.phpfile for defining routes.
Routing System
All web routes are defined in the routes/web.php file. This file registers routes to the $router object.
Router.php matches the current URL against the registered routes and dispatches the appropriate controller action.
Route Examples
Basic Routes (GET / POST):
// routes/web.php
$router->get('/', ['HomeController', 'index']);
$router->get('/about', ['AboutController', 'index']);
$router->post('/posts', ['PostController', 'store']);
Dynamic Routes (and Order):
/posts/create) must always be defined before dynamic routes (like /posts/{id}). Otherwise, the router will mistakenly treat "create" as an ID value.
// routes/web.php (Correct Example)
// (Correct) Static route first
$router->get('/posts/create', ['PostController', 'create']);
// (Correct) Dynamic route second
$router->get('/posts/{id}', ['PostController', 'show']);
Controllers
Controllers reside in app/Controllers and are responsible for handling request logic (Constitution Section 2 - MVC).
All controllers should extend BaseController. This gives them access to the application instance ($this->app) and the template rendering method $this->view().
Full Example: HomeController.php
This is a perfect example demonstrating "Strict Separation of Concerns" (Constitution Section 2).
<?php
namespace App\Controllers;
use App\Models\User;
class HomeController extends BaseController
{
public function index(): void
{
// 1. Business Logic
$user = User::find(1);
// 2. Prepare ALL translated variables for the view
$pageTitle = $this->app->translator->get('messages.home.page_title');
$heroSubtitle = $this->app->translator->get(
'messages.home.hero_subtitle',
['name' => $user->name ?? $this->app->translator->get('messages.guest')]
);
$versionLabel = $this->app->translator->get('messages.home.version_label');
// 3. Render the view, passing all final translated strings.
$this->view('home', compact(
'pageTitle',
'heroSubtitle',
'versionLabel'
));
}
}
Models & Database
The framework uses the **"Hybrid Active Record"** pattern (Constitution Section 2).
This is achieved by having all models (like User.php and Post.php) extend src/Database/Model/BaseModel.php.
Due to the extensive nature of this component, a separate, detailed guide is provided.
Go to: Comprehensive Query Builder GuideThe new guide explains in detail how to perform:
- Basic Retrievals (
find,all,first). - All types of
WHEREclauses (where,orWhere,whereIn,whereBetween, Nested). LIKEclauses (whereStarts,whereHas).- Aggregates and Ordering (
count,orderBy,limit). - Pagination (
paginate). - Creation, Updates, and Deletion (
save,delete).
Views & Templates
Views follow the "Strict Separation" rule (Constitution Section 2 - MVC):
- Only responsible for displaying variables passed to them.
- Absolutely no logic (like translator calls or DB access) allowed within views.
- Always use
<?= htmlspecialchars(...) ?>when outputting variables to prevent XSS.
Example: View File views/themes/default/post.php
$post->title (object access) because it receives an Object from PostController.
<article>
<h1><?= htmlspecialchars($post->title) ?></h1>
<p><?= nl2br(htmlspecialchars($post->body)) ?></p>
<hr>
<!-- Uses translated variables passed from the controller -->
<small><?= htmlspecialchars($publishedOnText) ?> <?= date('Y-m-d', strtotime($post->created_at)) ?></small>
</article>
<a href="/posts"><?= htmlspecialchars($backLinkText) ?></a>
Translation System (i18n)
Translation is **mandatory** for all user-facing text (Constitution Section 1.5).
The framework uses a modular system with lazy loading.
"Dot Notation" Mechanism
To access a translated string, use $translator->get('filename.key').
Example 1: Nested Key
// Key 'page_title' inside 'home' array in 'messages.php'
$title = $this->app->translator->get('messages.home.page_title');
// $title = "Welcome to phpLiteCore"
Example 2: Lazy Loading
// The translator will automatically load "validation.php"
$error = $this->app->translator->get('validation.required');
// $error = "The {{field}} field is required."
Example 3: Passing Placeholders
// Key: 'messages.posts.not_found' => 'Post with ID {{id}} not found.'
$id = urldecode($id_from_url);
$message = $this->app->translator->get(
'messages.posts.not_found',
['id' => $id]
);
// $message = "Post with ID {decoded_id} not found."
Input Validation
The framework provides a simple Validator class (in src/Validation/Validator.php) which is now connected to the translation service.
Example: From PostController
// Inside the store() method in PostController
try {
$rules = [
'title' => 'required|min:5',
'body' => 'required|min:10',
];
$validatedData = Validator::validate($_POST, $rules);
// (Success) Create the post...
$post = new Post($validatedData);
$post->save();
Response::redirect('/posts');
} catch (ValidationException $e) {
// (Failure) $e->getErrors() will contain the translated errors
http_response_code(422);
echo json_encode(['errors' => $e->getErrors()]);
}
Error Handling
Error handling is managed by src/Bootstrap/ErrorHandler.php (Constitution Section 2).
Production Environment (ENV=production)
The system hides details, logs the error, emails the developer, and displays a generic, translated error page.
404 Not Found Errors
Trigger a translated 404 page from any controller using Response::notFound().
// Use the default translated 404 message
Response::notFound();
// Use a custom (must be pre-translated) message
$message = $this->app->translator->get('messages.posts.not_found', ['id' => $id]);
Response::notFound($message);
Asset Management
The framework relies on Webpack, NPM, and SCSS (Constitution Section 2).
Dependencies are defined in package.json, and build settings are in webpack.config.js.
Workflow
- Modify source files:
resources/js/app.jsorresources/scss/app.scss. - Run
npm run dev(for development) ornpm run build(for production). - Webpack builds the final files into
public/assets/app.jsandpublic/assets/app.css.
<!-- views/partials/header.php -->
<head>
<meta charset="UTF-8">
<title><?= htmlspecialchars($pageTitle ?? 'phpLiteCore') ?></title>
<!-- Include the built CSS file -->
<link rel="stylesheet" href="/assets/app.css">
</head>