Wepesi is a lightweight PHP framework for building web applications using MVC principles. It is designed to be simple, flexible, and easy to get started with — no heavy configuration required.
Features:
- Routing (GET, POST, PUT, PATCH, DELETE)
- Controllers
- Simple ORM (no migrations)
- Middleware
- Validation
- View rendering
- Sessions, JWT, and more — all built in
Requires PHP >= 8.0. Create a new project with:
composer create-project wepesi/wepesi my-projectThen open my-project/ in your web server root and configure your .env file (copy .env.example to .env). At minimum set the database credentials:
DB_HOST=127.0.0.1
DB_NAME=your_database
DB_USER=your_user
DB_PASSWORD=your_password
DB_PORT=3306
APP_ENV=dev
LANG=en-
Download or clone the repository:
git clone https://github.com/kivudesign/Wepesi.git my-project
-
Copy the environment file and fill in your settings:
cp .env.example .env
-
Place the project folder in your web server's document root:
- WAMP:
C:/wamp/www/my-project - XAMPP:
C:/xampp/htdocs/my-project - Linux/macOS Apache/Nginx: configure a virtual host pointing to the project root
- WAMP:
-
Navigate to
http://localhost/my-projectin your browser.
No extra modules or build steps are needed. The
.htaccessfile handles URL rewriting automatically.
my-project/
├── app/
│ ├── Controller/ # Your controllers
│ ├── Models/ # Your entity/model classes
│ ├── Middleware/ # Request middleware
│ ├── Routes/
│ │ ├── web.php # Web routes
│ │ └── api.php # API routes
│ └── Views/ # HTML view templates
├── config/ # Database and autoload configuration
├── src/ # Framework core (do not modify)
├── .env # Environment variables
├── .htaccess # URL rewriting
└── index.php # Application entry point
Routes are defined in app/Routes/web.php (web) or app/Routes/api.php (API). The $router object is available via $app->router().
// Closure handler
$router->get('/', function () {
echo 'Hello World!';
});
$router->post('/submit', function () {
// handle POST request
});$router->get('/users/:id', function ($id) {
echo 'User ID: ' . $id;
});Constrain the parameter to digits only using ->with():
$router->get('/users/:id', function ($id) {
echo 'User ID: ' . $id;
})->with('id', '[0-9]+');use App\Controller\UserController;
$router->get('/users', [UserController::class, 'index']);
$router->post('/users', [UserController::class, 'store']);$router->group('/admin', function () use ($router) {
$router->get('/', [AdminController::class, 'dashboard']);
$router->get('/users', [AdminController::class, 'users']);
});use App\Middleware\AuthMiddleware;
$router->get('/dashboard', [DashboardController::class, 'index'])
->middleware([AuthMiddleware::class, 'handle']);Routes defined inside $router->api() are automatically prefixed with /api:
use Wepesi\Core\Routing\Router;
$router->api(function (Router $router) {
$router->group('/v1', function (Router $router) {
$router->get('/users', [UserController::class, 'index']);
});
});
// Resolves to: /api/v1/usersuse Wepesi\Core\Http\Response;
$router->set404(function () {
Response::send('Route not found', 404);
});Controllers live in app/Controller/ and extend Wepesi\Core\Http\Controller.
<?php
namespace App\Controller;
use Wepesi\Core\Http\Controller;
use Wepesi\Core\Http\Input;
use Wepesi\Core\Http\Redirect;
use Wepesi\Core\Session;
class UserController extends Controller
{
public function __construct()
{
parent::__construct();
}
// Render a view
public function index(): void
{
$this->view->display('users/index');
}
// Read POST input and redirect
public function store(): void
{
$name = Input::post('name');
$email = Input::post('email');
// ... save to database ...
Redirect::to('/users');
}
// Store a value in session
public function setLocale(): void
{
Session::put('lang', Input::post('lang'));
Redirect::to('/');
}
}Pass data to a view:
public function show(int $id): void
{
$user = (new UserEntity())->where(['id' => $id])->findOne();
$this->view->assign('user', $user);
$this->view->display('users/show');
}Wepesi includes a simple ORM built around Entity classes. Create an entity in app/Models/ by extending Wepesi\Core\Database\Entity.
Database configuration is read from your
.envfile (DB_HOST,DB_NAME,DB_USER,DB_PASSWORD,DB_PORT). See Installation for details.
<?php
namespace App\Models;
use Wepesi\Core\Database\Entity;
class UserEntity extends Entity
{
// Without getName(), the framework lowercases the class short-name as the
// table name (e.g. "UserEntity" → "userentity"). Always override getName()
// to map the entity to the correct table:
protected function getName(): string
{
return 'users';
}
}$entity = new UserEntity();
// Fetch all users
$users = $entity->findAll();
// Fetch with conditions
$admins = $entity->where(['role' => 'admin'])->findAll();
// Fetch a single record
$user = $entity->where(['id' => 1])->findOne();
// Limit and order
$recent = $entity->orderby('created_at')->desc()->limit(10)->findAll();
// Count rows
$total = $entity->count();$entity = new UserEntity();
$result = $entity->save([
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'role' => 'member',
]);$entity = new UserEntity();
$result = $entity
->where(['id' => 42])
->fields(['name' => 'Jane Smith'])
->update();$entity = new UserEntity();
$result = $entity->where(['id' => 42])->delete();class PostEntity extends Entity {}
class UserEntity extends Entity
{
public function posts(): object
{
return $this->hasMany(new PostEntity());
}
}Use Wepesi\Core\Validation\Validate together with the rule classes to validate incoming data.
use Wepesi\Core\Validation\Validate;
use Wepesi\Core\Validation\Rules\StringRules;
use Wepesi\Core\Validation\Rules\NumberRules;
$data = [
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'age' => 25,
];
$validate = new Validate();
$validate->check($data, [
'name' => (new StringRules())->required()->min(2)->max(100)->generate(),
'email' => (new StringRules())->required()->email()->generate(),
'age' => (new NumberRules())->required()->positive()->generate(),
]);
if ($validate->passed()) {
// Validation succeeded — proceed with business logic
} else {
// Validation failed — inspect errors
$errors = $validate->errors();
}| Class | Common methods |
|---|---|
StringRules |
required(), min(n), max(n), email(), url(), match('field'), unique('table') |
NumberRules |
required(), min(n), max(n), positive() |
ArrayRules |
required(), string(), number(), structure([]) |
BooleanRules |
required() |
DateRules |
required(), min('date'), max('date') |
use Wepesi\Core\Http\Input;
use Wepesi\Core\Http\Response;
use Wepesi\Core\Validation\Validate;
use Wepesi\Core\Validation\Rules\StringRules;
public function store(): void
{
$data = [
'name' => Input::post('name'),
'email' => Input::post('email'),
];
$validate = new Validate();
$validate->check($data, [
'name' => (new StringRules())->required()->min(2)->generate(),
'email' => (new StringRules())->required()->email()->generate(),
]);
if (!$validate->passed()) {
Response::send(['errors' => $validate->errors()], 422);
return;
}
// ... save data ...
}Happy building! 🚀