Bearsampp 2026.7.11
Loading...
Searching...
No Matches
class.action.startup.php
Go to the documentation of this file.
1<?php
2/*
3 *
4 * * Copyright (c) 2022-2025 Bearsampp
5 * * License: GNU General Public License version 3 or later; see LICENSE.txt
6 * * Website: https://bearsampp.com
7 * * Github: https://github.com/Bearsampp
8 *
9 */
10
17{
18 private $splash;
19 private $restart;
20 private $startTime;
21 private $error;
22
23 private $rootPath;
24 private $filesToScan;
25
26 const GAUGE_SERVICES = 5;
27 const GAUGE_OTHERS = 19;
28
35 public function __construct($args)
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 }
67
77 public function processWindow($window, $id, $ctrl, $param1, $param2)
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 }
279
284 private function rotationLogs()
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 }
507
511 private function cleanTmpFolders()
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 }
537
541 private function cleanOldBehaviors()
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 }
557
561 private function killOldInstances()
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 }
592
596 private function refreshHostname()
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 }
606
610 private function checkLaunchStartup()
611 {
612 global $bearsamppConfig;
613
614 $this->writeLog( 'Check launch startup' );
615
616 if ( $bearsamppConfig->isLaunchStartup() ) {
618 }
619 else {
621 }
622 }
623
627 private function checkBrowser()
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 }
640
644 private function sysInfos()
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 }
654
658 private function refreshAliases()
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 }
668
672 private function refreshVhosts()
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 }
682
686 private function checkPath()
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 }
695
701 private function scanFolders()
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 }
742
746 private function changePath()
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 }
757
761 private function savePath()
762 {
763 global $bearsamppCore;
764
765 file_put_contents( Path::getLastPath(), $this->rootPath );
766 $this->writeLog( 'Save current path: ' . $this->rootPath );
767 }
768
772 private function checkPathRegKey()
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 }
797
805 private function checkBinsRegKey()
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 }
830
838 private function checkSystemPathRegKey()
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 }
872
877 private function updateConfig()
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 }
889
894 private function createSslCrts()
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 }
945
953
954 private function installServices()
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
967 $this->installServicesSequential($bearsamppBins, $bearsamppLang);
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 }
975
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 }
1125
1137 private function prepareService($sName, $service, $bearsamppBins, $bearsamppLang, $currentIndex = 0, $totalCount = 0)
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 }
1317
1322 private function refreshGitRepos()
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 }
1334
1342 private function checkApacheServiceWithTimeout($service)
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 }
1382
1391 private function checkMySQLServiceWithTimeout($service, $bin)
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 }
1430
1436 private function writeLog($log)
1437 {
1438 global $bearsamppRoot;
1440 }
1441}
$result
global $bearsamppBins
global $bearsamppLang
global $bearsamppRoot
$port
global $bearsamppCore
$proc
Definition ajax.php:45
prepareService($sName, $service, $bearsamppBins, $bearsamppLang, $currentIndex=0, $totalCount=0)
installServicesSequential($bearsamppBins, $bearsamppLang)
checkMySQLServiceWithTimeout($service, $bin)
checkApacheServiceWithTimeout($service)
processWindow($window, $id, $ctrl, $param1, $param2)
static getOsInfo()
const CMD_SYNTAX_CHECK
const SERVICE_NAME
const CMD_SYNTAX_CHECK
const CFG_HOSTNAME
const CFG_BROWSER
const VERBOSE_TRACE
const STARTUP_STARTING_TEXT
const STARTUP_SYS_INFOS
const STARTUP_REFRESH_GIT_REPOS_TEXT
const DISABLED
const STARTUP_SERVICE_ERROR
const STARTUP_CHECK_PATH_TEXT
const STARTUP_SCAN_FOLDERS_TEXT
const STARTUP_CHANGE_PATH_TEXT
const STARTUP_PREPARE_RESTART_TEXT
const STARTUP_ROTATION_LOGS_TEXT
const LOADING_START_SERVICE
const STARTUP_SERVICE_CREATE_ERROR
const STARTUP_SERVICE_PORT_ERROR
const STARTUP
const STARTUP_GEN_SSL_CRT_TEXT
const STARTUP_VERIFY_SERVICE_TEXT
const STARTUP_ERROR_TITLE
const STARTUP_CLEAN_OLD_BEHAVIORS_TEXT
const STARTUP_REFRESH_ALIAS_TEXT
const STARTUP_REGISTRY_ERROR_TEXT
const STARTUP_KILL_OLD_PROCS_TEXT
const STARTUP_CHECK_BROWSER_TEXT
const STARTUP_REFRESH_HOSTNAME_TEXT
const STARTUP_REGISTRY_TEXT
const STARTUP_UPDATE_CONFIG_TEXT
const STARTUP_REFRESH_VHOSTS_TEXT
const STARTUP_CLEAN_TMP_TEXT
static info($data, $file=null)
static debug($data, $file=null)
static warning($data, $file=null)
static trace($data, $file=null)
static error($data, $file=null)
static getRootPath($aetrayPath=false)
static getStartupLogFilePath($aetrayPath=false)
static getLogsPath($aetrayPath=false)
static formatWindowsPath($path)
static changePath($filesToScan, $rootPath=null)
static formatUnixPath($path)
static getTmpPath($aetrayPath=false)
static getLastPath($aetrayPath=false)
const SYSPATH_REG_ENTRY
const HKEY_LOCAL_MACHINE
const APP_PATH_REG_ENTRY
const APP_BINS_REG_ENTRY
static startAllServicesParallel($serviceInfos, ?callable $progressCallback=null, $startupTimeout=30)
static disableLaunchStartup()
static getMicrotime()
static deleteFolder($path)
static startLoading()
static isPortInUse($port)
static getFilesToScan($path=null, $useCache=true, $forceRefresh=false)
static clearFolder($path, $exclude=array())
static enableLaunchStartup()
static getDefaultBrowser()
static killBins($refreshProcs=false)
const EXECUTABLE_PATH
const PROCESS_ID
static getServices($forceRefresh=false)
global $bearsamppConfig
Definition homepage.php:41
const APP_TITLE
Definition root.php:13