Bearsampp 2026.9.23
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:19

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

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

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

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

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

99 {
100 global $bearsamppBins;
101
102 $name = '';
103
104 if ($sName == BinApache::SERVICE_NAME) {
105 $name = $bearsamppBins->getApache()->getName() . ' ' . $bearsamppBins->getApache()->getVersion();
106 } elseif ($sName == BinMysql::SERVICE_NAME) {
107 $name = $bearsamppBins->getMysql()->getName() . ' ' . $bearsamppBins->getMysql()->getVersion();
108 } elseif ($sName == BinMailpit::SERVICE_NAME) {
109 $name = $bearsamppBins->getMailpit()->getName() . ' ' . $bearsamppBins->getMailpit()->getVersion();
110 } elseif ($sName == BinMariadb::SERVICE_NAME) {
111 $name = $bearsamppBins->getMariadb()->getName() . ' ' . $bearsamppBins->getMariadb()->getVersion();
112 } elseif ($sName == BinPostgresql::SERVICE_NAME) {
113 $name = $bearsamppBins->getPostgresql()->getName() . ' ' . $bearsamppBins->getPostgresql()->getVersion();
114 } elseif ($sName == BinMemcached::SERVICE_NAME) {
115 $name = $bearsamppBins->getMemcached()->getName() . ' ' . $bearsamppBins->getMemcached()->getVersion();
116 } elseif ($sName == BinXlight::SERVICE_NAME) {
117 $name = $bearsamppBins->getXlight()->getName() . ' ' . $bearsamppBins->getXlight()->getVersion();
118 }
119
120 $name .= ' (' . $service->getName() . ')';
121
122 return $name;
123 }
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 681 of file class.action.quit.php.

682 {
683 Log::info('Performing quick cleanup verification...');
684
685 $startTime = microtime(true);
686 $maxTime = 2; // Maximum 2 seconds for verification
687
688 try {
689 // Quick temp file cleanup (non-blocking)
690 $tempCleanup = $this->cleanupTemporaryFiles();
691
692 // Check if we're running out of time
693 if (microtime(true) - $startTime > $maxTime) {
694 Log::debug('Cleanup verification timeout reached, skipping remaining checks');
695
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 } catch (\Exception $e) {
715 Log::warning('Quick cleanup verification failed: ' . $e->getMessage());
716 }
717 }

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

136 {
137 global $bearsamppBins, $bearsamppLang, $bearsamppWinbinder;
138
139 Log::info('Starting graceful shutdown process with optimized service order');
140
141 // Get all available services
142 $allServices = $bearsamppBins->getServices();
143
144 // Get optimal shutdown order
145 $shutdownOrder = $this->getServiceShutdownOrder();
146
147 Log::debug('Service shutdown order: ' . implode(' -> ', $shutdownOrder));
148
149 // Stop services in optimal order
150 foreach ($shutdownOrder as $sName) {
151 // Check if this service exists and is installed
152 if (!isset($allServices[$sName])) {
153 Log::debug('Service not found in available services: ' . $sName);
154 continue;
155 }
156
157 $service = $allServices[$sName];
158 $displayName = $this->getServiceDisplayName($sName, $service);
159
160 Log::info('Stopping service: ' . $displayName);
161
162 $this->splash->incrProgressBar();
163 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::EXIT_REMOVE_SERVICE_TEXT), $displayName));
164
165 // Delete (stop and remove) the service
166 $result = $service->delete();
167
168 if ($result) {
169 Log::info('Successfully stopped and removed service: ' . $displayName);
170 } else {
171 Log::warning('Failed to stop/remove service: ' . $displayName . ' (may not be installed)');
172 }
173 }
174
175 // Handle any services not in the shutdown order (for extensibility)
176 foreach ($allServices as $sName => $service) {
177 if (!in_array($sName, $shutdownOrder)) {
178 $displayName = $this->getServiceDisplayName($sName, $service);
179 Log::warning('Stopping unlisted service: ' . $displayName);
180
181 $this->splash->incrProgressBar();
182 $this->splash->setTextLoading(sprintf($bearsamppLang->getValue(Lang::EXIT_REMOVE_SERVICE_TEXT), $displayName));
183 $service->delete();
184 }
185 }
186
187 Log::info('All services stopped successfully');
188
189 // Purge "current" symlinks
190 $this->splash->setTextLoading('Removing symlinks...');
192
193 // Stop other processes
194 $this->splash->incrProgressBar();
195 $this->splash->setTextLoading($bearsamppLang->getValue(Lang::EXIT_STOP_OTHER_PROCESS_TEXT));
196 Win32Ps::killBins(true);
197
198 // Explicitly kill NodeJS if still running (it's not a Windows service)
199 Log::trace('Explicitly terminating NodeJS processes');
200 Win32Ps::killBins(['node.exe']);
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 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 234 of file class.action.quit.php.

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

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

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

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

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: