Bearsampp 2025.8.29
Loading...
Searching...
No Matches
class.action.quit.php
Go to the documentation of this file.
1<?php
2/*
3 *
4 * * Copyright (c) 2022-2025 Bearsampp
5 * * License: GNU General Public License version 3 or later; see LICENSE.txt
6 * * Website: https://bearsampp.com
7 * * Github: https://github.com/Bearsampp
8 *
9 */
10
17{
21 private $splash;
22
26 const GAUGE_PROCESSES = 1;
27 const GAUGE_OTHERS = 1;
28
35 public function __construct($args)
36 {
37 global $bearsamppCore, $bearsamppLang, $bearsamppBins, $bearsamppWinbinder, $arrayOfCurrents;
38
39 // Start splash screen
40 $this->splash = new Splash();
41 $this->splash->init(
42 $bearsamppLang->getValue( Lang::QUIT ),
43 self::GAUGE_PROCESSES * count( $bearsamppBins->getServices() ) + self::GAUGE_OTHERS,
44 sprintf( $bearsamppLang->getValue( Lang::EXIT_LEAVING_TEXT ), APP_TITLE . ' ' . $bearsamppCore->getAppVersion() )
45 );
46
47 // Set handler for the splash screen window
48 $bearsamppWinbinder->setHandler( $this->splash->getWbWindow(), $this, 'processWindow', 2000 );
49 $bearsamppWinbinder->mainLoop();
50 $bearsamppWinbinder->reset();
51 }
52
53
64 public function processWindow($window, $id, $ctrl, $param1, $param2)
65 {
66 global $bearsamppBins, $bearsamppLang, $bearsamppWinbinder;
67
68 // Stop all services
69 foreach ( $bearsamppBins->getServices() as $sName => $service ) {
70 $name = $bearsamppBins->getApache()->getName() . ' ' . $bearsamppBins->getApache()->getVersion();
71 if ( $sName == BinMysql::SERVICE_NAME ) {
72 $name = $bearsamppBins->getMysql()->getName() . ' ' . $bearsamppBins->getMysql()->getVersion();
73 }
74 elseif ( $sName == BinMailpit::SERVICE_NAME ) {
75 $name = $bearsamppBins->getMailpit()->getName() . ' ' . $bearsamppBins->getMailpit()->getVersion();
76 }
77 elseif ( $sName == BinMariadb::SERVICE_NAME ) {
78 $name = $bearsamppBins->getMariadb()->getName() . ' ' . $bearsamppBins->getMariadb()->getVersion();
79 }
80 elseif ( $sName == BinPostgresql::SERVICE_NAME ) {
81 $name = $bearsamppBins->getPostgresql()->getName() . ' ' . $bearsamppBins->getPostgresql()->getVersion();
82 }
83 elseif ( $sName == BinMemcached::SERVICE_NAME ) {
84 $name = $bearsamppBins->getMemcached()->getName() . ' ' . $bearsamppBins->getMemcached()->getVersion();
85 }
86 elseif ($sName == BinXlight::SERVICE_NAME) {
87 $name = $bearsamppBins->getXlight()->getName() . ' ' . $bearsamppBins->getXlight()->getVersion();
88 }
89 $name .= ' (' . $service->getName() . ')';
90
91 $this->splash->incrProgressBar();
92 $this->splash->setTextLoading( sprintf( $bearsamppLang->getValue( Lang::EXIT_REMOVE_SERVICE_TEXT ), $name ) );
93 $service->delete();
94 }
95
96 // Purge "current" symlinks
98
99 // Stop other processes
100 $this->splash->incrProgressBar();
101 $this->splash->setTextLoading( $bearsamppLang->getValue( Lang::EXIT_STOP_OTHER_PROCESS_TEXT ) );
102 Win32Ps::killBins( true );
103
104 // Terminate any remaining processes
105 // Final termination sequence
106 $this->splash->setTextLoading('Completing shutdown...');
107 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
108 $currentPid = Win32Ps::getCurrentPid();
109
110 // Terminate PHP processes with a timeout of 15 seconds
111 self::terminatePhpProcesses($currentPid, $window, $this->splash, 15);
112
113 // Force exit if still running
114 exit(0);
115 }
116
117 // Non-Windows fallback
118 $bearsamppWinbinder->destroyWindow($window);
119 exit(0);
120 }
121
131 public static function terminatePhpProcesses($excludePid, $window = null, $splash = null, $timeout = 10)
132 {
133 global $bearsamppWinbinder;
134
135 $currentPid = Win32Ps::getCurrentPid();
136 $startTime = microtime(true);
137
138 Util::logTrace('Starting PHP process termination (excluding PID: ' . $excludePid . ')');
139
140 $targets = ['php-win.exe', 'php.exe'];
141 foreach (Win32Ps::getListProcs() as $proc) {
142 // Check if we've exceeded our timeout
143 if (microtime(true) - $startTime > $timeout) {
144 Util::logTrace('Process termination timeout exceeded, continuing with remaining operations');
145 break;
146 }
147
148 $exe = strtolower(basename($proc[Win32Ps::EXECUTABLE_PATH]));
150
151 if (in_array($exe, $targets) && $pid != $excludePid) {
152 Util::logTrace('Terminating PHP process: ' . $pid);
153 Win32Ps::kill($pid);
154 usleep(100000); // 100ms delay between terminations
155 }
156 }
157
158 // Initiate self-termination with timeout
159 if ($splash !== null) {
160 $splash->setTextLoading('Final cleanup...');
161 }
162
163 try {
164 Util::logTrace('Initiating self-termination for PID: ' . $currentPid);
165 // Add a timeout wrapper around the killProc call
166 $killSuccess = Vbs::killProc($currentPid);
167 if (!$killSuccess) {
168 Util::logTrace('Self-termination via Vbs::killProc failed, using alternative method');
169 }
170 } catch (\Exception $e) {
171 Util::logTrace('Exception during self-termination: ' . $e->getMessage());
172 }
173
174 // Destroy window after process termination
175 // Fix for PHP 8.2: Check if window is not null before destroying
176 if ($window && $bearsamppWinbinder) {
177 try {
178 Util::logTrace('Destroying window');
179 $bearsamppWinbinder->destroyWindow($window);
180 } catch (\Exception $e) {
181 Util::logTrace('Exception during window destruction: ' . $e->getMessage());
182 }
183 }
184
185 // Force exit if still running after timeout
186 if (microtime(true) - $startTime > $timeout * 1.5) {
187 Util::logTrace('Forcing exit due to timeout');
188 exit(0);
189 }
190 }
191}
global $bearsamppBins
global $bearsamppLang
global $bearsamppCore
$proc
Definition ajax.php:43
static terminatePhpProcesses($excludePid, $window=null, $splash=null, $timeout=10)
processWindow($window, $id, $ctrl, $param1, $param2)
const SERVICE_NAME
const QUIT
const EXIT_LEAVING_TEXT
const EXIT_REMOVE_SERVICE_TEXT
const EXIT_STOP_OTHER_PROCESS_TEXT
static logTrace($data, $file=null)
static killProc($pid)
static getCurrentPid()
static getListProcs()
static killBins($refreshProcs=false)
static kill($pid)
const EXECUTABLE_PATH
const PROCESS_ID
const APP_TITLE
Definition root.php:13