Skip to content

Repository files navigation

MarwaPHP — AI Native PHP Framework

ComposerPHPPHPUnitPHPStanLicense

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.


AI-Native Features

FeatureDescription
AI Code GenerationLet AI generate controllers, models, and migrations with built-in scaffolding
Smart Task SchedulingAI-assisted cron expressions and natural language scheduling
Intelligent QueueAuto-scaling job processing with AI-driven worker management
Built-in Agent SupportFirst-class support for AI agents to interact with your application
Activity LoggingTrack AI interactions and system events automatically

1. Quick Start

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


2. Requirements

  • PHP 8.2+ (optimized for AI workloads)
  • Composer
  • Node.js 20+ (for Tailwind CSS builds)
  • SQLite (default) or MySQL/MariaDB

3. Running the Application

Local Development

# 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/

Frontend Assets

# 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

Common Commands

CommandDescription
php -S localhost:8000 -t public/Start local development server
php marwa migrateRun framework migrations
php marwa module:migrateRun module migrations
php marwa module:seedSeed module data
php marwa queue:work --daemonStart queue worker
php marwa schedule:runRun scheduled tasks
php marwa db:checkCheck database connection

Code Quality

composer test# Run PHPUnit
composer analyse # Run PHPStan (level 6)
composer lint # Check PHP syntax
composer ci # Full validation chain

4. Project Structure

app/
├── 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

5. Creating a New Module

Create a self-contained module following the Users module structure:

mkdir -p modules/Blog/{database/migrations,database/seeders,Http/Controllers,Models,resources/views,routes}

Module Manifest

<?phpdeclare(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'],
],
];

Migration Example

<?phpdeclare(strict_types=1);
useMarwa\DB\Schema\Migration;
returnnewclassextends Migration
{
publicfunctionup(): void
{
$this->schema->createTable('posts', function ($table) {
$table->id();
$table->string('title');
$table->text('body');
$table->integer('user_id');
$table->timestamps();
});
}
publicfunctiondown(): void
{
$this->schema->dropTable('posts');
}
};

Model Example

<?phpdeclare(strict_types=1);
namespaceApp\Modules\Blog\Models;
useApp\Modules\Users\Models\User;
useMarwa\DB\Eloquent\Model;
finalclass Post extends Model
{
protectedstring$table = 'posts';
protectedarray$fillable = ['title', 'body', 'user_id'];
publicfunctionauthor()
{
return$this->belongsTo(User::class, 'user_id');
}
}

Controller Example

<?phpdeclare(strict_types=1);
namespaceApp\Modules\Blog\Http\Controllers;
useApp\Modules\Blog\Models\Post;
useMarwa\Framework\Controllers\Controller;
finalclass PostController extends Controller
{
publicfunctionindex(): string
{
$posts = Post::query()->orderBy('created_at', 'DESC')->paginate(10);
returnview('blog::index', ['posts' => $posts]);
}
publicfunctionstore(): 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');
}
}

Routes Example

<?phpdeclare(strict_types=1);
useApp\Modules\Blog\Http\Controllers\PostController;
useMarwa\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');
});

Run Module Setup

php marwa module:migrate
php marwa module:seed

For detailed conventions, see docs/module-authoring.md.


6. Working with Permissions

Check in Controller

if (!auth()->user()->hasPermission('blog.create')) {
abort(403);
}

Check in Template

{% if can('blog.edit') %}
<ahref="{{ route('admin.blog.edit', {id: post.id}) }}">Edit</a>
{% endif %}

Protect Routes

Route::get('/protected', fn() => 'Hello')->middleware('can:blog.view');

7. Scheduled Tasks

Register in a service provider:

useMarwa\Framework\Scheduling\Task;
publicfunctionboot($app): void
{
$app->registerTask(
(newTask('blog:cleanup', function () {
return'Cleanup complete';
}))
->description('Remove old drafts')
->daily()
);
}

Available schedules: everyMinute(), hourly(), daily(), weekly(), monthly().

Run scheduled tasks:

php marwa schedule:run

8. Queue Jobs

Dispatch a Job

useApp\Modules\Queue\Support\Queue;
Queue::push(function () {
// Job logic
\Marwa\DB\Facades\DB::table('posts')->where('status', 'draft')->delete();
}, 'default');

Process Jobs

php marwa queue:work --daemon

Monitor at /admin/queue (admin only).


9. Activity Logging

useApp\Modules\Activity\Support\ActivityRecorder;
ActivityRecorder::record([
'user_id' => auth()->id(),
'action' => 'created_post',
'description' => 'Created: ' . $post->title,
'module' => 'Blog',
]);

View at /admin/activity.


10. Themes

  • Frontend: Set FRONTEND_THEME in .env (default: default)
  • Admin: Set ADMIN_THEME in .env (default: admin)

Theme views: resources/views/themes/{theme_name}/views/

Create a New Theme

mkdir -p resources/views/themes/my-theme/views
cp -r resources/views/themes/default/views/* resources/views/themes/my-theme/views/

11. Docker Deployment

Quick Start

cd docker
cp docker.env.example docker.env
docker compose --env-file docker.env -f docker-compose.nginx.yml up -d --build

Access at http://localhost:8080

For the Caddy stack:

cd docker
cp docker.caddy.env.example docker.caddy.env
docker compose --env-file docker.caddy.env -f docker-compose.caddy.yml up -d --build

Access at http://localhost

Available Stacks

StackCompose FilePort
Nginx + PHP-FPMdocker-compose.nginx.yml8080
Caddy + PHP-FPMdocker-compose.caddy.yml80

Environment Variables

DB_ENABLED=1DB_CONNECTION=mysqlDB_HOST=mariadbDB_PORT=3306DB_NAME=marwaDB_USER=marwaDB_PASSWORD=secretDB_ROOT_PASSWORD=secretADMIN_BOOTSTRAP_EMAIL=admin@example.comADMIN_BOOTSTRAP_PASSWORD=SecurePassword123!APP_ENV=productionAPP_DEBUG=0

Use the DB_* variables as the single source of truth. The Docker Compose files map these values to the MariaDB container bootstrap variables.

Common Operations

# View logs
docker compose --env-file docker.env -f docker-compose.nginx.yml logs -f
# Restart
docker compose --env-file docker.env -f docker-compose.nginx.yml restart
# Stop
docker compose --env-file docker.env -f docker-compose.nginx.yml down
# Reset database
docker compose --env-file docker.env -f docker-compose.nginx.yml down -v
# Rebuild
docker compose --env-file docker.env -f docker-compose.nginx.yml up -d --build

What Happens on Startup

The entrypoint runs automatically:

  1. Waits for database
  2. Runs php marwa migrate
  3. Runs php marwa module:migrate
  4. Runs php marwa module:seed
  5. Starts queue worker (php marwa queue:work --daemon)
  6. Starts scheduler (php marwa schedule:run)
  7. Launches PHP-FPM

12. Configuration

Database

Default is SQLite. For MySQL/MariaDB:

DB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_NAME=marwaDB_USER=marwaDB_PASSWORD=secret

Queue

Default is database queue. For file-based queue:

QUEUE_DRIVER=file

Environment

APP_ENV=local|production|stagingAPP_DEBUG=1|0

13. Testing

Create a Test

<?phpdeclare(strict_types=1);
namespaceTests\Unit;
usePHPUnit\Framework\TestCase;
finalclass BlogPostTest extends TestCase
{
publicfunctiontest_post_creation(): void
{
$this->assertTrue(true);
}
}

Run Tests

composer test# Run all tests
composer analyse # Run PHPStan
composer ci # Full validation

14. Best Practices

  1. Keep controllers thin - Put business logic in models/services
  2. Use framework helpers - view(), config(), auth(), validate_request()
  3. Follow PSR-12 - Run composer lint before committing
  4. Type everything - Use strict_types, typed properties/returns
  5. Module isolation - Keep modules self-contained
  6. No raw SQL - Use ORM/Query Builder
  7. Test behavior - Not implementation details
  8. Validate input - Sanitize all user data
  9. Leverage AI - Use Marwa's AI-native features for automation

15. AI Integration

MarwaPHP is built from the ground up to work with AI. Here's how to integrate:

Connect Your AI Agent

// Expose endpoints for AI agents to interact with
Route::post('/ai/agent/execute', [AgentController::class, 'execute'])
->middleware('ai.agent');

Smart Task Scheduling with AI

// AI understands natural language schedules$app->registerTask(
(newTask('ai:analyze', function () {
return'Analysis complete';
}))
->description('Run AI analytics on user data')
->smart('every 2 hours during business days')
);

AI-Powered Code Generation

# Generate module with AI assistance
php marwa ai:generate module Blog --intelligent

Activity Logging for AI Actions

useApp\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',
]);

Built for the AI Era

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.


GitHubPackagistDocumentation

About

A fast, lightweight PHP framework for modern web development — modular, MVC-based, and ideal for building scalable APIs and applications.

Topics

Resources

Contributing

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages