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

orchestral / testbench-core / 13766982851

10 Mar 2025 02:21PM UTC coverage: 92.494% (+0.08%) from 92.413%
13766982851

push

github

crynobone
wip

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

1491 of 1612 relevant lines covered (92.49%)

74.42 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
use Orchestra\Testbench\Foundation\Config;
19
use Orchestra\Testbench\Foundation\Env;
20
use Symfony\Component\Process\Process;
21

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

43
    return Foundation\Application::make($basePath, $resolvingCallback, $options);
3✔
44
}
45

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

62
    $command = $context->artisan($command, $parameters);
15✔
63

64
    return $command instanceof PendingCommand ? $command->run() : $command;
15✔
65
}
66

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

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

85
    $commander = is_file($vendorBinary = package_path('vendor', 'bin', $binary))
11✔
86
        ? $vendorBinary
×
87
        : $binary;
11✔
88

89
    return $remote->handle($commander, $command);
11✔
90
}
91

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

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

124
    if ($app->resolved($name)) {
191✔
125
        value($callback, $app->make($name), $app);
1✔
126
    }
127
}
128

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

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

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

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

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

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

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

219
    $laravelPath = base_path();
12✔
220
    $workbenchPath = workbench_path();
12✔
221
    $packagePath = package_path();
12✔
222

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

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

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

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

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

286
    return $path;
57✔
287
}
288

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

303
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
91✔
304
        ? TESTBENCH_WORKING_PATH
52✔
305
        : Env::get('TESTBENCH_WORKING_PATH', getcwd());
39✔
306

307
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
91✔
308
        return Sidekick\transform_relative_path($path, $workingPath);
7✔
309
    }
310

311
    $path = join_paths(...Arr::wrap($argumentCount > 1 ? \func_get_args() : $path));
91✔
312

313
    return str_starts_with($path, './')
91✔
314
        ? Sidekick\transform_relative_path($path, $workingPath)
×
315
        : join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path);
91✔
316
}
317

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

332
    return $config->getWorkbenchAttributes();
47✔
333
}
334

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

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

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

386
    return $filesystem->isFile(join_paths($appVendorPath, 'autoload.php')) &&
11✔
387
        $filesystem->hash(join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(join_paths($workingPath, 'autoload.php'));
11✔
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): int|bool
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): int|bool
428
{
429
    return Sidekick\phpunit_version_compare($version, $operator);
2✔
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
function join_paths(?string $basePath, string ...$paths): string
455
{
456
    return Sidekick\join_paths($basePath, ...$paths);
191✔
457
}
458

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

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

483
            /** @phpstan-ignore offsetAccess.notFound */
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