• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orchestral / testbench-dusk / 13355640673

16 Feb 2025 01:50PM UTC coverage: 90.455% (+0.4%) from 90.045%
13355640673

push

github

crynobone
Merge branch '10.x' into develop

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

199 of 220 relevant lines covered (90.45%)

8.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.47
/src/DuskServer.php
1
<?php
2

3
namespace Orchestra\Testbench\Dusk;
4

5
use Orchestra\Testbench\Dusk\Exceptions\UnableToStartServer;
6
use Symfony\Component\Process\PhpExecutableFinder;
7
use Symfony\Component\Process\Process;
8

9
use function Illuminate\Support\php_binary;
10
use function Orchestra\Testbench\defined_environment_variables;
11
use function Orchestra\Testbench\join_paths;
12

13
/**
14
 * @internal
15
 */
16
class DuskServer
17
{
18
    /**
19
     * Process pointer reference.
20
     *
21
     * @var \Symfony\Component\Process\Process|null
22
     */
23
    protected ?Process $process = null;
24

25
    /**
26
     * Laravel working path.
27
     *
28
     * @var string|null
29
     */
30
    protected ?string $basePath = null;
31

32
    /**
33
     * Laravel working URL.
34
     *
35
     * @var string|null
36
     */
37
    protected ?string $baseUrl = null;
38

39
    /**
40
     * List of local IPv6 hosts.
41
     *
42
     * @var array<int, string>
43
     */
44
    protected array $localIpv6Hosts = ['::0', '[::0]'];
45

46
    /**
47
     * Construct a new server.
48
     *
49
     * @param  string  $host
50
     * @param  int  $port
51
     * @param  int  $timeout
52
     */
53
    public function __construct(
54
        protected readonly string $host = '127.0.0.1',
55
        protected readonly int $port = 8001,
56
        protected readonly int $timeout = 6000
57
    ) {}
7✔
58

59
    /**
60
     * Set Laravel working path.
61
     *
62
     * @param  string|null  $basePath
63
     * @param  string|null  $baseUrl
64
     * @return void
65
     */
66
    public function setLaravel(?string $basePath = null, ?string $baseUrl = null): void
67
    {
68
        $this->basePath = $basePath;
4✔
69
        $this->baseUrl = $baseUrl;
4✔
70
    }
71

72
    /**
73
     * Store some temp contents in a file for later use.
74
     *
75
     * @param  mixed  $content
76
     * @return void
77
     */
78
    public function stash($content): void
79
    {
80
        file_put_contents($this->temp(), json_encode($content));
6✔
81
    }
82

83
    /**
84
     * Prepare the path of the temp file for a particular server.
85
     *
86
     * @return string
87
     */
88
    protected function temp(): string
89
    {
90
        return join_paths(
6✔
91
            \dirname(__DIR__),
6✔
92
            'tmp',
6✔
93
            \sprintf('%s__%d', ! \in_array($this->host, $this->localIpv6Hosts) ? $this->host : 'localhost', $this->port),
6✔
94
        );
6✔
95
    }
96

97
    /**
98
     * Retrieve the contents of the relevant file.
99
     *
100
     * @param  string|null  $key
101
     * @return mixed
102
     */
103
    public function getStash(?string $key = null)
104
    {
105
        $content = json_decode((string) file_get_contents($this->temp()), true);
×
106

107
        return $key ? ($content[$key] ?? null) : $content;
×
108
    }
109

110
    /**
111
     * Start a php server in a separate process.
112
     *
113
     * @return void
114
     *
115
     * @throws \Orchestra\Testbench\Dusk\Exceptions\UnableToStartServer
116
     */
117
    public function start(): void
118
    {
119
        $this->stop();
5✔
120
        $this->startServer();
5✔
121

122
        // We register the below, so if php is exited early, the child
123
        // process for the server is closed down, rather than left
124
        // hanging around for the user to close themselves.
125
        register_shutdown_function(function () {
5✔
126
            $this->stop();
×
127
        });
5✔
128
    }
129

130
    /**
131
     * Stop the php server.
132
     *
133
     * @return void
134
     */
135
    public function stop(): void
136
    {
137
        if (! isset($this->process)) {
5✔
138
            return;
5✔
139
        }
140

141
        $this->process->stop();
4✔
142
    }
143

144
    /**
145
     * Stop the php server.
146
     *
147
     * @return void
148
     */
149
    public function restart(): void
150
    {
151
        $this->stop();
×
152
        $this->clearOutput();
×
153
        $this->startServer();
×
154
    }
155

156
    /**
157
     * Clear the php server output.
158
     *
159
     * @return void
160
     */
161
    public function clearOutput(): void
162
    {
163
        if (isset($this->process)) {
23✔
164
            $this->process->clearOutput();
23✔
165
            $this->process->clearErrorOutput();
23✔
166
        }
167
    }
168

169
    /**
170
     * Start the server. Execute the command and open a
171
     * pointer to it. Tuck away the output as it's
172
     * not relevant for us during our testing.
173
     *
174
     * @return void
175
     *
176
     * @throws \Orchestra\Testbench\Dusk\Exceptions\UnableToStartServer
177
     */
178
    protected function startServer(): void
179
    {
180
        $this->guardServerStarting();
5✔
181

182
        $this->process = new Process(
5✔
183
            command: $this->prepareCommand(),
5✔
184
            cwd: join_paths($this->basePath(), 'public'),
5✔
185
            env: array_merge(defined_environment_variables(), [
5✔
186
                'APP_BASE_PATH' => $this->basePath(),
5✔
187
                'APP_URL' => $this->baseUrl(),
5✔
188
                'APP_ENV' => 'testing',
5✔
189
            ]),
5✔
190
            timeout: $this->timeout
5✔
191
        );
5✔
192

193
        $this->process->start();
5✔
194
    }
195

196
    /**
197
     * Verify that there isn't an existing server on the host and port
198
     * that we want to use.  Sometimes a server can be left oped when
199
     * PHP drops out, or the user may have another service running.
200
     *
201
     * @return void
202
     *
203
     * @throws \Orchestra\Testbench\Dusk\Exceptions\UnableToStartServer
204
     */
205
    protected function guardServerStarting(): void
206
    {
207
        /** @var resource|null $socket */
208
        $socket = rescue(function () {
5✔
209
            $errorNumber = 0;
5✔
210
            $errorString = '';
5✔
211
            $timeout = 1;
5✔
212

213
            return @fsockopen($this->host, $this->port, $errorNumber, $errorString, $timeout);
5✔
214
        }, null, false);
5✔
215

216
        if ($socket) {
5✔
217
            fclose($socket);
1✔
218
            throw new UnableToStartServer(\sprintf('%s:%d', $this->host, $this->port));
1✔
219
        }
220
    }
221

222
    /**
223
     * Prepare the command for starting the PHP server.
224
     *
225
     * @return array
226
     */
227
    protected function prepareCommand(): array
228
    {
229
        return [
5✔
230
            \function_exists(php_binary::class) ? php_binary() : (string) (new PhpExecutableFinder)->find(false), // @phpstan-ignore class.notFound
5✔
231
            '-S',
5✔
232
            \sprintf('%s:%s', $this->host, $this->port),
5✔
233
            join_paths(__DIR__, 'server.php'),
5✔
234
            '-t',
5✔
235
            join_paths($this->basePath(), 'public'),
5✔
236
        ];
5✔
237
    }
238

239
    /**
240
     * Figure out the path to the laravel application path for Testbench purposes.
241
     *
242
     * @return string
243
     */
244
    public function basePath(): string
245
    {
246
        return $this->basePath ?? default_skeleton_path();
7✔
247
    }
248

249
    /**
250
     * Figure out the path to the laravel application URL for testbench purposes.
251
     *
252
     * @return string
253
     */
254
    public function baseUrl(): string
255
    {
256
        return $this->baseUrl ?? \sprintf('http://%s:%d', $this->host, $this->port);
5✔
257
    }
258

259
    /**
260
     * Get the current process.
261
     *
262
     * @return \Symfony\Component\Process\Process|null
263
     */
264
    public function getProcess(): ?Process
265
    {
266
        return $this->process;
2✔
267
    }
268
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc