Release Notes
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
| Version | Codename | Date | Type | Risk | Primary Theme |
|---|---|---|---|---|---|
| 1.26.0 | Atria | 2026-01-31 | Minor | Low | Extension Discovery Fixes |
| 1.25.0 | Ankaa | 2026-01-31 | Minor | Low | Multi-File Route Discovery |
| 1.24.0 | Alpheratz | 2026-01-31 | Minor | Low | Encryption Service |
| 1.23.0 | Aldebaran | 2026-01-31 | Minor | Low | Blob Visibility + Signed URLs |
| 1.22.0 | Achernar | 2026-01-30 | Minor | Medium | Global State Removal / ApplicationContext DI |
| 1.21.0 | Mira | 2026-01-24 | Minor | Low | File Uploader Refactoring |
| 1.20.0 | Regulus | 2026-01-24 | Minor | Low | Framework Simplification |
| 1.19.2 | Canopus | 2026-01-24 | Patch | Low | ValidationException Consolidation + Query Building |
| 1.19.1 | Canopus | 2026-01-22 | Patch | Low | Simplified Configuration |
| 1.19.0 | Canopus | 2026-01-22 | Minor | Low | Search & Filtering DSL |
| 1.18.0 | Hadar | 2026-01-22 | Minor | Low | Webhooks System |
| 1.17.0 | Alnitak | 2026-01-22 | Minor | Low | Rate Limiting Enhancements |
| 1.16.0 | Meissa | 2026-01-22 | Minor | Low | API Versioning Strategy |
| 1.15.0 | Rigel | 2026-01-22 | Minor | Low | Real-Time Development Server |
| 1.14.0 | Bellatrix | 2026-01-22 | Minor | Low | Interactive CLI Wizards |
| 1.13.0 | Saiph | 2026-01-22 | Minor | Low | Enhanced Scaffold Commands + Factories/Seeders |
| 1.12.0 | Mintaka | 2026-01-21 | Minor | Low | API Resource Transformers |
| 1.11.0 | Alnilam | 2026-01-21 | Minor | Low | ORM / Active Record |
| 1.10.0 | Elnath | 2026-01-21 | Minor | Low | Exception Handler + Request Validation |
| 1.9.2 | Deneb | 2026-01-20 | Patch | Low | OpenAPI 3.1 + resource route expansion |
| 1.9.1 | Castor | 2026-01-19 | Patch | Low | OpenAPI documentation refactor + UI generation |
| 1.9.0 | Betelgeuse | 2026-01-17 | Minor | Medium | PHP 8.3 minimum + Symfony 7.3 compat |
| 1.8.1 | Vega | 2025-11-23 | Patch | Low | Password policy + async stream helper |
| 1.8.0 | Spica | 2025-11-13 | Minor | Low | Session + login response events |
| 1.7.4 | Arcturus | 2025-10-28 | Patch | Low | Auth status gate + migration docs |
| 1.7.3 | Pollux | 2025-10-21 | Patch | Low | QueryBuilder 2-arg where/orWhere fix |
| 1.7.2 | Antares | 2025-10-21 | Patch | Low | Route loading resilience + dev server logs |
| 1.7.1 | Canopus | 2025-10-21 | Patch | Low | Extension discovery/boot fix |
| 1.7.0 | Procyon | 2025-10-18 | Minor | Medium | Async & concurrency subsystem |
| 1.6.2 | Capella | 2025-10-14 | Patch | Low | Mail templates config ownership |
| 1.6.1 | Arcturus | 2025-10-14 | Patch | Low | JWT RS256 signing |
| 1.6.0 | Sirius | 2025-10-13 | Minor | Low | DI artifacts + conditional caching + DSN utils |
| 1.5.0 | Orion | 2025-10-13 | Minor | Medium | Notifications DI + safer email flow |
| 1.4.2 | Rigel | 2025-10-11 | Patch | Low | Docs + PSR-4 tidy-up |
| 1.4.1 | Rigel | 2025-10-11 | Patch | Low | Install flow hardening (SQLite-first) |
| 1.4.0 | Rigel | 2025-10-11 | Minor | Medium | Unified session store, legacy removal |
| 1.3.1 | Altair | 2025-10-10 | Patch | Low | Install UX (CI non-interactive) |
| 1.3.0 | Deneb | 2025-10-06 | Feature | Low | HTTP client retries |
| 1.2.0 | Vega | 2025-09-23 | Feature+Breaking | Medium | Tasks & Jobs overhaul |
| 1.1.0 | Polaris | 2025-09-22 | Infra | Low | Testing infrastructure |
| 1.0.0 | Aurora | 2025-09-20 | Major | High | First 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
PackageManifestnow falls back toinstalled.jsonwheninstalled.phplacks provider metadata- Composer's optimized
installed.phpmay omit theextrafield where providers are specified installed.jsoncontains 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
$discoveredflag 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
*.phpfiles inroutes/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
$routerand$contextin 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 stringsencryptBinary()/decryptBinary()for arbitrary binary dataisEncrypted($value)to detect encrypted strings by format- AAD (Additional Authenticated Data) binding prevents cross-field attacks
File Encryption
encryptFile($source, $dest)- Encrypt entire filesdecryptFile($source, $dest)- Decrypt encrypted files- CLI:
php glueful encryption:file encrypt /path/to/file
Key Rotation
encryption.previous_keysconfig 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-sourceencryption: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
publicorprivateindividually - Upload requests accept
visibilityparameter - 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
SignedUrlhelper 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_KEYif no dedicated secret configured
Configuration Options
uploads.default_visibility- Set default visibility for new uploadsuploads.signed_urls.enabled- Enable/disable signed URL generationuploads.signed_urls.secret- Dedicated secret for URL signinguploads.signed_urls.ttl- Default TTL in seconds (default: 3600)
Comprehensive Test Coverage
SignedUrlTest- 17 tests covering URL generation, validation, expiration, tamperingUploadControllerTest- 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
ApplicationContextas 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
QueueContextHolderclass replaces deprecated static trait properties - Fixed
InteractsWithQueuetrait to avoid deprecated static method calls on traits - All deprecation warnings resolved
Service Provider Updates
register()andboot()methods now receiveApplicationContextparameter- 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:cacheCLI command for cache management
Code Quality Improvements
- Fixed PHPStan errors (duplicate properties, missing returns, visibility)
- Fixed 25+ PHPCS line length violations
- Added PHPStan
banned_coderule to prevent$GLOBALSusage - 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()andgetFormattedDuration()utilities
Enhanced Configuration
- New
filesystem.uploaderconfiguration section - Global toggle:
THUMBNAIL_ENABLEDto 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
/dataprefix 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
EnhancedRateLimiterMiddlewareprovides tiered limits, multiple algorithms- Per-route rate limiting via
#[RateLimit]attribute still fully supported
Configuration Cleanup
- Removed unused
ENABLE_AUDITenvironment 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
PaginationBuilderimproved 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
QueryValidatornow supportsschema.tableformat
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_URLvariable - Removed
API_BASE_URL— no longer needed - Set
BASE_URLto your deployment URL (e.g.,https://api.example.com)
Simplified Version Configuration
- Consolidated to single
API_VERSIONvariable (integer format) - Removed
API_VERSION_FULL— docs version derived automatically - Removed
API_DEFAULT_VERSION— useAPI_VERSIONinstead - Changed from
API_VERSION=v1toAPI_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:
| Scenario | BASE_URL |
|---|---|
| Local development | http://localhost:8000 |
| API on subdomain | https://api.example.com |
| API on main domain | https://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
SearchAdapterInterfacefor 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:
| Class | Purpose |
|---|---|
SearchAdapterInterface | Contract for search adapters |
SearchAdapter | Base class with auto-migration |
SearchResult | Value object for search results |
DatabaseAdapter | SQL LIKE search (default) |
ElasticsearchAdapter | Elasticsearch integration |
MeilisearchAdapter | Meilisearch integration |
Optional Packages:
# For Elasticsearch support
composer require elasticsearch/elasticsearch:^8.0
# For Meilisearch support
composer require meilisearch/meilisearch-php:^1.0
Filtering Operators
| Operator | Aliases | Description | Example |
|---|---|---|---|
eq | = | Equal to | filter[status]=active |
ne | !=, <> | Not equal | filter[status][ne]=deleted |
gt | > | Greater than | filter[age][gt]=18 |
gte | >= | Greater or equal | filter[price][gte]=100 |
lt | < | Less than | filter[stock][lt]=10 |
lte | <= | Less or equal | filter[rating][lte]=5 |
in | In array | filter[status][in]=active,pending | |
nin | not_in | Not in array | filter[type][nin]=draft,archived |
like | contains | Contains substring | filter[name][like]=john |
starts | Starts with | filter[email][starts]=admin | |
ends | Ends with | filter[domain][ends]=.com | |
between | Range (inclusive) | filter[price][between]=10,100 | |
null | is_null | Is null | filter[deleted_at][null]=1 |
not_null | Is not null | filter[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
WebhookSubscriptionORM model withlistensTo()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)
WebhookDeliveryORM model for tracking
Auto-Migration
- Database tables created automatically on first use
- Follows
DatabaseLogHandlerpattern - Zero configuration required
Webhooks Components
Core Classes:
| Class | Purpose |
|---|---|
WebhookDispatcher | Central dispatcher with auto-migration |
WebhookSubscription | ORM model for subscriptions |
WebhookDelivery | ORM model for delivery tracking |
WebhookSignature | HMAC signature generation/verification |
WebhookPayload | Standardized payload builder |
Webhook | Static facade for easy access |
Event Integration:
| Class | Purpose |
|---|---|
DispatchesWebhooks | Trait for webhookable events |
#[Webhookable] | PHP 8 attribute for marking events |
WebhookEventListener | Bridge app events to webhooks |
WebhookDispatchedEvent | Event fired when webhooks queued |
CLI Commands:
| Command | Purpose |
|---|---|
webhook:list | List all webhook subscriptions |
webhook:test | Test a webhook endpoint |
webhook:retry | Retry failed webhook deliveries |
REST API Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
POST | /api/webhooks/subscriptions | Create subscription |
GET | /api/webhooks/subscriptions | List 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}/test | Send test webhook |
GET | /api/webhooks/deliveries | List deliveries |
POST | /api/webhooks/deliveries/{id}/retry | Retry 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
| Header | Description | Example |
|---|---|---|
X-Webhook-ID | Unique delivery ID | wh_del_01HXYZ... |
X-Webhook-Event | Event name | user.created |
X-Webhook-Timestamp | Unix timestamp | 1706011200 |
X-Webhook-Signature | HMAC signature | t=1706011200,v1=abc... |
Content-Type | Always JSON | application/json |
User-Agent | Glueful identifier | Glueful-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_subscriptionsandwebhook_deliveriestables - Webhooks are delivered asynchronously via the queue system
- Integrates with existing
WebhookDeliveredEventandWebhookFailedEvent
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
TierResolverfor 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:
| Class | Purpose |
|---|---|
RateLimitManager | Central orchestrator for rate limiting operations |
TierManager | Tier configuration and limit lookup |
TierResolver | Resolve user tier from request |
RateLimitHeaders | IETF-compliant header generation |
RateLimitResult | Immutable result value object |
Limiters:
| Class | Algorithm |
|---|---|
FixedWindowLimiter | Simple fixed time window |
SlidingWindowLimiter | Sliding window using sorted sets |
TokenBucketLimiter | Token bucket with burst support |
Storage Backends:
| Class | Purpose |
|---|---|
CacheStorage | Production storage using CacheStore |
MemoryStorage | In-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,ipmiddleware syntax continues to work - New
enhanced_rate_limitmiddleware 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:
| Class | Purpose |
|---|---|
VersionManager | Central manager for version resolution |
ApiVersion | Value object for version representation |
VersionNegotiationMiddleware | Automatic version detection |
Resolvers:
| Class | Strategy |
|---|---|
UrlPrefixResolver | Extract version from URL path (/v1/) |
HeaderResolver | Read X-API-Version header |
QueryParameterResolver | Read api_version query param |
AcceptHeaderResolver | Parse Accept header media type |
Attributes:
| Attribute | Purpose |
|---|---|
#[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_negotiationto 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
--watchoption - Configurable poll interval via
--poll-interval
Integrated Services
--queueoption 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:
| Class | Purpose |
|---|---|
FileWatcher | File change detection with polling |
RequestLogger | Colorized request logging |
LogEntry | Structured 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
| Option | Description |
|---|---|
--port=8000 | Specify the port (default: 8000) |
--host=localhost | Specify the host (default: localhost) |
--watch | Enable file watching for auto-restart |
--queue | Start integrated queue worker |
--poll-interval=1000 | File watch poll interval in ms |
Migration Notes
No breaking changes. All new features are opt-in via command flags.
- Existing
php glueful servecontinues to work unchanged - New features activated via
--watchand--queueflags - 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 validationsecret()- Hidden input for passwords/secretsconfirm()- Yes/no confirmation promptschoice(),multiChoice()- Single and multiple selection menussuggest()- Input with auto-completion suggestions
Progress Indicators
- Enhanced
ProgressBarwrapper with predefined formats iterate()generator for automatic progress trackingSpinnerclass with multiple animation styles- Easy
withProgress()andwithSpinner()helpers in BaseCommand
Interactive Scaffold Commands
- Scaffold commands now prompt for arguments when not provided
scaffold:modelsupports full interactive mode- Maintains CLI compatibility with
--no-interactionflag - Graceful fallback to defaults in CI/CD environments
Interactive Components
Prompter Methods:
| Method | Description |
|---|---|
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:
| Format | Description |
|---|---|
FORMAT_NORMAL | Basic progress display |
FORMAT_VERBOSE | Progress with elapsed time |
FORMAT_VERY_VERBOSE | Progress with elapsed and estimated time |
FORMAT_DEBUG | Full progress with memory usage |
FORMAT_WITH_MESSAGE | Progress with custom message |
Spinner Styles:
| Style | Animation |
|---|---|
dots | Braille dot animation (default) |
line | Classic line spinner |
arrows | Directional arrows |
bouncing | Bouncing dots |
growing | Growing bar |
circle | Circle quadrants |
square | Square quadrants |
toggle | Toggle switch |
simple | Simple dots |
BaseCommand Helpers
New Methods:
| Method | Description |
|---|---|
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 implementingRouteMiddlewarescaffold:job- Generate queue jobs with configurable retries and timeoutsscaffold:rule- Generate validation rules with parameter supportscaffold: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()andfor()methods HasFactorytrait for ORM models enablingModel::factory()syntax
Database Seeders
- Seeder base class with dependency ordering
- Transaction support for atomic seeding operations
db:seedcommand with production environment protectionscaffold:seedercommand for generating seeder classes
Scaffold Commands
New Commands:
| Command | Purpose |
|---|---|
scaffold:middleware | Generate middleware implementing RouteMiddleware |
scaffold:job | Generate queue job classes extending Job |
scaffold:rule | Generate validation rule classes implementing Rule |
scaffold:test | Generate PHPUnit test classes (unit/feature) |
scaffold:factory | Generate model factory classes |
scaffold:seeder | Generate database seeder classes |
db:seed | Run database seeders |
Command Options:
| Command | Options |
|---|---|
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:
| Class | Purpose |
|---|---|
Factory | Base class for model factories with fluent interface |
FakerBridge | Bridge to optional Faker library with availability checking |
HasFactory | Trait for models to enable Model::factory() syntax |
Factory Methods:
| Method | Purpose |
|---|---|
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:
| Class | Purpose |
|---|---|
Seeder | Base class for database seeders |
DatabaseSeeder | Main orchestrator for all seeders |
Seeder Methods:
| Method | Purpose |
|---|---|
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/fakeras a dev dependency:composer require --dev fakerphp/faker - The
db:seedcommand requires--forceflag to run in production environments. - Generated files are placed in
database/factories/anddatabase/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()anddateAttribute()for safe attribute accessrelationshipResource()andrelationshipCollection()for nested transformationswhenLoaded(),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()andwithLinks()helpers
Resource Components
Core Classes:
| Class | Purpose |
|---|---|
JsonResource | Base class for transforming arrays/objects to JSON |
ModelResource | Extended resource with ORM-specific helpers |
ResourceCollection | Collection wrapper with metadata support |
PaginatedResourceResponse | Pagination handling with link generation |
AnonymousResourceCollection | Collection without dedicated class |
MissingValue | Sentinel for conditional attribute omission |
Conditional Attribute Methods:
| Method | Purpose |
|---|---|
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:
| Method | Purpose |
|---|---|
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:
| Class | Purpose |
|---|---|
Model | Base class for all models with CRUD, attributes, and relations |
Builder | Query builder wrapper with model-aware functionality |
Collection | Rich collection class for model results |
Pivot | Pivot model for many-to-many relationships |
Relationships:
| Relationship | Use Case |
|---|---|
HasOne | One-to-one (owning side) |
HasMany | One-to-many |
BelongsTo | Inverse of HasOne/HasMany |
BelongsToMany | Many-to-many with pivot table |
HasOneThrough | One-to-one through intermediate model |
HasManyThrough | One-to-many through intermediate model |
Traits:
| Trait | Purpose |
|---|---|
HasAttributes | Attribute get/set, casting, dirty tracking |
HasRelationships | Relationship definition and eager loading |
HasTimestamps | Automatic created_at/updated_at |
HasEvents | Model lifecycle event integration |
HasGlobalScopes | Global query scope management |
SoftDeletes | Soft delete with deleted_at column |
Custom Casts:
| Cast | Purpose |
|---|---|
AsJson | JSON encode/decode |
AsArrayObject | JSON to ArrayObject |
AsCollection | JSON to Collection |
AsDateTime | String to DateTimeImmutable |
AsEncryptedString | Transparent encryption |
AsEnum | Backed enum casting |
Attribute | Custom 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
ExceptionHandlerInterfacecontract for customizable exception handling - Typed HTTP exceptions for Client (4xx), Server (5xx), and Domain errors
ExceptionMiddlewarefor 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 methodsFormRequestbase class with authorization and data preparation hooksValidatedRequestwrapper 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):
| Exception | HTTP Code | Use Case |
|---|---|---|
BadRequestException | 400 | Malformed request |
UnauthorizedException | 401 | Missing/invalid credentials |
ForbiddenException | 403 | Insufficient permissions |
NotFoundException | 404 | Resource not found |
MethodNotAllowedException | 405 | Wrong HTTP method |
ConflictException | 409 | Resource conflict |
UnprocessableEntityException | 422 | Validation failed |
TooManyRequestsException | 429 | Rate limit exceeded |
Server Exceptions (5xx):
| Exception | HTTP Code | Use Case |
|---|---|---|
InternalServerException | 500 | Unexpected server error |
ServiceUnavailableException | 503 | Service temporarily down |
GatewayTimeoutException | 504 | Upstream timeout |
Domain Exceptions:
| Exception | Use Case |
|---|---|
ModelNotFoundException | Entity not found in database |
AuthenticationException | Authentication failure |
AuthorizationException | Permission denied |
TokenExpiredException | JWT/session expired |
Request Validation
New Validation Rules:
Confirmed- Password confirmation matchingDate,Before,After- Date validation and comparisonUrl,Uuid,Json- Format validationExists- Database existence checkNullable,Sometimes- Conditional validationFile,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
identifierfield jsonSchemaDialectdeclaration included- Default version changed from 3.0.0 to 3.1.0
Resource Route Expansion
- New
ResourceRouteExpanderclass 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
hideClientButtonandshowDeveloperToolsoptions - Tags in sidebar now sorted alphabetically
- Output file renamed from
swagger.jsontoopenapi.json
Bug Fixes
- Database: Fixed
SchemaBuilder::getTableColumns()returning empty arrays due to incorrectarray_is_list()check on associative column data.
Removed
TableDefinitionGeneratorclass - resource routes now expand directly from database schemas--databaseand--tableoptions fromgenerate:openapicommand (no longer needed)
Migration Notes
- No breaking changes. The output file is now
openapi.jsoninstead ofswagger.json. - If you had custom scripts referencing
swagger.json, update the path. - Config key
paths.swaggerrenamed topaths.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
DocumentationUIGeneratorclass generates interactive HTML documentation - Supports Scalar (default), Swagger UI, and Redoc
- New
--uioption forgenerate:openapicommand - Centralized configuration in
config/documentation.php
Documentation Architecture Refactor
- Replaced
ApiDefinitionGeneratorwith focusedTableDefinitionGeneratorandOpenApiGenerator - Renamed
ApiDefinitionsCommandtoOpenApiDocsCommand CommentsDocGeneratornow usesphpDocumentor/ReflectionDocBlockfor 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 constraintsRegex- Validates values against regular expression patterns
Dependencies
- Updated Symfony packages from
^7.3to^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
ApiDefinitionGeneratoris removed but the command interface remains compatible. - If you referenced internal documentation classes directly, update to use
OpenApiGeneratorandTableDefinitionGenerator.
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 toApplication::registerCommandClass(string $class)due to Symfony Console 7.3 adding a conflictingaddCommand(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
CSRFMiddlewarefor 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.
- Environment: Ensure PHP 8.3 or higher is installed
- Method Rename: If you called
$app->addCommand(MyCommand::class), update to$app->registerCommandClass(MyCommand::class) - Dependencies: Run
composer updateto 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$requireLowercaseflag alongside existing numeric, special-character, and uppercase toggles, enabling mixed-case enforcement without custom validators. - Async I/O:
async_stream()accepts raw resources,AsyncStream, orBufferedAsyncStreaminstances 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 successfulcache->set. - Login response hooks in
AuthController::login()just before returning.
- Session cached hook in
- 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.mdcovering 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 tosecurity.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)andorWhere($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:
servefurther 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:
servereclassifies 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 bymigrate:status/migrate:runonce providers are discovered at boot. - CLI:
extensions:why/extensions:listnow 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\\FiberSchedulerwithspawn,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\\CurlMultiHttpClientwith cooperative polling and optionalmax_concurrentpoolAsync()for concurrent requests;HttpStreamingClient::sendAsyncStream()for chunked callbacksFakeHttpClientfor test isolation
Async I/O + Helpers
AsyncStreamandBufferedAsyncStreamwith 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\\Promisewiththen/catch/finally,all/race
Configuration & DI
- New
config/async.phpforscheduler,http,streams,limits AsyncProviderwiresMetrics,Scheduler,HttpClient; registersAsyncMiddleware(aliasasync)
Migration Notes
- Backward‑compatible defaults: limits disabled when set to 0
- To adopt in routes, add middleware alias
asyncor 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_pathin the extension - Or set
services.mail.templates.pathin 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
opensslextension
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:compilenow emitsservices.jsonatstorage/cache/container/services.jsonwith fields:shared,tags,provider,type,alias_of.di:container:mapprefers the compiledservices.jsonin production (no reflection overhead).ContainerFactoryprefers a precompiled container class in production when available.
Conditional HTTP Caching
- New
ConditionalCacheMiddlewarehandles 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\\DsnParserwithparseDbDsn()andparseRedisDsn().- New CLI:
config:dsn:validatevalidates 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
NotificationsProviderregisters sharedChannelManagerandNotificationDispatcherin 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
ExtensionManagerprechecks; 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:runexecuted with--force; in quiet installs also--no-interactionand--quietcache:clearduring install passes--forceand 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
newcalls. - 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 installskips the environment confirmation prompt when any of these flags are present:--quiet,--no-interaction, or--force.- Keeps helpful output; add Symfony's global
-qif 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 aroundInputInterface::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 --forceto 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
RetryableHttpClientfor 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 behaviorbuildWithRetries()- Build client with retries enabledgetRetryConfig()- 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.
Related Documentation
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 maintenanceDatabaseBackupTask- Automated backups with retention policiesLogCleanupTask- Log file cleanupNotificationRetryTask- Retry failed notificationsSessionCleanupTask- Session maintenance
Queue Job Wrappers
Reliable queue integration with dedicated job classes:
CacheMaintenanceJob,DatabaseBackupJob,LogCleanupJobNotificationRetryJob,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']);
Related Documentation
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
TestCaseclass for application-level tests - Framework state reset utilities for isolation
- Expanded event system documentation
What's New
- Testing Utilities: Base
TestCasewith 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.
Related Documentation
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
UploadedFilesupport - 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
BootProfilerfor startup timing analysisMetricsMiddlewarefor request trackingApiMetricsServicefor 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);
Related Documentation
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
NotificationsProviderbindsChannelManagerandNotificationDispatcheras 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
ExtensionManagerprechecks; 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 bymigrate:status/migrate:run. - CLI
extensions:whyandextensions:listnow 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)andorWhere($column, $value)now normalize to=internally, avoiding aTypeErrorand 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.