Release Notes

Curated highlights, migration guidance, and structured summaries of Glueful framework releases.

This page is a curated layer over the raw authoritative CHANGELOG.md. For complete detail (including every Added/Changed/Removed/Fix line) consult the full changelog.

Release Summary

VersionCodenameDateTypeRiskPrimary Theme
1.26.0Atria2026-01-31MinorLowExtension Discovery Fixes
1.25.0Ankaa2026-01-31MinorLowMulti-File Route Discovery
1.24.0Alpheratz2026-01-31MinorLowEncryption Service
1.23.0Aldebaran2026-01-31MinorLowBlob Visibility + Signed URLs
1.22.0Achernar2026-01-30MinorMediumGlobal State Removal / ApplicationContext DI
1.21.0Mira2026-01-24MinorLowFile Uploader Refactoring
1.20.0Regulus2026-01-24MinorLowFramework Simplification
1.19.2Canopus2026-01-24PatchLowValidationException Consolidation + Query Building
1.19.1Canopus2026-01-22PatchLowSimplified Configuration
1.19.0Canopus2026-01-22MinorLowSearch & Filtering DSL
1.18.0Hadar2026-01-22MinorLowWebhooks System
1.17.0Alnitak2026-01-22MinorLowRate Limiting Enhancements
1.16.0Meissa2026-01-22MinorLowAPI Versioning Strategy
1.15.0Rigel2026-01-22MinorLowReal-Time Development Server
1.14.0Bellatrix2026-01-22MinorLowInteractive CLI Wizards
1.13.0Saiph2026-01-22MinorLowEnhanced Scaffold Commands + Factories/Seeders
1.12.0Mintaka2026-01-21MinorLowAPI Resource Transformers
1.11.0Alnilam2026-01-21MinorLowORM / Active Record
1.10.0Elnath2026-01-21MinorLowException Handler + Request Validation
1.9.2Deneb2026-01-20PatchLowOpenAPI 3.1 + resource route expansion
1.9.1Castor2026-01-19PatchLowOpenAPI documentation refactor + UI generation
1.9.0Betelgeuse2026-01-17MinorMediumPHP 8.3 minimum + Symfony 7.3 compat
1.8.1Vega2025-11-23PatchLowPassword policy + async stream helper
1.8.0Spica2025-11-13MinorLowSession + login response events
1.7.4Arcturus2025-10-28PatchLowAuth status gate + migration docs
1.7.3Pollux2025-10-21PatchLowQueryBuilder 2-arg where/orWhere fix
1.7.2Antares2025-10-21PatchLowRoute loading resilience + dev server logs
1.7.1Canopus2025-10-21PatchLowExtension discovery/boot fix
1.7.0Procyon2025-10-18MinorMediumAsync & concurrency subsystem
1.6.2Capella2025-10-14PatchLowMail templates config ownership
1.6.1Arcturus2025-10-14PatchLowJWT RS256 signing
1.6.0Sirius2025-10-13MinorLowDI artifacts + conditional caching + DSN utils
1.5.0Orion2025-10-13MinorMediumNotifications DI + safer email flow
1.4.2Rigel2025-10-11PatchLowDocs + PSR-4 tidy-up
1.4.1Rigel2025-10-11PatchLowInstall flow hardening (SQLite-first)
1.4.0Rigel2025-10-11MinorMediumUnified session store, legacy removal
1.3.1Altair2025-10-10PatchLowInstall UX (CI non-interactive)
1.3.0Deneb2025-10-06FeatureLowHTTP client retries
1.2.0Vega2025-09-23Feature+BreakingMediumTasks & Jobs overhaul
1.1.0Polaris2025-09-22InfraLowTesting infrastructure
1.0.0Aurora2025-09-20MajorHighFirst stable split

v1.26.0 - Atria (Minor)

Released: January 31, 2026

Fixed extension discovery reliability and improved ExtensionManager efficiency for CLI tools and documentation generation.

Key Highlights

Extension Discovery Fallback

  • PackageManifest now falls back to installed.json when installed.php lacks provider metadata
  • Composer's optimized installed.php may omit the extra field where providers are specified
  • installed.json contains complete package metadata and is used as reliable fallback

Lazy Auto-Discovery

  • ExtensionManager::getProviders() now triggers discovery if not yet run
  • CLI commands that create their own container automatically discover extensions
  • Fixes empty provider lists in documentation generation and other CLI tools

Discovery Efficiency

  • Added $discovered flag to ensure discovery runs exactly once
  • Prevents redundant discovery when zero extensions are legitimately installed
  • More efficient than checking for empty providers array

Technical Details

The issue occurred because Composer's installed.php (used for fast autoloading) doesn't always include the extra field where Glueful extension providers are declared. The fix adds a fallback:

// PackageManifest::discover()
$providers = $this->extractFromInstalledPhp($installed);
if ($providers !== []) {
    return $providers;
}
// Fallback to installed.json which has complete metadata
return $this->extractFromInstalledJson();

Risk Assessment

  • Risk Level: Low
  • Breaking Changes: None
  • Migration Effort: None required

v1.25.0 - Ankaa (Minor)

Released: January 31, 2026

Enhanced RouteManifest with automatic discovery of multiple application route files, enabling domain-driven route organization.

Key Highlights

Multi-File Route Discovery

  • All *.php files in routes/ directory auto-discovered
  • Alphabetical loading order for deterministic behavior
  • Files starting with underscore (_helpers.php) excluded as partials
  • Double-load prevention tracks files to avoid duplicate registration

Route Loading Priority

  • Application routes load first (highest priority)
  • Framework API routes load second (act as fallbacks)
  • Public routes (health, docs) load last without prefix

Domain-Driven Organization

  • Split large route files into domain-specific files
  • Each file receives $router and $context in scope
  • Full control over prefixes per file

Example Structure

routes/
├── api.php           # Main/shared routes
├── identity.php      # Auth, profile, preferences
├── parps.php         # Domain-specific routes
├── social.php        # Follow, block
├── engagement.php    # Reactions, comments, bookmarks
├── moderation.php    # Reports
└── _helpers.php      # Shared helpers (excluded from auto-load)

Usage

// routes/identity.php
$router->group(['prefix' => api_prefix($context)], function (Router $router) {
    $router->post('/auth/login', [AuthController::class, 'login']);
    $router->post('/auth/register', [AuthController::class, 'register']);
    $router->get('/profile', [ProfileController::class, 'show']);
});

// routes/social.php
$router->group(['prefix' => api_prefix($context)], function (Router $router) {
    $router->post('/follow/{uuid}', [FollowController::class, 'follow']);
    $router->delete('/follow/{uuid}', [FollowController::class, 'unfollow']);
});

Risk Assessment

  • Risk Level: Low
  • Breaking Changes: None
  • Migration Effort: None required (backward compatible with single route file)

v1.24.0 - Alpheratz (Minor)

Released: January 31, 2026

Comprehensive encryption service providing secure, easy-to-use AES-256-GCM encryption for strings, files, and database fields with key rotation support.

Key Highlights

EncryptionService Core

  • AES-256-GCM authenticated encryption (industry standard)
  • Random 12-byte nonce per encryption (prevents ciphertext repetition)
  • 16-byte authentication tag for tamper detection
  • Key ID in output format enables O(1) key lookup during rotation
  • Self-identifying format: $glueful$v1$<key_id>$<nonce>$<ciphertext>$<tag>

String & Binary Encryption

  • encrypt($value, $aad) / decrypt($encrypted, $aad) for UTF-8 strings
  • encryptBinary() / decryptBinary() for arbitrary binary data
  • isEncrypted($value) to detect encrypted strings by format
  • AAD (Additional Authenticated Data) binding prevents cross-field attacks

File Encryption

  • encryptFile($source, $dest) - Encrypt entire files
  • decryptFile($source, $dest) - Decrypt encrypted files
  • CLI: php glueful encryption:file encrypt /path/to/file

Key Rotation

  • encryption.previous_keys config array for old keys
  • O(1) key lookup via key ID (no trial decryption needed)
  • CLI: php glueful encryption:rotate --table=users --columns=ssn,api_key
  • Seamless migration: old data decrypts with previous keys

CLI Commands

  • encryption:test - Verify encryption is working (6 self-tests)
  • encryption:file - Encrypt/decrypt files with --force, --delete-source
  • encryption:rotate - Re-encrypt database columns with --batch-size, --dry-run

Test Coverage

  • 32 unit tests covering all functionality
  • Core encryption, AAD binding, key validation, binary handling
  • Key rotation, file encryption, error handling

Usage Example

use Glueful\Encryption\EncryptionService;

$encryption = new EncryptionService($context);

// Basic encryption
$encrypted = $encryption->encrypt('sensitive data');
$decrypted = $encryption->decrypt($encrypted);

// With AAD (prevents cross-field attacks)
$encrypted = $encryption->encrypt($ssn, aad: 'user.ssn');
$decrypted = $encryption->decrypt($encrypted, aad: 'user.ssn');

// Binary data
$encrypted = $encryption->encryptBinary($binaryData);
$binary = $encryption->decryptBinary($encrypted);

// File encryption
$encryption->encryptFile('/path/to/file', '/path/to/file.enc');
$encryption->decryptFile('/path/to/file.enc', '/path/to/file');

Configuration

// config/encryption.php
return [
    'key' => env('APP_KEY'),  // base64:... format supported
    'cipher' => 'aes-256-gcm',
    'previous_keys' => array_filter(
        explode(',', env('APP_PREVIOUS_KEYS', ''))
    ),
];

Risk Assessment

  • Risk Level: Low
  • Breaking Changes: None
  • Migration Effort: None required (opt-in feature)

v1.23.0 - Aldebaran (Minor)

Released: January 31, 2026

Enhanced blob storage system with per-blob visibility controls, HMAC-signed URLs for secure temporary access, and comprehensive test coverage.

Key Highlights

Per-Blob Visibility

  • Blobs can now be marked as public or private individually
  • Upload requests accept visibility parameter
  • Defaults to configured uploads.default_visibility (private)
  • Public blobs accessible without auth (unless global access is private)
  • Private blobs require authentication or valid signed URL

Signed URL Support

  • New SignedUrl helper class for HMAC-based URL signing
  • Time-limited access with customizable TTL (default 1 hour, max 7 days)
  • New endpoint: POST /blobs/{uuid}/signed-url
  • Automatic signature validation on blob retrieval
  • Falls back to APP_KEY if no dedicated secret configured

Configuration Options

  • uploads.default_visibility - Set default visibility for new uploads
  • uploads.signed_urls.enabled - Enable/disable signed URL generation
  • uploads.signed_urls.secret - Dedicated secret for URL signing
  • uploads.signed_urls.ttl - Default TTL in seconds (default: 3600)

Comprehensive Test Coverage

  • SignedUrlTest - 17 tests covering URL generation, validation, expiration, tampering
  • UploadControllerTest - 38 tests covering resize params, caching, access control, visibility
  • Total: 55 new tests with 95 assertions

Usage Example

// Upload with visibility
POST /blobs
{
    "file": "...",
    "visibility": "private"
}

// Generate signed URL for temporary access
POST /blobs/{uuid}/signed-url?ttl=7200

// Response
{
    "uuid": "abc123",
    "signed_url": "https://example.com/blobs/abc123?expires=...&signature=...",
    "expires_in": 7200,
    "expires_at": "2026-01-31 14:00:00"
}

Risk Assessment

  • Risk Level: Low
  • Breaking Changes: None
  • Migration Effort: None required

v1.22.0 - Achernar (Minor)

Released: January 30, 2026

Major refactoring release replacing global state with explicit dependency injection via ApplicationContext. Improves testability, enables multi-app support, and prepares for long-running server environments.

Key Highlights

ApplicationContext Dependency Injection

  • All helper functions now require ApplicationContext as first parameter
  • config($context, $key), app($context, $id), base_path($context, $path)
  • Enables true state isolation for testing and multi-app scenarios
  • Prepares framework for Swoole/RoadRunner long-running servers

PHP 8.3 Compatibility

  • New QueueContextHolder class replaces deprecated static trait properties
  • Fixed InteractsWithQueue trait to avoid deprecated static method calls on traits
  • All deprecation warnings resolved

Service Provider Updates

  • register() and boot() methods now receive ApplicationContext parameter
  • Extensions must update to new signatures
  • Enables proper DI access throughout provider lifecycle

Console Command Auto-Discovery

  • Commands auto-discovered from src/Console/Commands/ directory
  • Just add #[AsCommand] attribute - no manual registration needed
  • Production caching for fast startup (auto-generated on first run)
  • New commands:cache CLI command for cache management

Code Quality Improvements

  • Fixed PHPStan errors (duplicate properties, missing returns, visibility)
  • Fixed 25+ PHPCS line length violations
  • Added PHPStan banned_code rule to prevent $GLOBALS usage
  • Cleaned up phpstan.neon excludePaths for removed files

Migration Required

This release requires updating code that uses helper functions:

// Before (1.21.x)
$value = config('app.name');
$service = app(MyService::class);

// After (1.22.0)
$value = config($context, 'app.name');
$service = app($context, MyService::class);

Extensions must update provider signatures:

// Before
public function boot(): void { }

// After
public function boot(ApplicationContext $context): void { }

Risk Assessment

  • Risk Level: Medium
  • Breaking Changes: Helper function signatures, ServiceProvider interface
  • Migration Effort: Low-Medium (search-replace for most cases)

v1.21.0 - Mira (Minor)

Released: January 24, 2026

Feature release refactoring the file upload system with improved architecture, pure PHP media metadata extraction, and enhanced configurability.

Key Highlights

ThumbnailGenerator

  • New dedicated class for thumbnail creation using ImageProcessor (Intervention Image)
  • Configurable width, height, and quality settings via config or method parameters
  • Configurable supported formats (JPEG, PNG, GIF, WebP by default)
  • Configurable thumbnail subdirectory for organized storage

MediaMetadataExtractor with getID3

  • Pure PHP metadata extraction — no external binaries required
  • Removed fragile ffprobe/shell_exec dependency for video duration
  • Supports images, audio, and video files
  • Falls back gracefully when getID3 is not installed

MediaMetadata Value Object

  • New readonly value object for type-safe metadata representation
  • Properties: type, width, height, durationSeconds
  • Helper methods: isImage(), isVideo(), isAudio(), hasDimensions()
  • getAspectRatio() and getFormattedDuration() utilities

Enhanced Configuration

  • New filesystem.uploader configuration section
  • Global toggle: THUMBNAIL_ENABLED to enable/disable thumbnails
  • Configurable dimensions: THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT
  • Configurable quality and subdirectory settings
  • Configurable thumbnail formats array

Storage Adapter Documentation

  • Added installation instructions for optional Flysystem adapters
  • S3/MinIO/DigitalOcean Spaces: league/flysystem-aws-s3-v3
  • Google Cloud Storage: league/flysystem-google-cloud-storage
  • Azure Blob Storage: league/flysystem-azure-blob-storage
  • SFTP/FTP adapters documented

Usage

Upload media with automatic thumbnail:

use Glueful\Uploader\FileUploader;

$uploader = new FileUploader($storage);
$result = $uploader->uploadMedia($file, 'media/images');

// Result includes file info, thumbnail URL, and metadata
$metadata = $result['metadata'];
if ($metadata->isImage()) {
    echo "Image: {$metadata->width}x{$metadata->height}";
} elseif ($metadata->isVideo()) {
    echo "Video duration: " . $metadata->getFormattedDuration();
}

Configuration (.env):

THUMBNAIL_ENABLED=true
THUMBNAIL_WIDTH=400
THUMBNAIL_HEIGHT=400
THUMBNAIL_QUALITY=80
THUMBNAIL_SUBDIRECTORY=thumbs

Migration

No breaking changes. The new classes are used internally by FileUploader.

New dependency: Add james-heinrich/getid3 if not already installed:

composer require james-heinrich/getid3

v1.20.0 - Regulus (Minor)

Released: January 24, 2026

Minor release focusing on framework simplification by removing unused subsystems and improving API URL structure for better separation of concerns.

Key Highlights

Resource Routes URL Structure

  • Added /data prefix to all generic CRUD resource routes
  • Routes changed from /api/v1/{table} to /api/v1/data/{table}
  • Prevents conflicts with custom application routes using same table names
  • Clearer separation between generic data API and custom endpoints

Async/Fiber System Removed

  • Removed entire Fiber-based async concurrency subsystem (~30 files)
  • System was unused in practice — Queue system handles background jobs
  • Removed AsyncProvider, config/async.php, and all async helpers
  • Simplifies codebase and reduces maintenance surface

Rate Limiting Consolidated

  • Removed basic rate limiting system (6 files)
  • Enhanced rate limiting system retained with all advanced features
  • EnhancedRateLimiterMiddleware provides tiered limits, multiple algorithms
  • Per-route rate limiting via #[RateLimit] attribute still fully supported

Configuration Cleanup

  • Removed unused ENABLE_AUDIT environment variable
  • Removed duplicate pagination settings from security config
  • Cleaner configuration with less unused options

Migration

Resource Routes (Breaking Change):

If your application calls generic resource endpoints, update the URLs:

- GET /api/v1/users
+ GET /api/v1/data/users

- POST /api/v1/products
+ POST /api/v1/data/products

- PUT /api/v1/orders/abc-123
+ PUT /api/v1/data/orders/abc-123

Async System (if used):

If you were using the Fiber-based async system (rare), migrate to the Queue system:

- use function Glueful\async;
- use function Glueful\await_all;
-
- $results = await_all([
-     async(fn() => $this->fetchUsers()),
-     async(fn() => $this->fetchProducts()),
- ]);
+ // Use queue jobs for background processing
+ $queue = service(\Glueful\Queue\QueueManager::class);
+ $queue->push(FetchUsersJob::class, ['callback' => $callbackUrl]);

Rate Limiting:

No migration needed. If using RateLimiterMiddleware alias, it now points to EnhancedRateLimiterMiddleware. All existing #[RateLimit] attributes and route middleware continue to work.


v1.19.2 - Canopus (Patch)

Released: January 24, 2026

Patch release consolidating ValidationException classes, improving SQL query building, and enhancing cross-database compatibility.

Key Highlights

ValidationException Consolidation

  • Unified from 3 exception classes to 1 canonical class
  • Removed legacy Glueful\Exceptions\ValidationException
  • Removed empty Glueful\Uploader\ValidationException
  • All code now uses Glueful\Validation\ValidationException
  • Static factory methods: forField(), forFields(), withErrors()

Database Query Building Improvements

  • PaginationBuilder improved regex for ORDER BY and LIMIT removal
  • Handles MySQL offset syntax (LIMIT 10, 20) and placeholders (LIMIT ?)
  • Added detection for UNION, INTERSECT, EXCEPT, CTEs, and window functions
  • QueryValidator now supports schema.table format

WhereClause Enhancements

  • Expanded valid operators: IS, IS NOT, BETWEEN, NOT BETWEEN, REGEXP, RLIKE, ILIKE
  • Added operator injection protection
  • Invalid NULL comparisons now throw clear exceptions

Migration

If using the legacy ValidationException directly:

- use Glueful\Exceptions\ValidationException;
+ use Glueful\Validation\ValidationException;

- throw new ValidationException('Error message');
+ throw ValidationException::forField('field', 'Error message');

- throw new ValidationException(['field' => 'error']);
+ throw ValidationException::withErrors(['field' => 'error']);

v1.19.1 - Canopus (Patch)

Released: January 22, 2026

Patch release simplifying API configuration by consolidating URL and version environment variables for cleaner deployment setup.

Key Changes

Simplified URL Configuration

  • All URLs now derive from single BASE_URL variable
  • Removed API_BASE_URL — no longer needed
  • Set BASE_URL to your deployment URL (e.g., https://api.example.com)

Simplified Version Configuration

  • Consolidated to single API_VERSION variable (integer format)
  • Removed API_VERSION_FULL — docs version derived automatically
  • Removed API_DEFAULT_VERSION — use API_VERSION instead
  • Changed from API_VERSION=v1 to API_VERSION=1

Migration

Update your .env file:

- BASE_URL=http://localhost:8000
- API_BASE_URL=http://localhost:8000
- API_VERSION=v1
- API_VERSION_FULL=1.0.0
- API_DEFAULT_VERSION=1
+ BASE_URL=http://localhost:8000
+ API_VERSION=1

Deployment examples:

ScenarioBASE_URL
Local developmenthttp://localhost:8000
API on subdomainhttps://api.example.com
API on main domainhttps://example.com

v1.19.0 - Canopus (Minor)

Released: January 22, 2026

Feature release introducing a powerful Search & Filtering DSL with multiple search engine adapters, QueryFilter classes, and comprehensive filtering operators for building flexible API queries.

Key Highlights

Search Engine Adapters

  • Pluggable search backends: Database (LIKE), Elasticsearch, Meilisearch
  • Unified SearchAdapterInterface for consistent API
  • Auto-migration for search index tracking table
  • Optional dependency support (no extra packages required for basic usage)

QueryFilter Classes

  • Reusable filter classes for API resources
  • Support for 20+ operators (eq, ne, gt, lt, in, between, like, etc.)
  • Automatic query building with secure parameter binding
  • Field whitelisting for security

Searchable ORM Trait

  • Add search functionality to any ORM model
  • Automatic index synchronization on model changes
  • Custom searchable fields configuration
  • Batch indexing support

scaffold:filter Command

  • Generate QueryFilter classes from CLI
  • Automatic field detection from models
  • Customizable operator sets per field

Search Engine Adapters

Core Classes:

ClassPurpose
SearchAdapterInterfaceContract for search adapters
SearchAdapterBase class with auto-migration
SearchResultValue object for search results
DatabaseAdapterSQL LIKE search (default)
ElasticsearchAdapterElasticsearch integration
MeilisearchAdapterMeilisearch integration

Optional Packages:

# For Elasticsearch support
composer require elasticsearch/elasticsearch:^8.0

# For Meilisearch support
composer require meilisearch/meilisearch-php:^1.0

Filtering Operators

OperatorAliasesDescriptionExample
eq=Equal tofilter[status]=active
ne!=, <>Not equalfilter[status][ne]=deleted
gt>Greater thanfilter[age][gt]=18
gte>=Greater or equalfilter[price][gte]=100
lt<Less thanfilter[stock][lt]=10
lte<=Less or equalfilter[rating][lte]=5
inIn arrayfilter[status][in]=active,pending
ninnot_inNot in arrayfilter[type][nin]=draft,archived
likecontainsContains substringfilter[name][like]=john
startsStarts withfilter[email][starts]=admin
endsEnds withfilter[domain][ends]=.com
betweenRange (inclusive)filter[price][between]=10,100
nullis_nullIs nullfilter[deleted_at][null]=1
not_nullIs not nullfilter[verified_at][not_null]=1

Quick Usage

use Glueful\Api\Filtering\QueryFilter;
use Glueful\Api\Filtering\Adapters\DatabaseAdapter;
use Glueful\Api\Filtering\Concerns\Searchable;

// Create a QueryFilter
class UserFilter extends QueryFilter
{
    protected array $filterable = [
        'name' => ['eq', 'like', 'starts'],
        'email' => ['eq', 'like'],
        'status' => ['eq', 'in', 'nin'],
        'created_at' => ['gt', 'gte', 'lt', 'lte', 'between'],
    ];

    protected array $sortable = ['name', 'email', 'created_at'];
}

// Apply filter to query
$filter = new UserFilter($request);
$users = $filter->apply(QueryBuilder::table('users'))->get();

// Use Searchable trait in models
class User extends Model
{
    use Searchable;

    protected array $searchableFields = ['name', 'email', 'bio'];
}

// Search users
$results = User::search('john doe', [
    'fields' => ['name', 'email'],
    'limit' => 20,
]);

// Use search adapter directly
$adapter = new DatabaseAdapter('users');
$results = $adapter->search('john', [
    'fields' => ['name', 'email'],
    'limit' => 10,
]);

Configuration

// config/api.php
'filtering' => [
    'default_limit' => 25,
    'max_limit' => 100,
    'allow_all_fields' => false,
    'strict_mode' => true,

    // Search engine configuration
    'search' => [
        // Options: 'database', 'elasticsearch', 'meilisearch'
        'driver' => env('API_SEARCH_DRIVER', 'database'),
        'index_prefix' => env('SEARCH_INDEX_PREFIX', ''),
        'auto_index' => env('SEARCH_AUTO_INDEX', false),

        // Elasticsearch (requires: elasticsearch/elasticsearch:^8.0)
        'elasticsearch' => [
            'hosts' => [env('ELASTICSEARCH_HOST', 'localhost:9200')],
        ],

        // Meilisearch (requires: meilisearch/meilisearch-php:^1.0)
        'meilisearch' => [
            'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
            'key' => env('MEILISEARCH_KEY'),
        ],
    ],
],

CLI Commands

# Generate a filter class
php glueful scaffold:filter UserFilter

# Generate with specific model
php glueful scaffold:filter UserFilter --model=User

# Generate with custom fields
php glueful scaffold:filter ProductFilter --fields=name,price,category,status

v1.18.0 - Hadar (Minor)

Released: January 22, 2026

Feature release introducing a comprehensive Webhooks System with event-driven integrations, subscription management, HMAC signature verification, and reliable delivery with exponential backoff retry.

Key Highlights

Event-Based Subscriptions

  • Subscribe to specific events or wildcard patterns (user.*, *)
  • Multiple event patterns per subscription
  • WebhookSubscription ORM model with listensTo() wildcard matching

HMAC-SHA256 Signatures

  • Stripe-style signature format (t=timestamp,v1=signature)
  • Timing-safe comparison to prevent timing attacks
  • Timestamp validation to prevent replay attacks
  • Configurable tolerance for signature expiration

Reliable Delivery System

  • Queue-based delivery via DeliverWebhookJob
  • Exponential backoff retry: 1m, 5m, 30m, 2h, 12h
  • Maximum 5 retry attempts (configurable)
  • WebhookDelivery ORM model for tracking

Auto-Migration

  • Database tables created automatically on first use
  • Follows DatabaseLogHandler pattern
  • Zero configuration required

Webhooks Components

Core Classes:

ClassPurpose
WebhookDispatcherCentral dispatcher with auto-migration
WebhookSubscriptionORM model for subscriptions
WebhookDeliveryORM model for delivery tracking
WebhookSignatureHMAC signature generation/verification
WebhookPayloadStandardized payload builder
WebhookStatic facade for easy access

Event Integration:

ClassPurpose
DispatchesWebhooksTrait for webhookable events
#[Webhookable]PHP 8 attribute for marking events
WebhookEventListenerBridge app events to webhooks
WebhookDispatchedEventEvent fired when webhooks queued

CLI Commands:

CommandPurpose
webhook:listList all webhook subscriptions
webhook:testTest a webhook endpoint
webhook:retryRetry failed webhook deliveries

REST API Endpoints

MethodEndpointPurpose
POST/api/webhooks/subscriptionsCreate subscription
GET/api/webhooks/subscriptionsList subscriptions
GET/api/webhooks/subscriptions/{id}Get subscription
PATCH/api/webhooks/subscriptions/{id}Update subscription
DELETE/api/webhooks/subscriptions/{id}Delete subscription
POST/api/webhooks/subscriptions/{id}/testSend test webhook
GET/api/webhooks/deliveriesList deliveries
POST/api/webhooks/deliveries/{id}/retryRetry delivery

Quick Usage

use Glueful\Api\Webhooks\Webhook;
use Glueful\Api\Webhooks\WebhookSignature;

// Dispatch a webhook
Webhook::dispatch('user.created', ['user' => $userData]);

// Create a subscription
$subscription = Webhook::subscribe(
    ['user.*', 'order.completed'],
    'https://example.com/webhooks'
);

// Make an event webhookable
class UserCreated extends BaseEvent
{
    use DispatchesWebhooks;

    public function webhookEventName(): string
    {
        return 'user.created';
    }

    public function webhookPayload(): array
    {
        return ['user' => $this->user];
    }
}

// Verify webhook signature (for receiving webhooks)
$isValid = WebhookSignature::verify(
    $payload,
    $request->headers->get('X-Webhook-Signature'),
    $secret
);

Webhook Payload Structure

{
    "id": "wh_evt_01HXYZ123456789ABCDEF",
    "event": "user.created",
    "created_at": "2026-01-22T12:00:00+00:00",
    "data": {
        "user": {
            "id": "usr_01HXYZ987654321FEDCBA",
            "email": "[email protected]",
            "name": "John Doe"
        }
    }
}

Webhook Headers

HeaderDescriptionExample
X-Webhook-IDUnique delivery IDwh_del_01HXYZ...
X-Webhook-EventEvent nameuser.created
X-Webhook-TimestampUnix timestamp1706011200
X-Webhook-SignatureHMAC signaturet=1706011200,v1=abc...
Content-TypeAlways JSONapplication/json
User-AgentGlueful identifierGlueful-Webhooks/1.0

Configuration

// config/api.php
'webhooks' => [
    'enabled' => true,
    'queue' => 'webhooks',
    'connection' => null,
    'signature_header' => 'X-Webhook-Signature',
    'signature_algorithm' => 'sha256',
    'timeout' => 30,
    'user_agent' => 'Glueful-Webhooks/1.0',
    'retry' => [
        'max_attempts' => 5,
        'backoff' => [60, 300, 1800, 7200, 43200],
    ],
    'require_https' => true,
    'cleanup' => [
        'keep_successful_days' => 7,
        'keep_failed_days' => 30,
    ],
]

Migration Notes

No breaking changes. The Webhooks System is opt-in and additive. Database tables are created automatically on first use.

  • Auto-migration creates webhook_subscriptions and webhook_deliveries tables
  • Webhooks are delivered asynchronously via the queue system
  • Integrates with existing WebhookDeliveredEvent and WebhookFailedEvent

v1.17.0 - Alnitak (Minor)

Released: January 22, 2026

Feature release introducing comprehensive Rate Limiting Enhancements with per-route limits, tiered access, cost-based limiting, and multiple algorithms.

Key Highlights

Per-Route Rate Limiting

  • #[RateLimit] attribute for declarative rate limiting on controllers and methods
  • IS_REPEATABLE for defining multiple time windows (e.g., per-minute AND per-hour limits)
  • Configurable by tier, key pattern, algorithm, and identifier (IP, user, custom)

Tiered Rate Limiting

  • Built-in tiers: anonymous, free, pro, enterprise
  • Per-tier limits for minute, hour, and day windows
  • TierResolver for automatic tier detection from request user data
  • Unlimited tier support for enterprise users

Multiple Algorithms

  • Fixed Window - Simple time-window counting
  • Sliding Window - More accurate request distribution using sorted sets
  • Token Bucket - Burst-friendly with refill rate calculation

Cost-Based Limiting

  • #[RateLimitCost] attribute for expensive operations
  • Consume multiple units per request (e.g., exports cost 100 units)
  • Reason tracking for cost documentation

Rate Limiting Components

Core Classes:

ClassPurpose
RateLimitManagerCentral orchestrator for rate limiting operations
TierManagerTier configuration and limit lookup
TierResolverResolve user tier from request
RateLimitHeadersIETF-compliant header generation
RateLimitResultImmutable result value object

Limiters:

ClassAlgorithm
FixedWindowLimiterSimple fixed time window
SlidingWindowLimiterSliding window using sorted sets
TokenBucketLimiterToken bucket with burst support

Storage Backends:

ClassPurpose
CacheStorageProduction storage using CacheStore
MemoryStorageIn-memory storage for testing

Attributes

RateLimit Attribute:

#[RateLimit(
    attempts: 60,           // Max requests
    perMinutes: 1,          // Time window (or perHours, perDays)
    tier: 'free',           // Optional tier restriction
    key: 'custom:{ip}',     // Custom key pattern
    algorithm: 'sliding',   // fixed, sliding, or bucket
    by: 'ip'                // Identifier: ip, user, or custom
)]

RateLimitCost Attribute:

#[RateLimitCost(
    cost: 100,
    reason: 'Full data export operation'
)]

IETF-Compliant Headers

The middleware adds standardized rate limit headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1706011260
RateLimit-Limit: 60
RateLimit-Remaining: 45
RateLimit-Reset: 1706011260
RateLimit-Policy: 60;w=60
Retry-After: 30  (when rate limited)

Quick Usage

use Glueful\Api\RateLimiting\Attributes\{RateLimit, RateLimitCost};

class UserController
{
    // 60 requests per minute, 1000 per hour
    #[RateLimit(attempts: 60, perMinutes: 1)]
    #[RateLimit(attempts: 1000, perHours: 1)]
    public function index(): Response { }

    // Tiered limits - different limits per subscription tier
    #[RateLimit(tier: 'free', attempts: 100, perDays: 1)]
    #[RateLimit(tier: 'pro', attempts: 10000, perDays: 1)]
    public function search(): Response { }

    // Cost-based limiting - expensive operations
    #[RateLimit(attempts: 1000, perDays: 1)]
    #[RateLimitCost(cost: 100, reason: 'Full data export')]
    public function export(): Response { }
}

// Route-level configuration
$router->get('/api/users', [UserController::class, 'index'])
    ->middleware(['enhanced_rate_limit']);

Configuration

// config/api.php
'rate_limiting' => [
    'enabled' => true,
    'algorithm' => 'sliding',
    'default_tier' => 'anonymous',
    'tiers' => [
        'anonymous' => [
            'requests_per_minute' => 30,
            'requests_per_hour' => 500,
            'requests_per_day' => 5000,
        ],
        'free' => [
            'requests_per_minute' => 60,
            'requests_per_hour' => 1000,
            'requests_per_day' => 10000,
        ],
        'pro' => [
            'requests_per_minute' => 300,
            'requests_per_hour' => 10000,
            'requests_per_day' => 100000,
        ],
        'enterprise' => [
            'requests_per_minute' => null,  // unlimited
            'requests_per_hour' => null,
            'requests_per_day' => null,
        ],
    ],
    'headers' => [
        'enabled' => true,
        'include_legacy' => true,  // X-RateLimit-* headers
        'include_ietf' => true,    // RateLimit-* headers
    ],
    'bypass_ips' => '127.0.0.1,::1',
],

Migration Notes

No breaking changes. The new enhanced rate limiting system is opt-in and coexists with the existing RateLimiterMiddleware.

  • Existing rate_limit:100,60,ip middleware syntax continues to work
  • New enhanced_rate_limit middleware is opt-in for attribute-based limiting
  • Both systems can coexist in the same application

v1.16.0 - Meissa (Minor)

Released: January 22, 2026

Feature release introducing comprehensive API Versioning with multiple resolution strategies, deprecation system, and sunset headers.

Key Highlights

Multiple Version Strategies

  • URL Prefix: /v1/users, /v2/users
  • Header: X-API-Version: 2.0
  • Query Parameter: ?api_version=2
  • Accept Header: Accept: application/vnd.api+json;version=2

Version Attributes

  • #[Version] for declaring version requirements on routes
  • #[Deprecated] for marking deprecated endpoints with messages
  • #[Sunset] for specifying retirement dates

Version Negotiation

  • Automatic version detection from request
  • Fallback to default version when not specified
  • Version comparison and constraint matching

API Versioning Components

Core Classes:

ClassPurpose
VersionManagerCentral manager for version resolution
ApiVersionValue object for version representation
VersionNegotiationMiddlewareAutomatic version detection

Resolvers:

ClassStrategy
UrlPrefixResolverExtract version from URL path (/v1/)
HeaderResolverRead X-API-Version header
QueryParameterResolverRead api_version query param
AcceptHeaderResolverParse Accept header media type

Attributes:

AttributePurpose
#[Version]Declare version requirements
#[Deprecated]Mark deprecated endpoints
#[Sunset]Specify sunset/retirement date

Quick Usage

use Glueful\Api\Versioning\Attributes\{Version, Deprecated, Sunset};

class UserController
{
    #[Version('1.0')]
    public function indexV1(): Response
    {
        return $this->json(['users' => $this->legacyFormat()]);
    }

    #[Version('2.0')]
    public function indexV2(): Response
    {
        return $this->json(['data' => $this->modernFormat()]);
    }

    #[Version('1.0')]
    #[Deprecated(message: 'Use v2 endpoint instead', since: '2026-01-01')]
    #[Sunset(date: '2026-06-01')]
    public function legacyEndpoint(): Response
    {
        // Deprecation warning headers automatically added
        return $this->json(['legacy' => true]);
    }
}

Response Headers

Deprecation and sunset information is communicated via headers:

Deprecation: true
Sunset: Sat, 01 Jun 2026 00:00:00 GMT
X-API-Deprecated-Message: Use v2 endpoint instead
X-API-Version: 1.0

Configuration

// config/api.php
'versioning' => [
    'enabled' => true,
    'default' => 'v1',
    'strategy' => 'url',  // url, header, query, accept
    'header' => 'X-API-Version',
    'query_param' => 'api_version',
    'deprecation' => [
        'sunset_header' => true,
        'warning_header' => true,
    ],
],

Migration Notes

No breaking changes. API versioning is opt-in and additive.

  • Versioning is entirely opt-in - existing routes continue to work unchanged
  • Apply #[Version] attributes only where version-specific behavior is needed
  • Use middleware version_negotiation to enable automatic version detection

v1.15.0 - Rigel (Minor)

Released: January 22, 2026

Feature release introducing Real-Time Development Server with file watching, integrated services, and enhanced logging.

Key Highlights

File Watching

  • Automatic file change detection with configurable polling
  • Auto-restart on code changes with --watch option
  • Configurable poll interval via --poll-interval

Integrated Services

  • --queue option for integrated queue worker
  • Port auto-selection when preferred port is in use
  • Graceful shutdown handling

Enhanced Logging

  • Colorized HTTP request logging
  • Method and status code highlighting
  • Request timing information

Development Server Components

Core Classes:

ClassPurpose
FileWatcherFile change detection with polling
RequestLoggerColorized request logging
LogEntryStructured request log entries

Quick Usage

# Basic development server
php glueful serve

# With file watching (auto-restart on changes)
php glueful serve --watch

# With integrated queue worker
php glueful serve --queue

# Custom port and poll interval
php glueful serve --port=8080 --watch --poll-interval=2000

# Full development setup
php glueful serve --watch --queue --port=8000

Command Options

OptionDescription
--port=8000Specify the port (default: 8000)
--host=localhostSpecify the host (default: localhost)
--watchEnable file watching for auto-restart
--queueStart integrated queue worker
--poll-interval=1000File watch poll interval in ms

Migration Notes

No breaking changes. All new features are opt-in via command flags.

  • Existing php glueful serve continues to work unchanged
  • New features activated via --watch and --queue flags
  • Completes Priority 2 developer experience features

v1.14.0 - Bellatrix (Minor)

Released: January 22, 2026

Feature release introducing Interactive CLI Wizards for enhanced developer experience with interactive prompts, progress bars, and spinner animations.

Key Highlights

Prompter Class

  • Fluent API for CLI prompts with automatic non-interactive fallback
  • ask(), askRequired() - Text input with validation
  • secret() - Hidden input for passwords/secrets
  • confirm() - Yes/no confirmation prompts
  • choice(), multiChoice() - Single and multiple selection menus
  • suggest() - Input with auto-completion suggestions

Progress Indicators

  • Enhanced ProgressBar wrapper with predefined formats
  • iterate() generator for automatic progress tracking
  • Spinner class with multiple animation styles
  • Easy withProgress() and withSpinner() helpers in BaseCommand

Interactive Scaffold Commands

  • Scaffold commands now prompt for arguments when not provided
  • scaffold:model supports full interactive mode
  • Maintains CLI compatibility with --no-interaction flag
  • Graceful fallback to defaults in CI/CD environments

Interactive Components

Prompter Methods:

MethodDescription
ask(question, default, validator)Text input with optional validation
askRequired(question, default)Required text input
secret(question, validator)Hidden input for passwords
confirm(question, default)Yes/no confirmation
choice(question, choices, default)Single selection from options
multiChoice(question, choices, defaults)Multiple selection
suggest(question, suggestions, default)Input with auto-completion

Progress Formats:

FormatDescription
FORMAT_NORMALBasic progress display
FORMAT_VERBOSEProgress with elapsed time
FORMAT_VERY_VERBOSEProgress with elapsed and estimated time
FORMAT_DEBUGFull progress with memory usage
FORMAT_WITH_MESSAGEProgress with custom message

Spinner Styles:

StyleAnimation
dotsBraille dot animation (default)
lineClassic line spinner
arrowsDirectional arrows
bouncingBouncing dots
growingGrowing bar
circleCircle quadrants
squareSquare quadrants
toggleToggle switch
simpleSimple dots

BaseCommand Helpers

New Methods:

MethodDescription
getPrompter()Get Prompter instance
isInteractive()Check if running interactively
prompt()Quick text input
promptRequired()Quick required text input
multiChoice()Multi-select from options
suggest()Input with auto-completion
createEnhancedProgressBar()Get enhanced progress bar
createSpinner()Create spinner instance
withProgress(items, callback)Process items with progress
withSpinner(callback, message)Run task with spinner
confirmDestructive(message)Confirmation for destructive ops

Migration Notes

No breaking changes. All interactive features gracefully fallback to defaults when --no-interaction flag is used.

Non-Interactive Mode:

# All prompts use defaults in CI/CD
php glueful scaffold:model User --migration --no-interaction

Requirements

  • No additional dependencies required
  • All features built on existing Symfony Console components

v1.13.0 - Saiph (Minor)

Released: January 22, 2026

Feature release introducing Enhanced Scaffold Commands and Database Factories & Seeders, completing Priority 2 developer experience features.

Key Highlights

Enhanced Scaffold Commands

  • scaffold:middleware - Generate route middleware implementing RouteMiddleware
  • scaffold:job - Generate queue jobs with configurable retries and timeouts
  • scaffold:rule - Generate validation rules with parameter support
  • scaffold:test - Generate PHPUnit unit and feature test classes

Database Factories

  • Factory base class with fluent interface for test data generation
  • Factory states for model variations (e.g., ->admin(), ->unverified())
  • Sequences for rotating attribute values across created models
  • Relationship support with has() and for() methods
  • HasFactory trait for ORM models enabling Model::factory() syntax

Database Seeders

  • Seeder base class with dependency ordering
  • Transaction support for atomic seeding operations
  • db:seed command with production environment protection
  • scaffold:seeder command for generating seeder classes

Scaffold Commands

New Commands:

CommandPurpose
scaffold:middlewareGenerate middleware implementing RouteMiddleware
scaffold:jobGenerate queue job classes extending Job
scaffold:ruleGenerate validation rule classes implementing Rule
scaffold:testGenerate PHPUnit test classes (unit/feature)
scaffold:factoryGenerate model factory classes
scaffold:seederGenerate database seeder classes
db:seedRun database seeders

Command Options:

CommandOptions
scaffold:middleware--force, --path
scaffold:job--queue, --tries, --backoff, --timeout, --unique
scaffold:rule--params, --implicit
scaffold:test--unit, --feature, --class, --methods
scaffold:factory--model, --force, --path
scaffold:seeder--model, --force, --path
db:seed--force (required in production)

Factory Components

Core Classes:

ClassPurpose
FactoryBase class for model factories with fluent interface
FakerBridgeBridge to optional Faker library with availability checking
HasFactoryTrait for models to enable Model::factory() syntax

Factory Methods:

MethodPurpose
definition()Define default model attributes
count(int $n)Set number of models to create
state(array|string|callable)Apply state transformations
sequence(array...)Rotate attribute values
make() / create()Build models (without/with persistence)
has(string $relation, int|Factory)Create with has-many relationships
for(string $relation, Factory|Model)Create with belongs-to relationships
recycle(Collection|Model)Reuse existing models for relationships

Seeder Components

Core Classes:

ClassPurpose
SeederBase class for database seeders
DatabaseSeederMain orchestrator for all seeders

Seeder Methods:

MethodPurpose
run()Abstract method to implement seeding logic
call(string|array $class)Call other seeders
withTransaction(callable)Wrap operations in database transaction
truncate(string $table)Clear table before seeding

Quick Usage

// Define a factory
class UserFactory extends Factory
{
    protected string $model = User::class;

    public function definition(): array
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'status' => 'active',
        ];
    }

    public function admin(): static
    {
        return $this->state(['role' => 'admin']);
    }
}

// Use the factory
$user = User::factory()->create();
$admins = User::factory()->count(5)->admin()->create();

// Create with relationships
$user = User::factory()
    ->has('posts', 3)
    ->create();
// Define a seeder
class UserSeeder extends Seeder
{
    protected array $dependencies = [RoleSeeder::class];

    public function run(): void
    {
        User::factory()->admin()->create([
            'email' => '[email protected]',
        ]);

        User::factory()->count(50)->create();
    }
}

// Run seeders
// php glueful db:seed
// php glueful db:seed UserSeeder
// php glueful db:seed --force (production)

Console Commands

# Generate scaffold classes
php glueful scaffold:middleware AuthMiddleware
php glueful scaffold:job ProcessPaymentJob --queue=payments --tries=3
php glueful scaffold:rule PhoneNumber --params=country
php glueful scaffold:test UserTest --feature --class=UserController

# Generate factories and seeders
php glueful scaffold:factory UserFactory --model=User
php glueful scaffold:seeder UserSeeder --model=User

# Run seeders
php glueful db:seed
php glueful db:seed UserSeeder
php glueful db:seed --force  # Required in production

Migration Notes

  • No breaking changes. All features are opt-in and additive.
  • Factories require fakerphp/faker as a dev dependency: composer require --dev fakerphp/faker
  • The db:seed command requires --force flag to run in production environments.
  • Generated files are placed in database/factories/ and database/seeders/.

v1.12.0 - Mintaka (Minor)

Released: January 21, 2026

Feature release introducing API Resource Transformers, completing the Priority 1 features for the framework's output layer.

Key Highlights

JSON Resource Transformation

  • JsonResource base class for transforming any data to JSON
  • Conditional attributes with when(), mergeWhen(), whenHas(), whenNotNull()
  • Response wrapping with configurable wrapper key
  • Additional metadata support via additional() method

Model Resources

  • ModelResource with ORM-specific helpers
  • attribute() and dateAttribute() for safe attribute access
  • relationshipResource() and relationshipCollection() for nested transformations
  • whenLoaded(), whenCounted(), whenPivotLoaded() for relationships

Collections & Pagination

  • ResourceCollection for multiple items with metadata
  • PaginatedResourceResponse with link generation
  • Support for both QueryBuilder and ORM pagination formats
  • withPaginationFrom() and withLinks() helpers

Resource Components

Core Classes:

ClassPurpose
JsonResourceBase class for transforming arrays/objects to JSON
ModelResourceExtended resource with ORM-specific helpers
ResourceCollectionCollection wrapper with metadata support
PaginatedResourceResponsePagination handling with link generation
AnonymousResourceCollectionCollection without dedicated class
MissingValueSentinel for conditional attribute omission

Conditional Attribute Methods:

MethodPurpose
when($condition, $value, $default)Include attribute conditionally
mergeWhen($condition, $attributes)Merge multiple attributes conditionally
whenHas($key)Include if key exists in source
whenNotNull($value)Include if value is not null
whenLoaded($relation)Include if relationship is loaded
whenCounted($relation)Include relationship count if available
whenPivotLoaded($table, $key)Include pivot table data

ModelResource Helpers:

MethodPurpose
attribute($key, $default)Get model attribute with default
dateAttribute($key)Format date as ISO 8601
whenDateNotNull($key)Include date only if not null
relationshipResource($relation, $class)Transform single relationship
relationshipCollection($relation, $class)Transform collection relationship
isRelationLoaded($relation)Check if relationship is loaded

Quick Usage

use Glueful\Http\Resources\JsonResource;
use Glueful\Http\Resources\ModelResource;

// Basic resource
class UserResource extends JsonResource
{
    public function toArray(): array
    {
        return [
            'id' => $this->resource['uuid'],
            'name' => $this->resource['name'],
            'email' => $this->when(
                $this->isAdmin(),
                $this->resource['email']
            ),
        ];
    }
}

// Model resource with relationships
class PostResource extends ModelResource
{
    public function toArray(): array
    {
        return [
            'id' => $this->attribute('uuid'),
            'title' => $this->attribute('title'),
            'created_at' => $this->dateAttribute('created_at'),
            'author' => $this->relationshipResource('author', UserResource::class),
            'comments_count' => $this->whenCounted('comments'),
        ];
    }
}

// Controller usage
public function index(): Response
{
    $posts = Post::with('author')->withCount('comments')->paginate(25);

    return PostResource::collection($posts['data'])
        ->withPaginationFrom($posts)
        ->withLinks('/api/posts')
        ->toResponse();
}

Console Commands

# Generate a basic resource
php glueful scaffold:resource UserResource

# Generate a model resource
php glueful scaffold:resource UserResource --model

# Generate a collection
php glueful scaffold:resource UserCollection --collection

# Overwrite existing
php glueful scaffold:resource UserResource --force

Migration Notes

  • No breaking changes. API Resources are opt-in and additive.
  • Existing controllers and responses continue to work unchanged.
  • Resources provide a transformation layer; use them when you need consistent JSON structures.

v1.11.0 - Alnilam (Minor)

Released: January 21, 2026

Feature release introducing the ORM/Active Record system, completing the data layer of Priority 1 features.

Key Highlights

Active Record ORM

  • Complete Active Record pattern implementation built on QueryBuilder
  • Model base class with CRUD operations and mass assignment protection
  • Builder class with eager loading and global scope support
  • Rich Collection class for model results

Relationships

  • HasOne, HasMany, BelongsTo for basic relationships
  • BelongsToMany with pivot table support
  • HasOneThrough and HasManyThrough for indirect relationships
  • Eager loading to prevent N+1 query problems

Model Features

  • Attribute casting with custom cast classes
  • SoftDeletes trait for recoverable deletion
  • HasTimestamps for automatic date management
  • Model lifecycle events integrated with framework events

ORM Components

Core Classes:

ClassPurpose
ModelBase class for all models with CRUD, attributes, and relations
BuilderQuery builder wrapper with model-aware functionality
CollectionRich collection class for model results
PivotPivot model for many-to-many relationships

Relationships:

RelationshipUse Case
HasOneOne-to-one (owning side)
HasManyOne-to-many
BelongsToInverse of HasOne/HasMany
BelongsToManyMany-to-many with pivot table
HasOneThroughOne-to-one through intermediate model
HasManyThroughOne-to-many through intermediate model

Traits:

TraitPurpose
HasAttributesAttribute get/set, casting, dirty tracking
HasRelationshipsRelationship definition and eager loading
HasTimestampsAutomatic created_at/updated_at
HasEventsModel lifecycle event integration
HasGlobalScopesGlobal query scope management
SoftDeletesSoft delete with deleted_at column

Custom Casts:

CastPurpose
AsJsonJSON encode/decode
AsArrayObjectJSON to ArrayObject
AsCollectionJSON to Collection
AsDateTimeString to DateTimeImmutable
AsEncryptedStringTransparent encryption
AsEnumBacked enum casting
AttributeCustom getter/setter accessors

Quick Usage

use Glueful\Database\ORM\Model;
use Glueful\Database\ORM\Concerns\{HasTimestamps, SoftDeletes};

class User extends Model
{
    use HasTimestamps, SoftDeletes;

    protected string $table = 'users';
    protected array $fillable = ['name', 'email'];
    protected array $casts = [
        'settings' => 'array',
        'email_verified_at' => 'datetime',
    ];

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}

// CRUD operations
$user = User::find(1);
$user = User::create(['name' => 'John', 'email' => '[email protected]']);
$user->update(['name' => 'Jane']);
$user->delete(); // Soft delete

// Eager loading
$users = User::with('posts')->get();

// Query scopes
$active = User::where('status', 'active')->get();

Console Commands

# Generate a new model
php glueful scaffold:model User

# With migration
php glueful scaffold:model Post --migration

# With soft deletes and timestamps
php glueful scaffold:model Comment --soft-deletes --timestamps

# With fillable attributes
php glueful scaffold:model Product --fillable=name,price,description

Migration Notes

  • No breaking changes. The ORM is opt-in and additive.
  • Existing QueryBuilder code continues to work unchanged.
  • Models require Model::setContainer() which is called automatically during framework boot.

v1.10.0 - Elnath (Minor)

Released: January 21, 2026

Feature release introducing centralized exception handling and declarative request validation, completing the foundation layer of Priority 1 features.

Key Highlights

Centralized Exception Handling

  • New ExceptionHandlerInterface contract for customizable exception handling
  • Typed HTTP exceptions for Client (4xx), Server (5xx), and Domain errors
  • ExceptionMiddleware for automatic exception-to-response conversion
  • Environment-aware error responses (detailed in dev, safe in production)

Declarative Request Validation

  • #[Validate] attribute for inline validation on controller methods
  • FormRequest base class with authorization and data preparation hooks
  • ValidatedRequest wrapper for type-safe validated data access
  • Laravel-style string rule syntax via RuleParser

Exception Handler

New typed exception classes provide semantic error handling:

Client Exceptions (4xx):

ExceptionHTTP CodeUse Case
BadRequestException400Malformed request
UnauthorizedException401Missing/invalid credentials
ForbiddenException403Insufficient permissions
NotFoundException404Resource not found
MethodNotAllowedException405Wrong HTTP method
ConflictException409Resource conflict
UnprocessableEntityException422Validation failed
TooManyRequestsException429Rate limit exceeded

Server Exceptions (5xx):

ExceptionHTTP CodeUse Case
InternalServerException500Unexpected server error
ServiceUnavailableException503Service temporarily down
GatewayTimeoutException504Upstream timeout

Domain Exceptions:

ExceptionUse Case
ModelNotFoundExceptionEntity not found in database
AuthenticationExceptionAuthentication failure
AuthorizationExceptionPermission denied
TokenExpiredExceptionJWT/session expired

Request Validation

New Validation Rules:

  • Confirmed - Password confirmation matching
  • Date, Before, After - Date validation and comparison
  • Url, Uuid, Json - Format validation
  • Exists - Database existence check
  • Nullable, Sometimes - Conditional validation
  • File, Image, Dimensions - File upload validation

Quick Usage

// Throwing typed exceptions
throw new NotFoundException('User not found', ['id' => $userId]);
throw new TooManyRequestsException(retryAfter: 60);

// Using #[Validate] attribute
#[Validate(['email' => 'required|email', 'name' => 'required|max:255'])]
public function store(ValidatedRequest $request): Response
{
    $data = $request->validated();
}

// Using FormRequest class
// Generate with: php bin/glueful make:request CreateUserRequest
public function store(CreateUserRequest $request): Response
{
    $data = $request->validated();
}

Migration Notes

  • No breaking changes. Both features are opt-in and additive.
  • Existing controllers continue to work unchanged.
  • Add 'validate' middleware to routes for automatic validation.

v1.9.2 - Deneb (Patch)

Released: January 20, 2026

OpenAPI 3.1 support with automatic resource route expansion from database schemas and documentation UI improvements.

Key Highlights

OpenAPI 3.1 Support

  • Full JSON Schema draft 2020-12 alignment
  • Nullable types use array syntax (type: ["string", "null"])
  • License supports SPDX identifier field
  • jsonSchemaDialect declaration included
  • Default version changed from 3.0.0 to 3.1.0

Resource Route Expansion

  • New ResourceRouteExpander class automatically expands {resource} routes
  • Generates table-specific endpoints with full database schemas
  • No more intermediate JSON files - schemas expand directly from database
  • Resource tags renamed from "Resources - {table}" to "Table - {table}"

Documentation UI Improvements

  • Scalar: Added hideClientButton and showDeveloperTools options
  • Tags in sidebar now sorted alphabetically
  • Output file renamed from swagger.json to openapi.json

Bug Fixes

  • Database: Fixed SchemaBuilder::getTableColumns() returning empty arrays due to incorrect array_is_list() check on associative column data.

Removed

  • TableDefinitionGenerator class - resource routes now expand directly from database schemas
  • --database and --table options from generate:openapi command (no longer needed)

Migration Notes

  • No breaking changes. The output file is now openapi.json instead of swagger.json.
  • If you had custom scripts referencing swagger.json, update the path.
  • Config key paths.swagger renamed to paths.openapi.

v1.9.1 - Castor (Patch)

Released: January 19, 2026

Major refactor of the OpenAPI documentation system with interactive UI generation support for Scalar, Swagger UI, and Redoc.

Key Highlights

Documentation UI Generation

  • New DocumentationUIGenerator class generates interactive HTML documentation
  • Supports Scalar (default), Swagger UI, and Redoc
  • New --ui option for generate:openapi command
  • Centralized configuration in config/documentation.php

Documentation Architecture Refactor

  • Replaced ApiDefinitionGenerator with focused TableDefinitionGenerator and OpenApiGenerator
  • Renamed ApiDefinitionsCommand to OpenApiDocsCommand
  • CommentsDocGenerator now uses phpDocumentor/ReflectionDocBlock for robust PHPDoc parsing
  • Extension routes discovered via ExtensionManager::getProviders() for Composer packages

New Validation Rules

  • Numeric - Validates numeric values with optional range, integer-only, and positive-only constraints
  • Regex - Validates values against regular expression patterns

Dependencies

  • Updated Symfony packages from ^7.3 to ^7.4
  • Added phpdocumentor/reflection-docblock: ^6.0

Quick Usage

# Generate docs with default UI (Scalar)
php glueful generate:openapi --ui

# Generate with specific UI
php glueful generate:openapi --ui=swagger-ui
php glueful generate:openapi --ui=redoc

Migration Notes

  • No breaking changes. The old ApiDefinitionGenerator is removed but the command interface remains compatible.
  • If you referenced internal documentation classes directly, update to use OpenApiGenerator and TableDefinitionGenerator.

v1.9.0 - Betelgeuse (Minor)

Released: January 17, 2026

Raises minimum PHP version to 8.3 and addresses Symfony Console 7.3 compatibility. Review the upgrade guide before updating.

Breaking Changes

  • PHP 8.3 Required: Minimum PHP version raised from 8.2 to 8.3. Ensure your environment is updated before upgrading.
  • Console Method Renamed: Application::addCommand(string $class) renamed to Application::registerCommandClass(string $class) due to Symfony Console 7.3 adding a conflicting addCommand(Command|callable) method.

Key Highlights

  • Routing: RouteManifest::load() now prevents double-loading routes during framework initialization, eliminating "Route already defined" warnings in CLI commands.
  • Security: Fixed PHPStan strict boolean check in CSRFMiddleware for explicit cookie token validation.
  • Tests: Added missing PSR-4 namespace declarations to async test files for proper autoloading.
  • CI: Test matrix now targets PHP 8.3 and 8.4 (dropped PHP 8.2 support).

New APIs

  • RouteManifest::reset() - Reset loaded state (for testing)
  • RouteManifest::isLoaded() - Check if routes have been loaded

Migration Notes

Action Required: Update your PHP environment to 8.3+ before upgrading.

  1. Environment: Ensure PHP 8.3 or higher is installed
  2. Method Rename: If you called $app->addCommand(MyCommand::class), update to $app->registerCommandClass(MyCommand::class)
  3. Dependencies: Run composer update to refresh the lock file

v1.8.1 - Vega (Patch)

Released: November 23, 2025

Tightens password policy helpers with lowercase enforcement and makes the async stream helper smarter about buffering existing async transports.

Key Highlights

  • Security: Utils::validatePassword() now supports a $requireLowercase flag alongside existing numeric, special-character, and uppercase toggles, enabling mixed-case enforcement without custom validators.
  • Async I/O: async_stream() accepts raw resources, AsyncStream, or BufferedAsyncStream instances and normalizes them before optionally wrapping in a buffer. This keeps buffered helpers type-safe and resolves analyzer warnings about expected vs. actual types.

Migration Notes

  • No breaking changes. Opt into the lowercase flag when updating password policies, and the stream helper works seamlessly with existing calls.

v1.8.0 - Spica (Minor)

Released: November 13, 2025

First-class session and login response events. Safely enrich cached session payloads and shape login responses without modifying framework code.

Key Highlights

  • Events/Auth:
    • SessionCachedEvent: Dispatched after a session is written to cache (and DB). Listeners can augment the cached payload (e.g., user.organization) or warm caches.
    • LoginResponseBuildingEvent: Dispatched just before returning the login JSON. Provides a mutable response map so apps can add fields (e.g., context.organization).
    • LoginResponseBuiltEvent: Dispatched after the response is finalized for analytics/metrics.
  • Wiring:
    • Session cached hook in SessionCacheManager::storeSession() after successful cache->set.
    • Login response hooks in AuthController::login() just before returning.
  • Docs: Proposal updated with final API (setter-based mutation, paths under src/...) and example listeners.

Migration Notes

  • Backward compatible: No behavior change unless listeners are registered.
  • Performance: Events are synchronous; offload heavy work to queues.
  • Guidance: Prefer adding app-specific fields under context.* to avoid collisions.

v1.7.4 - Arcturus (Patch)

Released: October 28, 2025

Minimal, configurable account‑status gate in AuthenticationService (secure by default).

Key Highlights

  • Auth: Enforce allowed statuses during username/password login and refresh‑token flows via security.auth.allowed_login_statuses (default ['active']).
    • Fails silently to prevent account enumeration.
    • Policy intentionally lean for app‑level extension.
  • Docs: Added docs/migration-examples-views-functions.md covering three approaches to create views/functions in migrations (SchemaBuilder queue, direct PDO exec, QueryBuilder DDL).

Migration Notes

  • If you experimented with auth.allowed_login_statuses, move it to security.auth.allowed_login_statuses.

v1.7.3 - Pollux (Patch)

Released: October 21, 2025

QueryBuilder 2‑argument where/orWhere fix and clearer dev‑server logs.

Key Highlights

  • Database/QueryBuilder: Normalize 2‑argument where($column, $value) and orWhere($column, $value) to use = internally.
    • Prevents a TypeError when non‑string values were misread as the operator.
    • Improves portability for boolean filters across PostgreSQL/MySQL/SQLite.
  • CLI: serve further reclassifies PHP built‑in server access/lifecycle lines written to STDERR (e.g., “Accepted”, “Closed without sending a request”, “200: GET /…”) as normal output, while preserving real errors.

v1.7.2 - Antares (Patch)

Released: October 21, 2025

Route loading resilience and quieter dev‑server logs.

Key Highlights

  • Extensions: ServiceProvider::loadRoutesFrom() is idempotent and exception‑safe now.
    • Prevents duplicate route registration if a routes file is loaded more than once.
    • Catches exceptions from route files; logs and continues in production, rethrows in non‑production for fast feedback.
  • CLI: serve reclassifies common PHP built‑in server access/startup lines from STDERR as normal output, reducing false [ERROR] noise.

v1.7.1 - Canopus (Patch)

Released: October 21, 2025

Fixes extension discovery/boot sequencing so extensions reliably load and their migrations appear.

Key Highlights

  • Extensions: Call ExtensionManager::discover() before ::boot() during framework initialization so enabled providers load correctly.
  • Migrations: Extension migrations registered via loadMigrationsFrom() are discovered by migrate:status/migrate:run once providers are discovered at boot.
  • CLI: extensions:why/extensions:list now reflect included providers after boot, improving diagnostics.

Impact

  • Apps that previously saw “No pending migrations found” for extension migrations should now see them once the provider is enabled. No config changes required.

v1.7.0 - Procyon

Released: October 18, 2025

Major async/concurrency subsystem. Fiber‑based scheduler, async HTTP client with streaming/pooling, buffered I/O, cooperative cancellation, metrics instrumentation, and a Promise‑style wrapper. Centralized configuration and DI wiring included.

Key Highlights

Fiber‑Based Async Scheduler

  • Glueful\\Async\\FiberScheduler with spawn, all, race, sleep
  • Resource‑limit guards (max concurrent tasks, per‑task execution time, optional memory/FDS caps)
  • Rich metrics hooks (suspend/resume, queue depth, cancellations, resource limits)

Async HTTP + Streaming

  • Glueful\\Async\\Http\\CurlMultiHttpClient with cooperative polling and optional max_concurrent
  • poolAsync() for concurrent requests; HttpStreamingClient::sendAsyncStream() for chunked callbacks
  • FakeHttpClient for test isolation

Async I/O + Helpers

  • AsyncStream and BufferedAsyncStream with line/whole‑read helpers and buffered reads/writes
  • Helpers: scheduler(), async(), await(), await_all(), await_race(), async_sleep(), async_sleep_default(), async_stream(), cancellation_token()
  • Promise wrapper: Glueful\\Async\\Promise with then/catch/finally, all/race

Configuration & DI

  • New config/async.php for scheduler, http, streams, limits
  • AsyncProvider wires Metrics, Scheduler, HttpClient; registers AsyncMiddleware (alias async)

Migration Notes

  • Backward‑compatible defaults: limits disabled when set to 0
  • To adopt in routes, add middleware alias async or use helpers (async(), await_all(), etc.)

v1.6.2 - Capella

Released: October 14, 2025

Template configuration responsibility moved to the Email Notification extension.

What's Changed

  • The primary templates directory is now controlled by the Email Notification extension configuration
  • Framework no longer sets a default services.mail.templates.path
  • Custom paths, caching, layout, mappings, and globals remain supported

Migration Notes

  • If you relied on the framework default templates path, configure email-notification.templates.extension_path in the extension
  • Or set services.mail.templates.path in your app config

v1.6.1 - Arcturus

Released: October 14, 2025

JWT RS256 signing support for generating JWTs using an RSA private key.

Key Highlights

  • Auth/JWT: JWTService::signRS256(array $claims, string $privateKey)
  • Requires PHP openssl extension

Risk scale: High = architectural changes / broad API shifts; Medium = targeted breaking or migration; Low = additive or internal refactors.

v1.6.0 - Sirius

Released: October 13, 2025

Minor features and DX improvements. This release focuses on compiled DI artifacts, conditional HTTP caching helpers, and configuration utilities.

Key Highlights

Compiled Container Artifacts

  • di:container:compile now emits services.json at storage/cache/container/services.json with fields: shared, tags, provider, type, alias_of.
  • di:container:map prefers the compiled services.json in production (no reflection overhead).
  • ContainerFactory prefers a precompiled container class in production when available.

Conditional HTTP Caching

  • New ConditionalCacheMiddleware handles ETag/If‑None‑Match and Last‑Modified/If‑Modified‑Since to return 304 efficiently.
  • Response::withLastModified(DateTimeInterface) helper for setting validators.

DSN Utilities

  • Glueful\\Config\\DsnParser with parseDbDsn() and parseRedisDsn().
  • New CLI: config:dsn:validate validates DSNs from flags or environment (e.g., DATABASE_URL, REDIS_URL).

Notes

  • OpenTelemetry support is planned as a Glueful extension package (not part of the core framework).

v1.5.0 - Orion

Released: October 13, 2025

Notifications DI wiring and safer email flows. This release introduces a shared notifications provider and makes email verification/password reset flows more robust.

Key Highlights

Notifications DI Provider

  • New NotificationsProvider registers shared ChannelManager and NotificationDispatcher in the container.
  • Extensions can register channels/hooks against the dispatcher during boot.

Safer Email Flows

  • Email verification and SendNotification now prefer DI‑resolved dispatcher/channel with safe fallbacks.
  • Removed hard ExtensionManager prechecks; channel availability is evaluated at send time.
  • Soft diagnostics when the email channel is unavailable or when no channels succeeded.

What's Changed

  • Retry configuration aligned to email-notification.retry.

v1.4.2 - Rigel (Patch)

Released: October 11, 2025

Developer‑facing tidy‑ups; no runtime behavior changes.

Fixes

  • PSR‑4 autoloading for tests: corrected a test namespace to match Glueful\\Tests\\… and remove Composer warnings during autoload generation.

Documentation

  • Roadmap and this page updated to reflect 1.4.1’s SQLite‑first install flow and non‑interactive flags.

v1.4.1 - Rigel (Patch)

Released: October 11, 2025

Installation flow hardening and SQLite‑first defaults. This patch improves non‑interactive installs and avoids fragile checks during initial setup.

Key Highlights

SQLite‑first Install & Non‑Interactive Flags

  • Install enforces SQLite during setup; other engines can be configured after install
  • migrate:run executed with --force; in quiet installs also --no-interaction and --quiet
  • cache:clear during install passes --force and respects quiet mode flags
  • Removed DB connection health check during install (SQLite does not require network; migrations surface issues)

Quick Usage

# Default (SQLite) install
php glueful install --quiet --force

# After switching DB in .env, run migrations
php glueful migrate:run -f --no-interaction

Notes

  • Post‑install message now includes a brief guide for switching databases and running migrations later.

v1.4.0 - Rigel

Released: October 11, 2025

Rigel consolidates session management behind a single, testable SessionStore API and removes the legacy TokenStorageService. It unifies TTL policy, standardizes DI resolution, and hardens cache-key handling for tokens.

Key Highlights

Unified Session Store

  • New SessionStoreInterface + default SessionStore for create/update/revoke/lookup/health
  • Canonical TTL helpers: getAccessTtl(), getRefreshTtl() (provider + remember‑me aware)
  • DI consistency with SessionStoreResolver utility and ResolvesSessionStore trait
  • Safe cache keys for token mappings (hashed tokens + sanitized prefixes)

What's Changed

  • TokenManager defers TTL policy to SessionStore and persists sessions via the store.
  • JwtAuthenticationProvider and SessionCacheManager resolve the store via the trait; fewer ad‑hoc new calls.
  • SessionAnalytics prefers the store for listing sessions (falls back to cache query when needed).
  • JWTService cleanup; rely on DB‑backed revocation.

Removed

  • TokenStorageService and TokenStorageInterface (fully migrated to SessionStore).
  • Deprecated code paths tied to the legacy storage/invalidation.

Migration Notes

If you previously referenced TokenStorageService, migrate to SessionStoreInterface:

use Glueful\\Auth\\Interfaces\\SessionStoreInterface;

/** @var SessionStoreInterface $store */
$store = container()->get(SessionStoreInterface::class);

// Create
$created = $store->create($user, $tokens, 'jwt', false);

// Read / Update / Revoke
$session = $store->getByAccessToken($tokens['access_token']);
$updated = $store->updateTokens($tokens['refresh_token'], $newTokens);
$revoked = $store->revoke($newTokens['access_token']);

v1.3.1 - Altair

Released: October 10, 2025

Altair improves CI/automation ergonomics: the installation command now runs truly non-interactive for scripted environments, and static analysis warnings are cleaned up.

Key Highlights

Install Command: Non-Interactive Mode

  • php glueful install skips the environment confirmation prompt when any of these flags are present: --quiet, --no-interaction, or --force.
  • Keeps helpful output; add Symfony's global -q if you want near-silent runs.

What's Changed

  • Unattended installs: skip Have you set all required environment variables? when running with --quiet, --no-interaction, or --force.
  • DX: remove redundant method_exists() check around InputInterface::isInteractive() to satisfy PHPStan.

Quick Usage

# CI-safe, non-interactive install
php glueful install --quiet --force

# Or use Symfony's global flag to reduce verbosity further
php glueful install --quiet --force -q

Migration

  • No action required. For CI pipelines, prefer --quiet --force to avoid prompts.

v1.3.0 - Deneb

Released: October 6, 2025

Deneb refines the HTTP client with first-class, configurable retries via Symfony's retry system, improving resilience and clarity for API integrations.

Key Highlights

HTTP Client Retry Support

  • Automatic retry logic with configurable strategies
  • Built on Symfony's RetryableHttpClient for production-ready reliability
  • Sensible defaults for common scenarios (payments, external APIs)

What's New

  • Retry Configuration API: New Client::withRetry(array $config) method to wrap any configured client with retry logic
  • ApiClientBuilder Enhancements:
    • retries() - Configure retry behavior
    • buildWithRetries() - Build client with retries enabled
    • getRetryConfig() - Inspect retry configuration
  • Smart Defaults: Preset retry configurations for payments and external service integrations

Code Example

use Glueful\Http\Client;
use Glueful\Http\Builders\ApiClientBuilder;

// Configure retries with custom settings via DI + scoped client
$client = app(Client::class)
    ->createScopedClient(['base_uri' => 'https://api.example.com'])
    ->withRetry([
        'max_retries' => 3,
        'delay_ms' => 1000,
        'multiplier' => 2.0,
        'status_codes' => [429, 500, 502, 503, 504],
    ]);

// Or use builder with presets
$apiClient = (new ApiClientBuilder(app(Client::class)))
    ->baseUri('https://payment-gateway.com')
    ->forPayments()
    ->buildWithRetries();

Additional Context

Underlying change: retry logic migrated from prior internal handling to Symfony's strategy model (status-based exponential backoff) improving observability and consistency.


v1.2.0 - Vega

Released: September 23, 2025

Vega introduces robust task management architecture and enhanced testing reliability. Named after one of the brightest stars in the night sky, this release brings enhanced reliability and clarity to task execution and framework testing infrastructure.

Key Highlights

Tasks/Jobs Architecture

  • Clean separation of business logic (Tasks) from queue execution (Jobs)
  • Support for both direct execution and queued processing
  • Comprehensive task management system for common operations

What's New

Task Management System

New task classes for common maintenance operations:

  • CacheMaintenanceTask - Comprehensive cache maintenance
  • DatabaseBackupTask - Automated backups with retention policies
  • LogCleanupTask - Log file cleanup
  • NotificationRetryTask - Retry failed notifications
  • SessionCleanupTask - Session maintenance

Queue Job Wrappers

Reliable queue integration with dedicated job classes:

  • CacheMaintenanceJob, DatabaseBackupJob, LogCleanupJob
  • NotificationRetryJob, SessionCleanupJob
  • Built-in failure handling and logging

Enhanced Console Commands

# New cache maintenance command with improved options
php glueful cache:maintenance --operation=clearExpiredKeys

# Enqueue instead of running immediately
php glueful cache:maintenance --operation=fullCleanup --queue

Comprehensive Testing Suite

  • Complete integration test coverage for all Tasks and Jobs
  • Enhanced test bootstrap with proper DI container management
  • Fixed test interference issues

Stability & Fixes

  • Improved container state isolation in tests.
  • Reduced interference between suites by refining bootstrap resets.

Removed (for clarity)

  • Legacy Cron classes replaced by Tasks + Queue Jobs.

Migration Guide

Breaking Changes: The src/Cron/ directory has been removed in favor of the new src/Tasks/ + src/Queue/Jobs/ pattern.

Step 1: Update Namespace Imports

Before:

use Glueful\Cron\CacheMaintenance;
use Glueful\Cron\DatabaseBackup;

After:

use Glueful\Tasks\CacheMaintenanceTask;
use Glueful\Queue\Jobs\CacheMaintenanceJob;

Step 2: Update Service Registration

Before:

// In your service provider
$container->set('cron.cache', CacheMaintenance::class);

After:

// Tasks are auto-registered via TasksProvider
// For queued execution, push the Job wrapper via QueueManager:
use Glueful\Queue\Jobs\CacheMaintenanceJob;
use Glueful\Queue\QueueManager;

$queue = app(QueueManager::class);
$queue->push(CacheMaintenanceJob::class, ['operation' => 'clearExpiredKeys'], 'maintenance');

Step 3: Update Direct Execution

Before:

$cronJob = new CacheMaintenance();
$cronJob->execute();

After:

/** @var CacheMaintenanceTask $task */
$task = app(CacheMaintenanceTask::class);
$task->handle(['driver' => 'redis', 'operation' => 'clearExpiredKeys']);

v1.1.0 - Polaris

Released: September 22, 2025

Polaris introduces comprehensive testing infrastructure and enhanced documentation to guide framework development. Like the North Star that guides navigation, this release provides developers with the tools and knowledge to build robust applications.

Key Highlights

Testing Infrastructure

  • Base TestCase class for application-level tests
  • Framework state reset utilities for isolation
  • Expanded event system documentation

What's New

  • Testing Utilities: Base TestCase with container access ($this->get()), app boot, refresh support
  • Event Documentation: Expanded conceptual and usage coverage (listeners, dispatch patterns)
  • (Planned / Pending) Event abstraction & HTTP helper methods (e.g. post(), assertStatus()) — not yet present in current codebase; will appear in a future minor.

Example (Current Capabilities)

use Glueful\Testing\TestCase;

final class UserLifecycleTest extends TestCase
{
    public function test_boots_application(): void
    {
        $this->assertNotNull($this->app());
        $this->assertTrue($this->has(\Glueful\Logging\LogManager::class));
    }
}

Helpers like $this->post() or $this->assertStatus() will be documented once implemented; they are intentionally omitted here to avoid confusion.


v1.0.0 - Aurora

Released: September 20, 2025

Aurora — The first stable release of the split Glueful Framework package. This version establishes the framework runtime as a standalone library with comprehensive features and sets a clear baseline for future 1.x releases.

Key Highlights

Major Features

  • Custom Dependency Injection system (DSL + compiled container)
  • Permissions & authorization layer
  • Observability & metrics (profiling + middleware)
  • Security middleware suite (auth, rate limiting, CSRF, headers, guards)
  • File upload pipeline (validation, S3, signed URLs)
  • Extensions system v2 (deterministic provider discovery)

What's New

Router

  • Fast static/dynamic matching with first-segment bucketing
  • Attribute-based route definitions (#[Get], #[Post], etc.)
  • Route cache compiler with automatic invalidation
  • Standardized JSON errors for 404/405
use Glueful\Routing\Attributes\{Get, Post};

class UserController
{
    #[Get('/users/{id}')]
    public function show(int $id): Response
    {
        // Route automatically registered
    }

    #[Post('/users')]
    public function create(Request $request): Response
    {
        // POST /users
    }
}

Dependency Injection Overhaul

  • Custom lightweight container optimized for Glueful
  • DSL for service registration with compile-time generation
  • Compiled container support for faster production startup
// Typical application bootstrap (dev/prod aware)
use Glueful\Container\Bootstrap\ContainerFactory;

$container = ContainerFactory::create(prod: false); // prod:true enables compilation path

// Overriding / adding a service at runtime (e.g. during tests)
$testContainer = $container->with([
    'fake.clock' => fn() => new \Glueful\Support\Clock\FrozenClock('2025-10-08T00:00:00Z'),
]);

// Accessing a service
$logger = $testContainer->get('logger');

// Defining services in a Provider (preferred for framework + extensions)
use Glueful\Container\Providers\BaseServiceProvider;
use Glueful\Container\Definition\FactoryDefinition;

final class PaymentProvider extends BaseServiceProvider
{
    public function defs(): array
    {
        return [
            // FactoryDefinition gives you full control
            'payment.client' => new FactoryDefinition('payment.client', function(\Psr\Container\ContainerInterface $c) {
                return new PaymentClient($c->get('http.client'), baseUrl: config('payments.base_url'));
            }),
            // Simple autowire helper (shared by default)
            PaymentService::class => $this->autowire(PaymentService::class),
        ];
    }
}

Service registration happens through Provider defs() returning definitions; runtime overrides use $container->with([...]).

Security Enhancements

New middleware suite:

  • Authentication & Authorization
  • Rate Limiting
  • CSRF Protection
  • Security Headers
  • Admin Guard
  • IP Allow-listing

New CLI security commands:

php glueful security:check
php glueful security:scan
php glueful security:vulnerabilities

File Upload System

  • Native Symfony UploadedFile support
  • Extension and MIME validation
  • Hazard scanning for security
  • S3 storage with signed URLs
  • Configurable ACL (private by default)
use Glueful\Uploader\FileUploader;

// Upload using the framework uploader (validates and stores metadata)
$uploader = app(FileUploader::class);
$result = $uploader->handleUpload(
    $token,                   // your upload token / CSRF guard
    ['user_id' => $userId, 'key' => 'document'],
    $request->files->all()    // or $_FILES
);

// $result contains: ['uuid' => '...', 'url' => 'https://...']
// To create a presigned link for a known path on S3:
// $signedUrl = app(Glueful\Uploader\Storage\FlysystemStorage::class)
//     ->getSignedUrl('documents/example.pdf', 3600);

Observability & Metrics

  • BootProfiler for startup timing analysis
  • MetricsMiddleware for request tracking
  • ApiMetricsService for API analytics
  • Pluggable tracing middleware

Extensions System v2

  • Deterministic provider discovery
  • App providers + vendor extensions unified
  • Extension service compilation for better performance

Migration Guide

Breaking Changes: This release includes significant architectural changes. Please review carefully before upgrading.

1. Dependency Injection Container

The framework now uses a custom DI container instead of Symfony DI.

Before:

use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$container->register('my.service', MyService::class);

After:

// Create container (typically done via ContainerFactory in bootstrap)
use Glueful\Container\Bootstrap\ContainerFactory;

$container = ContainerFactory::create(prod: false); // prod:true enables compilation

// Runtime service override (e.g., in tests)
$container = $container->with([
    'my.service' => fn($c) => new MyService($c->get('dependency')),
]);

// Or define services in a Provider (recommended for extensions/framework services)
use Glueful\Container\Providers\BaseServiceProvider;
use Glueful\Container\Definition\FactoryDefinition;

final class MyServiceProvider extends BaseServiceProvider
{
    public function defs(): array
    {
        return [
            // Factory definition
            'my.service' => new FactoryDefinition('my.service', function($c) {
                return new MyService($c->get('dependency'));
            }),
            // Or autowire
            MyService::class => $this->autowire(MyService::class),
        ];
    }
}

2. Event System (PSR-14)

Event system migrated from custom implementation to PSR-14.

Before:

$events->listen('user.created', function($event) {
    // Handle event
});

After:

use Psr\EventDispatcher\EventDispatcherInterface;
use Glueful\Events\Contracts\BaseEvent;
use Glueful\Events\ListenerProvider;

class UserCreated extends BaseEvent
{
    public function __construct(public User $user) {}
}

// Register listener via ListenerProvider
$provider = app(ListenerProvider::class);
$provider->addListener(UserCreated::class, function (UserCreated $event) {
    // Handle event
});

// Dispatch via PSR-14 dispatcher
$dispatcher = app(EventDispatcherInterface::class);
$dispatcher->dispatch(new UserCreated($user));

3. Storage Configuration

Migrated to Flysystem with updated configuration structure.

Before:

// config/storage.php
return [
    's3' => [
        'key' => env('AWS_KEY'),
        'secret' => env('AWS_SECRET'),
    ]
];

After:

// config/storage.php
return [
    's3' => [
        'driver' => 's3',
        'key' => env('S3_ACCESS_KEY_ID'),
        'secret' => env('S3_SECRET_ACCESS_KEY'),
        'region' => env('S3_REGION', 'us-east-1'),
        'bucket' => env('S3_BUCKET'),
        'endpoint' => env('S3_ENDPOINT'),
        'use_path_style_endpoint' => true,

        // Optional behavior hints
        'acl' => env('S3_ACL', 'private'),
        'signed_urls' => env('S3_SIGNED_URLS', true),
        'signed_ttl' => (int) env('S3_SIGNED_URL_TTL', 3600),
        'cdn_base_url' => env('S3_CDN_BASE_URL'),
    ],
];

4. Environment Variable Updates

Several environment variables have been renamed for consistency:

# Redis
REDIS_CACHE_DB REDIS_DB

# Mail
MAIL_SECURE MAIL_ENCRYPTION

# Logging
LOG_PATH LOG_FILE_PATH
# New: LOG_TO_DB=false (default)

# S3 Storage
# New variables:
S3_ACL=private
S3_SIGNED_URLS=true
S3_SIGNED_URL_TTL=3600

# PSR-15 Middleware
PSR15_ENABLED=true
PSR15_AUTO_DETECT=true
PSR15_STRICT=false

5. Validation System

Moved to rules-based system with clearer composition.

Before:

$validator = new Validator([
    'email' => 'required|email',
]);

After:

use Glueful\Validation\Validator;
use Glueful\Validation\Rules\{Required, Email};

$validator = new Validator([
    'email' => [new Required(), new Email()],
]);

$errors = $validator->validate($data);

6. Router Changes

Routes now return standardized JSON for errors.

404 Response:

{
    "error": "Not Found",
    "message": "The requested resource was not found",
    "status": 404
}

405 Response:

{
    "error": "Method Not Allowed",
    "message": "Method POST not allowed. Allowed methods: GET, PUT",
    "status": 405,
    "allowed_methods": ["GET", "PUT"]
}

7. FileUploader Changes

FileUploader now resolves repositories via the DI container.

Before:

$uploader = new FileUploader($storage);
$uploader->setRepository($repository);

After:

// Repository is auto-resolved from container
$uploader = $container->get(FileUploader::class);

Upgrade Checklist

When upgrading between major or minor versions:

  • Review breaking changes for your target version
  • Update environment variables (.env)
  • Update configuration files in config/
  • Run composer update
  • Clear compiled caches: php glueful cache:clear
  • Update service provider registrations
  • Run your test suite
  • Review and update custom middleware
  • Check deprecated features in your codebase

Semantic Versioning

Glueful follows Semantic Versioning:

  • Major versions (2.0.0): Breaking changes, major new features
  • Minor versions (1.1.0): New features, backward-compatible
  • Patch versions (1.0.1): Bug fixes, backward-compatible

Support Policy

  • Latest minor version: Active development, new features, bug fixes
  • Previous minor version: Bug fixes for 6 months
  • Older versions: Security fixes only for 12 months

Release Names

Glueful releases are named after stars and celestial objects:

  • Aurora (1.0.0) - The dawn of the framework
  • Polaris (1.1.0) - The guiding star
  • Vega (1.2.0) - Bright and reliable
  • Deneb (1.3.0) - Distant but brilliant
  • Betelgeuse (1.9.0) - A red supergiant marking a new era
  • Castor (1.9.1) - One of the twin stars, bringing documentation clarity
  • Elnath (1.10.0) - The butting horn, bringing structured error handling and validation
  • Alnilam (1.11.0) - The central star of Orion's Belt, bringing the ORM foundation
  • Mintaka (1.12.0) - The western star of Orion's Belt, completing the output transformation layer
  • Saiph (1.13.0) - The sword of Orion, enhancing developer productivity with scaffolding and factories
  • Bellatrix (1.14.0) - The Amazon Star, powering interactive CLI wizards
  • Rigel (1.15.0) - The bright foot of Orion, illuminating real-time development
  • Meissa (1.16.0) - The head of Orion, governing API versioning strategy
  • Alnitak (1.17.0) - The eastern star of Orion's Belt, protecting APIs with rate limiting
  • Hadar (1.18.0) - Beta Centauri, a bright beacon enabling event-driven webhook integrations

v1.5.0 - Orion (Minor)

Released: October 13, 2025

Notification system wiring improvements with a shared DI provider and a safer email verification flow that avoids hard prechecks.

Key Highlights

Notifications DI Provider

  • New NotificationsProvider binds ChannelManager and NotificationDispatcher as shared services
  • Extensions can register channels and hooks during boot without ad‑hoc construction

Safer Email Verification & Password Reset

  • EmailVerification/SendNotification resolve dispatcher/channel via DI first (with a clean fallback)
  • Removed ExtensionManager prechecks; rely on dispatcher/channel availability at send time
  • Soft diagnostics log when the email channel is unavailable or no channels succeed

Other Changes

  • Align retry configuration lookup to email-notification.retry (consistent with the extension)
  • Namespacing and static analysis fixes; line‑length formatting for diagnostics

Migration Notes

If you referenced config('extensions.EmailNotification.retry'), update to config('email-notification.retry'). No breaking API changes are introduced in 1.5.0.


v1.7.1 - Canopus

Released: October 21, 2025

Patch release that fixes extension discovery and boot sequencing so enabled extensions reliably load at runtime.

What’s Fixed

  • Framework initializes extensions by calling ExtensionManager::discover() before ::boot().
  • Extension migrations registered via loadMigrationsFrom() are now discovered by migrate:status / migrate:run.
  • CLI extensions:why and extensions:list now accurately reflect included providers after boot.

Impact

  • If you saw “EXCLUDED from final provider list” or “No pending migrations found” for extension migrations, this patch resolves it. No configuration changes required.

v1.7.2 - Antares

Released: October 21, 2025

Patch release improving route loading resilience in extensions and reducing noise in the development server logs.

What’s Fixed

  • Extensions: ServiceProvider::loadRoutesFrom() is now idempotent and exception‑safe.
    • Prevents duplicate route registration when a routes file would otherwise be loaded twice.
    • Catches exceptions from route files; logs and continues in production, rethrows in non‑production for fast feedback.

Developer Experience

  • CLI serve: Reclassifies PHP built‑in server access/startup lines (written to STDERR) as normal output to avoid false [ERROR] entries while preserving real error reporting.

v1.7.3 - Pollux

Released: October 21, 2025

Patch release fixing 2‑argument where/orWhere handling and refining dev‑server logging.

What’s Fixed

  • Database/QueryBuilder: The 2‑argument forms where($column, $value) and orWhere($column, $value) now normalize to = internally, avoiding a TypeError and improving portability of boolean and integer filters across PostgreSQL/MySQL/SQLite.

Developer Experience

  • CLI serve: Additional refinement of PHP built‑in server access/lifecycle lines (written to STDERR) so they appear as normal output instead of false [ERROR] entries; real errors remain highlighted.