PHPackages                             alexius-byte/rider-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. alexius-byte/rider-php

ActiveLibrary[Framework](/categories/framework)

alexius-byte/rider-php
======================

Mini PHP Framework

1.7(1w ago)026proprietaryPHPPHP ^8.1

Since May 20Pushed 2d agoCompare

[ Source](https://github.com/alexius-byte/rider-php)[ Packagist](https://packagist.org/packages/alexius-byte/rider-php)[ RSS](/packages/alexius-byte-rider-php/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)DependenciesVersions (9)Used By (0)

Rider X Framework
=================

[](#rider-x-framework)

PHP 8.1+ mini framework. Zero external dependencies.

---

Dotenv
------

[](#dotenv)

```
Dotenv::load(__DIR__ . '/.env');

$_ENV['APP_ENV'];   // string
$_ENV['APP_DEBUG']; // bool
$_ENV['DB_PORT'];   // int
```

Existing `$_ENV` keys are never overwritten.

---

Router
------

[](#router)

### Apache — .htaccess

[](#apache--htaccess)

```
Options -Indexes

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L,QSA]
```

### Nginx

[](#nginx)

```
server {
    root /var/www/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
```

### Basic routes

[](#basic-routes)

```
$router = new Router();

$router->get('/users', [UserController::class, 'index']);
$router->post('/users', [UserController::class, 'store']);
$router->put('/users/{id}', [UserController::class, 'update']);
$router->delete('/users/{id}', [UserController::class, 'destroy']);

// Closure
$router->get('/', function (array $params) {
    echo 'Hello';
});

$router->dispatch();

if ($error = $router->hasError()) {
    http_response_code($error); // 404 or 405
}
```

Route parameters arrive as `$params['id']` inside the handler.

### Groups

[](#groups)

```
$router->group('/admin');
$router->get('/dashboard', [AdminController::class, 'dashboard']); // → /admin/dashboard
$router->get('/users', [AdminController::class, 'users']);         // → /admin/users

$router->resetGroup();
$router->get('/', [HomeController::class, 'index']); // → /
```

### Middleware

[](#middleware)

```
class AuthMiddleware extends AbstractMiddleware
{
    public function run(): bool
    {
        if (!isset($_SESSION['user'])) {
            return $this->json(['error' => 'Unauthorized'], 401);
        }
        return true;
    }
}
```

```
$router->middleware(AuthMiddleware::class, AnotherMiddleware::class);
$router->get('/profile', [UserController::class, 'profile']);

$router->resetMiddleware();
```

Returning `false` from `run()` stops the chain — the route handler is not executed.
`AbstractMiddleware` provides `json(array, status)` and `redirect(url, status)` helpers.

### Route classes

[](#route-classes)

```
class SiteRoutes extends AbstractRoute
{
    public function run(): void
    {
        $this->get('/', [HomeController::class, 'index']);
        $this->post('/contact', [HomeController::class, 'contact']);
    }
}

$router->addRouters(new SiteRoutes());
```

Groups and middleware are available inside `AbstractRoute` via `$this->group()`, `$this->middleware()`, `$this->resetGroup()`, `$this->resetMiddleware()`.

### Tracker

[](#tracker)

Auto-registers all public methods of a controller as routes.

```
$router->tracker(HttpMethod::GET, 'posts', PostController::class);
// GET /posts/index   → PostController::index()
// GET /posts/show    → PostController::show()
// GET /posts/create  → PostController::create()
```

Magic methods and inherited methods are ignored.

---

ORM
---

[](#orm)

### Model setup

[](#model-setup)

```
class Product extends Model
{
    protected string $table      = 'products';
    protected string $primaryKey = 'id';
    protected bool   $timestamps = true;
    protected array  $fillable   = ['code', 'name', 'price'];
}
```

`timestamps` adds `created_at` / `updated_at` automatically on `create()` and `update()`.
`fillable` is a whitelist — columns outside it are discarded on `create()` and `update()`.

### Read

[](#read)

```
$db = new Product();

$db->findById(1);
$db->findAll();
$db->findBy('name', 'Notebook');
$db->first('email', 'a@a.com');
$db->random();
$db->count();
```

### Sort

[](#sort)

In-memory sort by a key extracted from each item. The callback receives one item and returns any comparable value — lower values come first.

```
$users = $db->sortBy($users, fn($u) => $u->name);

$users = $db->sortBy($users, fn($u) => match($u->status) {
    'active'  => 0,
    'pending' => 1,
    default   => 2,
});
```

Each key is computed once (O(n)), not once per comparison.

### Write

[](#write)

```
$db->create(['code' => 'ABC', 'name' => 'Notebook', 'price' => 3500]);

$product = $db->findById(1);
$product->name = 'Updated';
$db->update($product);

$db->delete(1);
$db->truncate();
$db->deleteOlderThan(60, 'created_at'); // returns int (rows deleted)

$db->upsert(['id' => 1, 'name' => 'Notebook', 'price' => 3500]); // insert or update
```

`upsert()` uses `INSERT ... ON DUPLICATE KEY UPDATE`. The primary key must be present in the array. On conflict, all fields are updated except the primary key and `created_at`. Requires a `PRIMARY KEY` or `UNIQUE` constraint on the target column.

### Raw

[](#raw)

```
$db->query('SELECT * FROM products WHERE price > ?', 100);
$db->query('SELECT * FROM products WHERE active = ? AND role = ?', [1, 'admin']);

$db->execute('UPDATE products SET active = 0 WHERE id = ?', [5]);
```

### Transaction

[](#transaction)

```
$db->transaction(function (Product $db) {
    $db->create(['code' => 'A', 'name' => 'A', 'price' => 10]);
    $db->delete(5);
});
```

Rolls back automatically on any exception.

### Export

[](#export)

```
$export = new ExportDatabase(__DIR__ . '/backup.sql');
$export->setExclude(['logs', 'sessions']);
$export->export();
```

### Truncate multiple tables

[](#truncate-multiple-tables)

```
$truncate = new TruncateTables();
$truncate->truncateAll(['users', 'migrations']);
$truncate->addTable('logs');
$truncate->truncate();
```

---

Paginator
---------

[](#paginator)

### Simple mode

[](#simple-mode)

Loads all rows into PHP memory, then slices. Suitable for small tables only.

```
$users = new User();

$page = (new Paginator())->create(
    query: fn() => $users->findAll(),
    page: 1,
    perPage: 25,
);
```

### Efficient mode

[](#efficient-mode)

Two queries — `COUNT(*)` first, then `SELECT … LIMIT ? OFFSET ?`. Use this for any table that can grow.

```
$page = (new Paginator())->create(
    query: fn($limit, $offset) => $users->findAll($limit, $offset),
    page: 1,
    perPage: 25,
    count: fn() => $users->count(),
);

// Filtered
$page = (new Paginator())->create(
    query: fn($limit, $offset) => $users->findBy('status', 'active', limit: $limit, offset: $offset),
    page: 1,
    perPage: 25,
    count: fn() => $users->count('status', 'active'),
);
```

### Page object

[](#page-object)

```
$page->data;        // array — records for the current page
$page->total;       // int   — total records across all pages
$page->perPage;     // int   — records per page (capped at 1 000)
$page->currentPage; // int   — current page number
$page->lastPage;    // int   — total number of pages
$page->from;        // int   — index of the first record shown (1-based), 0 when empty
$page->to;          // int   — index of the last record shown, 0 when empty
$page->hasNext;     // bool
$page->hasPrev;     // bool
```

Requesting a page beyond `lastPage` returns `data: []`, `from: 0`, `to: 0`, `hasNext: false`, `hasPrev: false`.
`perPage` is silently capped at `1 000`.

---

Migrations
----------

[](#migrations)

```
php migrate.php                           # run pending migrations
php migrate.php rollback                  # undo last batch
php migrate.php status                    # list ran / pending
php migrate.php make create_users_table   # generate migration file
php migrate.php make:seeder UserSeeder    # generate seeder file
php migrate.php seed                      # run all seeders
php migrate.php seed UserSeeder           # run specific seeder
```

### Migration

[](#migration)

```
class CreateUsersTable extends AbstractMigration
{
    public function up(): void
    {
        $this->execute("
            CREATE TABLE users (
                id         INT AUTO_INCREMENT PRIMARY KEY,
                name       VARCHAR(100) NOT NULL,
                email      VARCHAR(150) NOT NULL UNIQUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ");
    }

    public function down(): void
    {
        $this->execute("DROP TABLE IF EXISTS users");
    }
}
```

Migration files live in `database/migrations/` and are named with a timestamp prefix (`2025_01_01_120000_create_users_table.php`) for ordering. The `migrations` table is created automatically on first run.

Each `php migrate.php` call groups all pending migrations into a **batch**. `rollback` undoes the entire last batch at once.

### Seeder

[](#seeder)

```
class UserSeeder extends AbstractSeeder
{
    public function run(): void
    {
        $this->insert('users', [
            ['name' => 'Admin',  'email' => 'admin@site.com'],
            ['name' => 'Editor', 'email' => 'editor@site.com'],
        ]);
    }
}
```

Seeder files live in `database/seeders/`. `insert()` uses prepared statements — all rows in a single query.

---

Upload
------

[](#upload)

```
$uploader = new UploadFiles('/var/app/storage/uploads');
$path = $uploader->upload($_FILES['photo'], AllowedType::Image);
```

Custom size limit (default 5 MB):

```
$uploader = new UploadFiles('/var/app/storage/uploads', 10); // 10 MB
```

### Allowed types

[](#allowed-types)

CaseAccepted MIMEsExtensions`AllowedType::Image`image/jpeg, image/png, image/gif, image/webpjpg, png, gif, webp`AllowedType::Zip`application/zip, application/x-zip-compressedzip`AllowedType::Pdf`application/pdfpdf`AllowedType::Video`video/mp4, video/mpeg, video/quicktime, video/x-msvideomp4, mpeg, mov, avi`AllowedType::Document`application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.documentdoc, docxMIME is detected via `finfo` — `$_FILES['type']` is never trusted. The stored filename is a random 32-char hex string; the original name is discarded. The storage directory must be outside `public/`.

---

CORS
----

[](#cors)

```
// Allow all origins
Cors::allow('*');

// Allow specific origins only
Cors::allow(['https://app.example.com', 'https://example.com']);

// With credentials (cookies / Authorization header)
Cors::allow(
    origins: ['https://app.example.com'],
    credentials: true
);

// Custom methods, headers, and cache duration
Cors::allow(
    origins: '*',
    methods: ['GET', 'POST'],
    headers: ['Content-Type', 'Authorization'],
    maxAge: 3600
);
```

Call before any output or routing logic — `OPTIONS` preflight requests are answered with `204` and execution stops immediately.

`credentials: true` is silently ignored when `origins` is `'*'` — browsers block that combination regardless.

### Parameters

[](#parameters)

ParameterTypeDefault`origins``string|array``'*'``methods``array``['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']``headers``array``['Content-Type', 'Authorization', 'X-Requested-With']``credentials``bool``false``maxAge``int``86400`When `origins` is an array, the request `Origin` is matched against the list. If no match is found, no CORS headers are emitted. A `Vary: Origin` header is added automatically for specific-origin responses so proxies cache them correctly.

---

Container
---------

[](#container)

```
$container = new Container();
```

### Binding

[](#binding)

```
// new instance on every get()
$container->bind(LoggerInterface::class, FileLogger::class);

// factory closure
$container->bind(Mailer::class, fn($c) => new Mailer($c->get(Config::class)));

// single instance for every get()
$container->singleton(Database::class, DatabaseMysql::class);

// pre-built instance
$container->instance('config', $config);
```

### Resolving

[](#resolving)

```
$logger = $container->get(LoggerInterface::class);

$container->has(LoggerInterface::class); // bool
```

### Auto-wiring

[](#auto-wiring)

Classes with typed constructor parameters are resolved automatically — no registration needed.

```
class UserService
{
    public function __construct(
        private readonly UserRepository $repository,
        private readonly Mailer $mailer,
    ) {}
}

$service = $container->get(UserService::class);
```

Constructor parameters are resolved recursively. Unresolvable built-in types without a default value throw `ContainerException`.

### Exceptions

[](#exceptions)

ExceptionWhen`NotFoundException``get()` called with an id that has no binding and is not an existing class`ContainerException`Class is not instantiable, parameter cannot be resolved, or circular dependency detected---

SSL
---

[](#ssl)

### Symmetric encryption (AES-256-GCM)

[](#symmetric-encryption-aes-256-gcm)

```
$encrypted = SSL::encode('sensitive data', $key);
$plain     = SSL::decode($encrypted, $key);
```

`$key` is hashed with SHA-256 internally — any string length is accepted. Output is base64-encoded and carries the IV and authentication tag.
`decode()` throws `SslException` if the data is tampered or the key is wrong.

### RSA key pair generation

[](#rsa-key-pair-generation)

```
SSL::generateKeyPair('/var/app/storage/keys', 'public', 'secret');
// → /var/app/storage/keys/public.pem
// → /var/app/storage/keys/secret.pem  (chmod 0600)
```

```
SSL::generateKeyPair('/var/app/storage/keys', 'public', 'secret', 4096); // custom bits
```

The private key is saved with `0600` permissions. The directory must exist before calling.

---

Cache
-----

[](#cache)

### Drivers

[](#drivers)

```
$cache = new ArrayCache();                          // in-memory, request lifetime
$cache = new FileCache('/var/app/storage/cache');   // persistent on disk
```

Both implement `CacheInterface` (PSR-16 compatible) and are interchangeable.

### Basic operations

[](#basic-operations)

```
$cache->set('key', $value);           // no expiry
$cache->set('key', $value, 300);      // expires in 300 seconds
$cache->set('key', $value, new DateInterval('PT5M')); // DateInterval

$cache->get('key');                   // returns null if missing or expired
$cache->get('key', 'default');        // custom default

$cache->has('key');                   // bool — also evicts expired entries
$cache->delete('key');
$cache->clear();
```

### Multiple keys

[](#multiple-keys)

```
$cache->setMultiple(['a' => 1, 'b' => 2], 60);

foreach ($cache->getMultiple(['a', 'b'], 0) as $key => $value) {
    // $key => $value
}

$cache->deleteMultiple(['a', 'b']);
```

`getMultiple` and `setMultiple` return/accept generators — no intermediate array allocation.

### TTL

[](#ttl)

ValueBehavior`null`No expiry`int`Seconds from now`DateInterval`Resolved via `DateTime::add()``FileCache` stores entries as SHA-256-named files; expired entries are evicted lazily on read.

---

Validation
----------

[](#validation)

```
$v = Validator::make($_POST)
    ->field('name')->required()->string()->max(100)
    ->field('email')->required()->email()
    ->field('age')->required()->int()->min(18)->max(120)
    ->field('website')->nullable()->url()
    ->field('role')->required()->in(['admin', 'editor', 'user'])
    ->field('slug')->required()->regex('/^[a-z0-9-]+$/');

if (!$v->passes()) {
    $errors = $v->errors(); // ['field' => 'message', ...]
}

// Or throw on failure
$v->validate(); // throws ValidationException — message never carries user data
```

### Custom messages

[](#custom-messages)

```
$v = Validator::make($_POST)
    ->field('email')
        ->required()->message('Email is required')
        ->email()->message('Enter a valid email')
    ->field('age')
        ->required()->message('Age is required')
        ->int()->message('Age must be a number')
        ->min(18)->message('You must be at least 18 years old');
```

`message()` only applies to the rule immediately before it. If that rule passed, `message()` is a no-op.

### Rules

[](#rules)

RuleAcceptsBehavior`required()`—Fails if `null` or `''``string()`—Fails if not a string`int()`—Fails if not a valid integer representation`float()`—Fails if not a valid float representation`email()`—Validates via `filter_var``url()`—Validates via `filter_var``min(int|float)`limitNumeric value ≥ limit · String length ≥ limit (mb-safe)`max(int|float)`limitNumeric value ≤ limit · String length ≤ limit (mb-safe)`in(array)`optionsStrict type comparison`regex(string)`pattern`preg_match` — pattern must include delimiters`nullable()`—Skips all other rules if value is `null` or `''``cpf()`—Validates Brazilian CPF — accepts `000.000.000-00` or `00000000000`The first error per field wins — subsequent failing rules on the same field are silently ignored.

### Schema

[](#schema)

Declarative shorthand validator. All fields are required by default. Returns only the declared fields. Throws `SchemaException` on failure.

```
$data = Schema::object($_POST, [
    'name'     => 'string|min:2|max:100',
    'email'    => 'email|max:30',
    'age'      => 'int|min:18|max:120',
    'website'  => 'optional|url',
    'role'     => 'in:admin,editor,user',
    'slug'     => 'regex:/^[a-z0-9-]+$/',
    'document' => 'cpf',
]);

$data['name'];
$data['email'];
```

```
try {
    $data = Schema::object($_POST, ['email' => 'email']);
} catch (SchemaException $e) {
    $e->errors(); // ['email' => 'The email field must be a valid email address']
}
```

Rules are pipe-separated (`|`); parameters use a colon (`min:6`, `in:a,b,c`).
The first failing rule per field wins.
Fields not declared in the schema are discarded from the return value.

### Schema rules

[](#schema-rules)

RuleParameterBehavior`optional`—Field is not required — skips all other rules if value is `null` or `''``string`—Fails if not a string`int`—Fails if not a valid integer representation`float`—Fails if not a valid float representation`email`—Validates via `filter_var``url`—Validates via `filter_var``min`limitNumeric value ≥ limit · String length ≥ limit (mb-safe)`max`limitNumeric value ≤ limit · String length ≤ limit (mb-safe)`in`valuesComma-separated list — strict string comparison`regex`pattern`preg_match` — pattern must include delimiters`cpf`—Validates Brazilian CPF — accepts `000.000.000-00` or `00000000000`

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance99

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

8

Last Release

10d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0359ef4f975a90b6b5ca591e1a3f1d9a1f0f8177f6baa395cba28e02a7688d37?d=identicon)[alexius-byte](/maintainers/alexius-byte)

---

Top Contributors

[![alexius-byte](https://avatars.githubusercontent.com/u/286071034?v=4)](https://github.com/alexius-byte "alexius-byte (31 commits)")

### Embed Badge

![Health badge](/badges/alexius-byte-rider-php/health.svg)

```
[![Health](https://phpackages.com/badges/alexius-byte-rider-php/health.svg)](https://phpackages.com/packages/alexius-byte-rider-php)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k104.3M822](/packages/laravel-socialite)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k38.6M289](/packages/laravel-dusk)[pinguo/php-msf

Pinguo Micro Service Framework For PHP

1.7k4.2k](/packages/pinguo-php-msf)[nineinchnick/edatatables

Grid widget for the Yii Framework, wrapper for the DataTables jQuery plugin

173.2k](/packages/nineinchnick-edatatables)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
