Files
desk-moloni/modules/desk_moloni/config/bootstrap.php
Emanuel Almeida 8c4f68576f chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow
- Standardized file signatures to Descomplicar® format
- Updated development configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 01:27:37 +01:00

450 lines
11 KiB
PHP

/**
* Descomplicar® Crescimento Digital
* https://descomplicar.pt
*/
<?php
/**
* Desk-Moloni v3.0 Bootstrap Configuration
*
* Initializes the module environment, sets up autoloading,
* and prepares the system for CLI and web operations.
*/
declare(strict_types=1);
// Define module constants
if (!defined('DESK_MOLONI_VERSION')) {
define('DESK_MOLONI_VERSION', '3.0.1');
}
if (!defined('DESK_MOLONI_MODULE_DIR')) {
define('DESK_MOLONI_MODULE_DIR', dirname(__DIR__));
}
if (!defined('DESK_MOLONI_PROJECT_ROOT')) {
define('DESK_MOLONI_PROJECT_ROOT', dirname(dirname(dirname(__DIR__))));
}
// Environment detection
$cli_mode = (php_sapi_name() === 'cli');
$debug_mode = isset($_ENV['DESK_MOLONI_DEBUG']) ? (bool)$_ENV['DESK_MOLONI_DEBUG'] : false;
// Set error reporting based on environment
if ($debug_mode) {
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
} else {
error_reporting(E_ERROR | E_WARNING | E_PARSE);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
}
// Set timezone
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
// Set memory limit for CLI operations
if ($cli_mode) {
ini_set('memory_limit', '256M');
set_time_limit(0);
}
/**
* Simple autoloader for Desk-Moloni classes
*/
spl_autoload_register(function ($className) {
// Only handle DeskMoloni namespace
if (strpos($className, 'DeskMoloni\\') !== 0) {
return false;
}
// Convert namespace to file path
$relativePath = str_replace('DeskMoloni\\', '', $className);
$relativePath = str_replace('\\', DIRECTORY_SEPARATOR, $relativePath);
// Common locations to check
$possiblePaths = [
DESK_MOLONI_MODULE_DIR . '/src/' . $relativePath . '.php',
DESK_MOLONI_MODULE_DIR . '/lib/' . $relativePath . '.php',
DESK_MOLONI_MODULE_DIR . '/classes/' . $relativePath . '.php'
];
foreach ($possiblePaths as $path) {
if (file_exists($path)) {
require_once $path;
return true;
}
}
return false;
});
/**
* Load Perfex CRM environment if available
*/
function loadPerfexEnvironment(): bool
{
$perfexPaths = [
DESK_MOLONI_PROJECT_ROOT . '/application/config/config.php',
DESK_MOLONI_PROJECT_ROOT . '/config/config.php',
dirname(DESK_MOLONI_PROJECT_ROOT) . '/application/config/config.php'
];
foreach ($perfexPaths as $configPath) {
if (file_exists($configPath)) {
// Set up basic Perfex environment
if (!defined('BASEPATH')) {
define('BASEPATH', dirname($configPath) . '/');
}
if (!defined('APPPATH')) {
define('APPPATH', dirname($configPath) . '/application/');
}
return true;
}
}
return false;
}
/**
* Initialize database connection
*/
function initializeDatabase(): ?PDO
{
try {
$configFile = DESK_MOLONI_MODULE_DIR . '/config/config.php';
if (!file_exists($configFile)) {
return null;
}
$config = include $configFile;
if (!isset($config['database'])) {
return null;
}
$dbConfig = $config['database'];
// Try to get password from Perfex config if not provided
$password = $dbConfig['password'] ?? '';
if (empty($password) && loadPerfexEnvironment()) {
// In a real implementation, this would extract the password from Perfex config
$password = ''; // Placeholder
}
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=utf8mb4',
$dbConfig['host'],
$dbConfig['database']
);
$options = [
PDO::ATTR_ERRMODE => 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;
}