1414
1515class ProcessManager {
1616 private const APP_CONFIG_KEY = 'process_registry ' ;
17+ private const APP_CONFIG_HINTS_KEY = 'process_registry_hints ' ;
18+
19+ private ProcessSignaler $ signaler ;
20+ private ListeningPidResolver $ pidResolver ;
1721
1822 public function __construct (
1923 private IAppConfig $ appConfig ,
2024 private LoggerInterface $ logger ,
25+ ?ProcessSignaler $ signaler = null ,
26+ ?ListeningPidResolver $ pidResolver = null ,
2127 ) {
28+ $ this ->signaler = $ signaler ?? new ProcessSignaler ($ logger );
29+ $ this ->pidResolver = $ pidResolver ?? new ListeningPidResolver ();
2230 }
2331
2432 /**
@@ -36,6 +44,7 @@ public function register(string $source, int $pid, array $context = []): void {
3644 'createdAt ' => time (),
3745 ];
3846 $ this ->saveRegistry ($ registry );
47+ $ this ->setSourceHint ($ source , $ context );
3948 }
4049
4150 public function unregister (string $ source , int $ pid ): void {
@@ -83,6 +92,16 @@ public function countRunning(string $source): int {
8392 * @param null|callable(array{pid: int, context: array<string, scalar>, createdAt: int}): bool $filter
8493 */
8594 public function findRunningPid (string $ source , ?callable $ filter = null ): int {
95+ $ entries = $ this ->listRunning ($ source );
96+ foreach ($ entries as $ entry ) {
97+ if ($ filter !== null && !$ filter ($ entry )) {
98+ continue ;
99+ }
100+ return $ entry ['pid ' ];
101+ }
102+
103+ $ this ->hydrateFromFallback ($ source );
104+
86105 foreach ($ this ->listRunning ($ source ) as $ entry ) {
87106 if ($ filter !== null && !$ filter ($ entry )) {
88107 continue ;
@@ -96,7 +115,7 @@ public function findRunningPid(string $source, ?callable $filter = null): int {
96115 public function stopAll (string $ source , int $ signal = SIGTERM ): int {
97116 $ stopped = 0 ;
98117 foreach ($ this ->listRunning ($ source ) as $ entry ) {
99- if ($ this ->terminate ($ entry ['pid ' ], $ signal )) {
118+ if ($ this ->stopPid ($ entry ['pid ' ], $ signal )) {
100119 $ stopped ++;
101120 }
102121 $ this ->unregister ($ source , $ entry ['pid ' ]);
@@ -105,55 +124,89 @@ public function stopAll(string $source, int $signal = SIGTERM): int {
105124 return $ stopped ;
106125 }
107126
108- public function isRunning (int $ pid ): bool {
109- if ($ pid <= 0 ) {
110- return false ;
127+ public function stopPid (int $ pid , int $ signal = SIGTERM ): bool {
128+ return $ this ->signaler ->stopPid ($ pid , $ signal );
129+ }
130+
131+ /**
132+ * @param array<string, scalar> $context
133+ */
134+ public function setSourceHint (string $ source , array $ context ): void {
135+ if ($ source === '' || $ context === []) {
136+ return ;
111137 }
112138
113- if (function_exists ('posix_kill ' )) {
114- return @posix_kill ($ pid , 0 );
139+ $ hints = $ this ->getHints ();
140+ $ hints [$ source ] = $ context ;
141+ $ this ->saveHints ($ hints );
142+ }
143+
144+ /**
145+ * @return int[]
146+ */
147+ public function findListeningPids (int $ port ): array {
148+ return $ this ->pidResolver ->findListeningPids ($ port );
149+ }
150+
151+ public function isRunning (int $ pid ): bool {
152+ return $ this ->signaler ->isRunning ($ pid );
153+ }
154+
155+ private function hydrateFromFallback (string $ source ): void {
156+ $ hint = $ this ->getSourceHint ($ source );
157+ if (!is_array ($ hint ) || $ hint === []) {
158+ return ;
115159 }
116160
117- if (!function_exists ('exec ' )) {
118- return false ;
161+ $ port = $ this ->getPortFromHint ($ hint );
162+ if ($ port <= 0 ) {
163+ return ;
119164 }
120165
121166 try {
122- exec ( sprintf ( ' kill -0 %d 2>/dev/null ' , $ pid ), $ output , $ exitCode );
123- return $ exitCode === 0 ;
124- } catch ( \ Throwable $ e ) {
125- $ this -> logger -> debug ( ' Failed to probe process status ' , [
126- 'pid ' => $ pid ,
167+ $ pids = $ this -> findListeningPids ( $ port );
168+ } catch ( \ RuntimeException $ e ) {
169+ $ this -> logger -> debug ( ' Unable to hydrate process registry from fallback ' , [
170+ ' source ' => $ source ,
171+ 'port ' => $ port ,
127172 'error ' => $ e ->getMessage (),
128173 ]);
129- return false ;
174+ return ;
130175 }
131- }
132176
133- protected function terminate (int $ pid , int $ signal ): bool {
134- if ($ pid <= 0 ) {
135- return false ;
177+ foreach ($ pids as $ pid ) {
178+ if ($ pid <= 0 ) {
179+ continue ;
180+ }
181+ $ this ->register ($ source , $ pid , $ hint );
136182 }
183+ }
137184
138- if (function_exists ('posix_kill ' )) {
139- return @posix_kill ($ pid , $ signal );
185+ /**
186+ * @param array<string, scalar> $hint
187+ */
188+ private function getPortFromHint (array $ hint ): int {
189+ if (isset ($ hint ['port ' ]) && is_numeric ((string )$ hint ['port ' ])) {
190+ $ port = (int )$ hint ['port ' ];
191+ if ($ port > 0 ) {
192+ return $ port ;
193+ }
140194 }
141195
142- if (! function_exists ( ' exec ' )) {
143- return false ;
196+ if (isset ( $ hint [ ' uri ' ]) && is_string ( $ hint [ ' uri ' ] )) {
197+ return ( int )( parse_url ( $ hint [ ' uri ' ], PHP_URL_PORT ) ?? 0 ) ;
144198 }
145199
146- try {
147- exec (sprintf ('kill -%d %d 2>/dev/null ' , $ signal , $ pid ), $ output , $ exitCode );
148- return $ exitCode === 0 ;
149- } catch (\Throwable $ e ) {
150- $ this ->logger ->debug ('Failed to terminate process ' , [
151- 'pid ' => $ pid ,
152- 'signal ' => $ signal ,
153- 'error ' => $ e ->getMessage (),
154- ]);
155- return false ;
156- }
200+ return 0 ;
201+ }
202+
203+ /**
204+ * @return array<string, scalar>
205+ */
206+ private function getSourceHint (string $ source ): array {
207+ $ hints = $ this ->getHints ();
208+ $ hint = $ hints [$ source ] ?? [];
209+ return is_array ($ hint ) ? $ hint : [];
157210 }
158211
159212 /**
@@ -175,4 +228,25 @@ private function getRegistry(): array {
175228 private function saveRegistry (array $ registry ): void {
176229 $ this ->appConfig ->setValueString (Application::APP_ID , self ::APP_CONFIG_KEY , json_encode ($ registry ));
177230 }
231+
232+ /**
233+ * @return array<string, array<string, scalar>>
234+ */
235+ private function getHints (): array {
236+ $ raw = $ this ->appConfig ->getValueString (Application::APP_ID , self ::APP_CONFIG_HINTS_KEY , '{} ' );
237+ $ decoded = json_decode ($ raw , true );
238+ if (!is_array ($ decoded )) {
239+ return [];
240+ }
241+
242+ return $ decoded ;
243+ }
244+
245+ /**
246+ * @param array<string, array<string, scalar>> $hints
247+ */
248+ private function saveHints (array $ hints ): void {
249+ $ this ->appConfig ->setValueString (Application::APP_ID , self ::APP_CONFIG_HINTS_KEY , json_encode ($ hints ));
250+ }
251+
178252}
0 commit comments