Bearsampp 2026.9.23
Loading...
Searching...
No Matches
ActionStartup Class Reference

Public Member Functions

 __construct ($args)
 processWindow ($window, $id, $ctrl, $param1, $param2)

Data Fields

const GAUGE_OTHERS = 19
const GAUGE_SERVICES = 5

Private Member Functions

 changePath ()
 checkApacheServiceWithTimeout ($service)
 checkBinsRegKey ()
 checkBrowser ()
 checkLaunchStartup ()
 checkMySQLServiceWithTimeout ($service, $bin)
 checkPath ()
 checkPathRegKey ()
 checkSystemPathRegKey ()
 cleanOldBehaviors ()
 cleanTmpFolders ()
 createSslCrts ()
 installServices ()
 installServicesSequential ($bearsamppBins, $bearsamppLang)
 killOldInstances ()
 prepareService ($sName, $service, $bearsamppBins, $bearsamppLang, $currentIndex=0, $totalCount=0)
 refreshAliases ()
 refreshGitRepos ()
 refreshHostname ()
 refreshVhosts ()
 rotationLogs ()
 savePath ()
 scanFolders ()
 sysInfos ()
 updateConfig ()
 writeLog ($log)

Private Attributes

 $error
 $filesToScan
 $restart
 $rootPath
 $splash
 $startTime

Detailed Description

Class ActionStartup Handles the startup process of the Bearsampp application, including initializing services, cleaning temporary files, refreshing configurations, and more.

Definition at line 16 of file class.action.startup.php.

Constructor & Destructor Documentation

◆ __construct()

__construct ( $args)

ActionStartup constructor. Initializes the startup process, including the splash screen and various configurations.

Parameters
array$argsCommand line arguments.

Definition at line 48 of file class.action.startup.php.

49 {
50 global $bearsamppRoot, $bearsamppCore, $bearsamppLang, $bearsamppBins, $bearsamppWinbinder;
51 $this->writeLog('Starting ' . APP_TITLE);
52
53 // Admin check is now performed in root.php before ActionStartup is instantiated
54 // This prevents screen flashes and ensures the check happens before any WinBinder initialization
55 Log::info('Administrator privileges confirmed - proceeding with startup');
56
57 // Init
58 $this->splash = new Splash();
59 $this->restart = false;
60 $this->startTime = Util::getMicrotime();
61 $this->error = '';
62
63 $this->rootPath = Path::getRootPath();
64 $this->filesToScan = array();
65
66 $gauge = self::GAUGE_SERVICES * count($bearsamppBins->getServices());
67 $gauge += self::GAUGE_OTHERS + 1;
68
69 // Start splash screen
70 $this->splash->init(
72 $gauge,
73 sprintf($bearsamppLang->getValue(Lang::STARTUP_STARTING_TEXT), APP_TITLE . ' ' . $bearsamppCore->getAppVersion())
74 );
75
76 $bearsamppWinbinder->setHandler($this->splash->getWbWindow(), $this, 'processWindow', 1000);
77 $bearsamppWinbinder->mainLoop();
78 $bearsamppWinbinder->reset();
79 }
global $bearsamppBins
global $bearsamppLang
global $bearsamppRoot
global $bearsamppCore
const STARTUP_STARTING_TEXT
const STARTUP
static info($data, $file=null)
static getRootPath($aetrayPath=false)
static getMicrotime()
const APP_TITLE
Definition root.php:19

References $bearsamppBins, $bearsamppCore, $bearsamppLang, $bearsamppRoot, APP_TITLE, Util\getMicrotime(), Path\getRootPath(), Log\info(), Lang\STARTUP, Lang\STARTUP_STARTING_TEXT, and writeLog().

Here is the call graph for this function:

Member Function Documentation

◆ changePath()

changePath ( )
private

Changes the application path and logs the number of files and occurrences changed.

Definition at line 765 of file class.action.startup.php.

766 {
767 global $bearsamppLang;
768
769 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_CHANGE_PATH_TEXT), $this->rootPath));
770 $this->splash->incrProgressBar();
771
772 $result = Path::changePath($this->filesToScan, $this->rootPath);
773 $this->writeLog('Nb files changed: ' . $result['countChangedFiles']);
774 $this->writeLog('Nb occurences changed: ' . $result['countChangedOcc']);
775 }
$result
const STARTUP_CHANGE_PATH_TEXT
static changePath($filesToScan, $rootPath=null)

References $bearsamppLang, $result, Path\changePath(), Lang\STARTUP_CHANGE_PATH_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkApacheServiceWithTimeout()

checkApacheServiceWithTimeout ( $service)
private

Specialized method to check Apache service with timeout protection. Apache service checks can sometimes hang, so this method provides a safer way to check.

Parameters
object$serviceThe Apache service object
Returns
mixed Service info array or false if service not installed or check timed out

Definition at line 1374 of file class.action.startup.php.

1375 {
1376 Log::trace('Starting specialized Apache service check with timeout protection');
1377
1378 // Set a timeout for the Apache service check
1379 $serviceCheckStartTime = microtime(true);
1380 $serviceCheckTimeout = 3; // 3 seconds timeout
1381
1382 try {
1383 // Use a non-blocking approach to check service
1384 $serviceInfos = false;
1385
1386 // First try a quick check if the service exists in the list
1387 $serviceList = Win32Service::getServices();
1388 if (is_array($serviceList) && isset($serviceList[$service->getName()])) {
1389 Log::trace('Apache service found in service list, getting details');
1390
1391 // Service exists, now try to get its details with timeout protection
1392 $startTime = microtime(true);
1393 $serviceInfos = $service->infos();
1394
1395 // Check if we've exceeded our timeout
1396 if (microtime(true) - $serviceCheckStartTime > $serviceCheckTimeout) {
1397 Log::trace("Apache service check timeout exceeded, assuming service needs reinstall");
1398
1399 return false;
1400 }
1401 } else {
1402 Log::trace('Apache service not found in service list');
1403
1404 return false;
1405 }
1406
1407 return $serviceInfos;
1408 } catch (\Exception $e) {
1409 Log::trace("Exception during Apache service check: " . $e->getMessage());
1410
1411 return false;
1412 } catch (\Throwable $e) {
1413 Log::trace("Throwable during Apache service check: " . $e->getMessage());
1414
1415 return false;
1416 }
1417 }
static trace($data, $file=null)
static getServices($forceRefresh=false)

References $startTime, Win32Service\getServices(), and Log\trace().

Referenced by prepareService().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkBinsRegKey()

checkBinsRegKey ( )
private

Checks and updates the application bins registry key. If the current registry key does not match the generated key, it updates the registry key. Logs the current and generated registry keys. Sets an error message if the registry key update fails. Sets a restart flag if the registry key is updated.

Definition at line 823 of file class.action.startup.php.

824 {
825 global $bearsamppLang, $bearsamppRegistry;
826
827 $currentAppBinsRegKey = $bearsamppRegistry->getAppBinsRegKey();
828 $genAppBinsRegKey = $bearsamppRegistry->getAppBinsRegKey(false);
829 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_REGISTRY_TEXT), Registry::APP_BINS_REG_ENTRY . ': ' . $genAppBinsRegKey));
830 $this->splash->incrProgressBar();
831
832 $this->writeLog('Current app bins reg key: ' . $currentAppBinsRegKey);
833 $this->writeLog('Gen app bins reg key: ' . $genAppBinsRegKey);
834 if ($currentAppBinsRegKey != $genAppBinsRegKey) {
835 if (!$bearsamppRegistry->setAppBinsRegKey($genAppBinsRegKey)) {
836 if (!empty($this->error)) {
837 $this->error .= PHP_EOL . PHP_EOL;
838 }
840 $this->error .= PHP_EOL . $bearsamppRegistry->getLatestError();
841 } else {
842 $this->writeLog('Need restart: checkBinsRegKey');
843 $this->restart = true;
844 }
845 }
846 }
const STARTUP_REGISTRY_ERROR_TEXT
const STARTUP_REGISTRY_TEXT
const APP_BINS_REG_ENTRY

References $bearsamppLang, Registry\APP_BINS_REG_ENTRY, Lang\STARTUP_REGISTRY_ERROR_TEXT, Lang\STARTUP_REGISTRY_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkBrowser()

checkBrowser ( )
private

Checks and sets the default browser configuration.

Definition at line 646 of file class.action.startup.php.

647 {
649
650 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_CHECK_BROWSER_TEXT));
651 $this->splash->incrProgressBar();
652 $this->writeLog('Check browser');
653
654 $currentBrowser = $bearsamppConfig->getBrowser();
655 if (empty($currentBrowser) || !file_exists($currentBrowser)) {
657 }
658 }
const CFG_BROWSER
const STARTUP_CHECK_BROWSER_TEXT
static getDefaultBrowser()
global $bearsamppConfig
Definition homepage.php:46

References $bearsamppConfig, $bearsamppLang, Config\CFG_BROWSER, Win32Native\getDefaultBrowser(), Lang\STARTUP_CHECK_BROWSER_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkLaunchStartup()

checkLaunchStartup ( )
private

Checks and sets the launch startup configuration.

Definition at line 630 of file class.action.startup.php.

631 {
632 global $bearsamppConfig;
633
634 $this->writeLog('Check launch startup');
635
636 if ($bearsamppConfig->isLaunchStartup()) {
638 } else {
640 }
641 }
static disableLaunchStartup()
static enableLaunchStartup()

References $bearsamppConfig, Util\disableLaunchStartup(), Util\enableLaunchStartup(), and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkMySQLServiceWithTimeout()

checkMySQLServiceWithTimeout ( $service,
$bin )
private

Specialized method to check MySQL service with timeout protection. MySQL service checks can sometimes hang, so this method provides a safer way to check.

Parameters
object$serviceThe MySQL service object
object$binThe MySQL bin object
Returns
mixed Service info array or false if service not installed or check timed out

Definition at line 1428 of file class.action.startup.php.

1429 {
1430 Log::trace('Starting specialized MySQL service check with timeout protection');
1431
1432 // Set a timeout for the MySQL service check
1433 $serviceCheckStartTime = microtime(true);
1434 $serviceCheckTimeout = 3; // 3 seconds timeout
1435
1436 try {
1437 // Use a non-blocking approach to check service
1438 $serviceInfos = false;
1439
1440 // First check if the service exists in the list
1441 $serviceList = Win32Service::getServices();
1442 if (is_array($serviceList) && isset($serviceList[$service->getName()])) {
1443 Log::trace('MySQL service found in service list, getting details');
1444
1445 // Service exists, now try to get its details with timeout protection
1446 $serviceInfos = $service->infos();
1447
1448 // Check if we've exceeded our timeout
1449 if (microtime(true) - $serviceCheckStartTime > $serviceCheckTimeout) {
1450 Log::trace("MySQL service check timeout exceeded, assuming service needs reinstall");
1451
1452 return false;
1453 }
1454 } else {
1455 Log::trace('MySQL service not found in service list');
1456
1457 return false;
1458 }
1459
1460 return $serviceInfos;
1461 } catch (\Exception $e) {
1462 Log::trace("Exception during MySQL service check: " . $e->getMessage());
1463
1464 return false;
1465 } catch (\Throwable $e) {
1466 Log::trace("Throwable during MySQL service check: " . $e->getMessage());
1467
1468 return false;
1469 }
1470 }

References Win32Service\getServices(), and Log\trace().

Referenced by prepareService().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkPath()

checkPath ( )
private

Checks the application path and logs the last path content.

Definition at line 705 of file class.action.startup.php.

706 {
708
709 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_CHECK_PATH_TEXT));
710 $this->splash->incrProgressBar();
711
712 $this->writeLog('Last path: ' . $bearsamppCore->getLastPathContent());
713 }
const STARTUP_CHECK_PATH_TEXT

References $bearsamppCore, $bearsamppLang, Lang\STARTUP_CHECK_PATH_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkPathRegKey()

checkPathRegKey ( )
private

Checks and updates the application path registry key.

Definition at line 791 of file class.action.startup.php.

792 {
793 global $bearsamppRoot, $bearsamppLang, $bearsamppRegistry;
794
795 $currentAppPathRegKey = $bearsamppRegistry->getAppPathRegKey();
796 $genAppPathRegKey = Path::formatWindowsPath(Path::getRootPath());
797 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_REGISTRY_TEXT), Registry::APP_PATH_REG_ENTRY . ': ' . $genAppPathRegKey));
798 $this->splash->incrProgressBar();
799
800 $this->writeLog('Current app path reg key: ' . $currentAppPathRegKey);
801 $this->writeLog('Gen app path reg key: ' . $genAppPathRegKey);
802 if ($currentAppPathRegKey != $genAppPathRegKey) {
803 if (!$bearsamppRegistry->setAppPathRegKey($genAppPathRegKey)) {
804 if (!empty($this->error)) {
805 $this->error .= PHP_EOL . PHP_EOL;
806 }
808 $this->error .= PHP_EOL . $bearsamppRegistry->getLatestError();
809 } else {
810 $this->writeLog('Need restart: checkPathRegKey');
811 $this->restart = true;
812 }
813 }
814 }
static formatWindowsPath($path)
const APP_PATH_REG_ENTRY

References $bearsamppLang, $bearsamppRoot, Registry\APP_PATH_REG_ENTRY, Path\formatWindowsPath(), Path\getRootPath(), Lang\STARTUP_REGISTRY_ERROR_TEXT, Lang\STARTUP_REGISTRY_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ checkSystemPathRegKey()

checkSystemPathRegKey ( )
private

Checks and updates the system PATH registry key. Ensures the application bins registry entry is at the beginning of the system PATH. Logs the current and new system PATH. Sets an error message if the system PATH update fails. Sets a restart flag if the system PATH is updated.

Definition at line 855 of file class.action.startup.php.

856 {
857 global $bearsamppLang, $bearsamppRegistry;
858
859 $currentSysPathRegKey = $bearsamppRegistry->getSysPathRegKey();
860 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_REGISTRY_TEXT), Registry::SYSPATH_REG_ENTRY));
861 $this->splash->incrProgressBar();
862
863 $this->writeLog('Current system PATH: ' . $currentSysPathRegKey);
864
865 $newSysPathRegKey = str_replace('%' . Registry::APP_BINS_REG_ENTRY . '%;', '', $currentSysPathRegKey);
866 $newSysPathRegKey = str_replace('%' . Registry::APP_BINS_REG_ENTRY . '%', '', $newSysPathRegKey);
867 $newSysPathRegKey = '%' . Registry::APP_BINS_REG_ENTRY . '%;' . $newSysPathRegKey;
868 $this->writeLog('New system PATH: ' . $newSysPathRegKey);
869
870 if ($currentSysPathRegKey != $newSysPathRegKey) {
871 if (!$bearsamppRegistry->setSysPathRegKey($newSysPathRegKey)) {
872 if (!empty($this->error)) {
873 $this->error .= PHP_EOL . PHP_EOL;
874 }
876 $this->error .= PHP_EOL . $bearsamppRegistry->getLatestError();
877 } else {
878 $this->writeLog('Need restart: checkSystemPathRegKey');
879 $this->restart = true;
880 }
881 } else {
882 $this->writeLog('Refresh system PATH: ' . $currentSysPathRegKey);
883 $bearsamppRegistry->setSysPathRegKey(str_replace('%' . Registry::APP_BINS_REG_ENTRY . '%', '', $currentSysPathRegKey));
884 $bearsamppRegistry->setSysPathRegKey($currentSysPathRegKey);
885 }
886 }
const SYSPATH_REG_ENTRY

References $bearsamppLang, Registry\APP_BINS_REG_ENTRY, Lang\STARTUP_REGISTRY_ERROR_TEXT, Lang\STARTUP_REGISTRY_TEXT, Registry\SYSPATH_REG_ENTRY, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cleanOldBehaviors()

cleanOldBehaviors ( )
private

Cleans old behaviors by removing outdated registry entries.

Definition at line 561 of file class.action.startup.php.

562 {
563 global $bearsamppLang, $bearsamppRegistry;
564
565 $this->writeLog('Clean old behaviors');
566
567 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_CLEAN_OLD_BEHAVIORS_TEXT));
568 $this->splash->incrProgressBar();
569
570 // App >= 1.0.13
571 $bearsamppRegistry->deleteValue(
573 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
575 );
576 }
const STARTUP_CLEAN_OLD_BEHAVIORS_TEXT
const HKEY_LOCAL_MACHINE

References $bearsamppLang, APP_TITLE, Registry\HKEY_LOCAL_MACHINE, Lang\STARTUP_CLEAN_OLD_BEHAVIORS_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cleanTmpFolders()

cleanTmpFolders ( )
private

Cleans temporary folders by removing unnecessary files.

Definition at line 530 of file class.action.startup.php.

531 {
533
534 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_CLEAN_TMP_TEXT));
535 $this->splash->incrProgressBar();
536
537 $this->writeLog('Clear tmp folders');
538 Util::clearFolder(Path::getTmpPath(), array('cachegrind', 'composer', 'openssl', 'mailpit', 'xlight', 'npm-cache', 'pip', 'opcache', '.gitignore'));
539 Util::clearFolder(Path::getTmpPath(), array('.gitignore'));
540
541 // Ensure opcache directory exists for persistent file cache
542 $opcachePath = Path::getTmpPath() . DIRECTORY_SEPARATOR . 'opcache';
543
544 if (!is_dir($opcachePath)) {
545 $this->writeLog('Creating opcache directory: ' . $opcachePath);
546 if (!@mkdir($opcachePath, 0755, true) && !is_dir($opcachePath)) {
547 $this->writeLog('Failed to create opcache directory: ' . $opcachePath);
548
549 return;
550 }
551 }
552
553 if (!is_writable($opcachePath)) {
554 $this->writeLog('Opcache directory is not writable: ' . $opcachePath);
555 }
556 }
const STARTUP_CLEAN_TMP_TEXT
static getTmpPath($aetrayPath=false)
static clearFolder($path, $exclude=array())

References $bearsamppCore, $bearsamppLang, $bearsamppRoot, Util\clearFolder(), Path\getTmpPath(), Lang\STARTUP_CLEAN_TMP_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ createSslCrts()

createSslCrts ( )
private

Creates SSL certificates if they do not already exist or are expired. Logs the creation process.

Definition at line 909 of file class.action.startup.php.

910 {
911 global $bearsamppLang, $bearsamppOpenSsl, $bearsamppBins;
912
913 Log::info('Checking SSL certificates during startup...');
914 $this->splash->incrProgressBar();
915
916 // Always ensure localhost exists and is valid
917 $localhostExpired = $bearsamppOpenSsl->isExpired('localhost');
918 Log::trace('Localhost SSL expired status: ' . ($localhostExpired ? 'YES' : 'NO'));
919
920 if ($localhostExpired) {
921 Log::info('SSL certificate for "localhost" is missing or expired. Checking if creation is necessary...');
922 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_GEN_SSL_CRT_TEXT), 'localhost'));
923
924 // Only create if NOT present or REALLY expired
925 // (isExpired already returns true if missing)
926 if (!$bearsamppOpenSsl->createCrt('localhost')) {
927 Log::error('FAILED call to createCrt("localhost")');
928 }
929
930 // Re-verify after creation to log success/failure
931 if ($bearsamppOpenSsl->isExpired('localhost')) {
932 Log::error('FAILED to verify localhost SSL certificate after creation attempt.');
933 } else {
934 Log::info('Successfully verified localhost SSL certificate.');
935 }
936 } else {
937 Log::trace('SSL certificate for "localhost" is valid.');
938 }
939
940 // Also check all Apache vhosts
941 $apache = $bearsamppBins->getApache();
942 if ($apache) {
943 $vhosts = $apache->getVhosts();
944 Log::info('Checking SSL certificates for ' . count($vhosts) . ' vhosts');
945 foreach ($vhosts as $vhost) {
946 if ($bearsamppOpenSsl->isExpired($vhost)) {
947 Log::info('SSL certificate for "' . $vhost . '" is missing or expired. Creating new one...');
948 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_GEN_SSL_CRT_TEXT), $vhost));
949 if (!$bearsamppOpenSsl->createCrt($vhost)) {
950 Log::error('FAILED call to createCrt("' . $vhost . '")');
951 }
952 } else {
953 Log::trace('SSL certificate for "' . $vhost . '" is valid.');
954 }
955 }
956 } else {
957 Log::info('Apache module not found or disabled, skipping vhost SSL check');
958 }
959
960 // Offer to trust an existing (previously untrusted) SSL Root CA in Firefox
961 $bearsamppOpenSsl->ensureFirefoxTrustPrompt();
962 }
const STARTUP_GEN_SSL_CRT_TEXT
static error($data, $file=null)

References $bearsamppBins, $bearsamppLang, Log\error(), Log\info(), Lang\STARTUP_GEN_SSL_CRT_TEXT, and Log\trace().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ installServices()

installServices ( )
private

Installs and starts services for the application. Checks if services are already installed and updates them if necessary. Logs the installation process and any errors encountered.

Uses optimized service checking and starting methods.

Definition at line 972 of file class.action.startup.php.

973 {
975
976 Log::trace('Starting installServices method');
977
978 if (!$this->restart) {
979 Log::trace('Normal startup mode - processing services');
980
981 // Admin check is now performed at the very beginning of processWindow()
982 // No need to check again here
983
984 // Service Installation
986 } else {
987 Log::trace('Restart mode - skipping service installation');
988 $this->splash->incrProgressBar(self::GAUGE_SERVICES * count($bearsamppBins->getServices()));
989 }
990
991 Log::trace('Completed installServices method');
992 }
installServicesSequential($bearsamppBins, $bearsamppLang)

References $bearsamppBins, $bearsamppLang, $bearsamppRoot, installServicesSequential(), and Log\trace().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ installServicesSequential()

installServicesSequential ( $bearsamppBins,
$bearsamppLang )
private

Service Installation Installs and starts services sequentially with proper progress tracking. Ensures exactly GAUGE_SERVICES (5) progress steps per service.

Parameters
object$bearsamppBinsThe bins object
object$bearsamppLangThe language object

Definition at line 1002 of file class.action.startup.php.

1003 {
1004 Log::trace('Starting sequential service installation');
1005 $installStartTime = Util::getMicrotime();
1006
1007 // Pre-fetch all services to speed up preparation phase
1009
1010 // Skip symlink creation during checking phase for performance
1012
1013 try {
1014 // Step 1: Check and prepare all services
1015 $servicesToStart = [];
1016 $serviceErrors = [];
1017
1018 $totalServiceCount = count($bearsamppBins->getServices());
1019 $currentServiceIndex = 0;
1020
1021 foreach ($bearsamppBins->getServices() as $sName => $service) {
1022 $currentServiceIndex++;
1023
1024 Log::trace('Preparing service: ' . $sName);
1025
1026 // prepareService() increments 1 step
1027 $serviceInfo = $this->prepareService($sName, $service, $bearsamppBins, $bearsamppLang, $currentServiceIndex, $totalServiceCount);
1028
1029 if ($serviceInfo['restart']) {
1030 $this->writeLog('Need restart: installService ' . $serviceInfo['bin']->getName());
1031 Log::trace('Restart required for service: ' . $serviceInfo['bin']->getName());
1032 $this->restart = true;
1033 // prepareService used 1 step, need 4 more to reach GAUGE_SERVICES (5 total)
1034 $this->splash->incrProgressBar(self::GAUGE_SERVICES - 1);
1035 continue;
1036 }
1037
1038 if (!empty($serviceInfo['error'])) {
1039 $serviceErrors[$sName] = $serviceInfo;
1040 // prepareService used 1 step, need 4 more to reach GAUGE_SERVICES (5 total)
1041 $this->splash->incrProgressBar(self::GAUGE_SERVICES - 1);
1042 continue;
1043 }
1044
1045 if ($serviceInfo['needsStart']) {
1046 $servicesToStart[$sName] = $serviceInfo;
1047 } else {
1048 // Service already running or doesn't need to start
1049 // prepareService used 1 step, need 4 more to reach GAUGE_SERVICES (5 total)
1050 $this->splash->incrProgressBar(self::GAUGE_SERVICES - 1);
1051 }
1052 }
1053 } finally {
1054 // Re-enable symlink creation and reload bins with symlinks created
1056 Log::trace('Re-enabling symlink creation after service checking');
1057 $bearsamppBins->reload();
1058 }
1059
1060 // Step 2: Start all services in parallel with progress updates
1061 if (!empty($servicesToStart)) {
1062 Log::trace('Starting ' . count($servicesToStart) . ' services in parallel');
1063
1064 $parallelStartTime = Util::getMicrotime();
1065 $serviceCount = 0;
1066 $totalServices = count($servicesToStart);
1067
1068 // Use parallel startup for optimized performance (40-60% faster)
1070 $servicesToStart,
1071 function ($current, $total, $serviceName) use (&$serviceCount, $bearsamppLang) {
1072 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::LOADING_START_SERVICE), $serviceName) . ' (' . $current . '/' . $total . ')');
1073 // Increment progress bar as each start command is sent
1074 if ($current > $serviceCount) {
1075 $this->splash->incrProgressBar();
1076 $serviceCount = $current;
1077 }
1078 }
1079 );
1080
1081 // Check which services are actually running and handle failures
1082 $parallelDuration = round(Util::getMicrotime() - $parallelStartTime, 3);
1083 Log::trace('Parallel startup phase completed in ' . $parallelDuration . ' seconds');
1084
1085 $verifyCount = 0;
1086 foreach ($servicesToStart as $sName => $serviceInfo) {
1087 $verifyCount++;
1088 $name = $serviceInfo['name'];
1089 $service = $serviceInfo['service'];
1090
1091 // Update splash during verification phase
1092 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_VERIFY_SERVICE_TEXT), $name) . ' (' . $verifyCount . '/' . $totalServices . ')');
1093
1094 if ($service->isRunning()) {
1095 $this->writeLog($name . ' service started successfully');
1096 Log::trace('Service ' . $name . ' verified running');
1097 } else {
1098 $error = $service->getError();
1099 if (empty($error)) {
1100 $error = 'Failed to start service after parallel startup';
1101 }
1102
1103 $serviceErrors[$sName] = $serviceInfo;
1104 $serviceErrors[$sName]['error'] = $error;
1105 Log::warning('Service ' . $name . ' failed to start: ' . $error);
1106
1107 // Run syntax check if available
1108 if (!empty($serviceInfo['syntaxCheckCmd'])) {
1109 try {
1110 $cmdSyntaxCheck = $serviceInfo['bin']->getCmdLineOutput($serviceInfo['syntaxCheckCmd']);
1111 if (!$cmdSyntaxCheck['syntaxOk']) {
1112 $serviceErrors[$sName]['error'] .= PHP_EOL . 'Syntax error: ' . $cmdSyntaxCheck['content'];
1113 }
1114 } catch (\Exception $e) {
1115 // Ignore syntax check errors
1116 }
1117 }
1118 }
1119
1120 // Increment progress bar for verification step
1121 // Total for parallel path: 1 (prepare) + 1 (start callback) + 3 (verify) = 5
1122 $this->splash->incrProgressBar(self::GAUGE_SERVICES - 2);
1123 }
1124
1125 Log::info('Parallel service startup completed: ' . count($servicesToStart) . ' services processed');
1126 }
1127
1128 // Step 3: Report any errors
1129 foreach ($serviceErrors as $sName => $serviceInfo) {
1130 if (!empty($serviceInfo['error'])) {
1131 Log::trace('Service error occurred for ' . $sName . ': ' . $serviceInfo['error']);
1132 if (!empty($this->error)) {
1133 $this->error .= PHP_EOL . PHP_EOL;
1134 }
1135 $this->error .= sprintf($bearsamppLang->getValue(Lang::STARTUP_SERVICE_ERROR), $serviceInfo['name']) . PHP_EOL . $serviceInfo['error'];
1136 }
1137 }
1138
1139 $installDuration = round(Util::getMicrotime() - $installStartTime, 3);
1140 $this->writeLog('Service installation completed in ' . $installDuration . 's');
1141 Log::trace('Service installation completed in ' . $installDuration . ' seconds');
1142 }
prepareService($sName, $service, $bearsamppBins, $bearsamppLang, $currentIndex=0, $totalCount=0)
const STARTUP_SERVICE_ERROR
const LOADING_START_SERVICE
const STARTUP_VERIFY_SERVICE_TEXT
static warning($data, $file=null)
static startAllServicesParallel($serviceInfos, ?callable $progressCallback=null, $startupTimeout=30)

References $bearsamppBins, $bearsamppLang, $error, Util\getMicrotime(), Win32Service\getServices(), Log\info(), Lang\LOADING_START_SERVICE, prepareService(), Symlinks\setSkipSymlinkCreation(), ServiceHelper\startAllServicesParallel(), Lang\STARTUP_SERVICE_ERROR, Lang\STARTUP_VERIFY_SERVICE_TEXT, Log\trace(), Log\warning(), and writeLog().

Referenced by installServices().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ killOldInstances()

killOldInstances ( )
private

Kills old instances of Bearsampp processes.

Definition at line 581 of file class.action.startup.php.

582 {
583 global $bearsamppLang;
584
585 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_KILL_OLD_PROCS_TEXT));
586 $this->splash->incrProgressBar();
587
588 // Stop services
589 /*foreach ($bearsamppBins->getServices() as $sName => $service) {
590 $serviceInfos = $service->infos();
591 if ($serviceInfos === false) {
592 continue;
593 }
594 $service->stop();
595 }*/
596
597 // Stop third party procs
598 $procsKilled = Win32Ps::killBins();
599 if (!empty($procsKilled)) {
600 $this->writeLog('Procs killed:');
601 $procsKilledSort = array();
602 foreach ($procsKilled as $proc) {
604 $procsKilledSort[] = '-> ' . basename($unixExePath) . ' (PID ' . $proc[Win32Ps::PROCESS_ID] . ') in ' . $unixExePath;
605 }
606 sort($procsKilledSort);
607 foreach ($procsKilledSort as $proc) {
608 $this->writeLog($proc);
609 }
610 }
611 }
$proc
Definition ajax.php:130
const STARTUP_KILL_OLD_PROCS_TEXT
static formatUnixPath($path)
static killBins($refreshProcs=false)
const EXECUTABLE_PATH
const PROCESS_ID

References $bearsamppLang, $proc, Win32Ps\EXECUTABLE_PATH, Path\formatUnixPath(), Win32Ps\killBins(), Win32Ps\PROCESS_ID, Lang\STARTUP_KILL_OLD_PROCS_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ prepareService()

prepareService ( $sName,
$service,
$bearsamppBins,
$bearsamppLang,
$currentIndex = 0,
$totalCount = 0 )
private

Prepares a service for startup (check, install if needed, but don't start yet)

Parameters
string$sNameService name
object$serviceService object
object$bearsamppBinsBins object
object$bearsamppLangLanguage object
int$currentIndexCurrent service index
int$totalCountTotal service count
Returns
array Service information array

Definition at line 1156 of file class.action.startup.php.

1157 {
1158 $serviceInfo = [
1159 'sName' => $sName,
1160 'service' => $service,
1161 'bin' => null,
1162 'name' => '',
1163 'port' => 0,
1164 'syntaxCheckCmd' => null,
1165 'error' => '',
1166 'restart' => false,
1167 'needsStart' => false,
1168 'startTime' => Util::getMicrotime()
1169 ];
1170
1171 // Identify service type and get bin
1172 $syntaxCheckCmd = null;
1173 $bin = null;
1174 $port = 0;
1175
1176 if ($sName == BinMailpit::SERVICE_NAME) {
1177 $bin = $bearsamppBins->getMailpit();
1178 $port = $bearsamppBins->getMailpit()->getSmtpPort();
1179 } elseif ($sName == BinMemcached::SERVICE_NAME) {
1180 $bin = $bearsamppBins->getMemcached();
1181 $port = $bearsamppBins->getMemcached()->getPort();
1182 } elseif ($sName == BinApache::SERVICE_NAME) {
1183 $bin = $bearsamppBins->getApache();
1184 $port = $bearsamppBins->getApache()->getPort();
1185 $syntaxCheckCmd = BinApache::CMD_SYNTAX_CHECK;
1186 } elseif ($sName == BinMysql::SERVICE_NAME) {
1187 $bin = $bearsamppBins->getMysql();
1188 $port = $bearsamppBins->getMysql()->getPort();
1189 $syntaxCheckCmd = BinMysql::CMD_SYNTAX_CHECK;
1190
1191 // Pre-initialize MySQL data if needed
1192 if (!file_exists($bin->getDataDir()) || count(glob($bin->getDataDir() . '/*')) === 0) {
1193 Log::trace('Pre-initializing MySQL data directory');
1194 $bin->initData();
1195 }
1196 } elseif ($sName == BinMariadb::SERVICE_NAME) {
1197 $bin = $bearsamppBins->getMariadb();
1198 $port = $bearsamppBins->getMariadb()->getPort();
1199 $syntaxCheckCmd = BinMariadb::CMD_SYNTAX_CHECK;
1200 } elseif ($sName == BinPostgresql::SERVICE_NAME) {
1201 $bin = $bearsamppBins->getPostgresql();
1202 $port = $bearsamppBins->getPostgresql()->getPort();
1203 } elseif ($sName == BinXlight::SERVICE_NAME) {
1204 $bin = $bearsamppBins->getXlight();
1205 $port = $bearsamppBins->getXlight()->getPort();
1206 }
1207
1208 $name = $bin->getName() . ' ' . $bin->getVersion() . ' (' . $service->getName() . ')';
1209
1210 $serviceInfo['bin'] = $bin;
1211 $serviceInfo['name'] = $name;
1212 $serviceInfo['port'] = $port;
1213 $serviceInfo['syntaxCheckCmd'] = $syntaxCheckCmd;
1214
1215 // Update splash with current service being checked (1 step)
1216 if ($currentIndex > 0 && $totalCount > 0) {
1217 $this->splash->setTextLoading('Checking ' . $bin->getName() . ' service (' . $currentIndex . '/' . $totalCount . ')');
1218 }
1219 $this->splash->incrProgressBar();
1220
1221 // Check if service is already installed
1222 $serviceAlreadyInstalled = false;
1223 $serviceToRemove = false;
1224
1225 // Skip service checks for disabled services
1226 if (!$bin->isEnable()) {
1227 Log::trace('Skipping service check for disabled bin: ' . $bin->getName());
1228 $serviceInfos = false;
1229 } else {
1230 if ($sName == BinApache::SERVICE_NAME) {
1231 $serviceInfos = $this->checkApacheServiceWithTimeout($service);
1232 } else {
1233 if ($sName == BinMysql::SERVICE_NAME) {
1234 $serviceInfos = $this->checkMySQLServiceWithTimeout($service, $bin);
1235 if ($serviceInfos === false && $service->isInstalled()) {
1236 Log::trace('MySQL service appears to be hanging, forcing restart');
1237 Win32Ps::killBins(['mysqld.exe']);
1238 $service->delete();
1239 $serviceToRemove = true;
1240 }
1241 } else {
1242 try {
1243 $serviceInfos = $service->infos();
1244 } catch (\Exception $e) {
1245 Log::trace("Exception during service check: " . $e->getMessage());
1246 $serviceInfos = false;
1247 } catch (\Throwable $e) {
1248 Log::trace("Throwable during service check: " . $e->getMessage());
1249 $serviceInfos = false;
1250 }
1251 }
1252 }
1253 }
1254
1255 if ($serviceInfos !== false) {
1256 $serviceAlreadyInstalled = true;
1257 $this->writeLog($name . ' service already installed');
1258
1259 // Check if service needs to be removed and reinstalled
1260 if ($sName == BinPostgresql::SERVICE_NAME) {
1261 $serviceGenPathName = trim(str_replace('"', '', $service->getBinPath()));
1262 $installedPathParts = explode(' ', $serviceInfos[Win32Service::VBS_PATH_NAME], 2);
1263 $serviceVbsPathName = trim(str_replace('"', '', $installedPathParts[0]));
1264 } else {
1265 $serviceGenPathName = trim(str_replace('"', '', $service->getBinPath() . ($service->getParams() ? ' ' . $service->getParams() : '')));
1266 $serviceVbsPathName = trim(str_replace('"', '', $serviceInfos[Win32Service::VBS_PATH_NAME]));
1267 }
1268
1269 $normalizedGenPath = preg_replace('/\s+/', ' ', $serviceGenPathName);
1270 $normalizedVbsPath = preg_replace('/\s+/', ' ', $serviceVbsPathName);
1271
1272 if ($normalizedGenPath !== $normalizedVbsPath && $serviceGenPathName != $serviceVbsPathName) {
1273 $serviceToRemove = true;
1274 $this->writeLog($name . ' service has to be removed');
1275 }
1276 }
1277
1278 // Remove service if needed (no progress increment - part of check phase)
1279 if ($serviceToRemove) {
1280 if (!$service->delete()) {
1281 $serviceInfo['restart'] = true;
1282
1283 return $serviceInfo;
1284 }
1285 }
1286
1287 // Check port availability (no progress increment - part of check phase)
1288 $isPortInUse = ServiceHelper::isPortInUse($port);
1289 if ($isPortInUse !== false) {
1290 // Port is in use - check if it's our service that's already running
1291 $isRunning = false;
1292 if ($serviceInfos !== false && is_array($serviceInfos)) {
1293 // Extract state from serviceInfos array (works for both NSSM and Win32Service)
1294 $state = $serviceInfos[Win32Service::SERVICE_STATE] ?? $serviceInfos['CurrentState'] ?? null;
1295
1296 // Check if running: state can be numeric '4' or string 'RUNNING'
1297 if ($state !== null) {
1298 $isRunning = ($state == Win32Service::WIN32_SERVICE_RUNNING) ||
1299 (strtoupper((string)$state) === 'RUNNING');
1300 } else {
1301 // Fallback to calling isRunning() if state is not found
1302 $isRunning = $service->isRunning();
1303 }
1304 } else {
1305 if ($serviceInfos === false) {
1306 // Fallback when serviceInfos is false
1307 $isRunning = $service->isRunning();
1308 }
1309 }
1310
1311 if ($isRunning) {
1312 // Service is already running and owns the port - this is OK
1313 $this->writeLog($name . ' service already running on port ' . $port);
1314 Log::trace('Service ' . $name . ' already running - no need to start');
1315 $serviceInfo['needsStart'] = false;
1316
1317 return $serviceInfo;
1318 }
1319
1320 // Fallback to a single status check if serviceInfos didn't give us the answer
1321 if ($serviceInfos === false && $service->isRunning()) {
1322 $this->writeLog($name . ' service already running on port ' . $port);
1323 Log::trace('Service ' . $name . ' already running - no need to start');
1324 $serviceInfo['needsStart'] = false;
1325
1326 return $serviceInfo;
1327 }
1328
1329 // Port is in use by something else - this is an error
1330 $serviceInfo['error'] = sprintf($bearsamppLang->getValue(Lang::STARTUP_SERVICE_PORT_ERROR), $port, $isPortInUse);
1331
1332 return $serviceInfo;
1333 }
1334
1335 // Install service if needed (no progress increment - part of check phase)
1336 if (!$serviceAlreadyInstalled || $serviceToRemove) {
1337 if (!$service->create()) {
1338 $serviceInfo['error'] = sprintf($bearsamppLang->getValue(Lang::STARTUP_SERVICE_CREATE_ERROR), $service->getError());
1339
1340 return $serviceInfo;
1341 }
1342 }
1343
1344 $serviceInfo['needsStart'] = true;
1345
1346 return $serviceInfo;
1347 }
$port
checkMySQLServiceWithTimeout($service, $bin)
checkApacheServiceWithTimeout($service)
const CMD_SYNTAX_CHECK
const SERVICE_NAME
const CMD_SYNTAX_CHECK
const STARTUP_SERVICE_CREATE_ERROR
const STARTUP_SERVICE_PORT_ERROR
static isPortInUse($port)

References $bearsamppBins, $bearsamppLang, $port, checkApacheServiceWithTimeout(), checkMySQLServiceWithTimeout(), BinApache\CMD_SYNTAX_CHECK, BinMariadb\CMD_SYNTAX_CHECK, BinMysql\CMD_SYNTAX_CHECK, Util\getMicrotime(), ServiceHelper\isPortInUse(), Win32Ps\killBins(), BinApache\SERVICE_NAME, BinMailpit\SERVICE_NAME, BinMariadb\SERVICE_NAME, BinMemcached\SERVICE_NAME, BinMysql\SERVICE_NAME, BinPostgresql\SERVICE_NAME, BinXlight\SERVICE_NAME, Win32Service\SERVICE_STATE, Lang\STARTUP_SERVICE_CREATE_ERROR, Lang\STARTUP_SERVICE_PORT_ERROR, Log\trace(), Win32Service\VBS_PATH_NAME, Win32Service\WIN32_SERVICE_RUNNING, and writeLog().

Referenced by installServicesSequential().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ processWindow()

processWindow ( $window,
$id,
$ctrl,
$param1,
$param2 )

Processes the main window events during startup.

Parameters
mixed$windowThe window handle.
int$idThe event ID.
mixed$ctrlThe control that triggered the event.
mixed$param1Additional parameter 1.
mixed$param2Additional parameter 2.

Definition at line 90 of file class.action.startup.php.

91 {
92 global $bearsamppRoot, $bearsamppCore, $bearsamppLang, $bearsamppBins, $bearsamppTools, $bearsamppApps, $bearsamppWinbinder;
93
94 Log::trace('Starting processWindow method');
95
96 // Admin check is now performed in the constructor before anything else
97 // No need to check again here
98
99 // Rotation logs
100 Log::trace('Performing log rotation');
101 $this->rotationLogs();
102
103 // Clean
104 Log::trace('Starting cleanup operations');
105 $this->cleanTmpFolders();
106 $this->cleanOldBehaviors();
107
108 // List procs
109 Log::trace('Listing running processes');
110 if ($bearsamppRoot->getProcs() !== false) {
111 $this->writeLog('List procs:');
112 $listProcs = array();
113 foreach ($bearsamppRoot->getProcs() as $proc) {
115 $listProcs[] = '-> ' . basename($unixExePath) . ' (PID ' . $proc[Win32Ps::PROCESS_ID] . ') in ' . $unixExePath;
116 }
117 sort($listProcs);
118 foreach ($listProcs as $proc) {
119 $this->writeLog($proc);
120 }
121 Log::trace('Found ' . count($listProcs) . ' running processes');
122 } else {
123 Log::trace('No processes found or unable to retrieve process list');
124 }
125
126 // List modules
127 Log::trace('Listing bins modules');
128 $this->writeLog('List bins modules:');
129 foreach ($bearsamppBins->getAll() as $module) {
130 if (!$module->isEnable()) {
131 $this->writeLog('-> ' . $module->getName() . ': ' . $bearsamppLang->getValue(Lang::DISABLED));
132 Log::trace('Bin module ' . $module->getName() . ' is disabled');
133 } else {
134 $this->writeLog('-> ' . $module->getName() . ': ' . $module->getVersion() . ' (' . $module->getRelease() . ')');
135 Log::trace('Bin module ' . $module->getName() . ': ' . $module->getVersion() . ' (' . $module->getRelease() . ')');
136 }
137 }
138
139 Log::trace('Listing tools modules');
140 $this->writeLog('List tools modules:');
141 foreach ($bearsamppTools->getAll() as $module) {
142 if (!$module->isEnable()) {
143 $this->writeLog('-> ' . $module->getName() . ': ' . $bearsamppLang->getValue(Lang::DISABLED));
144 Log::trace('Tool module ' . $module->getName() . ' is disabled');
145 } else {
146 $this->writeLog('-> ' . $module->getName() . ': ' . $module->getVersion() . ' (' . $module->getRelease() . ')');
147 Log::trace('Tool module ' . $module->getName() . ': ' . $module->getVersion() . ' (' . $module->getRelease() . ')');
148 }
149 }
150
151 Log::trace('Listing apps modules');
152 $this->writeLog('List apps modules:');
153 foreach ($bearsamppApps->getAll() as $module) {
154 if (!$module->isEnable()) {
155 $this->writeLog('-> ' . $module->getName() . ': ' . $bearsamppLang->getValue(Lang::DISABLED));
156 Log::trace('App module ' . $module->getName() . ' is disabled');
157 } else {
158 $this->writeLog('-> ' . $module->getName() . ': ' . $module->getVersion() . ' (' . $module->getRelease() . ')');
159 Log::trace('App module ' . $module->getName() . ': ' . $module->getVersion() . ' (' . $module->getRelease() . ')');
160 }
161 }
162
163 // Kill old instances
164 Log::trace('Killing old instances');
165 $this->killOldInstances();
166
167 // Prepare app
168 Log::trace('Preparing application - refreshing hostname');
169 $this->refreshHostname();
170
171 Log::trace('Checking launch startup settings');
172 $this->checkLaunchStartup();
173
174 Log::trace('Checking browser configuration');
175 $this->checkBrowser();
176
177 Log::trace('Gathering system information');
178 $this->sysInfos();
179
180 Log::trace('Refreshing aliases');
181 $this->refreshAliases();
182
183 Log::trace('Refreshing virtual hosts');
184 $this->refreshVhosts();
185
186 // Check app path
187 Log::trace('Checking application path');
188 $this->checkPath();
189
190 Log::trace('Scanning folders');
191 $this->scanFolders();
192
193 Log::trace('Changing paths in files');
194 $this->changePath();
195
196 Log::trace('Saving current path');
197 $this->savePath();
198
199 // Check BEARSAMPP_PATH, BEARSAMPP_BINS and System Path reg keys
200 Log::trace('Checking PATH registry key');
201 $this->checkPathRegKey();
202
203 Log::trace('Checking BINS registry key');
204 $this->checkBinsRegKey();
205
206 Log::trace('Checking System PATH registry key');
207 $this->checkSystemPathRegKey();
208
209 // Update config
210 Log::trace('Updating configuration');
211 $this->updateConfig();
212
213 // Create SSL certificates
214 Log::trace('Creating SSL certificates');
215 $this->createSslCrts();
216
217 // Install
218 Log::trace('Installing services');
219 $this->installServices();
220
221 // Actions if everything OK
222 if (!$this->restart && empty($this->error)) {
223 Log::trace('Startup completed successfully - refreshing Git repositories');
224 $this->refreshGitRepos();
225 $startupTime = round(Util::getMicrotime() - $this->startTime, 3);
226 $this->writeLog('Started in ' . $startupTime . 's');
227 Log::trace('Application started successfully in ' . $startupTime . ' seconds');
228
229 // Log total startup time in VERBOSE_TRACE mode (mode 3)
230 global $bearsamppConfig;
231 if ($bearsamppConfig->getLogsVerbose() == Config::VERBOSE_TRACE) {
232 $minutes = floor($startupTime / 60); // floor() returns int-compatible value
233 $seconds = fmod($startupTime, 60);
234 $formattedTime = sprintf('%d:%05.2f', $minutes, $seconds);
235 Log::trace('=== TOTAL STARTUP TIME: ' . $formattedTime . ' ===');
236 }
237 } else {
238 Log::trace('Startup issues detected - incrementing progress bar');
239 $this->splash->incrProgressBar(2);
240 }
241
242 if ($this->restart) {
243 Log::trace('Restart required - preparing to restart application');
244 $this->writeLog(APP_TITLE . ' has to be restarted');
245 $this->splash->setTextLoading(
246 sprintf(
248 APP_TITLE . ' ' . $bearsamppCore->getAppVersion()
249 )
250 );
251
252 Log::trace('Deleting all services before restart');
253 foreach ($bearsamppBins->getServices() as $sName => $service) {
254 Log::trace('Deleting service: ' . $sName);
255 $service->delete();
256 }
257
258 Log::trace('Setting execution action to RESTART');
260 }
261
262 if (!empty($this->error)) {
263 Log::trace('Errors occurred during startup: ' . $this->error);
264 $this->writeLog('Error: ' . $this->error);
265 $bearsamppWinbinder->messageBoxError($this->error, $bearsamppLang->getValue(Lang::STARTUP_ERROR_TITLE));
266 }
267
268 Log::trace('Starting loading screen');
269 // Moved Util::startLoading() to after splash window destruction to prevent double progress bars
270
271 // Give the loading window time to initialize before we terminate this process
272 Log::trace('Waiting for loading window to initialize');
273 usleep(500000); // 500ms delay to allow loading window to start
274
275 Log::trace('Loading process started');
276
277 // Closing cli to finish startup
278 Log::trace('Finishing startup process');
279
280 // Safely reset WinBinder and destroy the splash window
281 $bearsamppWinbinder->destroyWindow($window);
282 $bearsamppWinbinder->reset();
283
284 // Start loading screen AFTER splash window is destroyed to prevent double progress bars
286
287 // Exit this startup process cleanly - the loading window will continue running
288 Log::trace('Exiting startup process cleanly');
289 exit(0);
290 }
const VERBOSE_TRACE
const DISABLED
const STARTUP_PREPARE_RESTART_TEXT
const STARTUP_ERROR_TITLE
static startLoading()

References $bearsamppBins, $bearsamppConfig, $bearsamppCore, $bearsamppLang, $bearsamppRoot, $proc, APP_TITLE, changePath(), checkBinsRegKey(), checkBrowser(), checkLaunchStartup(), checkPath(), checkPathRegKey(), checkSystemPathRegKey(), cleanOldBehaviors(), cleanTmpFolders(), createSslCrts(), Lang\DISABLED, Win32Ps\EXECUTABLE_PATH, exit, Path\formatUnixPath(), Util\getMicrotime(), installServices(), killOldInstances(), Win32Ps\PROCESS_ID, refreshAliases(), refreshGitRepos(), refreshHostname(), refreshVhosts(), ActionExec\RESTART, rotationLogs(), savePath(), scanFolders(), Util\startLoading(), Lang\STARTUP_ERROR_TITLE, Lang\STARTUP_PREPARE_RESTART_TEXT, sysInfos(), Log\trace(), updateConfig(), Config\VERBOSE_TRACE, and writeLog().

Here is the call graph for this function:

◆ refreshAliases()

refreshAliases ( )
private

Refreshes the aliases in the Apache configuration.

Definition at line 677 of file class.action.startup.php.

678 {
680
681 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_REFRESH_ALIAS_TEXT));
682 $this->splash->incrProgressBar();
683 $this->writeLog('Refresh aliases');
684
685 $bearsamppBins->getApache()->refreshAlias($bearsamppConfig->isOnline());
686 }
const STARTUP_REFRESH_ALIAS_TEXT

References $bearsamppBins, $bearsamppConfig, $bearsamppLang, Lang\STARTUP_REFRESH_ALIAS_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refreshGitRepos()

refreshGitRepos ( )
private

Refreshes Git repositories if the scan on startup is enabled. Logs the number of repositories found.

Definition at line 1353 of file class.action.startup.php.

1354 {
1355 global $bearsamppLang, $bearsamppTools;
1356
1357 $this->splash->incrProgressBar();
1358 if ($bearsamppTools->getGit()->isScanStartup()) {
1359 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_REFRESH_GIT_REPOS_TEXT));
1360
1361 $repos = $bearsamppTools->getGit()->findRepos(false);
1362 $this->writeLog('Update GIT repos: ' . count($repos) . ' found');
1363 }
1364 }
const STARTUP_REFRESH_GIT_REPOS_TEXT

References $bearsamppLang, Lang\STARTUP_REFRESH_GIT_REPOS_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refreshHostname()

refreshHostname ( )
private

Refreshes the hostname in the configuration.

Definition at line 616 of file class.action.startup.php.

617 {
619
620 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_REFRESH_HOSTNAME_TEXT));
621 $this->splash->incrProgressBar();
622 $this->writeLog('Refresh hostname');
623
624 $bearsamppConfig->replace(Config::CFG_HOSTNAME, gethostname());
625 }
const CFG_HOSTNAME
const STARTUP_REFRESH_HOSTNAME_TEXT

References $bearsamppConfig, $bearsamppLang, Config\CFG_HOSTNAME, Lang\STARTUP_REFRESH_HOSTNAME_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ refreshVhosts()

refreshVhosts ( )
private

Refreshes the virtual hosts in the Apache configuration.

Definition at line 691 of file class.action.startup.php.

692 {
694
695 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_REFRESH_VHOSTS_TEXT));
696 $this->splash->incrProgressBar();
697 $this->writeLog('Refresh vhosts');
698
699 $bearsamppBins->getApache()->refreshVhosts($bearsamppConfig->isOnline());
700 }
const STARTUP_REFRESH_VHOSTS_TEXT

References $bearsamppBins, $bearsamppConfig, $bearsamppLang, Lang\STARTUP_REFRESH_VHOSTS_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rotationLogs()

rotationLogs ( )
private

Rotates the logs by archiving old logs and purging old archives. Enhanced with file lock checking to prevent permission denied errors.

Definition at line 296 of file class.action.startup.php.

297 {
299
300 Log::trace("Starting log rotation process");
301 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_ROTATION_LOGS_TEXT));
302 $this->splash->incrProgressBar();
303
304 $archivesPath = Path::getLogsPath() . '/archives';
305 if (!is_dir($archivesPath)) {
306 Log::trace("Creating archives directory: " . $archivesPath);
307 mkdir($archivesPath, 0777, true);
308
309 return;
310 }
311
312 $date = date('Y-m-d-His', time());
313 $archiveLogsPath = $archivesPath . '/' . $date;
314 $archiveScriptsPath = $archiveLogsPath . '/scripts';
315
316 // Create archive folders
317 Log::trace("Creating archive directories for current rotation");
318 if (!is_dir($archiveLogsPath)) {
319 Log::trace("Creating logs archive directory: " . $archiveLogsPath);
320 mkdir($archiveLogsPath, 0777, true);
321 } else {
322 Log::trace("Logs archive directory already exists: " . $archiveLogsPath);
323 }
324
325 if (!is_dir($archiveScriptsPath)) {
326 Log::trace("Creating scripts archive directory: " . $archiveScriptsPath);
327 mkdir($archiveScriptsPath, 0777, true);
328 } else {
329 Log::trace("Scripts archive directory already exists: " . $archiveScriptsPath);
330 }
331
332 // Count archives
333 Log::trace("Counting existing archives");
334 $archives = array();
335 $handle = @opendir($archivesPath);
336 if (!$handle) {
337 Log::trace("Failed to open archives directory: " . $archivesPath);
338
339 return;
340 }
341
342 while (false !== ($file = readdir($handle))) {
343 if ($file == '.' || $file == '..') {
344 continue;
345 }
346 $archives[] = $archivesPath . '/' . $file;
347 }
348 closedir($handle);
349 sort($archives);
350 Log::trace("Found " . count($archives) . " existing archives");
351
352 // Remove old archives
353 if (count($archives) > $bearsamppConfig->getMaxLogsArchives()) {
354 $total = count($archives) - $bearsamppConfig->getMaxLogsArchives();
355 Log::trace("Removing " . $total . " old archives");
356 for ($i = 0; $i < $total; $i++) {
357 Log::trace("Deleting old archive: " . $archives[$i]);
358 Util::deleteFolder($archives[$i]);
359 }
360 }
361
362 // Helper function to check if a file is locked
363 $isFileLocked = function ($filePath) {
364 if (!file_exists($filePath)) {
365 return false;
366 }
367
368 $handle = @fopen($filePath, 'r+');
369 if ($handle === false) {
370 Log::trace("File appears to be locked: " . $filePath);
371
372 return true; // File is locked
373 }
374
375 fclose($handle);
376
377 return false; // File is not locked
378 };
379
380 // Logs
381 Log::trace("Archiving log files");
382 $srcPath = Path::getLogsPath();
383 $handle = @opendir($srcPath);
384 if (!$handle) {
385 Log::trace("Failed to open logs directory: " . $srcPath);
386
387 return;
388 }
389
390 $logsCopied = 0;
391 $logsSkipped = 0;
392
393 while (false !== ($file = readdir($handle))) {
394 if ($file == '.' || $file == '..' || is_dir($srcPath . '/' . $file)) {
395 continue;
396 }
397
398 $sourceFile = $srcPath . '/' . $file;
399 $destFile = $archiveLogsPath . '/' . $file;
400
401 // Check if file is locked before attempting to copy
402 if ($isFileLocked($sourceFile)) {
403 Log::trace("Skipping locked log file: " . $file);
404 $logsSkipped++;
405 continue;
406 }
407
408 try {
409 if (copy($sourceFile, $destFile)) {
410 $logsCopied++;
411 Log::trace("Archived log file: " . $file);
412 } else {
413 $logsSkipped++;
414 Log::trace("Failed to copy log file: " . $file);
415 }
416 } catch (Exception $e) {
417 $logsSkipped++;
418 Log::trace("Exception copying log file " . $file . ": " . $e->getMessage());
419 }
420 }
421 closedir($handle);
422 Log::trace("Logs archived: " . $logsCopied . " copied, " . $logsSkipped . " skipped");
423
424 // Scripts
425 Log::trace("Archiving script files");
426 $srcPath = Path::getTmpPath();
427 $handle = @opendir($srcPath);
428 if (!$handle) {
429 Log::trace("Failed to open tmp directory: " . $srcPath);
430
431 return;
432 }
433
434 $scriptsCopied = 0;
435 $scriptsSkipped = 0;
436
437 while (false !== ($file = readdir($handle))) {
438 if ($file == '.' || $file == '..' || is_dir($srcPath . '/' . $file)) {
439 continue;
440 }
441
442 $sourceFile = $srcPath . '/' . $file;
443 $destFile = $archiveScriptsPath . '/' . $file;
444
445 // Check if file is locked before attempting to copy
446 if ($isFileLocked($sourceFile)) {
447 Log::trace("Skipping locked script file: " . $file);
448 $scriptsSkipped++;
449 continue;
450 }
451
452 try {
453 if (copy($sourceFile, $destFile)) {
454 $scriptsCopied++;
455 Log::trace("Archived script file: " . $file);
456 } else {
457 $scriptsSkipped++;
458 Log::trace("Failed to copy script file: " . $file);
459 }
460 } catch (Exception $e) {
461 $scriptsSkipped++;
462 Log::trace("Exception copying script file " . $file . ": " . $e->getMessage());
463 }
464 }
465 closedir($handle);
466 Log::trace("Scripts archived: " . $scriptsCopied . " copied, " . $scriptsSkipped . " skipped");
467
468 // Purge logs - only delete files that aren't locked
469 Log::trace("Purging log files");
470 $logsPath = Path::getLogsPath();
471 $handle = @opendir($logsPath);
472 if (!$handle) {
473 Log::trace("Failed to open logs directory for purging: " . $logsPath);
474
475 return;
476 }
477
478 $logsDeleted = 0;
479 $logsPurgeSkipped = 0;
480
481 while (false !== ($file = readdir($handle))) {
482 if ($file == '.' || $file == '..' || $file == 'archives' || $file == '.gitignore' || is_dir($logsPath . '/' . $file)) {
483 continue;
484 }
485
486 $filePath = $logsPath . '/' . $file;
487
488 // Check if file is locked before attempting to delete
489 if ($isFileLocked($filePath)) {
490 Log::trace("Skipping locked log file during purge: " . $file);
491 $logsPurgeSkipped++;
492 continue;
493 }
494
495 try {
496 if (is_link($filePath)) {
497 if (@unlink($filePath)) {
498 $logsDeleted++;
499 Log::trace("Purged log symlink: " . $file);
500 } else {
501 $logsPurgeSkipped++;
502 Log::trace("Failed to purge log symlink: " . $file);
503 }
504 } elseif (file_exists($filePath)) {
505 if (@unlink($filePath)) {
506 $logsDeleted++;
507 Log::trace("Purged log file: " . $file);
508 } else {
509 $logsPurgeSkipped++;
510 Log::trace("Failed to purge log file: " . $file);
511 }
512 } else {
513 $logsPurgeSkipped++;
514 Log::trace("Log file already removed or does not exist: " . $file);
515 }
516 } catch (Exception $e) {
517 $logsPurgeSkipped++;
518 Log::trace("Exception purging log file " . $file . ": " . $e->getMessage());
519 }
520 }
521 closedir($handle);
522 Log::trace("Logs purged: " . $logsDeleted . " deleted, " . $logsPurgeSkipped . " skipped");
523
524 Log::trace("Log rotation completed");
525 }
const STARTUP_ROTATION_LOGS_TEXT
static getLogsPath($aetrayPath=false)
static deleteFolder($path)

References $bearsamppBins, $bearsamppConfig, $bearsamppCore, $bearsamppLang, $bearsamppRoot, Util\deleteFolder(), Path\getLogsPath(), Path\getTmpPath(), Lang\STARTUP_ROTATION_LOGS_TEXT, and Log\trace().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ savePath()

savePath ( )
private

Saves the current application path.

Definition at line 780 of file class.action.startup.php.

781 {
782 global $bearsamppCore;
783
784 file_put_contents(Path::getLastPath(), $this->rootPath);
785 $this->writeLog('Save current path: ' . $this->rootPath);
786 }
static getLastPath($aetrayPath=false)

References $bearsamppCore, Path\getLastPath(), and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ scanFolders()

scanFolders ( )
private

Scans folders and logs the number of files to scan. Performance optimization: Skips expensive file scan when path hasn't changed. This saves 3-8 seconds on typical startups (95% of cases).

Definition at line 720 of file class.action.startup.php.

721 {
723
724 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_SCAN_FOLDERS_TEXT));
725 $this->splash->incrProgressBar();
726
727 $lastPath = $bearsamppCore->getLastPathContent();
728 $currentPath = $this->rootPath;
729
730 // Performance optimization: Skip scan if path hasn't changed
731 // BUT always scan if it's the first time or if we need to ensure placeholders are replaced.
732 // We removed the optimization because it prevents newly added modules from being initialized
733 // with the correct paths if the main project path remains the same.
734 /*
735 if ($lastPath === $currentPath && !empty($lastPath)) {
736 Log::debug('Path unchanged, skipping file scan (performance optimization)');
737 Log::trace('Last path: "' . $lastPath . '" matches current path: "' . $currentPath . '"');
738
739 $this->filesToScan = [];
740 $this->writeLog('Files to scan: 0 (path unchanged - scan skipped)');
741 return;
742 }
743 */
744
745 // Path changed or first run, perform full scan
746 if ($lastPath !== $currentPath || empty($lastPath)) {
747 Log::debug('Path changed or first run, performing full file scan');
748 Log::trace('Last path: "' . $lastPath . '" differs from current path: "' . $currentPath . '"');
749 $this->writeLog('Path changed detected - performing full scan');
750 } else {
751 Log::debug('Ensuring configuration files are checked for placeholders');
752 $this->writeLog('Scanning configuration files for placeholders');
753 }
754
755 $scanStartTime = Util::getMicrotime();
756 $this->filesToScan = Util::getFilesToScan();
757 $scanDuration = round(Util::getMicrotime() - $scanStartTime, 3);
758
759 $this->writeLog('Files to scan: ' . count($this->filesToScan) . ' (scanned in ' . $scanDuration . 's)');
760 }
const STARTUP_SCAN_FOLDERS_TEXT
static debug($data, $file=null)
static getFilesToScan($path=null, $useCache=true, $forceRefresh=false)

References $bearsamppCore, $bearsamppLang, $rootPath, Log\debug(), Util\getFilesToScan(), Util\getMicrotime(), Lang\STARTUP_SCAN_FOLDERS_TEXT, Log\trace(), and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sysInfos()

sysInfos ( )
private

Logs system information.

Definition at line 663 of file class.action.startup.php.

664 {
665 global $bearsamppLang;
666
667 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_SYS_INFOS));
668 $this->splash->incrProgressBar();
669
670 $os = Batch::getOsInfo();
671 $this->writeLog(sprintf('OS: %s', $os));
672 }
static getOsInfo()
const STARTUP_SYS_INFOS

References $bearsamppLang, Batch\getOsInfo(), Lang\STARTUP_SYS_INFOS, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ updateConfig()

updateConfig ( )
private

Updates the configuration for bins, tools, and apps. Logs the update process.

Definition at line 892 of file class.action.startup.php.

893 {
894 global $bearsamppLang, $bearsamppBins, $bearsamppTools, $bearsamppApps;
895
896 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::STARTUP_UPDATE_CONFIG_TEXT));
897 $this->splash->incrProgressBar();
898 $this->writeLog('Update config');
899
900 $bearsamppBins->update();
901 $bearsamppTools->update();
902 $bearsamppApps->update();
903 }
const STARTUP_UPDATE_CONFIG_TEXT

References $bearsamppBins, $bearsamppLang, Lang\STARTUP_UPDATE_CONFIG_TEXT, and writeLog().

Referenced by processWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ writeLog()

writeLog ( $log)
private

Writes a log message to the startup log file.

Parameters
string$logThe log message to write.

Definition at line 1477 of file class.action.startup.php.

1478 {
1479 global $bearsamppRoot;
1481 }
static getStartupLogFilePath($aetrayPath=false)

References $bearsamppRoot, Log\debug(), and Path\getStartupLogFilePath().

Referenced by __construct(), changePath(), checkBinsRegKey(), checkBrowser(), checkLaunchStartup(), checkPath(), checkPathRegKey(), checkSystemPathRegKey(), cleanOldBehaviors(), cleanTmpFolders(), installServicesSequential(), killOldInstances(), prepareService(), processWindow(), refreshAliases(), refreshGitRepos(), refreshHostname(), refreshVhosts(), savePath(), scanFolders(), sysInfos(), and updateConfig().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ $error

$error
private

Definition at line 28 of file class.action.startup.php.

Referenced by installServicesSequential().

◆ $filesToScan

$filesToScan
private

Definition at line 34 of file class.action.startup.php.

◆ $restart

$restart
private

Definition at line 22 of file class.action.startup.php.

◆ $rootPath

$rootPath
private

Definition at line 31 of file class.action.startup.php.

Referenced by scanFolders().

◆ $splash

$splash
private

Definition at line 19 of file class.action.startup.php.

◆ $startTime

$startTime
private

Definition at line 25 of file class.action.startup.php.

Referenced by checkApacheServiceWithTimeout().

◆ GAUGE_OTHERS

const GAUGE_OTHERS = 19

Definition at line 40 of file class.action.startup.php.

◆ GAUGE_SERVICES

const GAUGE_SERVICES = 5

Definition at line 37 of file class.action.startup.php.


The documentation for this class was generated from the following file: