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

orchestral / testbench-core / 14655165703

25 Apr 2025 01:43AM UTC coverage: 92.602% (+0.005%) from 92.597%
14655165703

push

github

crynobone
wip

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

1427 of 1541 relevant lines covered (92.6%)

64.83 hits per line

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

94.57
/src/functions.php
1
<?php
2

3
namespace Orchestra\Testbench;
4

5
use Closure;
6
use Illuminate\Contracts\Console\Kernel as ConsoleKernel;
7
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
8
use Illuminate\Filesystem\Filesystem;
9
use Illuminate\Foundation\Application;
10
use Illuminate\Routing\Router;
11
use Illuminate\Support\Arr;
12
use Illuminate\Support\Collection;
13
use Illuminate\Support\ProcessUtils;
14
use Illuminate\Support\Str;
15
use Illuminate\Testing\PendingCommand;
16
use InvalidArgumentException;
17
use Orchestra\Sidekick;
18
use PHPUnit\Runner\Version;
19

20
/**
21
 * Create Laravel application instance.
22
 *
23
 * @api
24
 *
25
 * @param  string|null  $basePath
26
 * @param  (callable(\Illuminate\Foundation\Application):(void))|null  $resolvingCallback
27
 * @param  array{extra?: array{providers?: array, dont-discover?: array, env?: array}, load_environment_variables?: bool, enabled_package_discoveries?: bool}  $options
28
 * @return \Orchestra\Testbench\Foundation\Application
29
 */
30
function container(?string $basePath = null, ?callable $resolvingCallback = null, array $options = []): Foundation\Application
31
{
32
    return Foundation\Application::make($basePath, $resolvingCallback, $options);
3✔
33
}
34

35
/**
36
 * Run artisan command.
37
 *
38
 * @api
39
 *
40
 * @param  \Orchestra\Testbench\Contracts\TestCase|\Illuminate\Contracts\Foundation\Application  $context
41
 * @param  string  $command
42
 * @param  array<string, mixed>  $parameters
43
 * @return int
44
 */
45
function artisan(Contracts\TestCase|ApplicationContract $context, string $command, array $parameters = []): int
46
{
47
    if ($context instanceof ApplicationContract) {
10✔
48
        return $context->make(ConsoleKernel::class)->call($command, $parameters);
1✔
49
    }
50

51
    $command = $context->artisan($command, $parameters);
10✔
52

53
    return $command instanceof PendingCommand ? $command->run() : $command;
10✔
54
}
55

56
/**
57
 * Run remote action using Testbench CLI.
58
 *
59
 * @api
60
 *
61
 * @param  array<int, string>|string  $command
62
 * @param  array<string, mixed>|string  $env
63
 * @param  bool|null  $tty
64
 * @return \Orchestra\Testbench\Foundation\Process\ProcessDecorator
65
 */
66
function remote(array|string $command, array|string $env = [], ?bool $tty = null): Foundation\Process\ProcessDecorator
67
{
68
    $remote = new Foundation\Process\RemoteCommand(
11✔
69
        package_path(), $env, $tty
11✔
70
    );
11✔
71

72
    $binary = \defined('TESTBENCH_DUSK') ? 'testbench-dusk' : 'testbench';
11✔
73

74
    $commander = is_file($vendorBinary = package_path('vendor', 'bin', $binary))
11✔
75
        ? $vendorBinary
×
76
        : $binary;
11✔
77

78
    return $remote->handle($commander, $command);
11✔
79
}
80

81
/**
82
 * Run callback only once.
83
 *
84
 * @api
85
 *
86
 * @param  mixed  $callback
87
 * @return \Closure():mixed
88
 *
89
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\once()` instead.
90
 *
91
 * @codeCoverageIgnore
92
 */
93
function once($callback): Closure
94
{
95
    return Sidekick\once($callback);
96
}
97

98
/**
99
 * Register after resolving callback.
100
 *
101
 * @api
102
 *
103
 * @param  \Illuminate\Contracts\Foundation\Application  $app
104
 * @param  string  $name
105
 * @param  (\Closure(object, \Illuminate\Contracts\Foundation\Application):(mixed))|null  $callback
106
 * @return void
107
 */
108
function after_resolving(ApplicationContract $app, string $name, ?Closure $callback = null): void
109
{
110
    $app->afterResolving($name, $callback);
171✔
111

112
    if ($app->resolved($name)) {
171✔
113
        value($callback, $app->make($name), $app);
5✔
114
    }
115
}
116

117
/**
118
 * Load migration paths.
119
 *
120
 * @api
121
 *
122
 * @param  \Illuminate\Contracts\Foundation\Application  $app
123
 * @param  array<int, string>|string  $paths
124
 * @return void
125
 */
126
function load_migration_paths(ApplicationContract $app, array|string $paths): void
127
{
128
    after_resolving($app, 'migrator', static function ($migrator) use ($paths) {
38✔
129
        foreach (Arr::wrap($paths) as $path) {
15✔
130
            /** @var \Illuminate\Database\Migrations\Migrator $migrator */
131
            $migrator->path($path);
15✔
132
        }
133
    });
38✔
134
}
135

136
/**
137
 * Get default environment variables.
138
 *
139
 * @return array<int, string>
140
 *
141
 * @deprecated
142
 *
143
 * @codeCoverageIgnore
144
 */
145
function default_environment_variables(): array
146
{
147
    return [];
148
}
149

150
/**
151
 * Get defined environment variables.
152
 *
153
 * @api
154
 *
155
 * @return array<string, mixed>
156
 */
157
function defined_environment_variables(): array
158
{
159
    return Collection::make(array_merge($_SERVER, $_ENV))
11✔
160
        ->keys()
11✔
161
        ->mapWithKeys(static fn (string $key) => [$key => Foundation\Env::forward($key)])
11✔
162
        ->unless(
11✔
163
            Foundation\Env::has('TESTBENCH_WORKING_PATH'), static fn ($env) => $env->put('TESTBENCH_WORKING_PATH', package_path())
11✔
164
        )->all();
11✔
165
}
166

167
/**
168
 * Get default environment variables.
169
 *
170
 * @api
171
 *
172
 * @param  iterable<string, mixed>  $variables
173
 * @return array<int, string>
174
 */
175
function parse_environment_variables($variables): array
176
{
177
    return Collection::make($variables)
4✔
178
        ->transform(static function ($value, $key) {
4✔
179
            if (\is_bool($value) || \in_array($value, ['true', 'false'])) {
4✔
180
                $value = \in_array($value, [true, 'true']) ? '(true)' : '(false)';
4✔
181
            } elseif (\is_null($value) || \in_array($value, ['null'])) {
1✔
182
                $value = '(null)';
1✔
183
            } else {
184
                $value = $key === 'APP_DEBUG' ? \sprintf('(%s)', Str::of($value)->ltrim('(')->rtrim(')')) : "'{$value}'";
1✔
185
            }
186

187
            return "{$key}={$value}";
4✔
188
        })->values()->all();
4✔
189
}
190

191
/**
192
 * Refresh router lookups.
193
 *
194
 * @api
195
 *
196
 * @param  \Illuminate\Routing\Router  $router
197
 * @return void
198
 */
199
function refresh_router_lookups(Router $router): void
200
{
201
    $router->getRoutes()->refreshNameLookups();
171✔
202
}
203

204
/**
205
 * Transform realpath to alias path.
206
 *
207
 * @api
208
 *
209
 * @param  string  $path
210
 * @param  string|null  $workingPath
211
 * @return string
212
 */
213
function transform_realpath_to_relative(string $path, ?string $workingPath = null, string $prefix = ''): string
214
{
215
    $separator = DIRECTORY_SEPARATOR;
13✔
216

217
    if (! \is_null($workingPath)) {
13✔
218
        return str_replace(rtrim($workingPath, $separator).$separator, $prefix.$separator, $path);
1✔
219
    }
220

221
    $laravelPath = base_path();
12✔
222
    $workbenchPath = workbench_path();
12✔
223
    $packagePath = package_path();
12✔
224

225
    return match (true) {
12✔
226
        str_starts_with($path, $laravelPath) => str_replace($laravelPath.$separator, '@laravel'.$separator, $path),
12✔
227
        str_starts_with($path, $workbenchPath) => str_replace($workbenchPath.$separator, '@workbench'.$separator, $path),
12✔
228
        str_starts_with($path, $packagePath) => str_replace($packagePath.$separator, '.'.$separator, $path),
12✔
229
        ! empty($prefix) => implode($separator, [$prefix, ltrim($path, $separator)]),
12✔
230
        default => $path,
12✔
231
    };
12✔
232
}
233

234
/**
235
 * Transform relative path.
236
 *
237
 * @api
238
 *
239
 * @param  string  $path
240
 * @param  string  $workingPath
241
 * @return string
242
 *
243
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\transform_relative_path()` instead.
244
 *
245
 * @codeCoverageIgnore
246
 */
247
function transform_relative_path(string $path, string $workingPath): string
248
{
249
    return Sidekick\transform_relative_path($path, $workingPath);
250
}
251

252
/**
253
 * Get the default skeleton path.
254
 *
255
 * @api
256
 *
257
 * @no-named-arguments
258
 *
259
 * @param  array<int, string|null>|string  ...$path
260
 * @return string
261
 */
262
function default_skeleton_path(array|string $path = ''): string
263
{
264
    return (string) realpath(
168✔
265
        Sidekick\join_paths(__DIR__, '..', 'laravel', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path))
168✔
266
    );
168✔
267
}
268

269
/**
270
 * Get the migration path by type.
271
 *
272
 * @api
273
 *
274
 * @param  string|null  $type
275
 * @return string
276
 *
277
 * @throws \InvalidArgumentException
278
 */
279
function default_migration_path(?string $type = null): string
280
{
281
    $path = realpath(
44✔
282
        \is_null($type) ? base_path('migrations') : base_path(Sidekick\join_paths('migrations', $type))
44✔
283
    );
44✔
284

285
    if ($path === false) {
44✔
286
        throw new InvalidArgumentException(\sprintf('Unable to resolve migration path for type [%s]', $type ?? 'laravel'));
×
287
    }
288

289
    return $path;
44✔
290
}
291

292
/**
293
 * Get the path to the package folder.
294
 *
295
 * @api
296
 *
297
 * @no-named-arguments
298
 *
299
 * @param  array<int, string|null>|string  ...$path
300
 * @return string
301
 */
302
function package_path(array|string $path = ''): string
303
{
304
    $argumentCount = \func_num_args();
68✔
305

306
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
68✔
307
        ? TESTBENCH_WORKING_PATH
34✔
308
        : Foundation\Env::get('TESTBENCH_WORKING_PATH', getcwd());
35✔
309

310
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
68✔
311
        return transform_relative_path($path, $workingPath);
×
312
    }
313

314
    $path = Sidekick\join_paths(...Arr::wrap($argumentCount > 1 ? \func_get_args() : $path));
68✔
315

316
    return str_starts_with($path, './')
68✔
317
        ? transform_relative_path($path, $workingPath)
×
318
        : Sidekick\join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path);
68✔
319
}
320

321
/**
322
 * Get the workbench configuration.
323
 *
324
 * @api
325
 *
326
 * @return array<string, mixed>
327
 */
328
function workbench(): array
329
{
330
    /** @var \Orchestra\Testbench\Contracts\Config $config */
331
    $config = app()->bound(Contracts\Config::class)
35✔
332
        ? app()->make(Contracts\Config::class)
33✔
333
        : new Foundation\Config;
2✔
334

335
    return $config->getWorkbenchAttributes();
35✔
336
}
337

338
/**
339
 * Get the path to the workbench folder.
340
 *
341
 * @api
342
 *
343
 * @no-named-arguments
344
 *
345
 * @param  array<int, string|null>|string  ...$path
346
 * @return string
347
 */
348
function workbench_path(array|string $path = ''): string
349
{
350
    return package_path('workbench', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path));
58✔
351
}
352

353
/**
354
 * Get the migration path by type.
355
 *
356
 * @api
357
 *
358
 * @param  string|null  $type
359
 * @return string
360
 *
361
 * @throws \InvalidArgumentException
362
 *
363
 * @deprecated
364
 */
365
function laravel_migration_path(?string $type = null): string
366
{
367
    return default_migration_path($type);
1✔
368
}
369

370
/**
371
 * Determine if vendor symlink exists on the laravel application.
372
 *
373
 * @api
374
 *
375
 * @param  \Illuminate\Contracts\Foundation\Application  $app
376
 * @param  string|null  $workingPath
377
 * @return bool
378
 */
379
function laravel_vendor_exists(ApplicationContract $app, ?string $workingPath = null): bool
380
{
381
    $filesystem = new Filesystem;
8✔
382

383
    $appVendorPath = $app->basePath('vendor');
8✔
384
    $workingPath ??= package_path('vendor');
8✔
385

386
    return $filesystem->isFile(join_paths($appVendorPath, 'autoload.php')) &&
8✔
387
        $filesystem->hash(join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(join_paths($workingPath, 'autoload.php'));
8✔
388
}
389

390
/**
391
 * Laravel version compare.
392
 *
393
 * @api
394
 *
395
 * @template TOperator of string|null
396
 *
397
 * @param  string  $version
398
 * @param  string|null  $operator
399
 * @return int|bool
400
 *
401
 * @phpstan-param  TOperator  $operator
402
 *
403
 * @phpstan-return (TOperator is null ? int : bool)
404
 */
405
function laravel_version_compare(string $version, ?string $operator = null)
406
{
407
    return Sidekick\laravel_version_compare($version, $operator);
1✔
408
}
409

410
/**
411
 * PHPUnit version compare.
412
 *
413
 * @api
414
 *
415
 * @template TOperator of string|null
416
 *
417
 * @param  string  $version
418
 * @param  string|null  $operator
419
 * @return int|bool
420
 *
421
 * @throws \RuntimeException
422
 *
423
 * @phpstan-param  TOperator  $operator
424
 *
425
 * @phpstan-return (TOperator is null ? int : bool)
426
 */
427
function phpunit_version_compare(string $version, ?string $operator = null)
428
{
429
    return Sidekick\phpunit_version_compare($version, $operator);
1✔
430
}
431

432
/**
433
 * Determine the PHP Binary.
434
 *
435
 * @api
436
 *
437
 * @param  bool  $escape
438
 * @return string
439
 */
440
function php_binary(bool $escape = false): string
441
{
442
    $phpBinary = Sidekick\php_binary();
11✔
443

444
    return $escape === true ? ProcessUtils::escapeArgument((string) $phpBinary) : $phpBinary;
11✔
445
}
446

447
/**
448
 * Join the given paths together.
449
 *
450
 * @param  string|null  $basePath
451
 * @param  string  ...$paths
452
 * @return string
453
 *
454
 * @codeCoverageIgnore
455
 */
456
function join_paths(?string $basePath, string ...$paths): string
457
{
458
    return Sidekick\join_paths($basePath, ...$paths);
459
}
460

461
/**
462
 * Ensure the provided `$app` return an instance of Laravel application or throw an exception.
463
 *
464
 * @internal
465
 *
466
 * @param  \Illuminate\Foundation\Application|null  $app
467
 * @param  string|null  $caller
468
 * @return \Illuminate\Foundation\Application
469
 *
470
 * @throws \Orchestra\Testbench\Exceptions\ApplicationNotAvailableException
471
 */
472
function laravel_or_fail($app, ?string $caller = null): Application
473
{
474
    if ($app instanceof Application) {
165✔
475
        return $app;
165✔
476
    }
477

478
    if (\is_null($caller)) {
1✔
479
        $caller = transform(debug_backtrace()[1] ?? null, function ($debug) {
1✔
480
            if (isset($debug['class']) && isset($debug['function'])) {
1✔
481
                return \sprintf('%s::%s', $debug['class'], $debug['function']);
1✔
482
            }
483

484
            return $debug['function'];
×
485
        });
1✔
486
    }
487

488
    throw Exceptions\ApplicationNotAvailableException::make($caller);
1✔
489
}
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