PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci" ]; return new PDO($dsn, $dbConfig['username'], $password, $options); } catch (Exception $e) { if ($debug_mode) { error_log("Database connection failed: " . $e->getMessage()); } return null; } } /** * Load configuration */ function loadConfiguration(): array { $config = []; // Load main config $mainConfigFile = DESK_MOLONI_MODULE_DIR . '/config/config.php'; if (file_exists($mainConfigFile)) { $config = include $mainConfigFile; } // Load environment-specific config $environment = $config['environment'] ?? 'production'; $envConfigFile = DESK_MOLONI_MODULE_DIR . "/config/config.{$environment}.php"; if (file_exists($envConfigFile)) { $envConfig = include $envConfigFile; $config = array_merge_recursive($config, $envConfig); } // Apply environment variables foreach ($_ENV as $key => $value) { if (strpos($key, 'DESK_MOLONI_') === 0) { $configKey = strtolower(str_replace('DESK_MOLONI_', '', $key)); $config[$configKey] = $value; } } return $config; } /** * Initialize logging */ function initializeLogging(array $config): void { $logDir = DESK_MOLONI_MODULE_DIR . '/logs'; if (!is_dir($logDir)) { mkdir($logDir, 0755, true); } // Set error log path $errorLogPath = $logDir . '/error.log'; ini_set('error_log', $errorLogPath); // Set up custom log handler if needed if (isset($config['logging']['level'])) { // Custom logging setup would go here } } /** * Initialize CLI environment */ function initializeCLI(): void { if (!function_exists('readline')) { // Provide basic readline functionality if not available function readline($prompt = '') { echo $prompt; return trim(fgets(STDIN)); } } // Set up signal handlers if available if (function_exists('pcntl_signal')) { // SIGTERM handler pcntl_signal(SIGTERM, function($signo) { echo "\nReceived SIGTERM, shutting down gracefully...\n"; exit(0); }); // SIGINT handler (Ctrl+C) pcntl_signal(SIGINT, function($signo) { echo "\nReceived SIGINT, shutting down gracefully...\n"; exit(0); }); } } /** * Global exception handler */ function handleUncaughtException(Throwable $exception): void { $message = sprintf( "[%s] Uncaught %s: %s in %s:%d\nStack trace:\n%s", date('Y-m-d H:i:s'), get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine(), $exception->getTraceAsString() ); error_log($message); if (php_sapi_name() === 'cli') { fprintf(STDERR, "Fatal error: %s\n", $exception->getMessage()); } } /** * Global error handler */ function handleError(int $errno, string $errstr, string $errfile, int $errline): bool { // Don't handle errors if error_reporting is 0 (@ operator used) if (error_reporting() === 0) { return false; } $errorTypes = [ E_ERROR => 'ERROR', E_WARNING => 'WARNING', E_PARSE => 'PARSE', E_NOTICE => 'NOTICE', E_CORE_ERROR => 'CORE_ERROR', E_CORE_WARNING => 'CORE_WARNING', E_COMPILE_ERROR => 'COMPILE_ERROR', E_COMPILE_WARNING => 'COMPILE_WARNING', E_USER_ERROR => 'USER_ERROR', E_USER_WARNING => 'USER_WARNING', E_USER_NOTICE => 'USER_NOTICE', E_STRICT => 'STRICT', E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR', E_DEPRECATED => 'DEPRECATED', E_USER_DEPRECATED => 'USER_DEPRECATED' ]; $errorType = $errorTypes[$errno] ?? 'UNKNOWN'; $message = sprintf( "[%s] %s: %s in %s:%d", date('Y-m-d H:i:s'), $errorType, $errstr, $errfile, $errline ); error_log($message); // Don't execute PHP internal error handler return true; } // Bootstrap initialization try { // Load configuration $config = loadConfiguration(); // Initialize logging initializeLogging($config); // Set exception and error handlers set_exception_handler('handleUncaughtException'); set_error_handler('handleError'); // Initialize CLI if in CLI mode if ($cli_mode) { initializeCLI(); } // Try to load Perfex environment loadPerfexEnvironment(); // Initialize database connection (lazy loading) $GLOBALS['desk_moloni_db'] = null; // Store configuration globally $GLOBALS['desk_moloni_config'] = $config; } catch (Throwable $e) { error_log("Bootstrap failed: " . $e->getMessage()); if ($cli_mode) { fprintf(STDERR, "Fatal error during bootstrap: %s\n", $e->getMessage()); exit(1); } else { // In web mode, try to fail gracefully http_response_code(500); if ($debug_mode) { echo "Bootstrap error: " . $e->getMessage(); } else { echo "Internal server error"; } exit(1); } } /** * Utility functions */ /** * Get database connection (lazy initialization) */ function getDeskMoloniDB(): ?PDO { if ($GLOBALS['desk_moloni_db'] === null) { $GLOBALS['desk_moloni_db'] = initializeDatabase(); } return $GLOBALS['desk_moloni_db']; } /** * Get configuration value */ function getDeskMoloniConfig(string $key = null, $default = null) { $config = $GLOBALS['desk_moloni_config'] ?? []; if ($key === null) { return $config; } // Support dot notation for nested keys $keys = explode('.', $key); $value = $config; foreach ($keys as $k) { if (!is_array($value) || !isset($value[$k])) { return $default; } $value = $value[$k]; } return $value; } /** * Log message to application log */ function deskMoloniLog(string $level, string $message, array $context = []): void { $logFile = DESK_MOLONI_MODULE_DIR . '/logs/application.log'; $contextString = ''; if (!empty($context)) { $contextString = ' ' . json_encode($context, JSON_UNESCAPED_SLASHES); } $logEntry = sprintf( "[%s] [%s] %s%s\n", date('Y-m-d H:i:s'), strtoupper($level), $message, $contextString ); file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX); } /** * Check if running in CLI mode */ function isDeskMoloniCLI(): bool { return php_sapi_name() === 'cli'; } /** * Check if debug mode is enabled */ function isDeskMoloniDebug(): bool { return getDeskMoloniConfig('debug', false) || (isset($_ENV['DESK_MOLONI_DEBUG']) && $_ENV['DESK_MOLONI_DEBUG']); } /** * Get module version */ function getDeskMoloniVersion(): string { $versionFile = DESK_MOLONI_MODULE_DIR . '/VERSION'; if (file_exists($versionFile)) { return trim(file_get_contents($versionFile)); } return DESK_MOLONI_VERSION; }