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

orchestral / testbench-core / 13800510149

11 Mar 2025 11:52PM UTC coverage: 92.492% (-0.02%) from 92.512%
13800510149

push

github

crynobone
wip

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

11 of 12 new or added lines in 2 files covered. (91.67%)

5 existing lines in 1 file now uncovered.

1503 of 1625 relevant lines covered (92.49%)

74.31 hits per line

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

94.44
/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

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

40
    return Foundation\Application::make($basePath, $resolvingCallback, $options);
3✔
41
}
42

43
/**
44
 * Run artisan command.
45
 *
46
 * @api
47
 *
48
 * @param  \Orchestra\Testbench\Contracts\TestCase|\Illuminate\Contracts\Foundation\Application  $context
49
 * @param  string  $command
50
 * @param  array<string, mixed>  $parameters
51
 * @return int
52
 */
53
function artisan(Contracts\TestCase|ApplicationContract $context, string $command, array $parameters = []): int
54
{
55
    if ($context instanceof ApplicationContract) {
15✔
56
        return $context->make(ConsoleKernel::class)->call($command, $parameters);
1✔
57
    }
58

59
    $command = $context->artisan($command, $parameters);
15✔
60

61
    return $command instanceof PendingCommand ? $command->run() : $command;
15✔
62
}
63

64
/**
65
 * Run remote action using Testbench CLI.
66
 *
67
 * @api
68
 *
69
 * @param  (\Closure():(mixed))|array<int, string>|string  $command
70
 * @param  array<string, mixed>|string  $env
71
 * @param  bool|null  $tty
72
 * @return \Orchestra\Testbench\Foundation\Process\ProcessDecorator
73
 */
74
function remote(Closure|array|string $command, array|string $env = [], ?bool $tty = null): Foundation\Process\ProcessDecorator
75
{
76
    $remote = new Foundation\Process\RemoteCommand(
12✔
77
        package_path(), $env, $tty
12✔
78
    );
12✔
79

80
    $binary = \defined('TESTBENCH_DUSK') ? 'testbench-dusk' : 'testbench';
12✔
81

82
    $commander = is_file($vendorBinary = package_path('vendor', 'bin', $binary))
12✔
UNCOV
83
        ? $vendorBinary
×
84
        : $binary;
12✔
85

86
    return $remote->handle($commander, $command);
12✔
87
}
88

89
/**
90
 * Run callback only once.
91
 *
92
 * @api
93
 *
94
 * @param  mixed  $callback
95
 * @return \Closure():mixed
96
 *
97
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\once()` instead.
98
 *
99
 * @codeCoverageIgnore
100
 */
101
#[\Deprecated(message: 'Use `Orchestra\Sidekick\once()` instead', since: '7.55.0')]
102
function once($callback): Closure
103
{
104
    return Sidekick\once($callback);
105
}
106

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

121
    if ($app->resolved($name)) {
192✔
122
        value($callback, $app->make($name), $app);
1✔
123
    }
124
}
125

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

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

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

182
            return "{$key}={$value}";
4✔
183
        })->values()->all();
4✔
184
}
185

186
/**
187
 * Refresh router lookups.
188
 *
189
 * @api
190
 *
191
 * @param  \Illuminate\Routing\Router  $router
192
 * @return void
193
 */
194
function refresh_router_lookups(Router $router): void
195
{
196
    $router->getRoutes()->refreshNameLookups();
192✔
197
}
198

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

212
    if (! \is_null($workingPath)) {
13✔
213
        return str_replace(rtrim($workingPath, $separator).$separator, $prefix.$separator, $path);
1✔
214
    }
215

216
    $laravelPath = base_path();
12✔
217
    $workbenchPath = workbench_path();
12✔
218
    $packagePath = package_path();
12✔
219

220
    return match (true) {
221
        str_starts_with($path, $laravelPath) => str_replace($laravelPath.$separator, '@laravel'.$separator, $path),
12✔
222
        str_starts_with($path, $workbenchPath) => str_replace($workbenchPath.$separator, '@workbench'.$separator, $path),
8✔
223
        str_starts_with($path, $packagePath) => str_replace($packagePath.$separator, '.'.$separator, $path),
8✔
224
        ! empty($prefix) => implode($separator, [$prefix, ltrim($path, $separator)]),
8✔
225
        default => $path,
12✔
226
    };
227
}
228

229
/**
230
 * Transform relative path.
231
 *
232
 * @api
233
 *
234
 * @param  string  $path
235
 * @param  string  $workingPath
236
 * @return string
237
 *
238
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\transform_relative_path()` instead.
239
 *
240
 * @codeCoverageIgnore
241
 */
242
#[\Deprecated(message: 'Use `Orchestra\Sidekick\transform_relative_path()` instead', since: '7.55.0')]
243
function transform_relative_path(string $path, string $workingPath): string
244
{
245
    return Sidekick\transform_relative_path($path, $workingPath);
246
}
247

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

263
/**
264
 * Get the migration path by type.
265
 *
266
 * @api
267
 *
268
 * @param  string|null  $type
269
 * @return string
270
 *
271
 * @throws \InvalidArgumentException
272
 */
273
function default_migration_path(?string $type = null): string
274
{
275
    $path = realpath(
57✔
276
        \is_null($type) ? base_path('migrations') : base_path(join_paths('migrations', $type))
57✔
277
    );
57✔
278

279
    if ($path === false) {
57✔
UNCOV
280
        throw new InvalidArgumentException(\sprintf('Unable to resolve migration path for type [%s]', $type ?? 'laravel'));
×
281
    }
282

283
    return $path;
57✔
284
}
285

286
/**
287
 * Get the path to the package folder.
288
 *
289
 * @api
290
 *
291
 * @no-named-arguments
292
 *
293
 * @param  array<int, string|null>|string  ...$path
294
 * @return string
295
 */
296
function package_path(array|string $path = ''): string
297
{
298
    $argumentCount = \func_num_args();
92✔
299

300
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
92✔
301
        ? TESTBENCH_WORKING_PATH
52✔
302
        : Foundation\Env::get('TESTBENCH_WORKING_PATH', getcwd());
40✔
303

304
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
92✔
305
        return Sidekick\transform_relative_path($path, $workingPath);
7✔
306
    }
307

308
    $path = join_paths(...Arr::wrap($argumentCount > 1 ? \func_get_args() : $path));
92✔
309

310
    return str_starts_with($path, './')
92✔
UNCOV
311
        ? Sidekick\transform_relative_path($path, $workingPath)
×
312
        : join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path);
92✔
313
}
314

315
/**
316
 * Get the workbench configuration.
317
 *
318
 * @api
319
 *
320
 * @return array<string, mixed>
321
 */
322
function workbench(): array
323
{
324
    /** @var \Orchestra\Testbench\Contracts\Config $config */
325
    $config = app()->bound(Contracts\Config::class)
47✔
326
        ? app()->make(Contracts\Config::class)
45✔
327
        : new Foundation\Config;
2✔
328

329
    return $config->getWorkbenchAttributes();
47✔
330
}
331

332
/**
333
 * Get the path to the workbench folder.
334
 *
335
 * @api
336
 *
337
 * @no-named-arguments
338
 *
339
 * @param  array<int, string|null>|string  ...$path
340
 * @return string
341
 */
342
function workbench_path(array|string $path = ''): string
343
{
344
    return package_path('workbench', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path));
73✔
345
}
346

347
/**
348
 * Get the migration path by type.
349
 *
350
 * @api
351
 *
352
 * @param  string|null  $type
353
 * @return string
354
 *
355
 * @throws \InvalidArgumentException
356
 *
357
 * @deprecated
358
 *
359
 * @codeCoverageIgnore
360
 */
361
#[\Deprecated(message: 'Use `Orchestra\Testbench\default_migration_path()` instead', since: '9.5.1')]
362
function laravel_migration_path(?string $type = null): string
363
{
364
    return default_migration_path($type);
365
}
366

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

380
    $appVendorPath = $app->basePath('vendor');
11✔
381
    $workingPath ??= package_path('vendor');
11✔
382

383
    return $filesystem->isFile(join_paths($appVendorPath, 'autoload.php')) &&
11✔
384
        $filesystem->hash(join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(join_paths($workingPath, 'autoload.php'));
11✔
385
}
386

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

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

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

441
    return $escape === true ? ProcessUtils::escapeArgument((string) $phpBinary) : $phpBinary;
12✔
442
}
443

444
/**
445
 * Join the given paths together.
446
 *
447
 * @param  string|null  $basePath
448
 * @param  string  ...$paths
449
 * @return string
450
 */
451
function join_paths(?string $basePath, string ...$paths): string
452
{
453
    return Sidekick\join_paths($basePath, ...$paths);
192✔
454
}
455

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

473
    if (\is_null($caller)) {
1✔
474
        $caller = transform(debug_backtrace()[1] ?? null, function ($debug) {
1✔
475
            /** @phpstan-ignore isset.offset */
476
            if (isset($debug['class']) && isset($debug['function'])) {
1✔
477
                return \sprintf('%s::%s', $debug['class'], $debug['function']);
1✔
478
            }
479

480
            /** @phpstan-ignore offsetAccess.notFound */
UNCOV
481
            return $debug['function'];
×
482
        });
1✔
483
    }
484

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