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

orchestral / sidekick / 20614903545

31 Dec 2025 08:03AM UTC coverage: 91.549% (-0.4%) from 91.943%
20614903545

Pull #60

github

web-flow
Merge 00df9c48f into 79e87f3f5
Pull Request #60: [1.2.x] Move `is_symlink()` and `join_paths()` to `Orchestra\Sidekick\Filesystem` namespace

9 of 12 new or added lines in 2 files covered. (75.0%)

5 existing lines in 1 file now uncovered.

195 of 213 relevant lines covered (91.55%)

6.48 hits per line

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

90.57
/src/functions.php
1
<?php
2

3
namespace Orchestra\Sidekick;
4

5
use BackedEnum;
6
use Closure;
7
use Composer\InstalledVersions;
8
use Composer\Semver\VersionParser;
9
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
10
use Illuminate\Foundation\Application;
11
use Illuminate\Support\Arr;
12
use OutOfBoundsException;
13
use PHPUnit\Runner\Version;
14
use RuntimeException;
15
use UnitEnum;
16

17
if (! \function_exists('Orchestra\Sidekick\enum_name')) {
18
    /**
19
     * Get the proper name from enum.
20
     *
21
     * @api
22
     *
23
     * @throws \RuntimeException
24
     */
25
    function enum_name(BackedEnum|UnitEnum $enum): string
26
    {
27
        return mb_convert_case(str_replace('_', ' ', $enum->name), MB_CASE_TITLE, 'UTF-8');
5✔
28
    }
29
}
30

31
if (! \function_exists('Orchestra\Sidekick\enum_value')) {
32
    /**
33
     * Get the proper name from enum.
34
     *
35
     * @api
36
     *
37
     * @template TValue
38
     * @template TDefault
39
     *
40
     * @param  TValue  $value
41
     * @param  TDefault|callable(TValue): TDefault  $default
42
     * @return ($value is empty ? TDefault : mixed)
43
     *
44
     * @throws \RuntimeException
45
     */
46
    function enum_value(mixed $value, mixed $default = null): mixed
47
    {
48
        return match (true) {
49
            $value instanceof BackedEnum => $value->value,
20✔
50
            $value instanceof UnitEnum => $value->name,
16✔
51

52
            default => $value ?? value($default),
20✔
53
        };
54
    }
55
}
56

57
if (! \function_exists('Orchestra\Sidekick\after_resolving')) {
58
    /**
59
     * Register after resolving callback.
60
     *
61
     * @api
62
     *
63
     * @template TLaravel of \Illuminate\Contracts\Foundation\Application
64
     *
65
     * @param  TLaravel  $app
66
     * @param  class-string|string  $name
67
     * @param  (\Closure(object, TLaravel):(mixed))|null  $callback
68
     */
69
    function after_resolving(ApplicationContract $app, string $name, ?Closure $callback = null): void
70
    {
71
        $app->afterResolving($name, $callback);
2✔
72

73
        if ($app->resolved($name)) {
2✔
74
            value($callback, $app->make($name), $app);
1✔
75
        }
76
    }
77
}
78

79
if (! \function_exists('Orchestra\Sidekick\join_paths')) {
80

81
    /**
82
     * Join the given paths together.
83
     *
84
     * @api
85
     * @deprecated
86
     */
87
    function join_paths(?string $basePath, string ...$paths): string
88
    {
89
        return Filesystem\join_paths($basePath, ...$paths);
20✔
90
    }
91
}
92

93
if (! \function_exists('Orchestra\Sidekick\once')) {
94
    /**
95
     * Run callback only once.
96
     *
97
     * @api
98
     *
99
     * @param  mixed  $callback
100
     * @return \Closure():mixed
101
     */
102
    function once($callback): Closure
103
    {
104
        $response = new UndefinedValue;
24✔
105

106
        return function () use ($callback, &$response) {
24✔
107
            if ($response instanceof UndefinedValue) {
24✔
108
                $response = value($callback) ?? null;
24✔
109
            }
110

111
            return $response;
24✔
112
        };
24✔
113
    }
114
}
115

116
if (! \function_exists('Orchestra\Sidekick\is_safe_callable')) {
117
    /**
118
     * Determine if the value is a callable and not a string matching an available function name.
119
     *
120
     * @api
121
     */
122
    function is_safe_callable(mixed $value): bool
123
    {
124
        if ($value instanceof Closure) {
6✔
125
            return true;
1✔
126
        }
127

128
        if (! \is_callable($value)) {
5✔
129
            return false;
2✔
130
        }
131

132
        if (\is_array($value)) {
3✔
133
            return \count($value) === 2 && array_is_list($value) && method_exists(...$value);
1✔
134
        }
135

136
        return ! \is_string($value);
2✔
137
    }
138
}
139

140
if (! \function_exists('Orchestra\Sidekick\is_symlink')) {
141
    /**
142
     * Determine if the path is a symlink for both Unix and Windows environments.
143
     *
144
     * @api
145
     * @deprecated
146
     */
147
    function is_symlink(string $path): bool
148
    {
NEW
UNCOV
149
        return Filesystem\is_symlink($path);
×
150
    }
151
}
152

153
if (! \function_exists('Orchestra\Sidekick\is_testbench_cli')) {
154
    /**
155
     * Determine if command executed via Testbench CLI.
156
     *
157
     * @api
158
     */
159
    function is_testbench_cli(?bool $dusk = null): bool
160
    {
161
        $usingTestbench = \defined('TESTBENCH_CORE');
1✔
162
        $usingTestbenchDusk = \defined('TESTBENCH_DUSK');
1✔
163

164
        return match ($dusk) {
1✔
UNCOV
165
            false => $usingTestbench === true && $usingTestbenchDusk === false,
×
166
            true => $usingTestbench === true && $usingTestbenchDusk === true,
1✔
167
            default => $usingTestbench === true,
1✔
168
        };
1✔
169
    }
170
}
171

172
if (! \function_exists('Orchestra\Sidekick\transform_relative_path')) {
173
    /**
174
     * Transform relative path.
175
     *
176
     * @api
177
     */
178
    function transform_relative_path(string $path, string $workingPath): string
179
    {
180
        return str_starts_with($path, './')
1✔
181
            ? rtrim($workingPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.mb_substr($path, 2)
1✔
182
            : $path;
1✔
183
    }
184
}
185

186
if (! \function_exists('Orchestra\Sidekick\working_path')) {
187
    /**
188
     * Get the working path.
189
     *
190
     * @api
191
     *
192
     * @no-named-arguments
193
     *
194
     * @param  array<int, string|null>|string  ...$path
195
     */
196
    function working_path(array|string $path = ''): string
197
    {
198
        return is_testbench_cli() && \function_exists('Orchestra\Testbench\package_path')
1✔
UNCOV
199
            ? \Orchestra\Testbench\package_path($path)
×
200
            : base_path(join_paths(...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)));
1✔
201
    }
202
}
203

204
if (! \function_exists('Orchestra\Sidekick\laravel_normalize_version')) {
205
    /**
206
     * Laravel normalize version.
207
     *
208
     * @api
209
     *
210
     * @throws \OutOfBoundsException
211
     */
212
    function laravel_normalize_version(): string
213
    {
214
        if (! class_exists(Application::class)) {
20✔
UNCOV
215
            throw new OutOfBoundsException('Unable to verify "laravel/framework" version');
×
216
        }
217

218
        /** @var string $version */
219
        $version = transform(
20✔
220
            Application::VERSION,
20✔
221
            fn (string $version) => match ($version) {
20✔
222
                '13.x-dev' => '13.0.0',
20✔
223
                default => $version,
20✔
224
            }
225
        );
20✔
226

227
        return (new VersionParser)->normalize($version);
20✔
228
    }
229
}
230

231
if (! \function_exists('Orchestra\Sidekick\phpunit_normalize_version')) {
232
    /**
233
     * PHPUnit normalize version.
234
     *
235
     * @api
236
     *
237
     * @throws \OutOfBoundsException
238
     */
239
    function phpunit_normalize_version(): string
240
    {
241
        if (! class_exists(Version::class)) {
1✔
UNCOV
242
            throw new OutOfBoundsException('Unable to verify "phpunit/phpunit" version');
×
243
        }
244

245
        /** @var string $version */
246
        $version = transform(
1✔
247
            Version::id(),
1✔
248
            fn (string $version) => match (true) {
1✔
249
                str_starts_with($version, '13.0-') => '13.0.0',
1✔
250
                default => $version,
1✔
251
            }
252
        );
1✔
253

254
        return (new VersionParser)->normalize($version);
1✔
255
    }
256
}
257

258
if (! \function_exists('Orchestra\Sidekick\laravel_version_compare')) {
259
    /**
260
     * Laravel version compare.
261
     *
262
     * @api
263
     *
264
     * @template TOperator of string|null
265
     *
266
     * @param  TOperator  $operator
267
     * @return (TOperator is null ? int : bool)
268
     *
269
     * @throws \RuntimeException
270
     *
271
     * @codeCoverageIgnore
272
     */
273
    function laravel_version_compare(string $version, ?string $operator = null): int|bool
274
    {
275
        if (! class_exists(Application::class)) {
276
            return package_version_compare('laravel/framework', $version, $operator);
277
        }
278

279
        $laravel = laravel_normalize_version();
280
        $version = (new VersionParser)->normalize($version);
281

282
        if (\is_null($operator)) {
283
            return version_compare($laravel, $version);
284
        }
285

286
        return version_compare($laravel, $version, $operator);
287
    }
288
}
289

290
if (! \function_exists('Orchestra\Sidekick\package_version_compare')) {
291
    /**
292
     * Package version compare.
293
     *
294
     * @api
295
     *
296
     * @template TOperator of string|null
297
     *
298
     * @phpstan-param  TOperator  $operator
299
     *
300
     * @phpstan-return (TOperator is null ? int : bool)
301
     *
302
     * @throws \OutOfBoundsException
303
     * @throws \RuntimeException
304
     *
305
     * @codeCoverageIgnore
306
     */
307
    function package_version_compare(string $package, string $version, ?string $operator = null): int|bool
308
    {
309
        $prettyVersion = InstalledVersions::getPrettyVersion($package);
310

311
        if (\is_null($prettyVersion)) {
312
            throw new RuntimeException(\sprintf('Unable to compare "%s" version', $package));
313
        }
314

315
        $versionParser = new VersionParser;
316

317
        $package = $versionParser->normalize($prettyVersion);
318
        $version = $versionParser->normalize($version);
319

320
        if (\is_null($operator)) {
321
            return version_compare($package, $version);
322
        }
323

324
        return version_compare($package, $version, $operator);
325
    }
326
}
327

328
if (! \function_exists('Orchestra\Sidekick\phpunit_version_compare')) {
329
    /**
330
     * PHPUnit version compare.
331
     *
332
     * @api
333
     *
334
     * @template TOperator of string|null
335
     *
336
     * @param  TOperator  $operator
337
     * @return (TOperator is null ? int : bool)
338
     *
339
     * @throws \OutOfBoundsException
340
     * @throws \RuntimeException
341
     *
342
     * @codeCoverageIgnore
343
     */
344
    function phpunit_version_compare(string $version, ?string $operator = null): int|bool
345
    {
346
        if (! class_exists(Version::class)) {
347
            return package_version_compare('phpunit/phpunit', $version, $operator);
348
        }
349

350
        $phpunit = phpunit_normalize_version();
351
        $version = (new VersionParser)->normalize($version);
352

353
        if (\is_null($operator)) {
354
            return version_compare($phpunit, $version);
355
        }
356

357
        return version_compare($phpunit, $version, $operator);
358
    }
359
}
360

361
if (! \function_exists('Orchestra\Sidekick\php_binary')) {
362
    /**
363
     * Determine the PHP Binary.
364
     *
365
     * @api
366
     *
367
     * @codeCoverageIgnore
368
     */
369
    function php_binary(): string
370
    {
371
        return (new PhpExecutableFinder)->find(false) ?: 'php';
372
    }
373
}
374

375
if (! \function_exists('Orchestra\Sidekick\windows_os')) {
376
    /**
377
     * Determine whether the current environment is Windows-based.
378
     *
379
     * @api
380
     *
381
     * @codeCoverageIgnore
382
     */
383
    function windows_os(): bool
384
    {
385
        return PHP_OS_FAMILY === 'Windows';
386
    }
387
}
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