9
9
*/
10
10
namespace PHPUnit \Util \PHP ;
11
11
12
+ use function array_key_exists ;
13
+ use function array_values ;
14
+ use function function_exists ;
15
+ use function hrtime ;
16
+ use function ini_get ;
17
+ use function is_array ;
18
+ use function pack ;
19
+ use function pcntl_fork ;
20
+ use function serialize ;
21
+ use function socket_close ;
22
+ use function socket_create_pair ;
23
+ use function socket_last_error ;
24
+ use function socket_read ;
25
+ use function socket_strerror ;
26
+ use function socket_write ;
27
+ use function str_contains ;
28
+ use function strlen ;
29
+ use function strtoupper ;
30
+ use function substr ;
31
+ use function unpack ;
32
+ use function unserialize ;
33
+ use Exception ;
12
34
use PHPUnit \Event \Facade ;
35
+ use PHPUnit \Event \Telemetry \HRTime ;
13
36
use PHPUnit \Framework \TestCase ;
14
37
use PHPUnit \Runner \CodeCoverage ;
15
38
use PHPUnit \TestRunner \TestResult \PassedTests ;
39
+ use RuntimeException ;
16
40
17
- final class PcntlFork {
41
+ final class PcntlFork
42
+ {
18
43
// IPC inspired from https://github.com/barracudanetworks/forkdaemon-php
19
44
private const SOCKET_HEADER_SIZE = 4 ;
20
45
21
- static public function isPcntlForkAvailable (): bool {
46
+ public static function isPcntlForkAvailable (): bool
47
+ {
22
48
$ disabledFunctions = ini_get ('disable_functions ' );
23
49
24
50
return
25
- function_exists ('pcntl_fork ' )
26
- && !str_contains ($ disabledFunctions , 'pcntl ' )
27
- && function_exists ('socket_create_pair ' )
28
- && !str_contains ($ disabledFunctions , 'socket ' )
29
- ;
51
+ function_exists ('pcntl_fork ' ) &&
52
+ !str_contains ($ disabledFunctions , 'pcntl ' ) &&
53
+ function_exists ('socket_create_pair ' ) &&
54
+ !str_contains ($ disabledFunctions , 'socket ' );
30
55
}
31
56
32
57
public function runTest (TestCase $ test ): void
33
58
{
34
- list ( $ socket_child , $ socket_parent) = $ this ->ipcInit ();
59
+ [ $ socket_child , $ socket_parent] = $ this ->ipcInit ();
35
60
36
61
$ pid = pcntl_fork ();
37
62
38
- if ($ pid === -1 ) {
39
- throw new \Exception ('could not fork ' );
40
- } else if ($ pid ) {
63
+ if ($ pid === -1 ) {
64
+ throw new Exception ('could not fork ' );
65
+ }
66
+
67
+ if ($ pid ) {
41
68
// we are the parent
42
69
43
70
socket_close ($ socket_parent );
@@ -47,6 +74,7 @@ public function runTest(TestCase $test): void
47
74
48
75
$ stderr = '' ;
49
76
$ stdout = '' ;
77
+
50
78
if (is_array ($ result ) && array_key_exists ('error ' , $ result )) {
51
79
$ stderr = $ result ['error ' ];
52
80
} else {
@@ -61,19 +89,21 @@ public function runTest(TestCase $test): void
61
89
62
90
socket_close ($ socket_child );
63
91
64
- $ offset = hrtime ();
92
+ $ offset = hrtime ();
65
93
$ dispatcher = Facade::instance ()->initForIsolation (
66
- \ PHPUnit \ Event \ Telemetry \ HRTime::fromSecondsAndNanoseconds (
94
+ HRTime::fromSecondsAndNanoseconds (
67
95
$ offset [0 ],
68
- $ offset [1 ]
69
- )
96
+ $ offset [1 ],
97
+ ),
70
98
);
71
99
72
100
$ test ->setInIsolation (true );
101
+
73
102
try {
74
103
$ test ->run ();
75
104
} catch (Throwable $ e ) {
76
105
$ this ->socketSend ($ socket_parent , ['error ' => $ e ->getMessage ()]);
106
+
77
107
exit ();
78
108
}
79
109
@@ -84,12 +114,13 @@ public function runTest(TestCase $test): void
84
114
'numAssertions ' => $ test ->numberOfAssertionsPerformed (),
85
115
'output ' => !$ test ->expectsOutput () ? $ test ->output () : '' ,
86
116
'events ' => $ dispatcher ->flush (),
87
- 'passedTests ' => PassedTests::instance ()
88
- ]
117
+ 'passedTests ' => PassedTests::instance (),
118
+ ],
89
119
);
90
120
91
121
// send result into parent
92
122
$ this ->socketSend ($ socket_parent , $ result );
123
+
93
124
exit ();
94
125
}
95
126
}
@@ -100,10 +131,10 @@ private function ipcInit(): array
100
131
$ domain = strtoupper (substr (PHP_OS , 0 , 3 )) == 'WIN ' ? AF_INET : AF_UNIX ;
101
132
102
133
// create a socket pair for IPC
103
- $ sockets = array () ;
104
- if ( socket_create_pair ( $ domain , SOCK_STREAM , 0 , $ sockets ) === false )
105
- {
106
- throw new \ RuntimeException ('socket_create_pair failed: ' . socket_strerror (socket_last_error ()));
134
+ $ sockets = [] ;
135
+
136
+ if ( socket_create_pair ( $ domain , SOCK_STREAM , 0 , $ sockets ) === false ) {
137
+ throw new RuntimeException ('socket_create_pair failed: ' . socket_strerror (socket_last_error ()));
107
138
}
108
139
109
140
return $ sockets ;
@@ -116,32 +147,30 @@ private function socketReceive($socket): mixed
116
147
{
117
148
// initially read to the length of the header size, then
118
149
// expand to read more
119
- $ bytes_total = self ::SOCKET_HEADER_SIZE ;
120
- $ bytes_read = 0 ;
121
- $ have_header = false ;
150
+ $ bytes_total = self ::SOCKET_HEADER_SIZE ;
151
+ $ bytes_read = 0 ;
152
+ $ have_header = false ;
122
153
$ socket_message = '' ;
123
- while ( $ bytes_read < $ bytes_total )
124
- {
154
+
155
+ while ( $ bytes_read < $ bytes_total ) {
125
156
$ read = @socket_read ($ socket , $ bytes_total - $ bytes_read );
126
- if ( $ read === false )
127
- {
128
- throw new \ RuntimeException ('socket_receive error: ' . socket_strerror (socket_last_error ()));
157
+
158
+ if ( $ read === false ) {
159
+ throw new RuntimeException ('socket_receive error: ' . socket_strerror (socket_last_error ()));
129
160
}
130
161
131
162
// blank socket_read means done
132
- if ($ read == '' )
133
- {
163
+ if ($ read == '' ) {
134
164
break ;
135
165
}
136
166
137
167
$ bytes_read += strlen ($ read );
138
168
$ socket_message .= $ read ;
139
169
140
- if (!$ have_header && $ bytes_read >= self ::SOCKET_HEADER_SIZE )
141
- {
142
- $ have_header = true ;
143
- list ($ bytes_total ) = array_values (unpack ('N ' , $ socket_message ));
144
- $ bytes_read = 0 ;
170
+ if (!$ have_header && $ bytes_read >= self ::SOCKET_HEADER_SIZE ) {
171
+ $ have_header = true ;
172
+ [$ bytes_total ] = array_values (unpack ('N ' , $ socket_message ));
173
+ $ bytes_read = 0 ;
145
174
$ socket_message = '' ;
146
175
}
147
176
}
@@ -151,25 +180,25 @@ private function socketReceive($socket): mixed
151
180
152
181
/**
153
182
* @param resource $socket
154
- * @param mixed $message
183
+ * @param mixed $message
155
184
*/
156
185
private function socketSend ($ socket , $ message ): void
157
186
{
158
187
$ serialized_message = @serialize ($ message );
159
- if ( $ serialized_message == false )
160
- {
161
- throw new \ RuntimeException ('socket_send failed to serialize message ' );
188
+
189
+ if ( $ serialized_message == false ) {
190
+ throw new RuntimeException ('socket_send failed to serialize message ' );
162
191
}
163
192
164
- $ header = pack ('N ' , strlen ($ serialized_message ));
165
- $ data = $ header . $ serialized_message ;
193
+ $ header = pack ('N ' , strlen ($ serialized_message ));
194
+ $ data = $ header . $ serialized_message ;
166
195
$ bytes_left = strlen ($ data );
167
- while ( $ bytes_left > 0 )
168
- {
196
+
197
+ while ( $ bytes_left > 0 ) {
169
198
$ bytes_sent = @socket_write ($ socket , $ data );
170
- if ( $ bytes_sent === false )
171
- {
172
- throw new \ RuntimeException ('socket_send failed to write to socket ' );
199
+
200
+ if ( $ bytes_sent === false ) {
201
+ throw new RuntimeException ('socket_send failed to write to socket ' );
173
202
}
174
203
175
204
$ bytes_left -= $ bytes_sent ;
0 commit comments