Bearsampp 2026.7.11
Loading...
Searching...
No Matches
ActionQuit Class Reference

Public Member Functions

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

Static Public Member Functions

static terminatePhpProcesses ($excludePid, $window=null, $splash=null, $timeout=10)

Data Fields

const GAUGE_OTHERS = 1
const GAUGE_PROCESSES = 1

Private Member Functions

 checkForOrphanedProcesses ()
 cleanupTemporaryFiles ()
 generateCleanupReport ($serviceVerification, $symlinkVerification, $tempCleanup, $orphanedProcesses)
 getServiceDisplayName ($sName, $service)
 getServiceShutdownOrder ()
 performQuickCleanupVerification ($services)
 verifyServicesStoppedAndCleanup ($services)
 verifySymlinksRemoved ()

Private Attributes

 $splash

Detailed Description

Class ActionQuit Handles the quitting process of the Bearsampp application. Displays a splash screen and stops all services and processes.

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

Constructor & Destructor Documentation

◆ __construct()

__construct ( $args)

ActionQuit constructor. Initializes the quitting process, displays the splash screen, and sets up the main loop.

Parameters
array$argsCommand line arguments.

Definition at line 35 of file class.action.quit.php.

36 {
37 global $bearsamppCore, $bearsamppLang, $bearsamppBins, $bearsamppWinbinder, $arrayOfCurrents;
38
39 Log::info('ActionQuit constructor called - starting exit process');
40 Log::debug('Number of services to stop: ' . count($bearsamppBins->getServices()));
41
42 // Start splash screen
43 $this->splash = new Splash();
44 $this->splash->init(
45 $bearsamppLang->getValue( Lang::QUIT ),
46 self::GAUGE_PROCESSES * count( $bearsamppBins->getServices() ) + self::GAUGE_OTHERS,
47 sprintf( $bearsamppLang->getValue( Lang::EXIT_LEAVING_TEXT ), APP_TITLE . ' ' . $bearsamppCore->getAppVersion() )
48 );
49
50 Log::debug('Splash screen initialized');
51
52 // Set handler for the splash screen window
53 $bearsamppWinbinder->setHandler( $this->splash->getWbWindow(), $this, 'processWindow', 2000 );
54 Log::debug('Window handler set, starting main loop');
55
56 $bearsamppWinbinder->mainLoop();
57 Log::debug('Main loop exited');
58
59 $bearsamppWinbinder->reset();
60 Log::info('ActionQuit constructor completed');
61 }
global $bearsamppBins
global $bearsamppLang
global $bearsamppCore
const QUIT
const EXIT_LEAVING_TEXT
static info($data, $file=null)
static debug($data, $file=null)
const APP_TITLE
Definition root.php:13

References $bearsamppBins, $bearsamppCore, $bearsamppLang, APP_TITLE, Log\debug(), Lang\EXIT_LEAVING_TEXT, Log\info(), and Lang\QUIT.

Here is the call graph for this function:

Member Function Documentation

◆ checkForOrphanedProcesses()

checkForOrphanedProcesses ( )
private

Check for orphaned Bearsampp processes that should have been terminated.

Returns
array List of orphaned processes

Definition at line 543 of file class.action.quit.php.

544 {
545 global $bearsamppRoot;
546
547 Log::info('Checking for orphaned processes...');
548
549 $orphaned = [
550 'found' => false,
551 'processes' => []
552 ];
553
554 try {
555 $procs = Win32Ps::getListProcs();
556 $bearsamppPath = strtolower(Path::formatUnixPath(Path::getRootPath()));
557 $currentPid = Win32Ps::getCurrentPid();
558
559 foreach ($procs as $proc) {
560 $exePath = strtolower(Path::formatUnixPath($proc[Win32Ps::EXECUTABLE_PATH]));
562
563 // Skip current process
564 if ($pid == $currentPid) {
565 continue;
566 }
567
568 // Check if process is from Bearsampp directory
569 if (strpos($exePath, $bearsamppPath) === 0) {
570 $processName = basename($exePath);
571
572 // Skip www directory processes (user applications)
573 if (strpos($exePath, $bearsamppPath . '/www/') === 0) {
574 continue;
575 }
576
577 // Skip the main Bearsampp executable
578 if (strtolower($processName) === 'bearsampp.exe') {
579 Log::debug('Skipping main Bearsampp process: ' . $processName . ' (PID: ' . $pid . ')');
580 continue;
581 }
582
583 // These are orphaned Bearsampp processes
584 $orphaned['found'] = true;
585 $orphaned['processes'][] = [
586 'pid' => $pid,
587 'name' => $processName,
588 'path' => $exePath
589 ];
590
591 Log::warning('Found orphaned process: ' . $processName . ' (PID: ' . $pid . ')');
592
593 // Attempt to kill orphaned process
594 try {
595 Win32Ps::kill($pid);
596 Log::info('Terminated orphaned process: ' . $processName . ' (PID: ' . $pid . ')');
597 } catch (\Exception $e) {
598 Log::error('Failed to terminate orphaned process ' . $processName . ': ' . $e->getMessage());
599 }
600 }
601 }
602
603 if (!$orphaned['found']) {
604 Log::info('No orphaned processes found');
605 } else {
606 Log::warning('Found ' . count($orphaned['processes']) . ' orphaned process(es)');
607 }
608
609 } catch (\Exception $e) {
610 Log::error('Error checking for orphaned processes: ' . $e->getMessage());
611 }
612
613 return $orphaned;
614 }
global $bearsamppRoot
$proc
Definition ajax.php:45
static warning($data, $file=null)
static error($data, $file=null)
static getRootPath($aetrayPath=false)
static formatUnixPath($path)
static getCurrentPid()
static getListProcs()
static kill($pid)
const EXECUTABLE_PATH
const PROCESS_ID

References $bearsamppRoot, $proc, Log\debug(), Log\error(), Win32Ps\EXECUTABLE_PATH, Path\formatUnixPath(), Win32Ps\getCurrentPid(), Win32Ps\getListProcs(), Path\getRootPath(), Log\info(), Win32Ps\kill(), Win32Ps\PROCESS_ID, and Log\warning().

Referenced by performQuickCleanupVerification().

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

◆ cleanupTemporaryFiles()

cleanupTemporaryFiles ( )
private

Clean up temporary files created during Bearsampp operation.

Returns
array Cleanup results

Definition at line 447 of file class.action.quit.php.

448 {
449 global $bearsamppCore;
450
451 Log::info('Cleaning up temporary files...');
452
453 $results = [
454 'success' => true,
455 'cleaned' => 0,
456 'failed' => [],
457 'size_freed' => 0
458 ];
459
460 $tmpPath = Path::getTmpPath();
461
462 if (!is_dir($tmpPath)) {
463 Log::debug('Temp directory does not exist: ' . $tmpPath);
464 return $results;
465 }
466
467 try {
468 $files = glob($tmpPath . '/*');
469
470 if ($files === false) {
471 Log::warning('Failed to list temporary files');
472 return $results;
473 }
474
475 foreach ($files as $file) {
476 // Skip certain files that should be preserved
477 $basename = basename($file);
478 if (in_array($basename, ['.', '..', '.gitkeep', 'README.md'])) {
479 continue;
480 }
481
482 try {
483 $size = is_file($file) ? filesize($file) : 0;
484
485 if (is_link($file)) {
486 if (@unlink($file) || @rmdir($file)) {
487 $results['cleaned']++;
488 $results['size_freed'] += $size;
489 Log::debug('Removed temp symlink: ' . $basename);
490 } else {
491 $results['failed'][] = $basename;
492 $results['success'] = false;
493 Log::warning('Failed to remove temp symlink: ' . $basename);
494 }
495 } elseif (is_file($file)) {
496 if (@unlink($file)) {
497 $results['cleaned']++;
498 $results['size_freed'] += $size;
499 Log::debug('Removed temp file: ' . $basename);
500 } else {
501 $results['failed'][] = $basename;
502 $results['success'] = false;
503 Log::warning('Failed to remove temp file: ' . $basename);
504 }
505 } elseif (is_dir($file)) {
506 Util::deleteFolder($file);
507 if (!file_exists($file)) {
508 $results['cleaned']++;
509 Log::debug('Removed temp directory: ' . $basename);
510 } else {
511 $results['failed'][] = $basename;
512 $results['success'] = false;
513 Log::warning('Failed to remove temp directory: ' . $basename);
514 }
515 }
516 } catch (\Exception $e) {
517 $results['failed'][] = $basename;
518 $results['success'] = false;
519 Log::error('Error removing temp file ' . $basename . ': ' . $e->getMessage());
520 }
521 }
522
523 $sizeMB = round($results['size_freed'] / 1024 / 1024, 2);
524 Log::info('Cleaned up ' . $results['cleaned'] . ' temporary files (' . $sizeMB . ' MB freed)');
525
526 if (!empty($results['failed'])) {
527 Log::warning('Failed to clean up ' . count($results['failed']) . ' files');
528 }
529
530 } catch (\Exception $e) {
531 Log::error('Error during temp file cleanup: ' . $e->getMessage());
532 $results['success'] = false;
533 }
534
535 return $results;
536 }
static getTmpPath($aetrayPath=false)
static deleteFolder($path)

References $bearsamppCore, Log\debug(), Util\deleteFolder(), Log\error(), Path\getTmpPath(), Log\info(), and Log\warning().

Referenced by performQuickCleanupVerification().

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

◆ generateCleanupReport()

generateCleanupReport ( $serviceVerification,
$symlinkVerification,
$tempCleanup,
$orphanedProcesses )
private

Generate a comprehensive cleanup report.

Parameters
array$serviceVerificationService verification results
array$symlinkVerificationSymlink verification results
array$tempCleanupTemp file cleanup results
array$orphanedProcessesOrphaned process check results
Returns
array Comprehensive cleanup report

Definition at line 625 of file class.action.quit.php.

626 {
627 $report = [
628 'success' => true,
629 'warnings' => [],
630 'errors' => [],
631 'summary' => []
632 ];
633
634 // Service verification
635 if (!$serviceVerification['all_stopped']) {
636 $report['success'] = false;
637
638 if (!empty($serviceVerification['still_running'])) {
639 $report['errors'][] = 'Services still running: ' . implode(', ', $serviceVerification['still_running']);
640 }
641
642 if (!empty($serviceVerification['verification_failed'])) {
643 $report['warnings'][] = 'Could not verify status of: ' . implode(', ', $serviceVerification['verification_failed']);
644 }
645 }
646
647 $report['summary'][] = 'Services checked: ' . count($serviceVerification['services']);
648
649 // Symlink verification
650 if (!$symlinkVerification['success']) {
651 $report['warnings'][] = 'Symlinks not fully removed: ' . implode(', ', $symlinkVerification['remaining']);
652 }
653
654 // Temp file cleanup
655 if ($tempCleanup['cleaned'] > 0) {
656 $sizeMB = round($tempCleanup['size_freed'] / 1024 / 1024, 2);
657 $report['summary'][] = 'Temp files cleaned: ' . $tempCleanup['cleaned'] . ' (' . $sizeMB . ' MB)';
658 }
659
660 if (!empty($tempCleanup['failed'])) {
661 $report['warnings'][] = 'Failed to clean ' . count($tempCleanup['failed']) . ' temp file(s)';
662 }
663
664 // Orphaned processes
665 if ($orphanedProcesses['found']) {
666 $report['warnings'][] = 'Found ' . count($orphanedProcesses['processes']) . ' orphaned process(es)';
667 foreach ($orphanedProcesses['processes'] as $proc) {
668 $report['summary'][] = 'Orphaned: ' . $proc['name'] . ' (PID: ' . $proc['pid'] . ')';
669 }
670 }
671
672 return $report;
673 }

References $proc.

◆ getServiceDisplayName()

getServiceDisplayName ( $sName,
$service )
private

Get the display name for a service.

Parameters
string$sNameThe service name constant
object$serviceThe service object
Returns
string The formatted display name

Definition at line 97 of file class.action.quit.php.

98 {
99 global $bearsamppBins;
100
101 $name = '';
102
103 if ($sName == BinApache::SERVICE_NAME) {
104 $name = $bearsamppBins->getApache()->getName() . ' ' . $bearsamppBins->getApache()->getVersion();
105 }
106 elseif ($sName == BinMysql::SERVICE_NAME) {
107 $name = $bearsamppBins->getMysql()->getName() . ' ' . $bearsamppBins->getMysql()->getVersion();
108 }
109 elseif ($sName == BinMailpit::SERVICE_NAME) {
110 $name = $bearsamppBins->getMailpit()->getName() . ' ' . $bearsamppBins->getMailpit()->getVersion();
111 }
112 elseif ($sName == BinMariadb::SERVICE_NAME) {
113 $name = $bearsamppBins->getMariadb()->getName() . ' ' . $bearsamppBins->getMariadb()->getVersion();
114 }
115 elseif ($sName == BinPostgresql::SERVICE_NAME) {
116 $name = $bearsamppBins->getPostgresql()->getName() . ' ' . $bearsamppBins->getPostgresql()->getVersion();
117 }
118 elseif ($sName == BinMemcached::SERVICE_NAME) {
119 $name = $bearsamppBins->getMemcached()->getName() . ' ' . $bearsamppBins->getMemcached()->getVersion();
120 }
121 elseif ($sName == BinXlight::SERVICE_NAME) {
122 $name = $bearsamppBins->getXlight()->getName() . ' ' . $bearsamppBins->getXlight()->getVersion();
123 }
124
125 $name .= ' (' . $service->getName() . ')';
126 return $name;
127 }
const SERVICE_NAME

References $bearsamppBins, BinApache\SERVICE_NAME, BinMailpit\SERVICE_NAME, BinMariadb\SERVICE_NAME, BinMemcached\SERVICE_NAME, BinMysql\SERVICE_NAME, BinPostgresql\SERVICE_NAME, and BinXlight\SERVICE_NAME.

Referenced by processWindow(), and verifyServicesStoppedAndCleanup().

Here is the caller graph for this function:

◆ getServiceShutdownOrder()

getServiceShutdownOrder ( )
private

Get the optimal service shutdown order based on dependencies. Services are ordered to stop dependent services first, then core services.

Returns
array Array of service names in shutdown order

Definition at line 70 of file class.action.quit.php.

71 {
72 // Define shutdown order: dependent services first, then core services
73 // This prevents connection errors and ensures clean shutdown
74 return [
75 // Tier 1: Application services (no dependencies on other services)
76 BinMailpit::SERVICE_NAME, // Mail testing tool
77 BinMemcached::SERVICE_NAME, // Caching service
78 BinXlight::SERVICE_NAME, // FTP server
79
80 // Tier 2: Database services (web server depends on these)
81 BinPostgresql::SERVICE_NAME, // PostgreSQL database
82 BinMariadb::SERVICE_NAME, // MariaDB database
83 BinMysql::SERVICE_NAME, // MySQL database
84
85 // Tier 3: Web server (depends on databases and other services)
86 BinApache::SERVICE_NAME, // Apache web server (stopped last)
87 ];
88 }

References BinApache\SERVICE_NAME, BinMailpit\SERVICE_NAME, BinMariadb\SERVICE_NAME, BinMemcached\SERVICE_NAME, BinMysql\SERVICE_NAME, BinPostgresql\SERVICE_NAME, and BinXlight\SERVICE_NAME.

Referenced by processWindow().

Here is the caller graph for this function:

◆ performQuickCleanupVerification()

performQuickCleanupVerification ( $services)
private

Perform quick cleanup verification without blocking the exit process. This is a lightweight version that only does essential checks.

Parameters
array$servicesArray of service objects
Returns
void

Definition at line 682 of file class.action.quit.php.

683 {
684 Log::info('Performing quick cleanup verification...');
685
686 $startTime = microtime(true);
687 $maxTime = 2; // Maximum 2 seconds for verification
688
689 try {
690 // Quick temp file cleanup (non-blocking)
691 $tempCleanup = $this->cleanupTemporaryFiles();
692
693 // Check if we're running out of time
694 if (microtime(true) - $startTime > $maxTime) {
695 Log::debug('Cleanup verification timeout reached, skipping remaining checks');
696 return;
697 }
698
699 // Quick orphaned process check (non-blocking)
700 $orphanedProcesses = $this->checkForOrphanedProcesses();
701
702 // Log summary
703 if ($tempCleanup['cleaned'] > 0) {
704 $sizeMB = round($tempCleanup['size_freed'] / 1024 / 1024, 2);
705 Log::info('Quick cleanup: ' . $tempCleanup['cleaned'] . ' temp files removed (' . $sizeMB . ' MB freed)');
706 }
707
708 if ($orphanedProcesses['found']) {
709 Log::info('Quick cleanup: ' . count($orphanedProcesses['processes']) . ' orphaned process(es) terminated');
710 }
711
712 $duration = round(microtime(true) - $startTime, 2);
713 Log::info('Quick cleanup verification completed in ' . $duration . ' seconds');
714
715 } catch (\Exception $e) {
716 Log::warning('Quick cleanup verification failed: ' . $e->getMessage());
717 }
718 }

References checkForOrphanedProcesses(), cleanupTemporaryFiles(), Log\debug(), Log\info(), and Log\warning().

Referenced by processWindow().

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 splash screen window events. Stops all services in optimal order, deletes symlinks, and kills remaining processes.

Parameters
resource$windowThe window resource.
int$idThe event ID.
int$ctrlThe control ID.
mixed$param1Additional parameter 1.
mixed$param2Additional parameter 2.

Definition at line 139 of file class.action.quit.php.

140 {
141 global $bearsamppBins, $bearsamppLang, $bearsamppWinbinder;
142
143 Log::info('Starting graceful shutdown process with optimized service order');
144
145 // Get all available services
146 $allServices = $bearsamppBins->getServices();
147
148 // Get optimal shutdown order
149 $shutdownOrder = $this->getServiceShutdownOrder();
150
151 Log::debug('Service shutdown order: ' . implode(' -> ', $shutdownOrder));
152
153 // Stop services in optimal order
154 foreach ($shutdownOrder as $sName) {
155 // Check if this service exists and is installed
156 if (!isset($allServices[$sName])) {
157 Log::debug('Service not found in available services: ' . $sName);
158 continue;
159 }
160
161 $service = $allServices[$sName];
162 $displayName = $this->getServiceDisplayName($sName, $service);
163
164 Log::info('Stopping service: ' . $displayName);
165
166 $this->splash->incrProgressBar();
167 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::EXIT_REMOVE_SERVICE_TEXT), $displayName));
168
169 // Delete (stop and remove) the service
170 $result = $service->delete();
171
172 if ($result) {
173 Log::info('Successfully stopped and removed service: ' . $displayName);
174 } else {
175 Log::warning('Failed to stop/remove service: ' . $displayName . ' (may not be installed)');
176 }
177 }
178
179 // Handle any services not in the shutdown order (for extensibility)
180 foreach ($allServices as $sName => $service) {
181 if (!in_array($sName, $shutdownOrder)) {
182 $displayName = $this->getServiceDisplayName($sName, $service);
183 Log::warning('Stopping unlisted service: ' . $displayName);
184
185 $this->splash->incrProgressBar();
186 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::EXIT_REMOVE_SERVICE_TEXT), $displayName));
187 $service->delete();
188 }
189 }
190
191 Log::info('All services stopped successfully');
192
193 // Purge "current" symlinks
194 $this->splash->setTextLoading('Removing symlinks...');
196
197 // Stop other processes
198 $this->splash->incrProgressBar();
199 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::EXIT_STOP_OTHER_PROCESS_TEXT));
200 Win32Ps::killBins(true);
201
202 // Explicitly kill NodeJS if still running (it's not a Windows service)
203 Log::trace('Explicitly terminating NodeJS processes');
204 Win32Ps::killBins(['node.exe']);
205
206 // Perform cleanup verification in background (non-blocking)
207 $this->splash->setTextLoading('Performing cleanup verification...');
208 $this->performQuickCleanupVerification($allServices);
209
210 // Terminate any remaining processes
211 // Final termination sequence
212 $this->splash->setTextLoading('Completing shutdown...');
213 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
214 $currentPid = Win32Ps::getCurrentPid();
215
216 // Terminate PHP processes with a timeout of 15 seconds
217 self::terminatePhpProcesses($currentPid, $window, $this->splash, 15);
218
219 // Force exit if still running
220 exit(0);
221 }
222
223 // Non-Windows fallback
224 $bearsamppWinbinder->destroyWindow($window);
225 exit(0);
226 }
$result
performQuickCleanupVerification($services)
static terminatePhpProcesses($excludePid, $window=null, $splash=null, $timeout=10)
getServiceDisplayName($sName, $service)
const EXIT_REMOVE_SERVICE_TEXT
const EXIT_STOP_OTHER_PROCESS_TEXT
static trace($data, $file=null)
static killBins($refreshProcs=false)

References $bearsamppBins, $bearsamppLang, $result, Log\debug(), Symlinks\deleteCurrentSymlinks(), exit, Lang\EXIT_REMOVE_SERVICE_TEXT, Lang\EXIT_STOP_OTHER_PROCESS_TEXT, Win32Ps\getCurrentPid(), getServiceDisplayName(), getServiceShutdownOrder(), Log\info(), Win32Ps\killBins(), performQuickCleanupVerification(), terminatePhpProcesses(), Log\trace(), and Log\warning().

Here is the call graph for this function:

◆ terminatePhpProcesses()

terminatePhpProcesses ( $excludePid,
$window = null,
$splash = null,
$timeout = 10 )
static

Terminates PHP processes with timeout handling.

Parameters
int$excludePidProcess ID to exclude
mixed$windowWindow handle or null
mixed$splashSplash screen or null
int$timeoutMaximum time to wait for termination (seconds)
Returns
void

Definition at line 237 of file class.action.quit.php.

238 {
239 global $bearsamppWinbinder, $bearsamppCore;
240
241 $currentPid = Win32Ps::getCurrentPid();
242 $startTime = microtime(true);
243
244 Log::trace('Starting PHP process termination (excluding PID: ' . $excludePid . ')');
245
246 // Get list of loading PIDs to exclude from termination
247 $loadingPids = array();
248 if (file_exists($bearsamppCore->getLoadingPid())) {
249 $pids = file($bearsamppCore->getLoadingPid());
250 foreach ($pids as $pid) {
251 $loadingPids[] = intval(trim($pid));
252 }
253 Log::trace('Loading PIDs to preserve: ' . implode(', ', $loadingPids));
254 }
255
256 $targets = ['php-win.exe', 'php.exe'];
257 foreach (Win32Ps::getListProcs() as $proc) {
258 // Check if we've exceeded our timeout
259 if (microtime(true) - $startTime > $timeout) {
260 Log::trace('Process termination timeout exceeded, continuing with remaining operations');
261 break;
262 }
263
264 $exe = strtolower(basename($proc[Win32Ps::EXECUTABLE_PATH]));
266
267 // Skip if this is the excluded PID or a loading window PID
268 if (in_array($exe, $targets) && $pid != $excludePid && !in_array($pid, $loadingPids)) {
269 Log::trace('Terminating PHP process: ' . $pid);
270 Win32Ps::kill($pid);
271 usleep(100000); // 100ms delay between terminations
272 } elseif (in_array($pid, $loadingPids)) {
273 Log::trace('Preserving loading window process: ' . $pid);
274 }
275 }
276
277 // Initiate self-termination with timeout
278 if ($splash !== null) {
279 $splash->setTextLoading('Final cleanup...');
280 }
281
282 try {
283 Log::trace('Initiating self-termination for PID: ' . $currentPid);
284 // Add a timeout wrapper around the killProc call
285 $killSuccess = Win32Native::killProcess($currentPid);
286 if (!$killSuccess) {
287 Log::trace('Self-termination via Win32Native::killProcess failed, using alternative method');
288 }
289 } catch (\Exception $e) {
290 Log::trace('Exception during self-termination: ' . $e->getMessage());
291 }
292
293 // Destroy window after process termination
294 // Fix for PHP 8.2: Check if window is not null before destroying
295 if ($window && $bearsamppWinbinder) {
296 try {
297 Log::trace('Destroying window');
298 $bearsamppWinbinder->destroyWindow($window);
299 } catch (\Exception $e) {
300 Log::trace('Exception during window destruction: ' . $e->getMessage());
301 }
302 }
303
304 // Force exit if still running after timeout
305 if (microtime(true) - $startTime > $timeout * 1.5) {
306 Log::trace('Forcing exit due to timeout');
307 exit(0);
308 }
309 }
static killProcess($pid)

References $bearsamppCore, $proc, $splash, Win32Ps\EXECUTABLE_PATH, exit, Win32Ps\getCurrentPid(), Win32Ps\getListProcs(), Win32Ps\kill(), Win32Native\killProcess(), Win32Ps\PROCESS_ID, and Log\trace().

Referenced by processWindow().

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

◆ verifyServicesStoppedAndCleanup()

verifyServicesStoppedAndCleanup ( $services)
private

Verify that all services are actually stopped and clean up any that are still running.

Parameters
array$servicesArray of service objects
Returns
array Verification results with status for each service

Definition at line 317 of file class.action.quit.php.

318 {
319 Log::info('Verifying all services are stopped...');
320
321 $results = [
322 'all_stopped' => true,
323 'services' => [],
324 'still_running' => [],
325 'verification_failed' => []
326 ];
327
328 foreach ($services as $sName => $service) {
329 $displayName = $this->getServiceDisplayName($sName, $service);
330
331 try {
332 // Check if service is still installed/running
333 $isInstalled = $service->isInstalled();
334 $isRunning = $isInstalled ? $service->isRunning() : false;
335
336 $results['services'][$sName] = [
337 'name' => $displayName,
338 'installed' => $isInstalled,
339 'running' => $isRunning
340 ];
341
342 if ($isRunning) {
343 Log::warning('Service still running after shutdown: ' . $displayName);
344 $results['still_running'][] = $displayName;
345 $results['all_stopped'] = false;
346
347 // Attempt to force stop
348 Log::info('Attempting to force stop: ' . $displayName);
349 $service->stop();
350 usleep(500000); // Wait 500ms
351
352 // Verify again
353 if ($service->isRunning()) {
354 Log::error('Failed to force stop service: ' . $displayName);
355 } else {
356 Log::info('Successfully force stopped service: ' . $displayName);
357 }
358 } elseif ($isInstalled) {
359 Log::debug('Service stopped but still installed: ' . $displayName);
360 } else {
361 Log::debug('Service verified stopped and removed: ' . $displayName);
362 }
363
364 } catch (\Exception $e) {
365 Log::error('Failed to verify service status for ' . $displayName . ': ' . $e->getMessage());
366 $results['verification_failed'][] = $displayName;
367 $results['all_stopped'] = false;
368 }
369 }
370
371 if ($results['all_stopped']) {
372 Log::info('All services verified stopped successfully');
373 } else {
374 Log::warning('Some services could not be verified as stopped');
375 }
376
377 return $results;
378 }

References Log\debug(), Log\error(), getServiceDisplayName(), Log\info(), and Log\warning().

Here is the call graph for this function:

◆ verifySymlinksRemoved()

verifySymlinksRemoved ( )
private

Verify that symlinks have been removed.

Returns
array Verification results

Definition at line 385 of file class.action.quit.php.

386 {
387 global $bearsamppRoot;
388
389 Log::info('Verifying symlinks are removed...');
390
391 $results = [
392 'success' => true,
393 'remaining' => []
394 ];
395
396 // Check common symlink locations
397 $symlinkPaths = [
398 Path::getRootPath() . '/apache',
399 Path::getRootPath() . '/php',
400 Path::getRootPath() . '/mysql',
401 Path::getRootPath() . '/mariadb',
402 Path::getRootPath() . '/postgresql',
403 Path::getRootPath() . '/nodejs',
404 Path::getRootPath() . '/memcached',
405 Path::getRootPath() . '/mailpit',
406 Path::getRootPath() . '/xlight'
407 ];
408
409 foreach ($symlinkPaths as $path) {
410 if (file_exists($path) || is_link($path)) {
411 Log::warning('Symlink still exists: ' . $path);
412 $results['remaining'][] = basename($path);
413 $results['success'] = false;
414
415 // Attempt to remove it using robust method
416 try {
417 $removed = \Symlinks::safeRemoveSymlink($path);
418
419 // Verify removal
420 if ($removed && !file_exists($path) && !is_link($path)) {
421 Log::info('Successfully removed remaining symlink: ' . $path);
422 $results['remaining'] = array_diff($results['remaining'], [basename($path)]);
423 if (empty($results['remaining'])) {
424 $results['success'] = true;
425 }
426 }
427 } catch (\Exception $e) {
428 Log::error('Failed to remove symlink ' . $path . ': ' . $e->getMessage());
429 }
430 }
431 }
432
433 if ($results['success']) {
434 Log::info('All symlinks verified removed');
435 } else {
436 Log::warning('Some symlinks could not be removed: ' . implode(', ', $results['remaining']));
437 }
438
439 return $results;
440 }

References $bearsamppRoot, Log\error(), Path\getRootPath(), Log\info(), Symlinks\safeRemoveSymlink(), and Log\warning().

Here is the call graph for this function:

Field Documentation

◆ $splash

$splash
private

Definition at line 21 of file class.action.quit.php.

Referenced by terminatePhpProcesses().

◆ GAUGE_OTHERS

const GAUGE_OTHERS = 1

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

◆ GAUGE_PROCESSES

const GAUGE_PROCESSES = 1

Gauge values for progress bar increments.

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


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