Skip to content

Jagepard/Rudra-Framework

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

409 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Maintainability CodeFactor PHP Version License

Rudra Framework

A lightweight, transparent PHP framework built on the principles of simplicity and clarity.

~15 MB Β· Lightweight Query Builder Β· No hidden dependencies Β· No magic Β· PHP 8.3 Attributes Β· Porto Architecture

Philosophy

Rudra follows the KISS principle β€” every tool is visible, every dependency is explicit, and every layer does exactly one thing. No "magic" happens behind the scenes: if you see it in the code, that's all there is.

  • Transparency β€” no hidden behavior, no implicit side effects
  • Simplicity β€” minimal surface area, easy to learn and extend
  • Explicit over implicit β€” dependencies are declared, not guessed
  • Modern PHP β€” built from the ground up on PHP 8.3+ features (attributes, typed constants, fibers)

Who is this for?

Rudra is built for developers who:

  • Prefer explicit code over framework magic β€” you want to see exactly what SQL is executed, what routes are registered, and what dependencies are injected
  • Value cognitive clarity β€” you need a framework that stays out of your way and doesn't require memorizing hidden conventions
  • Work on small-to-medium projects β€” you want structure and best practices without the overhead of enterprise-grade frameworks
  • Want full control β€” you believe that understanding your tools is more important than having hundreds of features out of the box

If you're looking for a batteries-included framework with built-in ORM, pre-configured queue workers, and ready-made broadcast drivers β€” Rudra is not for you.
If you want a lightweight, transparent foundation with simple primitives that you can extend exactly as needed β€” you're in the right place.

Requirements

  • PHP 8.3 or higher
  • PDO extension
  • Composer (for installation)

πŸš€ Getting started

Via Composer (stable)

composer create-project --prefer-dist rudra/framework newapp

Via Composer (dev)

composer create-project --prefer-dist --stability=dev rudra/framework newapp

Via Git + DDEV (stable)

git clone git@github.com:Jagepard/Rudra-Framework.git
cd Rudra-Framework
git checkout v26.7.27
ddev start
ddev launch

Via Git + DDEV (dev)

git clone git@github.com:Jagepard/Rudra-Framework.git
cd Rudra-Framework
ddev start
ddev launch

πŸ”‘ Secret Keys

Secret keys are automatically generated during Composer installation for all environments (local, ddev, production). If you need to regenerate them (e.g., after cloning the repository), simply run:

php rudra secret

βš™οΈ Configuration

Rudra uses environment-specific configuration files. The framework automatically loads the appropriate file based on your environment:

Environment Config File
DDEV config/setting.ddev.yml
Local development config/setting.local.yml
Production config/setting.production.yml

These files contain database credentials, container paths, debug settings, and other environment-specific values.

πŸ—„οΈ Database Configuration

Rudra uses a simple, explicit YAML configuration for database connections. There is no hidden magic or auto-guessing: you define exactly which driver and credentials to use for each environment.

Local Development (config/setting.local.yml):

database:
    dsn: mysql:dbname=db;host=127.0.0.1
    # dsn: pgsql:host=127.0.0.1;port=5432;dbname=db;
    # dsn: sqlite:/path-to/app/Ship/Data/rudra.sqlite
    username: jagepard
    password: password

DDEV Environment (config/setting.ddev.yml):

database:
    dsn: mysql:dbname=db;host=db;port=3306
    # dsn: pgsql:host=db;port=5432;dbname=db;
    # dsn: sqlite:/path-to/app/Ship/Data/rudra.sqlite
    username: root
    password: root
    # For pgsql in DDEV:
    # username: db
    # password: db

Production (config/setting.production.yml):

database:
    dsn: mysql:dbname=db;host=127.0.0.1 # Replace with your production host
    username: your_prod_user
    password: your_prod_password

πŸ’‘ Tip: The framework's Query Builder (Rudra-Model) automatically reads these settings based on the active environment. Simply uncomment the dsn line for your preferred database engine (MySQL, PostgreSQL, or SQLite).

🌐 Web Environment (auto-detected)

For web requests, Rudra automatically detects the environment in index.php:

$env = match (true) {
    getenv('IS_DDEV_PROJECT') === 'true' => 'ddev',
    php_sapi_name() === 'cli-server' => 'local',
    default  => 'production',
};

πŸ–₯️ Console Environment

For CLI commands (like php rudra migrate), the framework cannot auto-detect the environment as it does in index.php. Instead, it relies on the app_env.php file to determine which configuration file to load (e.g., 'local' β†’ config/setting.local.yml).

πŸš€ Quick Setup (Recommended)

Use the built-in interactive command to set or change your environment. It will automatically create or update the app_env.php file for you, preserving all comments and structure:

php rudra env

Follow the interactive prompt to select 1 (local), 2 (ddev), or 3 (production).

βš™οΈ Manual Setup

If you prefer, you can manually create or edit the app_env.php file in your project root:

<?php
// Do NOT commit this file to Git (.gitignore)
return 'local'; // ← Set to: 'local', 'ddev', or 'production'

ℹ️ Note: If app_env.php is missing (e.g., if you cloned the repository directly instead of using composer create-project), the framework defaults to 'local' for CLI commands. This is safe for local development but should not be relied upon in production β€” always create the file manually or set the APP_ENV server variable explicitly.

πŸ’‘ Tip: When adding new containers or settings during development, update config/setting.local.yml. For production deployment, copy the relevant sections to config/setting.production.yml.

Run the development server

php rudra serve

πŸš€ Creating Your First Page

Let's create a simple /hello/:name page that greets the user. This example shows the full flow: container β†’ controller β†’ route β†’ browser.

1. Create a container

php rudra make:container
Enter container name: web

This creates the App\Containers\Web\ namespace and registers it in config/setting.local.yml.

πŸ’‘ Don't forget to also register it in config/setting.ddev.yml and config/setting.production.yml if needed.

2. Create a controller

php rudra make:controller
Enter controller name: hello
Enter container: web

This generates App/Containers/Web/Controller/HelloController.php.

3. Define the route and action

Open the controller and add a method with the #[Routing] attribute:

<?php

namespace App\Containers\Web\Controller;

use App\Containers\Web\WebController;
use Rudra\Router\Attribute\Routing;
use Rudra\Container\Facades\Request;

class HelloController extends WebController
{
    #[Routing(url: 'hello/:name', method: 'GET')]
    public function greet(string $name): void
    {
        echo "<h1>Hello, {$name}!</h1>";
        echo "<p>Your lucky number is: " . random_int(1, 100) . "</p>";
    }
}

That's it. The route is registered automatically via the attribute β€” no separate routes.php needed.

4. Open in browser

http://127.0.0.1:8000/hello/world

You should see: Hello, world! Your lucky number is: 42


Alternative: routes.php

If you prefer to keep routes separate from controllers, create (or edit) App/Containers/Web/routes.php:

<?php

use Rudra\Router\RouterFacade as Router;
use App\Containers\Web\Controller\HelloController;

Router::get('hello/:name', [HelloController::class, 'greet']);

⚠️ When using routes.php for manual routing, make sure the file returns an empty array to disable automatic attribute-based route registration:

return [];

Alternative: closure route

For quick prototyping, you can even skip the controller entirely:

Router::get('hello/:name', function (string $name) {
    echo "Hello, {$name}!";
});

Then open http://127.0.0.1:8000 in your browser.

πŸ“¦ Ecosystem Components

Rudra is built on a modular component architecture. Each component is a standalone Composer package β€” use only what you need, or install everything via rudra/framework.

Component Description Package
πŸ“¦ Container DI container with 5 binding patterns (string, object, factory string, factory object, closure) rudra/container
🧭 Router HTTP router with PHP 8 attributes, middleware pipeline, REST resources, method spoofing rudra/router
🎯 Controller MVC controller abstraction with lifecycle hooks (init, before, after) rudra/controller
πŸ’Ύ Model Lightweight Query Builder + Repository pattern (not a heavy ORM) rudra/model
πŸ” Auth Authentication, session management, RBAC, "Remember Me" rudra/auth
πŸ”— OAuthClient OAuth 2.0 client (Yandex, VK, custom providers) rudra/oauth-client
🎧 EventDispatcher Events system with Listeners (pub/sub) and Observers (stateless notifications) rudra/event-dispatcher
βœ… Validation Fluent validation with CSRF, sanitization, custom rules, field aliases rudra/validation
πŸ–ΌοΈ View Template engine (PHP + Twig support) with file-based caching rudra/view
πŸ›‘οΈ Exception HTTP error handling with custom error pages and DebugBar integration rudra/exception
πŸ”€ Redirect HTTP redirection with status codes (1xx-5xx) rudra/redirect
πŸ“„ Pagination SQL OFFSET/LIMIT calculator with page links generator rudra/pagination
πŸ“œ Annotation Metadata reader for PHP 8 attributes + legacy docblock annotations rudra/annotation
πŸ–₯️ Cli CLI component with command routing rudra/cli
πŸ“ Docs Auto-generates Markdown API documentation from source code rudra/docs

πŸ”‘ Key Features in Action

πŸ” Authentication & RBAC

use Rudra\Auth\AuthFacade as Auth;

// Registration
$user = [
    "email"    => "user@email.com",
    "password" => Auth::bcrypt("password")
];

// Authentication (plain password vs hash from DB)
Auth::authentication($user, "password", ["admin/dashboard", "login"]);

// Authorization check (redirects to 'login' if not authenticated)
if (!Auth::authorization(null, "login")) {
    exit;
}

// Role-Based Access Control
if (!Auth::roleBasedAccess($currentRole, "editor", "error/403")) {
    exit;
}

🎧 Event Dispatcher (Listeners + Observers)

use Rudra\EventDispatcher\EventDispatcherFacade as Dispatcher;

// Listeners β€” pub/sub with payload
Dispatcher::addListener('user.registered', [UserListener::class, 'onRegister']);
Dispatcher::dispatch('user.registered', ['id' => 1, 'email' => '...']);

// Observers β€” stateless notifications (no payload)
Dispatcher::attachObserver('before', [AuditObserver::class, 'onEvent']);
Dispatcher::notify('before');

βœ… Fluent Validation

use Rudra\Container\Facades\Request;
use Rudra\Container\Facades\Session;
use Rudra\Validation\ValidationFacade as V;

$fields    = Request::post()->all();
$processed = [
    'name'        => V::sanitize($fields['name'])->required()->min(3)->max(50)->run(),
    'email'       => V::email($fields['email'])->run(),
    'age'         => V::sanitize($fields['age'])->integer()->between(18, 100)->run(),
    'description' => V::set($fields['description'])->run(), // without validation
    'csrf'        => V::sanitize($fields['csrf'])->csrf(Session::get('csrf_token'))->run(),
];

if (V::approve($processed)) {
    $data = V::getValidated($processed, ["csrf", "_method"]);
} else {
    $errors = V::getErrors($processed);
}

🧭 RESTful Resources

Registers standard CRUD routes with explicit plural and singular URL patterns. No magic pluralization β€” you define exactly what the URLs look like.

$router->resource('api/users', 'api/user', UserController::class);

This creates the following routes:

Method URL Controller Method Description
GET api/users index List all users
GET api/user/:id read Get single user
POST api/users create Create new user
PUT api/user/:id update Full update of user
PATCH api/user/:id update Partial update of user
DELETE api/user/:id delete Delete user

The default action names are ['index', 'read', 'create', 'update', 'delete'].

Custom Method Names

You can override the default action names by passing a custom array of 5 methods:

$router->resource('api/posts', 'api/post', PostController::class, [
    'actionIndex',   // GET    api/posts       β€” list all posts
    'actionView',    // GET    api/post/:id    β€” get single post
    'actionAdd',     // POST   api/posts       β€” create new post
    'actionUpdate',  // PUT/PATCH api/post/:id β€” update post
    'actionDrop'     // DELETE api/post/:id    β€” delete post
]);

The array order is fixed: [index, read, create, update, delete].

πŸ–ΌοΈ View with Caching

use Rudra\View\ViewFacade as View;

// Cache-first strategy with fallback
echo View::cache(['mainpage', "+1 day"]) ?? View::render(["layout", "mainpage"], [
    'content' => View::cache(["page_{$slug}", "+1 day"]) 
        ?? View::view(["page", "page_{$slug}"], ['foo' => 'bar']),
]);

// Or with helpers
data(['content' => cache(['mainpage']) ?? view(['index', 'mainpage'])]);
render('layout', data());

πŸ’Ύ Data Access Layer (Rudra-Model)

Rudra includes a lightweight, transparent data access layer β€” not a heavy ORM, but a simple Query Builder with Repository pattern.

Architecture: Entity β†’ Model β†’ Repository

A predictable delegation chain with automatic fallback:

  • Entity β€” entry point, defines the table name
  • Model β€” business logic layer (optional)
  • Repository β€” data access layer (optional, falls back to base CRUD)

If you don't create a Model or Repository, the base Repository handles standard CRUD automatically.

Zero Boilerplate

namespace App\Containers\User\Entity;

use Rudra\Model\Entity;

class User extends Entity
{
    public static ?string $table = 'users';
}

// Usage β€” no Model or Repository needed:
// These methods are provided by the base Repository automatically
$users = User::getAll();
$user  = User::find(1);
User::create(['name' => 'John', 'email' => 'john@example.com']);

Custom Repository Logic

namespace App\Containers\User\Repository;

use Rudra\Model\Repository;

class UserRepository extends Repository
{
    public function findActiveUsers(): array
    {
        return $this->qBuilder("SELECT * FROM {$this->table} WHERE active = 1");
    }
}

// Automatically routed:
$activeUsers = User::findActiveUsers();

Fluent Query Builder

use Rudra\Model\QBFacade as QB;

$query = QB::select('id, name, email')
    ->from('users')
    ->where('status = :status')
    ->and('role = :role')
    ->orderBy('created_at DESC')
    ->limit(10)
    ->get(); // Returns SQL query string

// Execute via Repository:
$results = User::qBuilder($query, ['status' => 'active', 'role' => 'admin']);

Schema Builder

use Rudra\Model\Schema;

Schema::create('users', function ($table) {
    $table->integer('id', autoincrement: true)
          ->string('name')
          ->string('email')
          ->text('bio', 'NULL')
          ->createdAt()
          ->updatedAt()
          ->primaryKey('id'); // Explicitly declare primary key
})->execute();

File-Based Caching

Simple, reliable JSON file caching β€” no Redis/Memcached required:

// Cache the result of User::getAll() method
$users = User::cache(['getAll']);

// Cache Post::findBy('category', 'news') with 1 hour TTL
$posts = Post::cache(['findBy', ['category', 'news']], '+1 hour');

// Auto-clears on create/update/delete

Why Not a Heavy ORM?

  • No magic β€” no hidden queries, no lazy loading surprises
  • No hidden dependencies β€” direct PDO access when needed
  • Predictable β€” you see exactly what SQL is executed
  • Fast β€” no reflection overhead, no entity hydration complexity
  • Simple β€” if you know SQL, you know Rudra-Model

πŸ› οΈ CLI Tools

Rudra ships with a rich set of CLI utilities following the Porto architecture. Run php rudra help to see the full list.

πŸš€ Launch & Help

Command Description
php rudra serve Start the built-in dev server (alias: run)
php rudra help List all available commands

πŸ› οΈ Generators (make:*)

Command Description
php rudra make:container πŸ“¦ Scaffold a new Porto container
php rudra make:controller 🧭 Generate a controller class
php rudra make:contract πŸ“œ Generate an interface (contract)
php rudra make:factory 🏭 Generate a factory class
php rudra make:listener πŸ‘‚ Generate an event listener
php rudra make:middleware πŸ›‘οΈ Generate a middleware class
php rudra make:migration πŸ—„οΈ Generate a database migration
php rudra make:model 🧱 Generate Entity + Repository pair
php rudra make:observer πŸ‘οΈ Generate an event observer
php rudra make:seed 🌱 Generate a database seeder
php rudra make:docs πŸ“ Generate documentation from source

πŸ—„οΈ Database

Command Description
php rudra migrate πŸ›€οΈ Run pending migrations (with duplicate protection)
php rudra seed 🌾 Execute seeders (with duplicate protection)

πŸ” Debug

Command Description
php rudra debug:router πŸ—ΊοΈ List all registered routes
php rudra debug:router:container πŸ” List routes for a specific container
php rudra debug:listeners 🎧 List registered event listeners
php rudra debug:observers πŸ”­ List registered event observers

πŸ” Security

Command Description
php rudra bcrypt πŸ” Hash a password with bcrypt
php rudra secret πŸ”‘ Generate a cryptographic secret (APP_KEY)

🧹 Maintenance & Utils

Command Description
php rudra cache:clear 🧹 Clear application cache
php rudra convert:array-to-yml πŸ”„ Convert a PHP array config to YAML (alias: to:yml)

License

This project is licensed under the Mozilla Public License 2.0 (MPL-2.0) β€” a free, open-source license that:

  • βœ… Requires preservation of copyright and license notices
  • βœ… Allows commercial and non-commercial use
  • βœ… Requires that modifications to original files remain open under MPL-2.0
  • βœ… Permits combining with proprietary code in larger works

πŸ“„ Full license text: LICENSE
🌐 Official MPL-2.0 page: https://mozilla.org/MPL/2.0/

Releases

Packages

Used by

Contributors

Languages