A modern PHP framework featuring attributes-based routing, multiple view engines, migrations, models, and more.
- Attribute-based routing system
- Multiple view engine support (Blade, Twig, Inertia)
- Database migrations and models
- Eloquent-style relationships using attributes
- Form validation using attributes
- Event system
- WebSocket support
- Redis/File-based caching
- Factory pattern for testing
- Middleware support
- Interactive console debugging
- PSR-3 compatible logging system
- Database and file handlers
- Multiple channels support
- Configurable formatters
- Command-line interface
- Built-in CRUD generators
- Database management
- WebSocket server control
- Interactive debugging tools
- Error handling
- Web and console error handlers
- Detailed backtraces
- Interactive debugging console
[Installation instructions to be added]
your-project/
├── app/
│ ├── Console/
│ │ └── Commands/ # Custom console commands
│ ├── Controllers/ # HTTP controllers
│ └── Database/
│ ├── Migrations/ # Database migrations
│ ├── Models/ # Database models
│ ├── Factories/ # Model factories
│ └── Seeders/ # Database seeders
├── bootstrap/
│ └── app.php # Application bootstrap file
├── console/
│ └── bin # Console application entry point
├── public/
│ └── index.php # Web application entry point
└── routes/
└── web.php # Web routes definition
Initialize the application instance:
<?php
require_once '../vendor/autoload.php';
require_once '../helpers/functions.php';
use DJWeb\Framework\Web\Application;
$app = Application::getInstance();
$app->bind('base_path', dirname(__DIR__));
Configure error handling and start the application:
<?php
use DJWeb\Framework\ErrorHandling\Backtrace;
use DJWeb\Framework\ErrorHandling\Handlers\WebHandler;
use DJWeb\Framework\ErrorHandling\Renderers\WebRenderer;
use DJWeb\Framework\Web\Application;
require_once '../bootstrap/app.php';
$errorHandler = new WebHandler(
new WebRenderer(
debug: true,
backtrace: new Backtrace()
),
);
$app = Application::getInstance();
$app->session->start();
$app->loadRoutes(
controllerNamespace: '\\App\\Controllers\\',
controllerDirectory: __DIR__ . '/../app/Controllers/'
);
$app->withRoutes(require_once '../routes/web.php');
echo $app->handle()->getBody()->getContents();
### Console Commands
Create custom console commands using attributes:
Set up the console application for running commands:
<?php
use DJWeb\Framework\Console\Application;
use DJWeb\Framework\Console\Utils\CommandNamespace;
use DJWeb\Framework\ErrorHandling\Handlers\ConsoleHandler;
use DJWeb\Framework\ErrorHandling\Renderers\Partials\ConsoleRendererFactory;
require_once 'vendor/autoload.php';
require_once 'helpers/functions.php';
$app = Application::getInstance();
// Configure error handling
$handler = new ConsoleHandler(new ConsoleRendererFactory($app)->create());
$handler->register();
// Set up application paths
$app->bind('base_path', dirname(__DIR__));
// Register console commands
$app->registerCommands(
new CommandNamespace(
namespace: 'App\\Console\\Commands',
path: dirname(__DIR__) . '/app/Console/Commands'
)
);
// Configure database-related paths
$app->bind('app.migrations_path', dirname(__DIR__) . '/app/Database/Migrations');
$app->bind('app.models_path', dirname(__DIR__) . '/app/Database/Models');
$app->bind('app.factories_path', dirname(__DIR__) . '/app/Database/Factories');
$app->bind('app.seeders_path', dirname(__DIR__) . '/app/Database/Seeders');
$app->handle();
Create custom console commands using attributes:
#[AsCommand('hello_world')]
class HelloWorldCommand extends Command
{
public function run(): int
{
$this->getOutput()->info('Hello World from console!');
return 0;
}
}
Database Commands:
# Run database seeder
database:seed --seeder=DatabaseSeeder
# Run migrations
migrate
# Generate a new migration
make:migration Name
# Create a new model
make:model Name --table=table_name
# Create a new factory
make:factory FactoryName
# Create a new seeder
make:seeder Name
Queue and Scheduling:
# Start the queue worker
queue:work
# Run the scheduler
schedule:work
Development Tools:
# Generate application security key
key:generate
# List all available commands
list
# Generate a new mailable class
make:mail Name
# Start WebSocket server
ws:start
Command Details:
database:seed
- Seeds the database using the specified seeder classkey:generate
- Generates a new application key for session and cookie encryptionlist
- Displays a list of all available console commandsmake:factory
- Creates a new model factory classmake:mail
- Generates a new mailable class for sending emailsmake:migration
- Creates a new database migration filemake:model
- Generates a new model class with optional table name specificationmake:seeder
- Creates a new database seeder classmigrate
- Runs pending database migrationsqueue:work
- Starts processing jobs from the queueschedule:work
- Starts the task schedulerws:start
- Launches the WebSocket server
The framework provides an interactive debugging terminal that automatically activates when an exception occurs in console commands. This powerful tool allows you to inspect the stack trace, examine variables, and analyze the application state at the time of the error.
When an exception occurs, you'll be dropped into an interactive debug session:
Debug mode: (type 'help' for available commands) _
trace
- Display the full stack trace:
Debug mode: (type 'help' for available commands) trace
#0 SomeClass::someFunction
-> /path/to/file.php:123
#1 AnotherClass::anotherFunction
-> /path/to/another_file.php:456
frame [number]
- Examine a specific stack frame:
Debug mode: (type 'help' for available commands) frame 0
Frame #0 Details:
Location: /path/to/file.php:123
Call: SomeClass::someFunction(arg1, arg2)
Source:
> 123| problematic_function_call();
124| next_line_of_code();
help
- Display available commandsexit
- Exit the debugging session
- Interactive command prompt
- Stack trace inspection
- Source code preview around the error
- Function call details with arguments
- Frame-by-frame navigation
- Use
frame
command to move up and down the stack trace - Examine the source code context around the error
- Check function arguments at each stack frame
- Use
trace
to get an overview of the call stack
Define routes using attributes with support for parameters and middleware:
#[RouteGroup('views')]
class ControllerRenderingViews extends Controller
{
#[Route('blade')]
#[Middleware(beforeGlobal: [LogRequests::class])]
public function index(): ResponseInterface
{
$this->withRenderer('blade');
return $this->render('index.blade.php', ['user' => 'test']);
}
#[Route('twig/<category:\d+>')]
public function category(Category $category): ResponseInterface
{
$this->withRenderer('twig');
return $this->render('index.twig', ['user' => 'test']);
}
}
Create database structure using type-safe migration classes:
return new class extends Migration
{
public function up(): void
{
$this->schema->createTable('categories', [
new IntColumn('id', nullable: false, autoIncrement: true),
new VarcharColumn('name'),
new TextColumn('description'),
new DateTimeColumn('created_at', current: true),
new DateTimeColumn('updated_at', currentOnUpdate: true),
new PrimaryColumn('id'),
]);
}
}
Define models with property typing and automatic change tracking:
class Category extends Model
{
public string $table {
get => 'categories';
}
#[FakeAs(FakerMethod::NAME)]
public string $name {
get => $this->name;
set {
$this->name = $value;
$this->markPropertyAsChanged('name');
}
}
protected array $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime'
];
}
Define relationships between models using attributes:
class Company extends Model
{
public string $table {
get => 'companies';
}
// One-to-Many relationship
#[HasMany(Post::class, foreign_key: 'company_id', local_key: 'id')]
public array $posts {
get => $this->relations->getRelation('posts');
}
// Has-Many-Through relationship
#[HasManyThrough(Comment::class, Post::class, 'company_id', 'post_id', 'id', 'id')]
public array $comments {
get => $this->relations->getRelation('comments');
}
}
Available relationship attributes:
#[HasMany]
- One-to-Many relationship#[HasManyThrough]
- Has-Many-Through relationship#[BelongsTo]
- Belongs-To relationship#[HasOne]
- One-to-One relationship
Populate your database with test data:
class CategorySeeder extends Seeder
{
public function run(): void
{
new CategoryFactory()->createMany(25);
}
}
Use attribute-based validation for form requests:
class LoginFormDTO extends FormRequest
{
#[Required]
#[IsValidated]
#[MaxLength(255)]
public protected(set) string $login;
#[Required]
#[IsValidated]
public protected(set) string $password;
}
Implement event-driven architecture:
// Dispatch events
$this->events->dispatch(new UserRegisteredEvent($user));
// Create listeners
class EmailNotificationListener
{
public function __invoke(UserRegisteredEvent $event): void
{
MailerFactory::createSmtpMailer(...Config::get('mail.default'))
->send(new WelcomeMailable($event->user));
}
}
Implement real-time features using WebSocket:
#[AsCommand('ws:start')]
class WsStart extends Command
{
public function run(): int
{
$loop = \React\EventLoop\Loop::get();
$server = new WebSocketServer($loop, '0.0.0.0', 8080);
$server->addListener('message', new MessageListener()->listen(...));
$server->run();
return 0;
}
}
Use the Cache facade for Redis or file-based caching:
Cache::set('key', 'value', 3600); // Store for 1 hour
$value = Cache::get('key'); // Retrieve value
The framework provides a PSR-compatible logging system with support for multiple channels and handlers. Logs can be stored in either database or files, with configurable formatters and retention periods.
return [
'default' => 'database',
'channels' => [
'stack' => [
'handler' => 'file',
'path' => __DIR__.'/../storage/logs/app.log',
'formatter' => 'text',
'max_days' => 14
],
'database' => [
'handler' => 'database'
],
'daily' => [
'handler' => 'file',
'path' => __DIR__.'/../storage/logs/daily.log',
'formatter' => 'json',
'max_days' => 7
]
]
];
If using database logging, the following migration creates the required table:
$this->schema->createTable('database_logs', [
new IntColumn('id', nullable: false, autoIncrement: true),
new VarcharColumn('level'),
new TextColumn('message'),
new TextColumn('metadata'),
new TextColumn('context'),
new DateTimeColumn('created_at', current: true),
new DateTimeColumn('updated_at', currentOnUpdate: true),
new PrimaryColumn('id'),
]);
Log messages using the Log facade:
use DJWeb\Framework\Log\Log;
// Basic logging
Log::info('User logged in');
// Logging with context
Log::info('Payment processed', [
'user_id' => 123,
'amount' => 99.99
]);
emergency
alert
critical
error
warning
notice
info
debug
- PSR-3 compatible logging interface
- Multiple logging channels support
- File and database handlers
- JSON and text formatters
- Configurable log retention
- Context and metadata support
- Automatic timestamps
-
File Handler
- Text or JSON formatting
- Configurable file paths
- Log rotation with max days setting
-
Database Handler
- Structured log storage
- Easy querying and analysis
- Automatic timestamps
- Metadata and context storage
-
Stack Handler
- Combine multiple handlers
- Flexible log routing
[Contributing guidelines to be added]
[License information to be added]