Bearsampp 2026.7.11
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 35 of file class.action.startup.php.

36 {
37 global $bearsamppRoot, $bearsamppCore, $bearsamppLang, $bearsamppBins, $bearsamppWinbinder;
38 $this->writeLog( 'Starting ' . APP_TITLE );
39
40 // Admin check is now performed in root.php before ActionStartup is instantiated
41 // This prevents screen flashes and ensures the check happens before any WinBinder initialization
42 Log::info('Administrator privileges confirmed - proceeding with startup');
43
44 // Init
45 $this->splash = new Splash();
46 $this->restart = false;
47 $this->startTime = Util::getMicrotime();
48 $this->error = '';
49
50 $this->rootPath = Path::getRootPath();
51 $this->filesToScan = array();
52
53 $gauge = self::GAUGE_SERVICES * count( $bearsamppBins->getServices() );
54 $gauge += self::GAUGE_OTHERS + 1;
55
56 // Start splash screen
57 $this->splash->init(
58 $bearsamppLang->getValue( Lang::STARTUP ),
59 $gauge,
60 sprintf( $bearsamppLang->getValue( Lang::STARTUP_STARTING_TEXT ), APP_TITLE . ' ' . $bearsamppCore->getAppVersion() )
61 );
62
63 $bearsamppWinbinder->setHandler( $this->splash->getWbWindow(), $this, 'processWindow', 1000 );
64 $bearsamppWinbinder->mainLoop();
65 $bearsamppWinbinder->reset();
66 }
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:13

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 746 of file class.action.startup.php.

747 {
748 global $bearsamppLang;
749
750 $this->splash->setTextLoading( sprintf( $bearsamppLang->getValue( Lang::STARTUP_CHANGE_PATH_TEXT ), $this->rootPath ) );
751 $this->splash->incrProgressBar();
752
753 $result = Path::changePath( $this->filesToScan, $this->rootPath );
754 $this->writeLog( 'Nb files changed: ' . $result['countChangedFiles'] );
755 $this->writeLog( 'Nb occurences changed: ' . $result['countChangedOcc'] );
756 }
$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 1342 of file class.action.startup.php.

1343 {
1344 Log::trace('Starting specialized Apache service check with timeout protection');
1345
1346 // Set a timeout for the Apache service check
1347 $serviceCheckStartTime = microtime(true);
1348 $serviceCheckTimeout = 3; // 3 seconds timeout
1349
1350 try {
1351 // Use a non-blocking approach to check service
1352 $serviceInfos = false;
1353
1354 // First try a quick check if the service exists in the list
1355 $serviceList = Win32Service::getServices();
1356 if (is_array($serviceList) && isset($serviceList[$service->getName()])) {
1357 Log::trace('Apache service found in service list, getting details');
1358
1359 // Service exists, now try to get its details with timeout protection
1360 $startTime = microtime(true);
1361 $serviceInfos = $service->infos();
1362
1363 // Check if we've exceeded our timeout
1364 if (microtime(true) - $serviceCheckStartTime > $serviceCheckTimeout) {
1365 Log::trace("Apache service check timeout exceeded, assuming service needs reinstall");
1366 return false;
1367 }
1368 } else {
1369 Log::trace('Apache service not found in service list');
1370 return false;
1371 }
1372
1373 return $serviceInfos;
1374 } catch (\Exception $e) {
1375 Log::trace("Exception during Apache service check: " . $e->getMessage());
1376 return false;
1377 } catch (\Throwable $e) {
1378 Log::trace("Throwable during Apache service check: " . $e->getMessage());
1379 return false;
1380 }
1381 }
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 805 of file class.action.startup.php.

806 {
807 global $bearsamppLang, $bearsamppRegistry;
808
809 $currentAppBinsRegKey = $bearsamppRegistry->getAppBinsRegKey();
810 $genAppBinsRegKey = $bearsamppRegistry->getAppBinsRegKey( false );
811 $this->splash->setTextLoading( sprintf( $bearsamppLang->getValue( Lang::STARTUP_REGISTRY_TEXT ), Registry::APP_BINS_REG_ENTRY . ': ' . $genAppBinsRegKey ) );
812 $this->splash->incrProgressBar();
813
814 $this->writeLog( 'Current app bins reg key: ' . $currentAppBinsRegKey );
815 $this->writeLog( 'Gen app bins reg key: ' . $genAppBinsRegKey );
816 if ( $currentAppBinsRegKey != $genAppBinsRegKey ) {
817 if ( !$bearsamppRegistry->setAppBinsRegKey( $genAppBinsRegKey ) ) {
818 if ( !empty( $this->error ) ) {
819 $this->error .= PHP_EOL . PHP_EOL;
820 }
822 $this->error .= PHP_EOL . $bearsamppRegistry->getLatestError();
823 }
824 else {
825 $this->writeLog( 'Need restart: checkBinsRegKey' );
826 $this->restart = true;
827 }
828 }
829 }
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 627 of file class.action.startup.php.

628 {
630
631 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_CHECK_BROWSER_TEXT ) );
632 $this->splash->incrProgressBar();
633 $this->writeLog( 'Check browser' );
634
635 $currentBrowser = $bearsamppConfig->getBrowser();
636 if ( empty( $currentBrowser ) || !file_exists( $currentBrowser ) ) {
638 }
639 }
const CFG_BROWSER
const STARTUP_CHECK_BROWSER_TEXT
static getDefaultBrowser()
global $bearsamppConfig
Definition homepage.php:41

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 610 of file class.action.startup.php.

611 {
612 global $bearsamppConfig;
613
614 $this->writeLog( 'Check launch startup' );
615
616 if ( $bearsamppConfig->isLaunchStartup() ) {
618 }
619 else {
621 }
622 }
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 1391 of file class.action.startup.php.

1392 {
1393 Log::trace('Starting specialized MySQL service check with timeout protection');
1394
1395 // Set a timeout for the MySQL service check
1396 $serviceCheckStartTime = microtime(true);
1397 $serviceCheckTimeout = 3; // 3 seconds timeout
1398
1399 try {
1400 // Use a non-blocking approach to check service
1401 $serviceInfos = false;
1402
1403 // First check if the service exists in the list
1404 $serviceList = Win32Service::getServices();
1405 if (is_array($serviceList) && isset($serviceList[$service->getName()])) {
1406 Log::trace('MySQL service found in service list, getting details');
1407
1408 // Service exists, now try to get its details with timeout protection
1409 $serviceInfos = $service->infos();
1410
1411 // Check if we've exceeded our timeout
1412 if (microtime(true) - $serviceCheckStartTime > $serviceCheckTimeout) {
1413 Log::trace("MySQL service check timeout exceeded, assuming service needs reinstall");
1414 return false;
1415 }
1416 } else {
1417 Log::trace('MySQL service not found in service list');
1418 return false;
1419 }
1420
1421 return $serviceInfos;
1422 } catch (\Exception $e) {
1423 Log::trace("Exception during MySQL service check: " . $e->getMessage());
1424 return false;
1425 } catch (\Throwable $e) {
1426 Log::trace("Throwable during MySQL service check: " . $e->getMessage());
1427 return false;
1428 }
1429 }

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 686 of file class.action.startup.php.

687 {
689
690 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_CHECK_PATH_TEXT ) );
691 $this->splash->incrProgressBar();
692
693 $this->writeLog( 'Last path: ' . $bearsamppCore->getLastPathContent() );
694 }
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 772 of file class.action.startup.php.

773 {
774 global $bearsamppRoot, $bearsamppLang, $bearsamppRegistry;
775
776 $currentAppPathRegKey = $bearsamppRegistry->getAppPathRegKey();
777 $genAppPathRegKey = Path::formatWindowsPath( Path::getRootPath() );
778 $this->splash->setTextLoading( sprintf( $bearsamppLang->getValue( Lang::STARTUP_REGISTRY_TEXT ), Registry::APP_PATH_REG_ENTRY . ': ' . $genAppPathRegKey ) );
779 $this->splash->incrProgressBar();
780
781 $this->writeLog( 'Current app path reg key: ' . $currentAppPathRegKey );
782 $this->writeLog( 'Gen app path reg key: ' . $genAppPathRegKey );
783 if ( $currentAppPathRegKey != $genAppPathRegKey ) {
784 if ( !$bearsamppRegistry->setAppPathRegKey( $genAppPathRegKey ) ) {
785 if ( !empty( $this->error ) ) {
786 $this->error .= PHP_EOL . PHP_EOL;
787 }
789 $this->error .= PHP_EOL . $bearsamppRegistry->getLatestError();
790 }
791 else {
792 $this->writeLog( 'Need restart: checkPathRegKey' );
793 $this->restart = true;
794 }
795 }
796 }
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 838 of file class.action.startup.php.

839 {
840 global $bearsamppLang, $bearsamppRegistry;
841
842 $currentSysPathRegKey = $bearsamppRegistry->getSysPathRegKey();
843 $this->splash->setTextLoading( sprintf( $bearsamppLang->getValue( Lang::STARTUP_REGISTRY_TEXT ), Registry::SYSPATH_REG_ENTRY ) );
844 $this->splash->incrProgressBar();
845
846 $this->writeLog( 'Current system PATH: ' . $currentSysPathRegKey );
847
848 $newSysPathRegKey = str_replace( '%' . Registry::APP_BINS_REG_ENTRY . '%;', '', $currentSysPathRegKey );
849 $newSysPathRegKey = str_replace( '%' . Registry::APP_BINS_REG_ENTRY . '%', '', $newSysPathRegKey );
850 $newSysPathRegKey = '%' . Registry::APP_BINS_REG_ENTRY . '%;' . $newSysPathRegKey;
851 $this->writeLog( 'New system PATH: ' . $newSysPathRegKey );
852
853 if ( $currentSysPathRegKey != $newSysPathRegKey ) {
854 if ( !$bearsamppRegistry->setSysPathRegKey( $newSysPathRegKey ) ) {
855 if ( !empty( $this->error ) ) {
856 $this->error .= PHP_EOL . PHP_EOL;
857 }
859 $this->error .= PHP_EOL . $bearsamppRegistry->getLatestError();
860 }
861 else {
862 $this->writeLog( 'Need restart: checkSystemPathRegKey' );
863 $this->restart = true;
864 }
865 }
866 else {
867 $this->writeLog( 'Refresh system PATH: ' . $currentSysPathRegKey );
868 $bearsamppRegistry->setSysPathRegKey( str_replace( '%' . Registry::APP_BINS_REG_ENTRY . '%', '', $currentSysPathRegKey ) );
869 $bearsamppRegistry->setSysPathRegKey( $currentSysPathRegKey );
870 }
871 }
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 541 of file class.action.startup.php.

542 {
543 global $bearsamppLang, $bearsamppRegistry;
544
545 $this->writeLog( 'Clean old behaviors' );
546
547 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_CLEAN_OLD_BEHAVIORS_TEXT ) );
548 $this->splash->incrProgressBar();
549
550 // App >= 1.0.13
551 $bearsamppRegistry->deleteValue(
553 'SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
555 );
556 }
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 511 of file class.action.startup.php.

512 {
514
515 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_CLEAN_TMP_TEXT ) );
516 $this->splash->incrProgressBar();
517
518 $this->writeLog( 'Clear tmp folders' );
519 Util::clearFolder( Path::getTmpPath(), array('cachegrind', 'composer', 'openssl', 'mailpit', 'xlight', 'npm-cache', 'pip', 'opcache', '.gitignore') );
520 Util::clearFolder( Path::getTmpPath(), array('.gitignore') );
521
522 // Ensure opcache directory exists for persistent file cache
523 $opcachePath = Path::getTmpPath() . DIRECTORY_SEPARATOR . 'opcache';
524
525 if (!is_dir($opcachePath)) {
526 $this->writeLog('Creating opcache directory: ' . $opcachePath);
527 if (!@mkdir($opcachePath, 0755, true) && !is_dir($opcachePath)) {
528 $this->writeLog('Failed to create opcache directory: ' . $opcachePath);
529 return;
530 }
531 }
532
533 if (!is_writable($opcachePath)) {
534 $this->writeLog('Opcache directory is not writable: ' . $opcachePath);
535 }
536 }
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 894 of file class.action.startup.php.

895 {
896 global $bearsamppLang, $bearsamppOpenSsl, $bearsamppBins;
897
898 Log::info('Checking SSL certificates during startup...');
899 $this->splash->incrProgressBar();
900
901 // Always ensure localhost exists and is valid
902 $localhostExpired = $bearsamppOpenSsl->isExpired('localhost');
903 Log::trace('Localhost SSL expired status: ' . ($localhostExpired ? 'YES' : 'NO'));
904
905 if ($localhostExpired) {
906 Log::info('SSL certificate for "localhost" is missing or expired. Checking if creation is necessary...');
907 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_GEN_SSL_CRT_TEXT), 'localhost'));
908
909 // Only create if NOT present or REALLY expired
910 // (isExpired already returns true if missing)
911 if (!$bearsamppOpenSsl->createCrt('localhost')) {
912 Log::error('FAILED call to createCrt("localhost")');
913 }
914
915 // Re-verify after creation to log success/failure
916 if ($bearsamppOpenSsl->isExpired('localhost')) {
917 Log::error('FAILED to verify localhost SSL certificate after creation attempt.');
918 } else {
919 Log::info('Successfully verified localhost SSL certificate.');
920 }
921 } else {
922 Log::trace('SSL certificate for "localhost" is valid.');
923 }
924
925 // Also check all Apache vhosts
926 $apache = $bearsamppBins->getApache();
927 if ($apache) {
928 $vhosts = $apache->getVhosts();
929 Log::info('Checking SSL certificates for ' . count($vhosts) . ' vhosts');
930 foreach ($vhosts as $vhost) {
931 if ($bearsamppOpenSsl->isExpired($vhost)) {
932 Log::info('SSL certificate for "' . $vhost . '" is missing or expired. Creating new one...');
933 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::STARTUP_GEN_SSL_CRT_TEXT), $vhost));
934 if (!$bearsamppOpenSsl->createCrt($vhost)) {
935 Log::error('FAILED call to createCrt("' . $vhost . '")');
936 }
937 } else {
938 Log::trace('SSL certificate for "' . $vhost . '" is valid.');
939 }
940 }
941 } else {
942 Log::info('Apache module not found or disabled, skipping vhost SSL check');
943 }
944 }
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 954 of file class.action.startup.php.

955 {
957
958 Log::trace('Starting installServices method');
959
960 if (!$this->restart) {
961 Log::trace('Normal startup mode - processing services');
962
963 // Admin check is now performed at the very beginning of processWindow()
964 // No need to check again here
965
966 // Service Installation
968 } else {
969 Log::trace('Restart mode - skipping service installation');
970 $this->splash->incrProgressBar(self::GAUGE_SERVICES * count($bearsamppBins->getServices()));
971 }
972
973 Log::trace('Completed installServices method');
974 }
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 984 of file class.action.startup.php.

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

562 {
563 global $bearsamppLang;
564
565 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_KILL_OLD_PROCS_TEXT ) );
566 $this->splash->incrProgressBar();
567
568 // Stop services
569 /*foreach ($bearsamppBins->getServices() as $sName => $service) {
570 $serviceInfos = $service->infos();
571 if ($serviceInfos === false) {
572 continue;
573 }
574 $service->stop();
575 }*/
576
577 // Stop third party procs
578 $procsKilled = Win32Ps::killBins();
579 if ( !empty( $procsKilled ) ) {
580 $this->writeLog( 'Procs killed:' );
581 $procsKilledSort = array();
582 foreach ( $procsKilled as $proc ) {
584 $procsKilledSort[] = '-> ' . basename( $unixExePath ) . ' (PID ' . $proc[Win32Ps::PROCESS_ID] . ') in ' . $unixExePath;
585 }
586 sort( $procsKilledSort );
587 foreach ( $procsKilledSort as $proc ) {
588 $this->writeLog( $proc );
589 }
590 }
591 }
$proc
Definition ajax.php:45
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 1137 of file class.action.startup.php.

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

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

659 {
661
662 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_REFRESH_ALIAS_TEXT ) );
663 $this->splash->incrProgressBar();
664 $this->writeLog( 'Refresh aliases' );
665
666 $bearsamppBins->getApache()->refreshAlias( $bearsamppConfig->isOnline() );
667 }
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 1322 of file class.action.startup.php.

1323 {
1324 global $bearsamppLang, $bearsamppTools;
1325
1326 $this->splash->incrProgressBar();
1327 if ( $bearsamppTools->getGit()->isScanStartup() ) {
1328 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_REFRESH_GIT_REPOS_TEXT ) );
1329
1330 $repos = $bearsamppTools->getGit()->findRepos( false );
1331 $this->writeLog( 'Update GIT repos: ' . count( $repos ) . ' found' );
1332 }
1333 }
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 596 of file class.action.startup.php.

597 {
599
600 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_REFRESH_HOSTNAME_TEXT ) );
601 $this->splash->incrProgressBar();
602 $this->writeLog( 'Refresh hostname' );
603
604 $bearsamppConfig->replace( Config::CFG_HOSTNAME, gethostname() );
605 }
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 672 of file class.action.startup.php.

673 {
675
676 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_REFRESH_VHOSTS_TEXT ) );
677 $this->splash->incrProgressBar();
678 $this->writeLog( 'Refresh vhosts' );
679
680 $bearsamppBins->getApache()->refreshVhosts( $bearsamppConfig->isOnline() );
681 }
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 284 of file class.action.startup.php.

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

762 {
763 global $bearsamppCore;
764
765 file_put_contents( Path::getLastPath(), $this->rootPath );
766 $this->writeLog( 'Save current path: ' . $this->rootPath );
767 }
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 701 of file class.action.startup.php.

702 {
704
705 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_SCAN_FOLDERS_TEXT ) );
706 $this->splash->incrProgressBar();
707
708 $lastPath = $bearsamppCore->getLastPathContent();
709 $currentPath = $this->rootPath;
710
711 // Performance optimization: Skip scan if path hasn't changed
712 // BUT always scan if it's the first time or if we need to ensure placeholders are replaced.
713 // We removed the optimization because it prevents newly added modules from being initialized
714 // with the correct paths if the main project path remains the same.
715 /*
716 if ($lastPath === $currentPath && !empty($lastPath)) {
717 Log::debug('Path unchanged, skipping file scan (performance optimization)');
718 Log::trace('Last path: "' . $lastPath . '" matches current path: "' . $currentPath . '"');
719
720 $this->filesToScan = [];
721 $this->writeLog('Files to scan: 0 (path unchanged - scan skipped)');
722 return;
723 }
724 */
725
726 // Path changed or first run, perform full scan
727 if ($lastPath !== $currentPath || empty($lastPath)) {
728 Log::debug('Path changed or first run, performing full file scan');
729 Log::trace('Last path: "' . $lastPath . '" differs from current path: "' . $currentPath . '"');
730 $this->writeLog('Path changed detected - performing full scan');
731 } else {
732 Log::debug('Ensuring configuration files are checked for placeholders');
733 $this->writeLog('Scanning configuration files for placeholders');
734 }
735
736 $scanStartTime = Util::getMicrotime();
737 $this->filesToScan = Util::getFilesToScan();
738 $scanDuration = round(Util::getMicrotime() - $scanStartTime, 3);
739
740 $this->writeLog('Files to scan: ' . count($this->filesToScan) . ' (scanned in ' . $scanDuration . 's)');
741 }
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 644 of file class.action.startup.php.

645 {
646 global $bearsamppLang;
647
648 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_SYS_INFOS ) );
649 $this->splash->incrProgressBar();
650
651 $os = Batch::getOsInfo();
652 $this->writeLog( sprintf( 'OS: %s', $os ) );
653 }
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 877 of file class.action.startup.php.

878 {
879 global $bearsamppLang, $bearsamppBins, $bearsamppTools, $bearsamppApps;
880
881 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::STARTUP_UPDATE_CONFIG_TEXT ) );
882 $this->splash->incrProgressBar();
883 $this->writeLog( 'Update config' );
884
885 $bearsamppBins->update();
886 $bearsamppTools->update();
887 $bearsamppApps->update();
888 }
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 1436 of file class.action.startup.php.

1437 {
1438 global $bearsamppRoot;
1440 }
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 21 of file class.action.startup.php.

Referenced by installServicesSequential().

◆ $filesToScan

$filesToScan
private

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

◆ $restart

$restart
private

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

◆ $rootPath

$rootPath
private

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

Referenced by scanFolders().

◆ $splash

$splash
private

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

◆ $startTime

$startTime
private

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

Referenced by checkApacheServiceWithTimeout().

◆ GAUGE_OTHERS

const GAUGE_OTHERS = 19

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

◆ GAUGE_SERVICES

const GAUGE_SERVICES = 5

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


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