A comprehensive application framework component for PHP 8.4+ that provides base classes, configuration management, event handling, and lifecycle management for building robust applications with the Neuron framework.
- Installation
- Quick Start
- Core Features
- Application Base Class
- Configuration Management
- Event System
- Initializers
- Command Line Applications
- Logging
- Error Handling
- Registry Pattern
- Testing
- Best Practices
- More Information
- PHP 8.4 or higher
- Extensions: curl, json
- Composer
composer require neuron-php/applicationuse Neuron\Application\Base;
use Neuron\Data\Setting\Source\Yaml;
class MyApplication extends Base
{
protected function onStart(): bool
{
\Neuron\Log\Log::info('Application starting...');
return true; // Return false to abort
}
protected function onRun(): void
{
// Main application logic
echo "Running application v" . $this->getVersion() . "\n";
}
protected function onStop(): void
{
\Neuron\Log\Log::info('Application stopped');
}
}
// Bootstrap and run
$settings = new Yaml('config/config.yaml');
$app = new MyApplication('1.0.0', $settings);
$app->run();- Application Lifecycle Management: onStart, onRun, onStop, onFinish hooks
- Configuration System: Flexible settings from YAML, INI, ENV sources
- Event-Driven Architecture: Global event emitter with listener configuration
- Initializer System: Automatic loading and execution of initialization code
- Logging Integration: Built-in logging with multiple destinations
- Error Handling: Comprehensive error and fatal error handlers
- Registry Pattern: Global object storage and retrieval
- Command Line Support: Specialized base class for CLI applications
- Settings Fallback: Automatic fallback to environment variables
The Base class provides core application functionality:
class MyApp extends Base
{
/**
* Called before the application starts
* Return false to abort startup
*/
protected function onStart(): bool
{
// Initialize resources
$db = $this->initDatabase();
$this->setRegistryObject('database', $db);
// Load configuration
if (!$this->loadConfiguration()) {
Log::error('Configuration failed');
return false; // Abort startup
}
return true;
}
/**
* Main application logic
*/
protected function onRun(): void
{
// Process requests, run main loop, etc.
$this->processRequests();
}
/**
* Called when application is stopping
*/
protected function onStop(): void
{
// Cleanup resources
$this->closeConnections();
}
/**
* Called after everything else
*/
protected function onFinish(): void
{
Log::info('Application finished');
}
/**
* Handle errors
*/
protected function onError($level, $message, $file, $line): void
{
Log::error("Error: $message in $file:$line");
}
/**
* Handle fatal errors
*/
protected function onFatal(): void
{
$error = error_get_last();
Log::fatal('Fatal error: ' . $error['message']);
$this->setCrashed(true);
}
}// Create and configure
$app = new MyApp('1.0.0', new Yaml('config.yaml'));
// Set parameters (e.g., from command line)
$app->setParameters($_SERVER['argv']);
// Run with optional parameters
$app->run(['--verbose', '--mode=production']);The application supports multiple configuration sources through the ISettingSource interface:
use Neuron\Data\Setting\Source\Yaml;
use Neuron\Data\Setting\Source\Ini;
use Neuron\Data\Setting\Source\Env;
// YAML configuration
$yamlSource = new Yaml('config/app.yaml');
$app = new MyApp('1.0.0', $yamlSource);
// INI configuration
$iniSource = new Ini('config/app.ini');
$app = new MyApp('1.0.0', $iniSource);
// Environment variables (fallback)
$envSource = new Env();
$app = new MyApp('1.0.0', $envSource);
// No configuration (defaults to environment)
$app = new MyApp('1.0.0');Example config.yaml:
system:
timezone: America/New_York
base_path: /app
environment: production
logging:
destination: \Neuron\Log\Destination\File
format: \Neuron\Log\Format\PlainText
file: app.log
level: info
events:
listeners_path: app/Listeners
database:
host: localhost
port: 3306
name: myapp
username: dbuser
password: secret
cache:
enabled: true
driver: redis
ttl: 3600class MyApp extends Base
{
protected function onStart(): bool
{
// Get settings
$dbHost = $this->getSetting('database', 'host');
$cacheEnabled = $this->getSetting('cache', 'enabled');
// Set runtime settings
$this->setSetting('app', 'mode', 'maintenance');
// Get the SettingManager instance
$settings = $this->getSettingManager();
if ($settings) {
$source = $settings->getSource();
// Work with the source directly
}
return true;
}
}// Configuration with environment fallback
$yamlSource = new Yaml('config.yaml');
$envFallback = new Env();
$settings = new SettingManager($yamlSource);
$settings->setFallback($envFallback);
$app = new MyApp('1.0.0', $settings);
// Now settings check YAML first, then environment variables
$apiKey = $app->getSetting('api', 'key'); // Checks config.yaml then API_KEY env varThe application provides a global event emitter through the CrossCutting\Event class:
use Neuron\Application\CrossCutting\Event;
// Emit events from anywhere
Event::emit(new UserRegisteredEvent($user));
// In your application
class MyApp extends Base
{
protected function onStart(): bool
{
// Initialize event system
$this->initEvents();
// Add listeners programmatically
Event::addListener(
UserRegisteredEvent::class,
new WelcomeEmailListener()
);
return true;
}
}Configure event listeners via YAML (event-listeners.yaml):
listeners:
UserRegisteredEvent:
- App\Listeners\SendWelcomeEmail
- App\Listeners\UpdateAnalytics
- App\Listeners\NotifyAdmins
OrderCompletedEvent:
- App\Listeners\UpdateInventory
- App\Listeners\SendInvoice
- App\Listeners\ProcessCommissionInitializers are classes that run during application startup for bootstrapping:
Create files in app/Initializers/:
// app/Initializers/DatabaseInitializer.php
namespace App\Initializers;
use Neuron\Patterns\IRunnable;
class DatabaseInitializer implements IRunnable
{
public function run(): void
{
// Initialize database connections
$db = new DatabaseConnection(
$_ENV['DB_HOST'],
$_ENV['DB_NAME']
);
$this->setRegistryObject('database', $db);
}
}Initializers are automatically loaded and executed during onStart():
They are atomic instances of classes implementing IRunnable and enable modular startup logic.
class InitTest implements IRunnable
{
public function run( array $Argv = [] ): mixed
{
Registry::getInstance()
->set( 'examples\Initializers\InitTest', 'Hello World!' );
return true;
}
}The CommandLineBase class extends Base with CLI-specific features:
use Neuron\Application\CommandLineBase;
class CliApp extends CommandLineBase
{
protected function onRun(): void
{
$args = $this->getParameters();
// Parse command line arguments
$command = $args[1] ?? 'help';
switch ($command) {
case 'process':
$this->processData();
break;
case 'import':
$this->importData();
break;
default:
$this->showHelp();
}
}
private function showHelp(): void
{
echo "Usage: php app.php [command]\n";
echo "Commands:\n";
echo " process - Process data\n";
echo " import - Import data\n";
}
}
// Run CLI app
$app = new CliApp('1.0.0');
$app->setParameters($argv);
$app->run();The application automatically initializes logging based on configuration:
use Neuron\Log\Log;
class MyApp extends Base
{
protected function onStart(): bool
{
// Logging is already initialized from config
// Access the Log singleton directly
Log::info('Application starting');
Log::debug('Debug message');
Log::warning('Warning message');
Log::error('Error occurred');
return true;
}
}class MyApp extends Base
{
public function __construct($version, $source = null)
{
parent::__construct($version, $source);
// Enable error handling
$this->_HandleErrors = true;
$this->_HandleFatal = true;
}
protected function onError($level, $message, $file, $line): void
{
// Custom error handling
Log::error("Error [$level]: $message in $file:$line");
// Send alert for critical errors
if ($level === E_ERROR) {
$this->sendAlert("Critical error: $message");
}
}
protected function onFatal(): void
{
$error = error_get_last();
// Log fatal error
\Neuron\Log\Log::fatal('Fatal: ' . $error['message']);
// Set crashed state
$this->setCrashed(true);
// Cleanup before exit
$this->emergencyCleanup();
}
}$app = new MyApp('1.0.0');
$app->run();
if ($app->getCrashed()) {
// Handle crash recovery
file_put_contents('crash.log', date('Y-m-d H:i:s') . ' - Application crashed' . PHP_EOL, FILE_APPEND);
// Restart or alert
exec('php restart.php');
}class MyApp extends Base
{
protected function onStart(): bool
{
// Store objects in registry
$this->setRegistryObject('database', $dbConnection);
$this->setRegistryObject('cache', $cacheManager);
$this->setRegistryObject('api.client', $apiClient);
// Retrieve objects
$db = $this->getRegistryObject('database');
$cache = $this->getRegistryObject('cache');
// Direct registry access
$registry = Registry::getInstance();
$registry->set('app.mode', 'production');
$mode = $registry->get('app.mode');
return true;
}
}// Use namespaced keys
$app->setRegistryObject('services.email', $emailService);
$app->setRegistryObject('services.payment', $paymentService);
$app->setRegistryObject('repositories.user', $userRepo);
// Store configurations
$app->setRegistryObject('config.api.keys', $apiKeys);
$app->setRegistryObject('config.features', $featureFlags);
// Store runtime state
$app->setRegistryObject('runtime.start_time', microtime(true));
$app->setRegistryObject('runtime.request_count', 0);use PHPUnit\Framework\TestCase;
class ApplicationTest extends TestCase
{
public function testApplicationStartup(): void
{
$settings = new Memory();
$settings->set('system', 'timezone', 'UTC');
$app = new MyApp('1.0.0', $settings);
// Test startup
$reflection = new ReflectionMethod($app, 'onStart');
$reflection->setAccessible(true);
$result = $reflection->invoke($app);
$this->assertTrue($result);
}
public function testErrorHandling(): void
{
$app = new MyApp('1.0.0');
$app->enableErrorHandling(true);
// Trigger error
$reflection = new ReflectionMethod($app, 'onError');
$reflection->setAccessible(true);
$reflection->invoke($app, E_WARNING, 'Test error', 'test.php', 100);
// Assert error was logged
$this->assertStringContainsString('Test error', $this->getLogContent());
}
}class MockApplication extends Base
{
public bool $started = false;
public bool $ran = false;
public bool $stopped = false;
protected function onStart(): bool
{
$this->started = true;
return true;
}
protected function onRun(): void
{
$this->ran = true;
}
protected function onStop(): void
{
$this->stopped = true;
}
}
// Test lifecycle
$app = new MockApplication('1.0.0');
$app->run();
$this->assertTrue($app->started);
$this->assertTrue($app->ran);
$this->assertTrue($app->stopped);class ProductionApp extends Base
{
protected function onStart(): bool
{
// 1. Initialize critical services first
if (!$this->initializeDatabase()) {
return false;
}
// 2. Load configuration
$this->loadConfiguration();
// 3. Set up logging
$this->setupLogging();
// 4. Initialize events
$this->initEvents();
// 5. Run initializers
$this->executeInitializers();
// 6. Validate environment
if (!$this->validateEnvironment()) {
$this->log('Environment validation failed', 'error');
return false;
}
return true;
}
private function validateEnvironment(): bool
{
// Check required settings
$required = ['database.host', 'api.key', 'cache.driver'];
foreach ($required as $setting) {
[$section, $key] = explode('.', $setting);
if (!$this->getSetting($section, $key)) {
\Neuron\Log\Log::error("Missing required setting: $setting");
return false;
}
}
return true;
}
}use Neuron\Data\Object\Version;
// Load version from file
$version = new Version();
$version->loadFromFile('.version.json');
$app = new MyApp($version->getAsString());
// Access version in app
echo "Running version: " . $app->getVersion();- Neuron Framework: neuronphp.com
- GitHub: github.com/neuron-php/application
- Packagist: packagist.org/packages/neuron-php/application