Build intelligent applications with the first PHP framework designed for the AI era.
MarwaPHP is an AI-native framework that brings smart automation to modern PHP development. From AI-powered code generation to intelligent task scheduling and built-in agent support — MarwaPHP is built to work seamlessly with AI tools and workflows.
Zero-boilerplate. Maximum intelligence. Pure PHP.
| Feature | Description |
|---|---|
| AI Code Generation | Let AI generate controllers, models, and migrations with built-in scaffolding |
| Smart Task Scheduling | AI-assisted cron expressions and natural language scheduling |
| Intelligent Queue | Auto-scaling job processing with AI-driven worker management |
| Built-in Agent Support | First-class support for AI agents to interact with your application |
| Activity Logging | Track AI interactions and system events automatically |
composer create-project memran/marwa-php my-app
cd my-app
php marwa migrate
php marwa module:migrate
php marwa module:seed
php -S localhost:8000 -t public/Access at http://localhost:8000
- PHP 8.2+ (optimized for AI workloads)
- Composer
- Node.js 20+ (for Tailwind CSS builds)
- SQLite (default) or MySQL/MariaDB
# Install dependencies
composer install
cp .env.example .env
# Setup database
php marwa migrate
php marwa module:migrate
php marwa module:seed
# Start local server
php -S localhost:8000 -t public/# Install Node dependencies
npm install
# Development (with hot reload)
npm run dev
# Production build
npm run build
# Build admin theme only
npm run css:build:admin| Command | Description |
|---|---|
php -S localhost:8000 -t public/ |
Start local development server |
php marwa migrate |
Run framework migrations |
php marwa module:migrate |
Run module migrations |
php marwa module:seed |
Seed module data |
php marwa queue:work --daemon |
Start queue worker |
php marwa schedule:run |
Run scheduled tasks |
php marwa db:check |
Check database connection |
composer test # Run PHPUnit
composer analyse # Run PHPStan (level 6)
composer lint # Check PHP syntax
composer ci # Full validation chainapp/
├── Http/
│ ├── Controllers/ # Thin controllers
│ └── Middleware/ # App-specific middleware
├── Providers/ # Service providers
└── Commands/ # Console commands
modules/ # Feature modules
├── Auth/ # Authentication
├── Users/ # User management
├── Activity/ # Activity logging
├── Settings/ # Settings management
├── BackgroundJobs/ # Scheduler UI
├── Queue/ # Queue management
├── Dashboard/ # Admin dashboard
└── Notifications/ # Notification system
config/ # App configuration
resources/
├── views/
│ ├── components/ # Shared Twig components
│ └── themes/ # Theme views (default, admin)
└── css/ # Source stylesheets
routes/ # HTTP routes
database/
├── migrations/ # Shared migrations
└── sqlite/ # SQLite database
tests/ # PHPUnit tests
Create a self-contained module following the Users module structure:
mkdir -p modules/Blog/{database/migrations,database/seeders,Http/Controllers,Models,resources/views,routes}<?php
declare(strict_types=1);
return [
'name' => 'Blog Module',
'slug' => 'blog',
'version' => '1.0.0',
'providers' => [
App\Modules\Blog\BlogServiceProvider::class,
],
'routes' => [
'http' => 'routes/http.php',
],
'migrations' => [
'database/migrations/2026_05_03_000001_create_posts_table.php',
],
'seeders' => [
'database/seeders/BlogPermissionsSeeder.php',
],
'permissions' => [
'blog.view' => 'View Blog',
'blog.create' => 'Create Posts',
'blog.edit' => 'Edit Posts',
'blog.delete' => 'Delete Posts',
],
'menu' => [
'section' => 'Content',
'label' => 'Blog',
'route' => 'admin.blog.index',
'icon' => 'pen-tool',
'permissions' => ['blog.view'],
],
];<?php
declare(strict_types=1);
use Marwa\DB\Schema\Migration;
return new class extends Migration
{
public function up(): void
{
$this->schema->createTable('posts', function ($table) {
$table->id();
$table->string('title');
$table->text('body');
$table->integer('user_id');
$table->timestamps();
});
}
public function down(): void
{
$this->schema->dropTable('posts');
}
};<?php
declare(strict_types=1);
namespace App\Modules\Blog\Models;
use App\Modules\Users\Models\User;
use Marwa\DB\Eloquent\Model;
final class Post extends Model
{
protected string $table = 'posts';
protected array $fillable = ['title', 'body', 'user_id'];
public function author()
{
return $this->belongsTo(User::class, 'user_id');
}
}<?php
declare(strict_types=1);
namespace App\Modules\Blog\Http\Controllers;
use App\Modules\Blog\Models\Post;
use Marwa\Framework\Controllers\Controller;
final class PostController extends Controller
{
public function index(): string
{
$posts = Post::query()->orderBy('created_at', 'DESC')->paginate(10);
return view('blog::index', ['posts' => $posts]);
}
public function store(): void
{
$data = validate_request([
'title' => 'required|min:3|max:255',
'body' => 'required|min:10',
]);
Post::query()->create([
'title' => $data['title'],
'body' => $data['body'],
'user_id' => auth()->id(),
]);
redirect('/admin/blog')->with('success', 'Post created');
}
}<?php
declare(strict_types=1);
use App\Modules\Blog\Http\Controllers\PostController;
use Marwa\Framework\Facades\Route;
Route::group(['prefix' => 'admin/blog', 'middleware' => ['auth', 'can:blog.view']], function () {
Route::get('/', [PostController::class, 'index'])->name('admin.blog.index');
Route::get('/create', [PostController::class, 'create'])->name('admin.blog.create');
Route::post('/', [PostController::class, 'store'])->name('admin.blog.store');
});php marwa module:migrate
php marwa module:seedFor detailed conventions, see docs/module-authoring.md.
if (!auth()->user()->hasPermission('blog.create')) {
abort(403);
}{% if can('blog.edit') %}
<a href="{{ route('admin.blog.edit', {id: post.id}) }}">Edit</a>
{% endif %}Route::get('/protected', fn() => 'Hello')->middleware('can:blog.view');Register in a service provider:
use Marwa\Framework\Scheduling\Task;
public function boot($app): void
{
$app->registerTask(
(new Task('blog:cleanup', function () {
return 'Cleanup complete';
}))
->description('Remove old drafts')
->daily()
);
}Available schedules: everyMinute(), hourly(), daily(), weekly(), monthly().
Run scheduled tasks:
php marwa schedule:runuse App\Modules\Queue\Support\Queue;
Queue::push(function () {
// Job logic
\Marwa\DB\Facades\DB::table('posts')->where('status', 'draft')->delete();
}, 'default');php marwa queue:work --daemonMonitor at /admin/queue (admin only).
use App\Modules\Activity\Support\ActivityRecorder;
ActivityRecorder::record([
'user_id' => auth()->id(),
'action' => 'created_post',
'description' => 'Created: ' . $post->title,
'module' => 'Blog',
]);View at /admin/activity.
- Frontend: Set
FRONTEND_THEMEin.env(default:default) - Admin: Set
ADMIN_THEMEin.env(default:admin)
Theme views: resources/views/themes/{theme_name}/views/
mkdir -p resources/views/themes/my-theme/views
cp -r resources/views/themes/default/views/* resources/views/themes/my-theme/views/cd docker
cp docker.env.example docker.env
docker compose -f docker-compose.yml up -d --buildAccess at http://localhost:8080
| Stack | Compose File | Port |
|---|---|---|
| Nginx + PHP-FPM | docker-compose.yml |
8080 |
| Caddy + PHP-FPM | docker-compose.fpm.yml |
80 |
DB_CONNECTION=mysql
DB_HOST=mariadb
DB_PORT=3306
DB_NAME=marwa
DB_USER=marwa
DB_PASSWORD=secret
ADMIN_BOOTSTRAP_EMAIL=admin@example.com
ADMIN_BOOTSTRAP_PASSWORD=SecurePassword123!
APP_ENV=production
APP_DEBUG=0# View logs
docker compose -f docker-compose.yml logs -f
# Restart
docker compose -f docker-compose.yml restart
# Stop
docker compose -f docker-compose.yml down
# Reset database
docker compose -f docker-compose.yml down -v
# Rebuild
docker compose -f docker-compose.yml up -d --buildThe entrypoint runs automatically:
- Waits for database
- Runs
php marwa migrate - Runs
php marwa module:migrate - Runs
php marwa module:seed - Starts queue worker (
php marwa queue:work --daemon) - Starts scheduler (
php marwa schedule:run) - Launches PHP-FPM
Default is SQLite. For MySQL/MariaDB:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=marwa
DB_USER=marwa
DB_PASSWORD=secretDefault is database queue. For file-based queue:
QUEUE_DRIVER=fileAPP_ENV=local|production|staging
APP_DEBUG=1|0<?php
declare(strict_types=1);
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
final class BlogPostTest extends TestCase
{
public function test_post_creation(): void
{
$this->assertTrue(true);
}
}composer test # Run all tests
composer analyse # Run PHPStan
composer ci # Full validation- Keep controllers thin - Put business logic in models/services
- Use framework helpers -
view(),config(),auth(),validate_request() - Follow PSR-12 - Run
composer lintbefore committing - Type everything - Use strict_types, typed properties/returns
- Module isolation - Keep modules self-contained
- No raw SQL - Use ORM/Query Builder
- Test behavior - Not implementation details
- Validate input - Sanitize all user data
- Leverage AI - Use Marwa's AI-native features for automation
MarwaPHP is built from the ground up to work with AI. Here's how to integrate:
// Expose endpoints for AI agents to interact with
Route::post('/ai/agent/execute', [AgentController::class, 'execute'])
->middleware('ai.agent');// AI understands natural language schedules
$app->registerTask(
(new Task('ai:analyze', function () {
return 'Analysis complete';
}))
->description('Run AI analytics on user data')
->smart('every 2 hours during business days')
);# Generate module with AI assistance
php marwa ai:generate module Blog --intelligentuse App\Modules\Activity\Support\ActivityRecorder;
// Track AI decisions and actions
ActivityRecorder::record([
'user_id' => null, // AI agent
'action' => 'ai_analyzed_data',
'description' => 'AI agent processed 1,000 records',
'module' => 'AI',
]);MarwaPHP isn't just another PHP framework — it's a new paradigm. Built with AI-first thinking:
- AI-native architecture — Every component designed with AI integration in mind
- Intelligent automation — Let AI handle the boring stuff so you can focus on logic
- Future-proof — Built to evolve with the rapidly changing AI landscape
- Developer experience — AI-assisted debugging, testing, and deployment
Ship faster. Code smarter. Let AI do the heavy lifting.