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

orchestral / sidekick / 19785293949

29 Nov 2025 02:40PM UTC coverage: 91.584% (-0.3%) from 91.848%
19785293949

Pull #57

github

web-flow
Merge a4e83c222 into a3d28f601
Pull Request #57: Add `Orchestra\Sidekick\package_version_compare()` function

16 of 18 new or added lines in 1 file covered. (88.89%)

185 of 202 relevant lines covered (91.58%)

6.12 hits per line

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

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

3
namespace Orchestra\Sidekick;
4

5
use Closure;
6
use Composer\InstalledVersions;
7
use Composer\Semver\VersionParser;
8
use Illuminate\Foundation\Application;
9
use Illuminate\Support\Arr;
10
use PHPUnit\Runner\Version;
11
use RuntimeException;
12

13
if (! \function_exists('Orchestra\Sidekick\join_paths')) {
14
    /**
15
     * Join the given paths together.
16
     *
17
     * @api
18
     */
19
    function join_paths(?string $basePath, string ...$paths): string
20
    {
21
        foreach ($paths as $index => $path) {
18✔
22
            if (empty($path) && $path !== '0') {
18✔
23
                unset($paths[$index]);
18✔
24
            } else {
25
                $paths[$index] = DIRECTORY_SEPARATOR.ltrim($path, DIRECTORY_SEPARATOR);
18✔
26
            }
27
        }
28

29
        return $basePath.implode('', $paths);
18✔
30
    }
31
}
32

33
if (! \function_exists('Orchestra\Sidekick\once')) {
34
    /**
35
     * Run callback only once.
36
     *
37
     * @api
38
     *
39
     * @param  mixed  $callback
40
     * @return \Closure():mixed
41
     */
42
    function once($callback): Closure
43
    {
44
        $response = new UndefinedValue;
22✔
45

46
        return function () use ($callback, &$response) {
22✔
47
            if ($response instanceof UndefinedValue) {
22✔
48
                $response = value($callback) ?? null;
22✔
49
            }
50

51
            return $response;
22✔
52
        };
22✔
53
    }
54
}
55

56
if (! \function_exists('Orchestra\Sidekick\is_safe_callable')) {
57
    /**
58
     * Determine if the value is a callable and not a string matching an available function name.
59
     *
60
     * @api
61
     */
62
    function is_safe_callable(mixed $value): bool
63
    {
64
        if ($value instanceof Closure) {
6✔
65
            return true;
1✔
66
        }
67

68
        if (! \is_callable($value)) {
5✔
69
            return false;
2✔
70
        }
71

72
        if (\is_array($value)) {
3✔
73
            $isList = \function_exists('array_is_list')
1✔
74
                ? array_is_list($value)
1✔
75
                : ! Arr::isAssoc($value);
×
76

77
            return \count($value) === 2 && $isList && method_exists(...$value);
1✔
78
        }
79

80
        return ! \is_string($value);
2✔
81
    }
82
}
83

84
if (! \function_exists('Orchestra\Sidekick\is_symlink')) {
85
    /**
86
     * Determine if the path is a symlink for both Unix and Windows environments.
87
     *
88
     * @api
89
     */
90
    function is_symlink(string $path): bool
91
    {
92
        if (windows_os() && is_dir($path) && readlink($path) !== $path) {
1✔
93
            return true;
×
94
        } elseif (is_link($path)) {
1✔
95
            return true;
×
96
        }
97

98
        return false;
1✔
99
    }
100
}
101

102
if (! \function_exists('Orchestra\Sidekick\is_testbench_cli')) {
103
    /**
104
     * Determine if command executed via Testbench CLI.
105
     *
106
     * @api
107
     */
108
    function is_testbench_cli(?bool $dusk = null): bool
109
    {
110
        $usingTestbench = \defined('TESTBENCH_CORE');
1✔
111
        $usingTestbenchDusk = \defined('TESTBENCH_DUSK');
1✔
112

113
        return match ($dusk) {
1✔
114
            false => $usingTestbench === true && $usingTestbenchDusk === false,
×
115
            true => $usingTestbench === true && $usingTestbenchDusk === true,
1✔
116
            default => $usingTestbench === true,
1✔
117
        };
1✔
118
    }
119
}
120

121
if (! \function_exists('Orchestra\Sidekick\transform_relative_path')) {
122
    /**
123
     * Transform relative path.
124
     *
125
     * @api
126
     */
127
    function transform_relative_path(string $path, string $workingPath): string
128
    {
129
        return str_starts_with($path, './')
1✔
130
            ? rtrim($workingPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.mb_substr($path, 2)
1✔
131
            : $path;
1✔
132
    }
133
}
134

135
if (! \function_exists('Orchestra\Sidekick\working_path')) {
136
    /**
137
     * Get the working path.
138
     *
139
     * @api
140
     *
141
     * @no-named-arguments
142
     *
143
     * @param  array<int, string|null>|string  ...$path
144
     */
145
    function working_path(array|string $path = ''): string
146
    {
147
        return is_testbench_cli() && \function_exists('Orchestra\Testbench\package_path')
1✔
148
            ? \Orchestra\Testbench\package_path($path)
×
149
            : base_path(join_paths(...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)));
1✔
150
    }
151
}
152

153
if (! \function_exists('Orchestra\Sidekick\laravel_normalize_version')) {
154
    /**
155
     * Laravel normalize version.
156
     *
157
     * @api
158
     *
159
     * @throws \RuntimeException
160
     */
161
    function laravel_normalize_version(): string
162
    {
163
        if (! class_exists(Application::class)) {
18✔
NEW
164
            throw new RuntimeException('Unable to verify Laravel Framework version');
×
165
        }
166

167
        /** @var string $version */
168
        $version = transform(
18✔
169
            Application::VERSION,
18✔
170
            fn (string $version) => match ($version) {
18✔
171
                '13.x-dev' => '13.0.0',
18✔
172
                default => $version,
18✔
173
            }
174
        );
18✔
175

176
        return (new VersionParser)->normalize($version);
18✔
177
    }
178
}
179

180
if (! \function_exists('Orchestra\Sidekick\phpunit_normalize_version')) {
181
    /**
182
     * PHPUnit normalize version.
183
     *
184
     * @api
185
     *
186
     * @throws \RuntimeException
187
     */
188
    function phpunit_normalize_version(): string
189
    {
190
        if (! class_exists(Version::class)) {
1✔
NEW
191
            throw new RuntimeException('Unable to verify PHPUnit version');
×
192
        }
193

194
        /** @var string $version */
195
        $version = transform(
1✔
196
            Version::id(),
1✔
197
            fn (string $version) => match (true) {
1✔
198
                str_starts_with($version, '12.4-') => '12.4.0',
1✔
199
                default => $version,
1✔
200
            }
201
        );
1✔
202

203
        return (new VersionParser)->normalize($version);
1✔
204
    }
205
}
206

207
if (! \function_exists('Orchestra\Sidekick\laravel_version_compare')) {
208
    /**
209
     * Laravel version compare.
210
     *
211
     * @api
212
     *
213
     * @template TOperator of string|null
214
     *
215
     * @phpstan-param  TOperator  $operator
216
     *
217
     * @phpstan-return (TOperator is null ? int : bool)
218
     *
219
     * @throws \RuntimeException
220
     *
221
     * @codeCoverageIgnore
222
     */
223
    function laravel_version_compare(string $version, ?string $operator = null): int|bool
224
    {
225
        if (! class_exists(Application::class)) {
226
            throw new RuntimeException('Unable to verify Laravel Framework version');
227
        }
228

229
        $versionParser = new VersionParser;
230

231
        $laravel = laravel_normalize_version();
232
        $version = $versionParser->normalize($version);
233

234
        if (\is_null($operator)) {
235
            return version_compare($laravel, $version);
236
        }
237

238
        return version_compare($laravel, $version, $operator);
239
    }
240
}
241

242
if (! \function_exists('Orchestra\Sidekick\package_version_compare')) {
243
    /**
244
     * Package version compare.
245
     *
246
     * @api
247
     *
248
     * @template TOperator of string|null
249
     *
250
     * @phpstan-param  TOperator  $operator
251
     *
252
     * @phpstan-return (TOperator is null ? int : bool)
253
     *
254
     * @throws \RuntimeException
255
     *
256
     * @codeCoverageIgnore
257
     */
258
    function package_version_compare(string $package, string $version, ?string $operator = null): int|bool
259
    {
260
        $prettyVersion = InstalledVersions::getPrettyVersion($package);
261

262
        if (\is_null($prettyVersion)) {
263
            throw new RuntimeException(\sprintf("Unable to verify '%s' version", $package));
264
        }
265

266
        $versionParser = new VersionParser;
267

268
        $package = $versionParser->normalize($prettyVersion);
269
        $version = $versionParser->normalize($version);
270

271
        if (\is_null($operator)) {
272
            return version_compare($package, $version);
273
        } elseif ($operator === '=') {
274

275
        }
276

277
        return version_compare($package, $version, $operator);
278
    }
279
}
280

281
if (! \function_exists('Orchestra\Sidekick\phpunit_version_compare')) {
282
    /**
283
     * PHPUnit version compare.
284
     *
285
     * @api
286
     *
287
     * @template TOperator of string|null
288
     *
289
     * @phpstan-param  TOperator  $operator
290
     *
291
     * @phpstan-return (TOperator is null ? int : bool)
292
     *
293
     * @throws \RuntimeException
294
     *
295
     * @codeCoverageIgnore
296
     */
297
    function phpunit_version_compare(string $version, ?string $operator = null): int|bool
298
    {
299
        if (! class_exists(Version::class)) {
300
            throw new RuntimeException('Unable to verify PHPUnit version');
301
        }
302

303
        $phpunit = phpunit_normalize_version();
304
        $version = (new VersionParser)->normalize($version);
305

306
        if (\is_null($operator)) {
307
            return version_compare($phpunit, $version);
308
        }
309

310
        return version_compare($phpunit, $version, $operator);
311
    }
312
}
313

314
if (! \function_exists('Orchestra\Sidekick\php_binary')) {
315
    /**
316
     * Determine the PHP Binary.
317
     *
318
     * @api
319
     *
320
     * @codeCoverageIgnore
321
     */
322
    function php_binary(): string
323
    {
324
        return (new PhpExecutableFinder)->find(false) ?: 'php';
325
    }
326
}
327

328
if (! \function_exists('Orchestra\Sidekick\windows_os')) {
329
    /**
330
     * Determine whether the current environment is Windows-based.
331
     *
332
     * @api
333
     *
334
     * @codeCoverageIgnore
335
     */
336
    function windows_os(): bool
337
    {
338
        return PHP_OS_FAMILY === 'Windows';
339
    }
340
}
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