Bearsampp 2026.5.5
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.

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 Log::info('Checking for orphaned processes...');
531
532 $orphaned = [
533 'found' => false,
534 'processes' => []
535 ];
536
537 try {
538 $procs = Win32Ps::getListProcs();
539 $bearsamppPath = strtolower(UtilPath::formatUnixPath($bearsamppRoot->getRootPath()));
540 $currentPid = Win32Ps::getCurrentPid();
541
542 foreach ($procs as $proc) {
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 Log::debug('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 Log::warning('Found orphaned process: ' . $processName . ' (PID: ' . $pid . ')');
575
576 // Attempt to kill orphaned process
577 try {
578 Win32Ps::kill($pid);
579 Log::info('Terminated orphaned process: ' . $processName . ' (PID: ' . $pid . ')');
580 } catch (\Exception $e) {
581 Log::error('Failed to terminate orphaned process ' . $processName . ': ' . $e->getMessage());
582 }
583 }
584 }
585
586 if (!$orphaned['found']) {
587 Log::info('No orphaned processes found');
588 } else {
589 Log::warning('Found ' . count($orphaned['processes']) . ' orphaned process(es)');
590 }
591
592 } catch (\Exception $e) {
593 Log::error('Error checking for orphaned processes: ' . $e->getMessage());
594 }
595
596 return $orphaned;
597 }
global $bearsamppRoot
$proc
Definition ajax.php:61
static warning($data, $file=null)
static error($data, $file=null)
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, UtilPath\formatUnixPath(), Win32Ps\getCurrentPid(), Win32Ps\getListProcs(), Log\info(), Win32Ps\kill(), Win32Ps\PROCESS_ID, and Log\warning().

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 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 = $bearsamppCore->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_file($file)) {
486 if (@unlink($file)) {
487 $results['cleaned']++;
488 $results['size_freed'] += $size;
489 Log::debug('Removed temp file: ' . $basename);
490 } else {
491 $results['failed'][] = $basename;
492 $results['success'] = false;
493 Log::warning('Failed to remove temp file: ' . $basename);
494 }
495 } elseif (is_dir($file)) {
496 // Don't remove directories, just files
497 Log::debug('Skipping temp directory: ' . $basename);
498 }
499 } catch (\Exception $e) {
500 $results['failed'][] = $basename;
501 $results['success'] = false;
502 Log::error('Error removing temp file ' . $basename . ': ' . $e->getMessage());
503 }
504 }
505
506 $sizeMB = round($results['size_freed'] / 1024 / 1024, 2);
507 Log::info('Cleaned up ' . $results['cleaned'] . ' temporary files (' . $sizeMB . ' MB freed)');
508
509 if (!empty($results['failed'])) {
510 Log::warning('Failed to clean up ' . count($results['failed']) . ' files');
511 }
512
513 } catch (\Exception $e) {
514 Log::error('Error during temp file cleanup: ' . $e->getMessage());
515 $results['success'] = false;
516 }
517
518 return $results;
519 }

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

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 Log::info('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 Log::debug('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 Log::info('Quick cleanup: ' . $tempCleanup['cleaned'] . ' temp files removed (' . $sizeMB . ' MB freed)');
689 }
690
691 if ($orphanedProcesses['found']) {
692 Log::info('Quick cleanup: ' . count($orphanedProcesses['processes']) . ' orphaned process(es) terminated');
693 }
694
695 $duration = round(microtime(true) - $startTime, 2);
696 Log::info('Quick cleanup verification completed in ' . $duration . ' seconds');
697
698 } catch (\Exception $e) {
699 Log::warning('Quick cleanup verification failed: ' . $e->getMessage());
700 }
701 }

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

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 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 // 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, 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(), and Log\warning().

◆ 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 Log::trace('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 Log::trace('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 Log::trace('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 Log::trace('Terminating PHP process: ' . $pid);
266 Win32Ps::kill($pid);
267 usleep(100000); // 100ms delay between terminations
268 } elseif (in_array($pid, $loadingPids)) {
269 Log::trace('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 Log::trace('Initiating self-termination for PID: ' . $currentPid);
280 // Add a timeout wrapper around the killProc call
281 $killSuccess = Win32Native::killProcess($currentPid);
282 if (!$killSuccess) {
283 Log::trace('Self-termination via Win32Native::killProcess failed, using alternative method');
284 }
285 } catch (\Exception $e) {
286 Log::trace('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 Log::trace('Destroying window');
294 $bearsamppWinbinder->destroyWindow($window);
295 } catch (\Exception $e) {
296 Log::trace('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 Log::trace('Forcing exit due to timeout');
303 exit(0);
304 }
305 }
static trace($data, $file=null)
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().

◆ 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 Log::info('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 Log::warning('Service still running after shutdown: ' . $displayName);
340 $results['still_running'][] = $displayName;
341 $results['all_stopped'] = false;
342
343 // Attempt to force stop
344 Log::info('Attempting to force stop: ' . $displayName);
345 $service->stop();
346 usleep(500000); // Wait 500ms
347
348 // Verify again
349 if ($service->isRunning()) {
350 Log::error('Failed to force stop service: ' . $displayName);
351 } else {
352 Log::info('Successfully force stopped service: ' . $displayName);
353 }
354 } elseif ($isInstalled) {
355 Log::debug('Service stopped but still installed: ' . $displayName);
356 } else {
357 Log::debug('Service verified stopped and removed: ' . $displayName);
358 }
359
360 } catch (\Exception $e) {
361 Log::error('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 Log::info('All services verified stopped successfully');
369 } else {
370 Log::warning('Some services could not be verified as stopped');
371 }
372
373 return $results;
374 }

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

◆ 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 Log::info('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 Log::warning('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 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(), Log\info(), and Log\warning().

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: