Bearsampp 2026.3.26
API documentation
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 Util::logInfo('ActionQuit constructor called - starting exit process');
40 Util::logDebug('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 Util::logDebug('Splash screen initialized');
51
52 // Set handler for the splash screen window
53 $bearsamppWinbinder->setHandler( $this->splash->getWbWindow(), $this, 'processWindow', 2000 );
54 Util::logDebug('Window handler set, starting main loop');
55
56 $bearsamppWinbinder->mainLoop();
57 Util::logDebug('Main loop exited');
58
59 $bearsamppWinbinder->reset();
60 Util::logInfo('ActionQuit constructor completed');
61 }
global $bearsamppBins
global $bearsamppLang
global $bearsamppCore
const QUIT
const EXIT_LEAVING_TEXT
static logInfo($data, $file=null)
static logDebug($data, $file=null)
const APP_TITLE
Definition root.php:13

References $bearsamppBins, $bearsamppCore, $bearsamppLang, APP_TITLE, Lang\EXIT_LEAVING_TEXT, Util\logDebug(), Util\logInfo(), and Lang\QUIT.

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 526 of file class.action.quit.php.

527 {
528 global $bearsamppRoot;
529
530 Util::logInfo('Checking for orphaned processes...');
531
532 $orphaned = [
533 'found' => false,
534 'processes' => []
535 ];
536
537 try {
538 $procs = Win32Ps::getListProcs();
539 $bearsamppPath = strtolower(Util::formatUnixPath($bearsamppRoot->getRootPath()));
540 $currentPid = Win32Ps::getCurrentPid();
541
542 foreach ($procs as $proc) {
543 $exePath = strtolower(Util::formatUnixPath($proc[Win32Ps::EXECUTABLE_PATH]));
545
546 // Skip current process
547 if ($pid == $currentPid) {
548 continue;
549 }
550
551 // Check if process is from Bearsampp directory
552 if (strpos($exePath, $bearsamppPath) === 0) {
553 $processName = basename($exePath);
554
555 // Skip www directory processes (user applications)
556 if (strpos($exePath, $bearsamppPath . '/www/') === 0) {
557 continue;
558 }
559
560 // Skip the main Bearsampp executable
561 if (strtolower($processName) === 'bearsampp.exe') {
562 Util::logDebug('Skipping main Bearsampp process: ' . $processName . ' (PID: ' . $pid . ')');
563 continue;
564 }
565
566 // These are orphaned Bearsampp processes
567 $orphaned['found'] = true;
568 $orphaned['processes'][] = [
569 'pid' => $pid,
570 'name' => $processName,
571 'path' => $exePath
572 ];
573
574 Util::logWarning('Found orphaned process: ' . $processName . ' (PID: ' . $pid . ')');
575
576 // Attempt to kill orphaned process
577 try {
578 Win32Ps::kill($pid);
579 Util::logInfo('Terminated orphaned process: ' . $processName . ' (PID: ' . $pid . ')');
580 } catch (\Exception $e) {
581 Util::logError('Failed to terminate orphaned process ' . $processName . ': ' . $e->getMessage());
582 }
583 }
584 }
585
586 if (!$orphaned['found']) {
587 Util::logInfo('No orphaned processes found');
588 } else {
589 Util::logWarning('Found ' . count($orphaned['processes']) . ' orphaned process(es)');
590 }
591
592 } catch (\Exception $e) {
593 Util::logError('Error checking for orphaned processes: ' . $e->getMessage());
594 }
595
596 return $orphaned;
597 }
global $bearsamppRoot
$proc
Definition ajax.php:61
static logError($data, $file=null)
static formatUnixPath($path)
static logWarning($data, $file=null)
static getCurrentPid()
static getListProcs()
static kill($pid)
const EXECUTABLE_PATH
const PROCESS_ID

References $bearsamppRoot, $proc, Win32Ps\EXECUTABLE_PATH, Util\formatUnixPath(), Win32Ps\getCurrentPid(), Win32Ps\getListProcs(), Win32Ps\kill(), Util\logDebug(), Util\logError(), Util\logInfo(), Util\logWarning(), and Win32Ps\PROCESS_ID.

Referenced by performQuickCleanupVerification().

◆ 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 Util::logInfo('Cleaning up temporary files...');
452
453 $results = [
454 'success' => true,
455 'cleaned' => 0,
456 'failed' => [],
457 'size_freed' => 0
458 ];
459
460 $tmpPath = $bearsamppCore->getTmpPath();
461
462 if (!is_dir($tmpPath)) {
463 Util::logDebug('Temp directory does not exist: ' . $tmpPath);
464 return $results;
465 }
466
467 try {
468 $files = glob($tmpPath . '/*');
469
470 if ($files === false) {
471 Util::logWarning('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_file($file)) {
486 if (@unlink($file)) {
487 $results['cleaned']++;
488 $results['size_freed'] += $size;
489 Util::logDebug('Removed temp file: ' . $basename);
490 } else {
491 $results['failed'][] = $basename;
492 $results['success'] = false;
493 Util::logWarning('Failed to remove temp file: ' . $basename);
494 }
495 } elseif (is_dir($file)) {
496 // Don't remove directories, just files
497 Util::logDebug('Skipping temp directory: ' . $basename);
498 }
499 } catch (\Exception $e) {
500 $results['failed'][] = $basename;
501 $results['success'] = false;
502 Util::logError('Error removing temp file ' . $basename . ': ' . $e->getMessage());
503 }
504 }
505
506 $sizeMB = round($results['size_freed'] / 1024 / 1024, 2);
507 Util::logInfo('Cleaned up ' . $results['cleaned'] . ' temporary files (' . $sizeMB . ' MB freed)');
508
509 if (!empty($results['failed'])) {
510 Util::logWarning('Failed to clean up ' . count($results['failed']) . ' files');
511 }
512
513 } catch (\Exception $e) {
514 Util::logError('Error during temp file cleanup: ' . $e->getMessage());
515 $results['success'] = false;
516 }
517
518 return $results;
519 }

References $bearsamppCore, Util\logDebug(), Util\logError(), Util\logInfo(), and Util\logWarning().

Referenced by performQuickCleanupVerification().

◆ 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 608 of file class.action.quit.php.

609 {
610 $report = [
611 'success' => true,
612 'warnings' => [],
613 'errors' => [],
614 'summary' => []
615 ];
616
617 // Service verification
618 if (!$serviceVerification['all_stopped']) {
619 $report['success'] = false;
620
621 if (!empty($serviceVerification['still_running'])) {
622 $report['errors'][] = 'Services still running: ' . implode(', ', $serviceVerification['still_running']);
623 }
624
625 if (!empty($serviceVerification['verification_failed'])) {
626 $report['warnings'][] = 'Could not verify status of: ' . implode(', ', $serviceVerification['verification_failed']);
627 }
628 }
629
630 $report['summary'][] = 'Services checked: ' . count($serviceVerification['services']);
631
632 // Symlink verification
633 if (!$symlinkVerification['success']) {
634 $report['warnings'][] = 'Symlinks not fully removed: ' . implode(', ', $symlinkVerification['remaining']);
635 }
636
637 // Temp file cleanup
638 if ($tempCleanup['cleaned'] > 0) {
639 $sizeMB = round($tempCleanup['size_freed'] / 1024 / 1024, 2);
640 $report['summary'][] = 'Temp files cleaned: ' . $tempCleanup['cleaned'] . ' (' . $sizeMB . ' MB)';
641 }
642
643 if (!empty($tempCleanup['failed'])) {
644 $report['warnings'][] = 'Failed to clean ' . count($tempCleanup['failed']) . ' temp file(s)';
645 }
646
647 // Orphaned processes
648 if ($orphanedProcesses['found']) {
649 $report['warnings'][] = 'Found ' . count($orphanedProcesses['processes']) . ' orphaned process(es)';
650 foreach ($orphanedProcesses['processes'] as $proc) {
651 $report['summary'][] = 'Orphaned: ' . $proc['name'] . ' (PID: ' . $proc['pid'] . ')';
652 }
653 }
654
655 return $report;
656 }

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().

◆ 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().

◆ 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 665 of file class.action.quit.php.

666 {
667 Util::logInfo('Performing quick cleanup verification...');
668
669 $startTime = microtime(true);
670 $maxTime = 2; // Maximum 2 seconds for verification
671
672 try {
673 // Quick temp file cleanup (non-blocking)
674 $tempCleanup = $this->cleanupTemporaryFiles();
675
676 // Check if we're running out of time
677 if (microtime(true) - $startTime > $maxTime) {
678 Util::logDebug('Cleanup verification timeout reached, skipping remaining checks');
679 return;
680 }
681
682 // Quick orphaned process check (non-blocking)
683 $orphanedProcesses = $this->checkForOrphanedProcesses();
684
685 // Log summary
686 if ($tempCleanup['cleaned'] > 0) {
687 $sizeMB = round($tempCleanup['size_freed'] / 1024 / 1024, 2);
688 Util::logInfo('Quick cleanup: ' . $tempCleanup['cleaned'] . ' temp files removed (' . $sizeMB . ' MB freed)');
689 }
690
691 if ($orphanedProcesses['found']) {
692 Util::logInfo('Quick cleanup: ' . count($orphanedProcesses['processes']) . ' orphaned process(es) terminated');
693 }
694
695 $duration = round(microtime(true) - $startTime, 2);
696 Util::logInfo('Quick cleanup verification completed in ' . $duration . ' seconds');
697
698 } catch (\Exception $e) {
699 Util::logWarning('Quick cleanup verification failed: ' . $e->getMessage());
700 }
701 }

References checkForOrphanedProcesses(), cleanupTemporaryFiles(), Util\logDebug(), Util\logInfo(), and Util\logWarning().

Referenced by processWindow().

◆ 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 Util::logInfo('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 Util::logDebug('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 Util::logDebug('Service not found in available services: ' . $sName);
158 continue;
159 }
160
161 $service = $allServices[$sName];
162 $displayName = $this->getServiceDisplayName($sName, $service);
163
164 Util::logInfo('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 Util::logInfo('Successfully stopped and removed service: ' . $displayName);
174 } else {
175 Util::logWarning('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 Util::logWarning('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 Util::logInfo('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 // Perform cleanup verification in background (non-blocking)
203 $this->splash->setTextLoading('Performing cleanup verification...');
204 $this->performQuickCleanupVerification($allServices);
205
206 // Terminate any remaining processes
207 // Final termination sequence
208 $this->splash->setTextLoading('Completing shutdown...');
209 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
210 $currentPid = Win32Ps::getCurrentPid();
211
212 // Terminate PHP processes with a timeout of 15 seconds
213 self::terminatePhpProcesses($currentPid, $window, $this->splash, 15);
214
215 // Force exit if still running
216 exit(0);
217 }
218
219 // Non-Windows fallback
220 $bearsamppWinbinder->destroyWindow($window);
221 exit(0);
222 }
$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 killBins($refreshProcs=false)

References $bearsamppBins, $bearsamppLang, $result, Symlinks\deleteCurrentSymlinks(), exit, Lang\EXIT_REMOVE_SERVICE_TEXT, Lang\EXIT_STOP_OTHER_PROCESS_TEXT, Win32Ps\getCurrentPid(), getServiceDisplayName(), getServiceShutdownOrder(), Win32Ps\killBins(), Util\logDebug(), Util\logInfo(), Util\logWarning(), performQuickCleanupVerification(), and terminatePhpProcesses().

◆ 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 233 of file class.action.quit.php.

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

References $bearsamppCore, $proc, $splash, Win32Ps\EXECUTABLE_PATH, exit, Win32Ps\getCurrentPid(), Win32Ps\getListProcs(), Win32Ps\kill(), Vbs\killProc(), Util\logTrace(), and Win32Ps\PROCESS_ID.

Referenced by processWindow().

◆ 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 313 of file class.action.quit.php.

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

References getServiceDisplayName(), Util\logDebug(), Util\logError(), Util\logInfo(), and Util\logWarning().

◆ verifySymlinksRemoved()

verifySymlinksRemoved ( )
private

Verify that symlinks have been removed.

Returns
array Verification results

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

382 {
383 global $bearsamppRoot;
384
385 Util::logInfo('Verifying symlinks are removed...');
386
387 $results = [
388 'success' => true,
389 'remaining' => []
390 ];
391
392 // Check common symlink locations
393 $symlinkPaths = [
394 $bearsamppRoot->getCurrentPath() . '/apache',
395 $bearsamppRoot->getCurrentPath() . '/php',
396 $bearsamppRoot->getCurrentPath() . '/mysql',
397 $bearsamppRoot->getCurrentPath() . '/mariadb',
398 $bearsamppRoot->getCurrentPath() . '/postgresql',
399 $bearsamppRoot->getCurrentPath() . '/nodejs',
400 $bearsamppRoot->getCurrentPath() . '/memcached',
401 $bearsamppRoot->getCurrentPath() . '/mailpit',
402 $bearsamppRoot->getCurrentPath() . '/xlight'
403 ];
404
405 foreach ($symlinkPaths as $path) {
406 if (file_exists($path) || is_link($path)) {
407 Util::logWarning('Symlink still exists: ' . $path);
408 $results['remaining'][] = basename($path);
409 $results['success'] = false;
410
411 // Attempt to remove it
412 try {
413 if (is_link($path)) {
414 @unlink($path);
415 } elseif (is_dir($path)) {
416 @rmdir($path);
417 }
418
419 // Verify removal
420 if (!file_exists($path)) {
421 Util::logInfo('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 Util::logError('Failed to remove symlink ' . $path . ': ' . $e->getMessage());
429 }
430 }
431 }
432
433 if ($results['success']) {
434 Util::logInfo('All symlinks verified removed');
435 } else {
436 Util::logWarning('Some symlinks could not be removed: ' . implode(', ', $results['remaining']));
437 }
438
439 return $results;
440 }

References $bearsamppRoot, Util\logError(), Util\logInfo(), and Util\logWarning().

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: