A modern schema-first migration engine for PHP.
PHP Schema Engine lets you define your database structure using a clean PHP DSL, compare it against the current database schema, generate SQL automatically, and apply changes through a lightweight CLI.
Current version:
0.3.0-alpha
- Schema-first database definition
- Fluent PHP DSL
- MySQL/MariaDB support
- Automatic schema introspection
- Table and column diffing
- Partial index diffing
- SQL generation
- Migration execution
- Migration history
- Model generation
- Dry-run mode
- Destructive operation protection
- Database reset support
- Snapshot generation
- Foreign key support
- Debug trace mode
composer require erilshackle/php-schema-engineFor local development:
composer installGenerate the default configuration file:
php bin/migrate --initThis creates:
schema-engine.phpGenerated config example:
<?php
return [
// Schema file path
'schema' => '/database/schema.php',
// Database connection configuration
'database' => [
'driver' => 'mysql',
'host' => $_ENV['DB_HOST'] ?? '127.0.0.1',
'port' => $_ENV['DB_PORT'] ?? 3306,
'database' => $_ENV['DB_NAME'] ?? 'app',
'username' => $_ENV['DB_USER'] ?? 'root',
'password' => $_ENV['DB_PASS'] ?? '',
],
// Model generator setup
'generator' => [
'models' => [
'enabled' => false,
'namespace' => 'App\\Models',
'path' => '/app/Models',
'extends' => null,
],
],
];Optional bootstrap file:
bootstrap.phpUseful for:
- loading
.env - bootstrapping frameworks
- loading helpers
- custom runtime setup
Create a schema file:
<?php
use SchemaEngine\DSL\Schema;
use SchemaEngine\DSL\Table;
return function (Schema $schema) {
$schema->table('users', function (Table $t) {
$t->id();
$t->string('name');
$t->string('email')
->unique();
$t->timestamps();
});
};Run a dry-run first:
php bin/migrate --dry-runApply the migration:
php bin/migrate --yes$schema->table('users', function ($t) {
$t->id();
$t->string('name');
$t->string('email')
->unique();
$t->int('age')
->default(0);
$t->timestamps();
});$t->id();
$t->string('name');
$t->char('code', 2);
$t->text('body');
$t->longText('content');
$t->int('age');
$t->bigInt('views');
$t->float('rating');
$t->double('score');
$t->decimal('price', 10, 2);
$t->boolean('active');
$t->datetime('published_at');
$t->timestamp('created_at');
$t->json('meta');
$t->uuid('uuid');$t->string('email')
->unique();
$t->string('slug')
->index();
$t->string('name')
->nullable();
$t->int('age')
->default(0);
$t->timestamp('created_at')
->defaultCurrentTimestamp();
$t->timestamp('updated_at')
->defaultRaw('CURRENT_TIMESTAMP');$t->id();
$t->timestamps();
$t->createdAt();
$t->updatedAt();
$t->softDeletes();
$t->rememberToken();Single-column indexes:
$t->string('email')
->unique();
$t->string('slug')
->index();Composite indexes:
$t->unique(['email', 'tenant_id']);
$t->index(['first_name', 'last_name']);Indexes are automatically introspected and partially diffed.
Current V1 behavior:
- missing indexes are automatically added
- removed indexes are automatically dropped
- changed indexes are ignored intentionally with warnings
Example warning:
Index 'email_unique' on table 'users' differs from desired schema and was ignored.Simple inferred relation:
$t->foreignId('user_id');Explicit references:
$t->foreignId('user_id')
->constrained() // or references('users')
->cascadeOnDelete();Custom table:
$t->foreignId('author_id')
->references('users');Custom referenced column:
$t->uuid('author_id')
->references('users', 'uuid');Available relation actions:
->cascadeOnDelete()
->cascadeOnUpdate()
->restrictOnDelete()
->restrictOnUpdate()
->nullOnDelete()
->nullOnUpdate()In V1, foreign keys are generated when creating new tables only. Foreign key changes after table creation are not automatically migrated yet.
Generate models from your schema:
php bin/migrate --generate-modelsExample generated model:
<?php
namespace App\Models;
class User
{
protected string $table = 'users';
}Generator configuration:
'generator' => [
'models' => [
'enabled' => true,
'namespace' => 'App\\Models',
'path' => '/app/Models',
'extends' => 'App\\Core\\Model',
],
],Dry-run:
php bin/migrate --dry-runApply migrations without confirmation:
php bin/migrate --yesAllow destructive operations:
php bin/migrate --forceShow migration history:
php bin/migrate --statusReset database:
php bin/migrate --fresh --force --yesReset database and clear history:
php bin/migrate --fresh --force --yes --clear-historyGenerate models:
php bin/migrate --generate-modelsShow stack trace on exceptions:
php bin/migrate --traceExecuted operations are stored in:
schema_migrationsThis table is managed internally by the engine and ignored during schema diffing.
By default, destructive operations are blocked.
This includes:
- dropping tables
- dropping columns
- dropping indexes
To allow destructive operations:
php bin/migrate --forceEnable stack traces during execution:
php bin/migrate --traceExample output:
[ERROR] Table users already exists
File: .../Migrator.php:62
Stack trace:
#0 .../Migrator.php:62 run()
#1 .../MigrateCommand.php:88 printPlannedSql()
#2 .../bin/migrate:24 run()PHP Schema Engine 0.1.0-alpha is intentionally conservative.
Current V1 limitations:
- MySQL/MariaDB only
- Index diffing is partial and intentionally conservative
- Existing indexes are not automatically modified
- Foreign key changes after table creation are not automatically migrated
- Rename detection is disabled by default
- Table recreation is not implemented yet
- No PostgreSQL grammar yet
- No SQLite grammar yet
- No ORM/query builder layer yet
PHP Schema Engine follows a schema-first approach.
The schema file is the source of truth.
Schema DSL
↓
Schema Metadata
↓
Database Introspection
↓
Diff Engine
↓
SQL Generation
↓
Migration ExecutionThe DSL is designed to stay expressive, while the internal architecture keeps metadata, diffing, SQL generation, and execution separated.
- Schema DSL
- MySQL introspection
- Column diffing
- Partial index diffing
- SQL generator
- CLI
- Migration history
- Model generation
- Foreign keys on create
- migrate:fresh
- migrate:reset
- better CLI output
- schema snapshots
- explicit rename operations
- advanced index diffing
- foreign key diffing
- table recreation mode
- rollback support
- stable public API
- stronger type system
- advanced MySQL grammar
- multi-driver foundation
MIT