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

orchestral / testbench-core / 13380259484

17 Feb 2025 11:39PM UTC coverage: 92.955% (-0.09%) from 93.043%
13380259484

Pull #310

github

web-flow
Merge 111a7f60b into 769ecefe8
Pull Request #310: Use `orchestra/sidekick`

3 of 3 new or added lines in 1 file covered. (100.0%)

4 existing lines in 1 file now uncovered.

1359 of 1462 relevant lines covered (92.95%)

63.36 hits per line

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

93.0
/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\Testbench\Foundation\Env;
18
use PHPUnit\Runner\Version;
19
use Symfony\Component\Process\Process;
20

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

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

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

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

57
/**
58
 * Run remote action using Testbench CLI.
59
 *
60
 * @api
61
 *
62
 * @param  array<int, string>|string  $command
63
 * @param  array<string, mixed>|string  $env
64
 * @param  bool|null  $tty
65
 * @return \Symfony\Component\Process\Process
66
 */
67
function remote(array|string $command, array|string $env = [], ?bool $tty = null): Process
68
{
69
    $binary = \defined('TESTBENCH_DUSK') ? 'testbench-dusk' : 'testbench';
10✔
70

71
    $commander = is_file($vendorBin = package_path('vendor', 'bin', $binary))
10✔
72
        ? ProcessUtils::escapeArgument((string) $vendorBin)
×
73
        : $binary;
10✔
74

75
    if (\is_string($env)) {
10✔
76
        $env = ['APP_ENV' => $env];
×
77
    }
78

79
    Arr::add($env, 'TESTBENCH_PACKAGE_REMOTE', '(true)');
10✔
80

81
    $process = Process::fromShellCommandline(
10✔
82
        command: Arr::join([php_binary(true), $commander, ...Arr::wrap($command)], ' '),
10✔
83
        cwd: package_path(),
10✔
84
        env: array_merge(defined_environment_variables(), $env)
10✔
85
    );
10✔
86

87
    if (\is_bool($tty)) {
10✔
88
        $process->setTty($tty);
×
89
    }
90

91
    return $process;
10✔
92
}
93

94
/**
95
 * Run callback only once.
96
 *
97
 * @api
98
 *
99
 * @param  mixed  $callback
100
 * @return \Closure():mixed
101
 *
102
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\once()` instead.
103
 */
104
function once($callback): Closure
105
{
106
    return \Orchestra\Sidekick\once($callback);
1✔
107
}
108

109
/**
110
 * Register after resolving callback.
111
 *
112
 * @api
113
 *
114
 * @param  \Illuminate\Contracts\Foundation\Application  $app
115
 * @param  string  $name
116
 * @param  (\Closure(object, \Illuminate\Contracts\Foundation\Application):(mixed))|null  $callback
117
 * @return void
118
 */
119
function after_resolving(ApplicationContract $app, string $name, ?Closure $callback = null): void
120
{
121
    $app->afterResolving($name, $callback);
170✔
122

123
    if ($app->resolved($name)) {
170✔
124
        value($callback, $app->make($name), $app);
5✔
125
    }
126
}
127

128
/**
129
 * Load migration paths.
130
 *
131
 * @api
132
 *
133
 * @param  \Illuminate\Contracts\Foundation\Application  $app
134
 * @param  array<int, string>|string  $paths
135
 * @return void
136
 */
137
function load_migration_paths(ApplicationContract $app, array|string $paths): void
138
{
139
    after_resolving($app, 'migrator', static function ($migrator) use ($paths) {
37✔
140
        foreach (Arr::wrap($paths) as $path) {
15✔
141
            /** @var \Illuminate\Database\Migrations\Migrator $migrator */
142
            $migrator->path($path);
15✔
143
        }
144
    });
37✔
145
}
146

147
/**
148
 * Get default environment variables.
149
 *
150
 * @return array<int, string>
151
 *
152
 * @deprecated
153
 *
154
 * @codeCoverageIgnore
155
 */
156
function default_environment_variables(): array
157
{
158
    return [];
159
}
160

161
/**
162
 * Get defined environment variables.
163
 *
164
 * @api
165
 *
166
 * @return array<string, mixed>
167
 */
168
function defined_environment_variables(): array
169
{
170
    return Collection::make(array_merge($_SERVER, $_ENV))
10✔
171
        ->keys()
10✔
172
        ->mapWithKeys(static fn (string $key) => [$key => Env::forward($key)])
10✔
173
        ->unless(
10✔
174
            Env::has('TESTBENCH_WORKING_PATH'), static fn ($env) => $env->put('TESTBENCH_WORKING_PATH', package_path())
10✔
175
        )->all();
10✔
176
}
177

178
/**
179
 * Get default environment variables.
180
 *
181
 * @api
182
 *
183
 * @param  iterable<string, mixed>  $variables
184
 * @return array<int, string>
185
 */
186
function parse_environment_variables($variables): array
187
{
188
    return Collection::make($variables)
4✔
189
        ->transform(static function ($value, $key) {
4✔
190
            if (\is_bool($value) || \in_array($value, ['true', 'false'])) {
4✔
191
                $value = \in_array($value, [true, 'true']) ? '(true)' : '(false)';
4✔
192
            } elseif (\is_null($value) || \in_array($value, ['null'])) {
1✔
193
                $value = '(null)';
1✔
194
            } else {
195
                $value = $key === 'APP_DEBUG' ? \sprintf('(%s)', Str::of($value)->ltrim('(')->rtrim(')')) : "'{$value}'";
1✔
196
            }
197

198
            return "{$key}={$value}";
4✔
199
        })->values()->all();
4✔
200
}
201

202
/**
203
 * Refresh router lookups.
204
 *
205
 * @api
206
 *
207
 * @param  \Illuminate\Routing\Router  $router
208
 * @return void
209
 */
210
function refresh_router_lookups(Router $router): void
211
{
212
    $router->getRoutes()->refreshNameLookups();
170✔
213
}
214

215
/**
216
 * Transform realpath to alias path.
217
 *
218
 * @api
219
 *
220
 * @param  string  $path
221
 * @param  string|null  $workingPath
222
 * @return string
223
 */
224
function transform_realpath_to_relative(string $path, ?string $workingPath = null, string $prefix = ''): string
225
{
226
    $separator = DIRECTORY_SEPARATOR;
13✔
227

228
    if (! \is_null($workingPath)) {
13✔
229
        return str_replace(rtrim($workingPath, $separator).$separator, $prefix.$separator, $path);
1✔
230
    }
231

232
    $laravelPath = base_path();
12✔
233
    $workbenchPath = workbench_path();
12✔
234
    $packagePath = package_path();
12✔
235

236
    return match (true) {
12✔
237
        str_starts_with($path, $laravelPath) => str_replace($laravelPath.$separator, '@laravel'.$separator, $path),
12✔
238
        str_starts_with($path, $workbenchPath) => str_replace($workbenchPath.$separator, '@workbench'.$separator, $path),
12✔
239
        str_starts_with($path, $packagePath) => str_replace($packagePath.$separator, '.'.$separator, $path),
12✔
240
        ! empty($prefix) => implode($separator, [$prefix, ltrim($path, $separator)]),
12✔
241
        default => $path,
12✔
242
    };
12✔
243
}
244

245
/**
246
 * Transform relative path.
247
 *
248
 * @api
249
 *
250
 * @param  string  $path
251
 * @param  string  $workingPath
252
 * @return string
253
 *
254
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\transform_relative_path()` instead.
255
 */
256
function transform_relative_path(string $path, string $workingPath): string
257
{
258
    return \Orchestra\Sidekick\transform_relative_path($path, $workingPath);
1✔
259
}
260

261
/**
262
 * Get the default skeleton path.
263
 *
264
 * @api
265
 *
266
 * @no-named-arguments
267
 *
268
 * @param  array<int, string|null>|string  ...$path
269
 * @return string
270
 */
271
function default_skeleton_path(array|string $path = ''): string
272
{
273
    return (string) realpath(join_paths(__DIR__, '..', 'laravel', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)));
167✔
274
}
275

276
/**
277
 * Get the migration path by type.
278
 *
279
 * @api
280
 *
281
 * @param  string|null  $type
282
 * @return string
283
 *
284
 * @throws \InvalidArgumentException
285
 */
286
function default_migration_path(?string $type = null): string
287
{
288
    $path = realpath(
43✔
289
        \is_null($type) ? base_path('migrations') : base_path(join_paths('migrations', $type))
43✔
290
    );
43✔
291

292
    if ($path === false) {
43✔
UNCOV
293
        throw new InvalidArgumentException(\sprintf('Unable to resolve migration path for type [%s]', $type ?? 'laravel'));
×
294
    }
295

296
    return $path;
43✔
297
}
298

299
/**
300
 * Get the path to the package folder.
301
 *
302
 * @api
303
 *
304
 * @no-named-arguments
305
 *
306
 * @param  array<int, string|null>|string  ...$path
307
 * @return string
308
 */
309
function package_path(array|string $path = ''): string
310
{
311
    $argumentCount = \func_num_args();
65✔
312

313
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
65✔
314
        ? TESTBENCH_WORKING_PATH
31✔
315
        : Env::get('TESTBENCH_WORKING_PATH', getcwd());
34✔
316

317
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
65✔
UNCOV
318
        return transform_relative_path($path, $workingPath);
×
319
    }
320

321
    $path = join_paths(...Arr::wrap($argumentCount > 1 ? \func_get_args() : $path));
65✔
322

323
    return str_starts_with($path, './')
65✔
UNCOV
324
        ? transform_relative_path($path, $workingPath)
×
325
        : join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path);
65✔
326
}
327

328
/**
329
 * Get the workbench configuration.
330
 *
331
 * @api
332
 *
333
 * @return array<string, mixed>
334
 */
335
function workbench(): array
336
{
337
    /** @var \Orchestra\Testbench\Contracts\Config $config */
338
    $config = app()->bound(Contracts\Config::class)
34✔
339
        ? app()->make(Contracts\Config::class)
32✔
340
        : new Foundation\Config;
2✔
341

342
    return $config->getWorkbenchAttributes();
34✔
343
}
344

345
/**
346
 * Get the path to the workbench folder.
347
 *
348
 * @api
349
 *
350
 * @no-named-arguments
351
 *
352
 * @param  array<int, string|null>|string  ...$path
353
 * @return string
354
 */
355
function workbench_path(array|string $path = ''): string
356
{
357
    return package_path('workbench', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path));
53✔
358
}
359

360
/**
361
 * Get the migration path by type.
362
 *
363
 * @api
364
 *
365
 * @param  string|null  $type
366
 * @return string
367
 *
368
 * @throws \InvalidArgumentException
369
 *
370
 * @deprecated
371
 */
372
function laravel_migration_path(?string $type = null): string
373
{
374
    return default_migration_path($type);
1✔
375
}
376

377
/**
378
 * Determine if vendor symlink exists on the laravel application.
379
 *
380
 * @api
381
 *
382
 * @param  \Illuminate\Contracts\Foundation\Application  $app
383
 * @param  string|null  $workingPath
384
 * @return bool
385
 */
386
function laravel_vendor_exists(ApplicationContract $app, ?string $workingPath = null): bool
387
{
388
    $filesystem = new Filesystem;
4✔
389

390
    $appVendorPath = $app->basePath('vendor');
4✔
391
    $workingPath ??= package_path('vendor');
4✔
392

393
    return $filesystem->isFile(join_paths($appVendorPath, 'autoload.php')) &&
4✔
394
        $filesystem->hash(join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(join_paths($workingPath, 'autoload.php'));
4✔
395
}
396

397
/**
398
 * Laravel version compare.
399
 *
400
 * @api
401
 *
402
 * @template TOperator of string|null
403
 *
404
 * @param  string  $version
405
 * @param  string|null  $operator
406
 * @return int|bool
407
 *
408
 * @phpstan-param  TOperator  $operator
409
 *
410
 * @phpstan-return (TOperator is null ? int : bool)
411
 *
412
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\laravel_version_compare()` instead.
413
 */
414
function laravel_version_compare(string $version, ?string $operator = null)
415
{
416
    return \Orchestra\Sidekick\laravel_version_compare($version, $operator);
1✔
417
}
418

419
/**
420
 * PHPUnit version compare.
421
 *
422
 * @api
423
 *
424
 * @template TOperator of string|null
425
 *
426
 * @param  string  $version
427
 * @param  string|null  $operator
428
 * @return int|bool
429
 *
430
 * @throws \RuntimeException
431
 *
432
 * @phpstan-param  TOperator  $operator
433
 *
434
 * @phpstan-return (TOperator is null ? int : bool)
435
 *
436
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\phpunit_version_compare()` instead.
437
 */
438
function phpunit_version_compare(string $version, ?string $operator = null)
439
{
440
    return \Orchestra\Sidekick\phpunit_version_compare($version, $operator);
1✔
441
}
442

443
/**
444
 * Determine the PHP Binary.
445
 *
446
 * @api
447
 *
448
 * @param  bool  $escape
449
 * @return string
450
 */
451
function php_binary(bool $escape = false): string
452
{
453
    $phpBinary = \Orchestra\Sidekick\php_binary();
10✔
454

455
    return $escape === true ? ProcessUtils::escapeArgument((string) $phpBinary) : $phpBinary;
10✔
456
}
457

458
/**
459
 * Join the given paths together.
460
 *
461
 * @param  string|null  $basePath
462
 * @param  string  ...$paths
463
 * @return string
464
 *
465
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\join_paths()` instead.
466
 */
467
function join_paths(?string $basePath, string ...$paths): string
468
{
469
    return \Orchestra\Sidekick\join_paths($basePath, ...$paths);
170✔
470
}
471

472
/**
473
 * Ensure the provided `$app` return an instance of Laravel application or throw an exception.
474
 *
475
 * @internal
476
 *
477
 * @param  \Illuminate\Foundation\Application|null  $app
478
 * @param  string|null  $caller
479
 * @return \Illuminate\Foundation\Application
480
 *
481
 * @throws \Orchestra\Testbench\Exceptions\ApplicationNotAvailableException
482
 */
483
function laravel_or_fail($app, ?string $caller = null): Application
484
{
485
    if ($app instanceof Application) {
164✔
486
        return $app;
164✔
487
    }
488

489
    if (\is_null($caller)) {
1✔
490
        $caller = transform(debug_backtrace()[1] ?? null, function ($debug) {
1✔
491
            if (isset($debug['class']) && isset($debug['function'])) {
1✔
492
                return \sprintf('%s::%s', $debug['class'], $debug['function']);
1✔
493
            }
494

UNCOV
495
            return $debug['function'];
×
496
        });
1✔
497
    }
498

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