likore / rediumphp
A lightweight, modern PHP framework for rapid API development
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/likore/rediumphp
Requires
- php: ^8.1
- ext-json: *
- ext-pdo: *
- lcobucci/jwt: ^5.5
- ramsey/uuid: ^4.7
- slim/psr7: ^1.7
- slim/slim: ^4.14
- symfony/property-access: ^6.4
- symfony/property-info: ^6.4
- symfony/serializer: ^6.4
- vlucas/phpdotenv: ^5.6
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
- symfony/var-dumper: ^7.4
This package is not auto-updated.
Last update: 2026-01-14 10:18:24 UTC
README
A lightweight, modern PHP framework for rapid API development, based on our production-tested patterns
Features
Attribute-Based Routing - Clean, declarative route definitions using PHP 8+ attributes
Built-in JWT Authentication - Token-based auth with permission system
Robust ORM - Full-featured Entity system with relationships, casting, and dirty tracking
Query Builder - Fluent interface for complex database queries
Validation System - 10+ validation rules with database uniqueness checks
Caching Layer - File-based caching with TTL and remember pattern
Event System - Event-driven architecture with priority listeners
File Upload Handler - Comprehensive file management with validation
Rate Limiting - IP-based API protection and throttling
Collection Class - Powerful array manipulation for entities
Response Helpers - Standardized API responses
Auto Parameter Injection - Automatic injection of request data into controller methods
Type-Safe - Full PHP 8.1+ type safety throughout
Zero Configuration - Works out of the box with sensible defaults
Installation
Framework Installation
composer require likore/rediumphp
Quick Start Project
# Clone or copy the quick-start example cp -r examples/quick-start my-api cd my-api # Install dependencies composer install # Setup environment cp .env.example .env # Edit .env with your database credentials # Import database mysql -u root -p < database.sql # Start development server php -S localhost:8000 index.php
Quick Example
1. Create a Model
<?php namespace App\Models; use Redium\Database\Model; class Product extends Model { protected string $table = 'products'; public function findByCategory(string $category): array { return $this->findAllBy('category', $category); } }
2. Create a Controller
<?php namespace App\Controllers; use Redium\Core\Controller; use Redium\Attributes\Route; use App\Models\Product; #[Route("/products")] class ProductController extends Controller { private Product $productModel; public function __construct() { $this->productModel = new Product(); } #[Route("/list", "GET")] public function list(): array { return $this->productModel->findAll(); } #[Route("/create", "POST", permission: "product.create")] public function create(array $data): array { $this->validate($data, ['name', 'price']); $this->productModel->save($data); return $this->success("Product created"); } #[Route("/{id}", "GET")] public function getProduct(int $id): array { return $this->productModel->findById($id); } }
3. Register Controller
<?php require 'vendor/autoload.php'; use Redium\Core\Application; use App\Controllers\ProductController; $app = new Application(__DIR__); $app->registerController(ProductController::class); $app->run();
That's it! Your API is ready at http://localhost:8000/v1/products/list
Core Concepts
Attribute-Based Routing
Routes are defined using PHP attributes directly on controller methods:
#[Route("/users")] // Controller-level prefix class UserController extends Controller { #[Route("/login", "POST")] // Public route public function login(string $email, string $password): array { // Handles POST /v1/users/login } #[Route("/list", "GET", permission: "user.read")] // Protected route public function list(): array { // Requires valid JWT with "user.read" permission } #[Route("/{id}", "PUT", permission: "user.update")] public function update(int $id, array $data): array { // Path parameters automatically injected // Request body automatically parsed to $data } }
Automatic Parameter Injection
The framework automatically injects parameters from:
- Request body (JSON)
- Path parameters (
{id},{name}, etc.) - Query parameters (
?page=1&size=10)
#[Route("/{id}", "PUT")] public function update( int $id, // From path: /users/123 string $name, // From body: {"name": "John"} int $page = 0 // From query: ?page=2 (with default) ): array { // All parameters automatically available }
Authentication & Permissions
Protect routes with the permission parameter:
#[Route("/admin", "GET", permission: "admin.access")] public function adminPanel(): array { // Only users with "admin.access" permission can access } #[Route("/public", "GET")] // No permission = public route public function publicEndpoint(): array { // Anyone can access }
Login and get token:
curl -X POST http://localhost:8000/v1/users/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"admin123"}'
Use token in requests:
curl http://localhost:8000/v1/users/list \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Database Layer
The base Model class provides common operations:
class User extends Model { protected string $table = 'users'; protected string $primaryKey = 'id'; // Default: 'id' } // CRUD Operations $user = new User(); // Create $user->save(['name' => 'John', 'email' => 'john@example.com']); // Read $all = $user->findAll(); // Get all with pagination $one = $user->findById(1); // Find by ID $byEmail = $user->findBy('email', 'john@example.com'); // Find by field // Update $user->update(['id' => 1, 'name' => 'Jane']); $user->updateBy('identifier123', 'status', 'ACTIVE'); // Delete $user->delete(1); // Count $total = $user->count(); $active = $user->countBy('status', 'ACTIVE');
View Projections (select specific fields):
// Only select specific fields $users = $user->findAll('id, name, email', $size = 10, $page = 0);
Configuration
Environment variables are automatically loaded from .env:
use Redium\Utils\Config; // Access config with dot notation $dbHost = Config::get('database.host'); $appName = Config::get('app.name', 'Default Name'); // Check environment if (Config::isDev()) { // Development-only code }
Helper Functions
Common utilities are available globally:
// Request/Response $data = getBody(); // Parse JSON request body $json = writeBody($data); // Encode to JSON throwError(404, "Not Found", "User not found"); // Send error response // Security $hash = password_encrypt($password); // Encrypt password $safe = getTextInput($userInput); // Sanitize input // Utilities $id = generateUniqueIdentifier(); // Generate UUID $serial = generateRandomSerialNumber(8); // Random alphanumeric $ip = getIpAddress(); // Get client IP // HTTP Requests $data = callGet('https://api.example.com/data'); $result = callPost('https://api.example.com/create', ['name' => 'John']);
API Endpoints (Quick Start Example)
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| POST | /v1/users/login |
none | User login |
| GET | /v1/users/list |
user.read | Get all users |
| GET | /v1/users/{identifier} |
user.read | Get single user |
| POST | /v1/users/create |
all | Create user (admin only) |
| PUT | /v1/users/{identifier} |
user.update | Update user |
| DELETE | /v1/users/{identifier} |
all | Delete user (admin only) |
Testing the Example
1. Login as Admin
curl -X POST http://localhost:8000/v1/users/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"admin123"}'
Response:
{
"user": {
"identifier": "admin001",
"first_name": "Admin",
"email": "admin@example.com",
"role": "ADMIN"
},
"token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}
2. Get Users (with token)
curl http://localhost:8000/v1/users/list \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
3. Create User (admin only)
curl -X POST http://localhost:8000/v1/users/create \ -H "Authorization: Bearer YOUR_TOKEN_HERE" \ -H "Content-Type: application/json" \ -d '{ "first_name": "Jane", "last_name": "Smith", "email": "jane@example.com", "password": "password123", "role": "USER" }'
Project Structure
your-api/
├── src/
│ ├── Controllers/ # API controllers with routes
│ ├── Models/ # Database models
│ └── Services/ # Business logic
├── .env # Environment configuration
├── index.php # Application entry point
├── composer.json
└── database.sql # Database schema
Environment Variables
# Application ENV=dev # dev or production APP_NAME="My API" APP_URL=http://localhost:8000 # Database DB_HOST=localhost DB_NAME=my_database DB_USER=root DB_PASSWORD= # JWT JWT_SECRET=your-secret-key JWT_EXPIRATION_HOURS=8 # CORS ALLOWED_URLS=http://localhost:3000,http://localhost:8000
Best Practices
1. Use Services for Business Logic
// ❌ Don't put logic in controllers #[Route("/create", "POST")] public function create(array $data): array { if (!$data['email']) throw new Exception("Email required"); $user = new User(); $user->save($data); // ... more logic } // ✅ Use services #[Route("/create", "POST")] public function create(array $data): array { return $this->userService->createUser($data); }
2. Validate Input
public function create(array $data): array { $this->validate($data, ['name', 'email', 'password']); // ... proceed with validated data }
3. Use View Projections
// ❌ Don't expose sensitive data $users = $this->userModel->findAll(); // Includes passwords! // ✅ Select only needed fields $users = $this->userModel->findAll('id, name, email, role');
4. Handle Errors Gracefully
public function getUser(string $id): array { $user = $this->userModel->findById($id); if (!$user) { throw new Exception("User not found"); } return $user; }
Requirements
- PHP 8.1 or higher
- MySQL/MariaDB
- Composer
- PDO extension
- JSON extension
License
MIT License - feel free to use in your projects!
Credits
Built by Likore, extracted from production-tested patterns in real-world applications.