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

orchestral / testbench-dusk / 13499830633

24 Feb 2025 02:02PM UTC coverage: 90.455%. Remained the same
13499830633

push

github

crynobone
wip

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

199 of 220 relevant lines covered (90.45%)

8.2 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\Process;
7

8
use function Orchestra\Sidekick\join_paths;
9
use function Orchestra\Sidekick\php_binary;
10
use function Orchestra\Testbench\defined_environment_variables;
11

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

258
    /**
259
     * Get the current process.
260
     *
261
     * @return \Symfony\Component\Process\Process|null
262
     */
263
    public function getProcess(): ?Process
264
    {
265
        return $this->process;
2✔
266
    }
267
}
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