php-framework/docs/packages/core.md

11 KiB

Core Package

The Core package provides the foundation for the framework including the module system, lifecycle events, multi-tenancy, and shared utilities.

Installation

composer require host-uk/core

Features

Module System

Auto-discover and lazy-load modules based on lifecycle events:

<?php

namespace Mod\Example;

use Core\Events\WebRoutesRegistering;

class Boot
{
    public static array $listens = [
        WebRoutesRegistering::class => 'onWebRoutes',
    ];

    public function onWebRoutes(WebRoutesRegistering $event): void
    {
        $event->views('example', __DIR__.'/Views');
        $event->routes(fn () => require __DIR__.'/Routes/web.php');
    }
}

Learn more about the Module System →

Lifecycle Events

Event-driven extension points throughout the framework:

  • WebRoutesRegistering - Public web routes
  • AdminPanelBooting - Admin panel initialization
  • ApiRoutesRegistering - REST API routes
  • ClientRoutesRegistering - Authenticated client routes
  • ConsoleBooting - Artisan commands
  • McpToolsRegistering - MCP tools
  • FrameworkBooted - Late-stage initialization

Learn more about Lifecycle Events →

Actions Pattern

Single-purpose business logic classes:

class CreatePost
{
    use Action;

    public function handle(array $data): Post
    {
        $post = Post::create($data);

        event(new PostCreated($post));

        return $post;
    }
}

// Usage
$post = CreatePost::run($data);

Learn more about Actions →

Multi-Tenancy

Workspace-scoped data isolation:

use Core\Mod\Tenant\Concerns\BelongsToWorkspace;

class Post extends Model
{
    use BelongsToWorkspace;
}

// Queries automatically scoped to current workspace
$posts = Post::all();

Learn more about Multi-Tenancy →

Activity Logging

Track changes to models with automatic workspace scoping:

use Core\Activity\Concerns\LogsActivity;

class Post extends Model
{
    use LogsActivity;

    protected array $activityLogAttributes = ['title', 'status'];
}

// Changes logged automatically
$post->update(['title' => 'New Title']);

// Retrieve activity
$activity = Activity::forSubject($post)->get();

Learn more about Activity Logging →

Seeder Discovery

Automatic seeder discovery with dependency ordering:

use Core\Database\Seeders\Attributes\SeederPriority;
use Core\Database\Seeders\Attributes\SeederAfter;

#[SeederPriority(50)]
#[SeederAfter(WorkspaceSeeder::class)]
class PostSeeder extends Seeder
{
    public function run(): void
    {
        Post::factory()->count(20)->create();
    }
}

Learn more about Seeders →

Configuration Management

Multi-profile configuration with versioning:

use Core\Config\ConfigService;

$config = app(ConfigService::class);

// Set configuration
$config->set('api.rate_limit', 10000, $profile);

// Get configuration
$rateLimit = $config->get('api.rate_limit', $profile);

// Export configuration
php artisan config:export production

// Import configuration
php artisan config:import production.json

CDN Integration

Unified CDN interface for BunnyCDN and Cloudflare:

use Core\Cdn\Facades\Cdn;

// Generate CDN URL
$url = Cdn::url('images/photo.jpg');

// Store file to CDN
$path = Cdn::store($uploadedFile, 'media');

// Delete from CDN
Cdn::delete($path);

// Purge cache
Cdn::purge('images/*');

Security Headers

Configurable security headers with CSP support:

// config/core.php
'security_headers' => [
    'csp' => [
        'directives' => [
            'default-src' => ["'self'"],
            'script-src' => ["'self'", "'nonce'"],
            'style-src' => ["'self'", "'unsafe-inline'"],
        ],
    ],
    'hsts' => [
        'enabled' => true,
        'max_age' => 31536000,
    ],
],

Email Shield

Disposable email detection and validation:

use Core\Mail\EmailShield;

$shield = app(EmailShield::class);

$result = $shield->validate('user@example.com');

if (! $result->isValid) {
    // Email is disposable, has syntax errors, etc.
    return back()->withErrors(['email' => $result->reason]);
}

Media Processing

Image optimization and responsive images:

use Core\Media\Image\ImageOptimizer;

$optimizer = app(ImageOptimizer::class);

// Optimize image
$optimized = $optimizer->optimize('path/to/image.jpg');

// Generate responsive variants
$variants = $optimizer->generateVariants($image, [
    'thumbnail' => ['width' => 150, 'height' => 150],
    'medium' => ['width' => 640],
    'large' => ['width' => 1024],
]);

Unified search interface across modules:

use Core\Search\Unified;

$search = app(Unified::class);

$results = $search->search('query', [
    'types' => ['posts', 'pages'],
    'limit' => 10,
]);

foreach ($results as $result) {
    echo $result->title;
    echo $result->url;
}

SEO Tools

SEO metadata generation and sitemap:

use Core\Seo\SeoMetadata;

$seo = app(SeoMetadata::class);

$seo->setTitle('Page Title')
    ->setDescription('Page description')
    ->setCanonicalUrl('https://example.com/page')
    ->setOgImage('https://example.com/og-image.jpg');

// Generate in view
{!! $seo->render() !!}

// Sitemap generation
php artisan seo:generate-sitemap

Artisan Commands

Module Management

# Create new module
php artisan make:mod Blog

# Create website module
php artisan make:website Marketing

# Create plugin module
php artisan make:plug Stripe

Configuration

# Export configuration
php artisan config:export production

# Import configuration
php artisan config:import production.json --profile=production

# Show configuration versions
php artisan config:version --profile=production

Activity Logs

# Prune old activity logs
php artisan activity:prune --days=90

Email Shield

# Prune email shield statistics
php artisan email-shield:prune --days=30

SEO

# Generate sitemap
php artisan seo:generate-sitemap

# Audit canonical URLs
php artisan seo:audit-canonical

# Test structured data
php artisan seo:test-structured-data --url=/blog/post-slug

Storage

# Warm cache
php artisan cache:warm

# Offload files to CDN
php artisan storage:offload --disk=public

Configuration

Core Configuration

// config/core.php
return [
    'module_paths' => [
        app_path('Core'),
        app_path('Mod'),
        app_path('Plug'),
    ],

    'modules' => [
        'auto_discover' => true,
        'cache_enabled' => true,
    ],

    'seeders' => [
        'auto_discover' => true,
        'paths' => [
            'Mod/*/Database/Seeders',
            'Core/*/Database/Seeders',
        ],
    ],

    'activity' => [
        'enabled' => true,
        'retention_days' => 90,
        'log_ip_address' => false,
    ],

    'workspace_cache' => [
        'enabled' => true,
        'ttl' => 3600,
        'use_tags' => true,
    ],
];

View full configuration options →

Testing

Feature Tests

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Mod\Blog\Actions\CreatePost;

class CreatePostTest extends TestCase
{
    public function test_creates_post(): void
    {
        $post = CreatePost::run([
            'title' => 'Test Post',
            'content' => 'Test content',
        ]);

        $this->assertDatabaseHas('posts', [
            'title' => 'Test Post',
        ]);
    }
}

Unit Tests

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Core\Module\ModuleScanner;

class ModuleScannerTest extends TestCase
{
    public function test_discovers_modules(): void
    {
        $scanner = new ModuleScanner();

        $modules = $scanner->scan([app_path('Mod')]);

        $this->assertNotEmpty($modules);
        $this->assertArrayHasKey('listens', $modules[0]);
    }
}

Database

Migrations

Core package includes migrations for:

  • activity_log - Activity logging
  • config_keys - Configuration keys
  • config_values - Configuration values
  • config_profiles - Configuration profiles
  • config_versions - Configuration versioning
  • email_shield_stats - Email validation statistics
  • workspaces - Multi-tenant workspaces
  • workspace_users - User-workspace relationships

Run migrations:

php artisan migrate

Events

Core package dispatches these events:

Lifecycle Events

  • Core\Events\WebRoutesRegistering
  • Core\Events\AdminPanelBooting
  • Core\Events\ApiRoutesRegistering
  • Core\Events\ClientRoutesRegistering
  • Core\Events\ConsoleBooting
  • Core\Events\McpToolsRegistering
  • Core\Events\FrameworkBooted

Configuration Events

  • Core\Config\Events\ConfigChanged
  • Core\Config\Events\ConfigInvalidated

Activity Events

  • Core\Activity\Events\ActivityLogged

Middleware

Multi-Tenancy

  • Core\Mod\Tenant\Middleware\RequireWorkspaceContext - Ensure workspace is set

Security

  • Core\Headers\SecurityHeaders - Apply security headers
  • Core\Bouncer\BlocklistService - IP blocklist
  • Core\Bouncer\Gate\ActionGateMiddleware - Action authorization

Service Providers

Register Core package in config/app.php:

'providers' => [
    // ...
    Core\CoreServiceProvider::class,
],

Or use auto-discovery (Laravel 11+).

Helpers

Global Helpers

// Get current workspace
$workspace = workspace();

// Create activity log
activity()
    ->performedOn($model)
    ->log('action');

// Generate CDN URL
$url = cdn_url('path/to/asset.jpg');

// Get CSP nonce
$nonce = csp_nonce();

Best Practices

1. Use Actions for Business Logic

// ✅ Good
$post = CreatePost::run($data);

// ❌ Bad
$post = Post::create($data);
event(new PostCreated($post));
Cache::forget('posts');

2. Log Activity for Audit Trail

class Post extends Model
{
    use LogsActivity;

    protected array $activityLogAttributes = ['title', 'status', 'published_at'];
}

3. Use Workspace Scoping

class Post extends Model
{
    use BelongsToWorkspace;
}

4. Leverage Module System

// Create focused modules with clear boundaries
Mod/Blog/
Mod/Commerce/
Mod/Analytics/

Changelog

See CHANGELOG.md

License

EUPL-1.2

Learn More