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

orchestral / testbench-core / 13403563703

19 Feb 2025 01:31AM UTC coverage: 92.259%. Remained the same
13403563703

push

github

crynobone
wip

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

7 of 8 new or added lines in 1 file covered. (87.5%)

2 existing lines in 1 file now uncovered.

1466 of 1589 relevant lines covered (92.26%)

74.02 hits per line

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

92.55
/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
    $binary = \defined('TESTBENCH_DUSK') ? 'testbench-dusk' : 'testbench';
11✔
80

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

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

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

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

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

101
    return $process;
11✔
102
}
103

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

118
    if ($app->resolved($name)) {
190✔
119
        value($callback, $app->make($name), $app);
1✔
120
    }
121
}
122

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

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

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

179
            return "{$key}={$value}";
4✔
180
        })->values()->all();
4✔
181
}
182

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

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

209
    if (! \is_null($workingPath)) {
13✔
210
        return str_replace(rtrim($workingPath, $separator).$separator, $prefix.$separator, $path);
1✔
211
    }
212

213
    $laravelPath = base_path();
12✔
214
    $workbenchPath = workbench_path();
12✔
215
    $packagePath = package_path();
12✔
216

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

226
/**
227
 * Get the default skeleton path.
228
 *
229
 * @api
230
 *
231
 * @no-named-arguments
232
 *
233
 * @param  array<int, string|null>|string  ...$path
234
 * @return string
235
 */
236
function default_skeleton_path(array|string $path = ''): string
237
{
238
    return (string) realpath(Sidekick\join_paths(__DIR__, '..', 'laravel', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)));
187✔
239
}
240

241
/**
242
 * Get the migration path by type.
243
 *
244
 * @api
245
 *
246
 * @param  string|null  $type
247
 * @return string
248
 *
249
 * @throws \InvalidArgumentException
250
 */
251
function default_migration_path(?string $type = null): string
252
{
253
    $path = realpath(
57✔
254
        \is_null($type) ? base_path('migrations') : base_path(Sidekick\join_paths('migrations', $type))
57✔
255
    );
57✔
256

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

261
    return $path;
57✔
262
}
263

264
/**
265
 * Get the path to the package folder.
266
 *
267
 * @api
268
 *
269
 * @no-named-arguments
270
 *
271
 * @param  array<int, string|null>|string  ...$path
272
 * @return string
273
 */
274
function package_path(array|string $path = ''): string
275
{
276
    $argumentCount = \func_num_args();
87✔
277

278
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
87✔
279
        ? TESTBENCH_WORKING_PATH
49✔
280
        : Env::get('TESTBENCH_WORKING_PATH', getcwd());
38✔
281

282
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
87✔
283
        return Sidekick\transform_relative_path($path, $workingPath);
7✔
284
    }
285

286
    $path = Sidekick\join_paths(...Arr::wrap($argumentCount > 1 ? \func_get_args() : $path));
87✔
287

288
    return str_starts_with($path, './')
87✔
NEW
289
        ? Sidekick\transform_relative_path($path, $workingPath)
×
290
        : Sidekick\join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path);
87✔
291
}
292

293
/**
294
 * Get the workbench configuration.
295
 *
296
 * @api
297
 *
298
 * @return array<string, mixed>
299
 */
300
function workbench(): array
301
{
302
    /** @var \Orchestra\Testbench\Contracts\Config $config */
303
    $config = app()->bound(Contracts\Config::class)
47✔
304
        ? app()->make(Contracts\Config::class)
45✔
305
        : new Foundation\Config;
2✔
306

307
    return $config->getWorkbenchAttributes();
47✔
308
}
309

310
/**
311
 * Get the path to the workbench folder.
312
 *
313
 * @api
314
 *
315
 * @no-named-arguments
316
 *
317
 * @param  array<int, string|null>|string  ...$path
318
 * @return string
319
 */
320
function workbench_path(array|string $path = ''): string
321
{
322
    return package_path('workbench', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path));
66✔
323
}
324

325
/**
326
 * Get the migration path by type.
327
 *
328
 * @api
329
 *
330
 * @param  string|null  $type
331
 * @return string
332
 *
333
 * @throws \InvalidArgumentException
334
 *
335
 * @deprecated
336
 *
337
 * @codeCoverageIgnore
338
 */
339
#[\Deprecated(message: 'Use `Orchestra\Testbench\default_migration_path()` instead', since: '9.5.1')]
340
function laravel_migration_path(?string $type = null): string
341
{
342
    return default_migration_path($type);
343
}
344

345
/**
346
 * Determine if vendor symlink exists on the laravel application.
347
 *
348
 * @api
349
 *
350
 * @param  \Illuminate\Contracts\Foundation\Application  $app
351
 * @param  string|null  $workingPath
352
 * @return bool
353
 */
354
function laravel_vendor_exists(ApplicationContract $app, ?string $workingPath = null): bool
355
{
356
    $filesystem = new Filesystem;
4✔
357

358
    $appVendorPath = $app->basePath('vendor');
4✔
359
    $workingPath ??= package_path('vendor');
4✔
360

361
    return $filesystem->isFile(Sidekick\join_paths($appVendorPath, 'autoload.php')) &&
4✔
362
        $filesystem->hash(Sidekick\join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(Sidekick\join_paths($workingPath, 'autoload.php'));
4✔
363
}
364

365
/**
366
 * Laravel version compare.
367
 *
368
 * @api
369
 *
370
 * @template TOperator of string|null
371
 *
372
 * @param  string  $version
373
 * @param  string|null  $operator
374
 * @return int|bool
375
 *
376
 * @phpstan-param  TOperator  $operator
377
 *
378
 * @phpstan-return (TOperator is null ? int : bool)
379
 *
380
 * @deprecated 10.0.0 Use `Orchestra\Sidekick\laravel_version_compare()` instead.
381
 *
382
 * @codeCoverageIgnore
383
 */
384
function laravel_version_compare(string $version, ?string $operator = null): int|bool
385
{
386
    return Sidekick\laravel_version_compare($version, $operator);
387
}
388

389
/**
390
 * PHPUnit version compare.
391
 *
392
 * @api
393
 *
394
 * @template TOperator of string|null
395
 *
396
 * @param  string  $version
397
 * @param  string|null  $operator
398
 * @return int|bool
399
 *
400
 * @throws \RuntimeException
401
 *
402
 * @phpstan-param  TOperator  $operator
403
 *
404
 * @phpstan-return (TOperator is null ? int : bool)
405
 *
406
 * @deprecated 10.0.0 Use `Orchestra\Sidekick\phpunit_version_compare()` instead.
407
 *
408
 * @codeCoverageIgnore
409
 */
410
function phpunit_version_compare(string $version, ?string $operator = null): int|bool
411
{
412
    return Sidekick\phpunit_version_compare($version, $operator);
413
}
414

415
/**
416
 * Determine the PHP Binary.
417
 *
418
 * @api
419
 *
420
 * @param  bool  $escape
421
 * @return string
422
 */
423
function php_binary(bool $escape = false): string
424
{
425
    $phpBinary = Sidekick\php_binary();
11✔
426

427
    return $escape === true ? ProcessUtils::escapeArgument((string) $phpBinary) : $phpBinary;
11✔
428
}
429

430
/**
431
 * Join the given paths together.
432
 *
433
 * @param  string|null  $basePath
434
 * @param  string  ...$paths
435
 * @return string
436
 *
437
 * @deprecated 10.0.0 Use `Orchestra\Sidekick\join_paths()` instead.
438
 *
439
 * @codeCoverageIgnore
440
 */
441
#[\Deprecated('Use `Orchestra\Sidekick\join_paths()` instead', since: '10.0.0')]
442
function join_paths(?string $basePath, string ...$paths): string
443
{
444
    return Sidekick\join_paths($basePath, ...$paths);
445
}
446

447
/**
448
 * Ensure the provided `$app` return an instance of Laravel application or throw an exception.
449
 *
450
 * @internal
451
 *
452
 * @param  \Illuminate\Foundation\Application|null  $app
453
 * @param  string|null  $caller
454
 * @return \Illuminate\Foundation\Application
455
 *
456
 * @throws \Orchestra\Testbench\Exceptions\ApplicationNotAvailableException
457
 */
458
function laravel_or_fail($app, ?string $caller = null): Application
459
{
460
    if ($app instanceof Application) {
184✔
461
        return $app;
184✔
462
    }
463

464
    if (\is_null($caller)) {
1✔
465
        $caller = transform(debug_backtrace()[1] ?? null, function ($debug) {
1✔
466
            /** @phpstan-ignore isset.offset */
467
            if (isset($debug['class']) && isset($debug['function'])) {
1✔
468
                return \sprintf('%s::%s', $debug['class'], $debug['function']);
1✔
469
            }
470

471
            /** @phpstan-ignore offsetAccess.notFound */
UNCOV
472
            return $debug['function'];
×
473
        });
1✔
474
    }
475

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