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

orchestral / testbench-core / 13380715685

18 Feb 2025 12:25AM UTC coverage: 92.288% (-0.2%) from 92.439%
13380715685

push

github

crynobone
wip

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

1472 of 1595 relevant lines covered (92.29%)

74.17 hits per line

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

92.86
/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\Config;
18
use Orchestra\Testbench\Foundation\Env;
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
 * @param  \Orchestra\Testbench\Foundation\Config|null  $config
30
 * @return \Orchestra\Testbench\Foundation\Application
31
 */
32
function container(
33
    ?string $basePath = null,
34
    ?callable $resolvingCallback = null,
35
    array $options = [],
36
    ?Config $config = null
37
): Foundation\Application {
38
    if ($config instanceof Config) {
3✔
39
        return Foundation\Application::makeFromConfig($config, $resolvingCallback, $options);
×
40
    }
41

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

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

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

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

66
/**
67
 * Run remote action using Testbench CLI.
68
 *
69
 * @api
70
 *
71
 * @param  array<int, string>|string  $command
72
 * @param  array<string, mixed>|string  $env
73
 * @param  bool|null  $tty
74
 * @return \Symfony\Component\Process\Process
75
 */
76
function remote(array|string $command, array|string $env = [], ?bool $tty = null): Process
77
{
78
    $binary = \defined('TESTBENCH_DUSK') ? 'testbench-dusk' : 'testbench';
11✔
79

80
    $commander = is_file($vendorBin = package_path('vendor', 'bin', $binary))
11✔
81
        ? ProcessUtils::escapeArgument((string) $vendorBin)
×
82
        : $binary;
11✔
83

84
    if (\is_string($env)) {
11✔
85
        $env = ['APP_ENV' => $env];
×
86
    }
87

88
    Arr::add($env, 'TESTBENCH_PACKAGE_REMOTE', '(true)');
11✔
89

90
    $process = Process::fromShellCommandline(
11✔
91
        command: Arr::join([php_binary(true), $commander, ...Arr::wrap($command)], ' '),
11✔
92
        cwd: package_path(),
11✔
93
        env: array_merge(defined_environment_variables(), $env)
11✔
94
    );
11✔
95

96
    if (\is_bool($tty)) {
11✔
97
        $process->setTty($tty);
×
98
    }
99

100
    return $process;
11✔
101
}
102

103
/**
104
 * Run callback only once.
105
 *
106
 * @api
107
 *
108
 * @param  mixed  $callback
109
 * @return \Closure():mixed
110
 *
111
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\once()` instead.
112
 *
113
 * @codeCoverageIgnore
114
 */
115
#[\Deprecated(message: 'Use `Orchestra\Sidekick\once()` instead', since: '7.55.0')]
116
function once($callback): Closure
117
{
118
    return \Orchestra\Sidekick\once($callback);
119
}
120

121
/**
122
 * Register after resolving callback.
123
 *
124
 * @api
125
 *
126
 * @param  \Illuminate\Contracts\Foundation\Application  $app
127
 * @param  string  $name
128
 * @param  (\Closure(object, \Illuminate\Contracts\Foundation\Application):(mixed))|null  $callback
129
 * @return void
130
 */
131
function after_resolving(ApplicationContract $app, string $name, ?Closure $callback = null): void
132
{
133
    $app->afterResolving($name, $callback);
190✔
134

135
    if ($app->resolved($name)) {
190✔
136
        value($callback, $app->make($name), $app);
1✔
137
    }
138
}
139

140
/**
141
 * Load migration paths.
142
 *
143
 * @api
144
 *
145
 * @param  \Illuminate\Contracts\Foundation\Application  $app
146
 * @param  array<int, string>|string  $paths
147
 * @return void
148
 */
149
function load_migration_paths(ApplicationContract $app, array|string $paths): void
150
{
151
    after_resolving($app, 'migrator', static function ($migrator) use ($paths) {
50✔
152
        foreach (Arr::wrap($paths) as $path) {
15✔
153
            /** @var \Illuminate\Database\Migrations\Migrator $migrator */
154
            $migrator->path($path);
15✔
155
        }
156
    });
50✔
157
}
158

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

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

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

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

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

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

230
    $laravelPath = base_path();
12✔
231
    $workbenchPath = workbench_path();
12✔
232
    $packagePath = package_path();
12✔
233

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

243
/**
244
 * Transform relative path.
245
 *
246
 * @api
247
 *
248
 * @param  string  $path
249
 * @param  string  $workingPath
250
 * @return string
251
 *
252
 * @deprecated 7.55.0 Use `Orchestra\Sidekick\transform_relative_path()` instead.
253
 *
254
 * @codeCoverageIgnore
255
 */
256
#[\Deprecated(message: 'Use `Orchestra\Sidekick\transform_relative_path()` instead', since: '7.55.0')]
257
function transform_relative_path(string $path, string $workingPath): string
258
{
259
    return \Orchestra\Sidekick\transform_relative_path($path, $workingPath);
260
}
261

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

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

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

297
    return $path;
57✔
298
}
299

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

314
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
87✔
315
        ? TESTBENCH_WORKING_PATH
49✔
316
        : Env::get('TESTBENCH_WORKING_PATH', getcwd());
38✔
317

318
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
87✔
319
        return transform_relative_path($path, $workingPath);
7✔
320
    }
321

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

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

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

343
    return $config->getWorkbenchAttributes();
47✔
344
}
345

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

361
/**
362
 * Get the migration path by type.
363
 *
364
 * @api
365
 *
366
 * @param  string|null  $type
367
 * @return string
368
 *
369
 * @throws \InvalidArgumentException
370
 *
371
 * @deprecated
372
 */
373
#[\Deprecated(message: 'Use `Orchestra\Testbench\default_migration_path()` instead', since: '9.5.1')]
374
function laravel_migration_path(?string $type = null): string
375
{
376
    return default_migration_path($type);
1✔
377
}
378

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

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

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

399
/**
400
 * Laravel version compare.
401
 *
402
 * @api
403
 *
404
 * @template TOperator of string|null
405
 *
406
 * @param  string  $version
407
 * @param  string|null  $operator
408
 * @return int|bool
409
 *
410
 * @phpstan-param  TOperator  $operator
411
 *
412
 * @phpstan-return (TOperator is null ? int : bool)
413
 */
414
function laravel_version_compare(string $version, ?string $operator = null): int|bool
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
function phpunit_version_compare(string $version, ?string $operator = null): int|bool
437
{
438
    return \Orchestra\Sidekick\phpunit_version_compare($version, $operator);
2✔
439
}
440

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

453
    return $escape === true ? ProcessUtils::escapeArgument((string) $phpBinary) : $phpBinary;
11✔
454
}
455

456
/**
457
 * Join the given paths together.
458
 *
459
 * @param  string|null  $basePath
460
 * @param  string  ...$paths
461
 * @return string
462
 */
463
function join_paths(?string $basePath, string ...$paths): string
464
{
465
    return \Orchestra\Sidekick\join_paths($basePath, ...$paths);
190✔
466
}
467

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

485
    if (\is_null($caller)) {
1✔
486
        $caller = transform(debug_backtrace()[1] ?? null, function ($debug) {
1✔
487
            /** @phpstan-ignore isset.offset */
488
            if (isset($debug['class']) && isset($debug['function'])) {
1✔
489
                return \sprintf('%s::%s', $debug['class'], $debug['function']);
1✔
490
            }
491

492
            /** @phpstan-ignore offsetAccess.notFound */
493
            return $debug['function'];
×
494
        });
1✔
495
    }
496

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