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

orchestral / sidekick / 14527901055

18 Apr 2025 01:33AM UTC coverage: 95.181% (+0.4%) from 94.737%
14527901055

Pull #19

github

web-flow
Merge d402c8409 into fa2d8a254
Pull Request #19: Add `Orchestra\Sidekick\is_safe_callable()` function

5 of 5 new or added lines in 1 file covered. (100.0%)

4 existing lines in 1 file now uncovered.

79 of 83 relevant lines covered (95.18%)

3.27 hits per line

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

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

3
namespace Orchestra\Sidekick;
4

5
use BackedEnum;
6
use Closure;
7
use Illuminate\Foundation\Application;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Str;
10
use PHPUnit\Runner\Version;
11
use RuntimeException;
12
use UnitEnum;
13

14
if (! \function_exists('Orchestra\Sidekick\enum_name')) {
15
    /**
16
     * Get the proper name from enum.
17
     *
18
     * @api
19
     *
20
     * @param  \BackedEnum|\UnitEnum  $enum
21
     * @return string
22
     *
23
     * @throws \RuntimeException
24
     */
25
    function enum_name($enum): string
26
    {
27
        if (PHP_VERSION_ID < 80100) {
5✔
UNCOV
28
            throw new RuntimeException(\sprintf('%s requires PHP 8.1 and above', __FUNCTION__));
×
29
        }
30

31
        return Str::title(str_replace('_', ' ', $enum->name));
5✔
32
    }
33
}
34

35
if (! \function_exists('Orchestra\Sidekick\enum_value')) {
36
    /**
37
     * Get the proper name from enum.
38
     *
39
     * @api
40
     *
41
     * @template TValue
42
     * @template TDefault
43
     *
44
     * @param  TValue  $value
45
     * @param  TDefault|callable(TValue): TDefault  $default
46
     * @return ($value is empty ? TDefault : mixed)
47
     *
48
     * @throws \RuntimeException
49
     */
50
    function enum_value(mixed $value, mixed $default = null): mixed
51
    {
52
        if (PHP_VERSION_ID < 80100) {
20✔
UNCOV
53
            throw new RuntimeException(\sprintf('%s requires PHP 8.1 and above', __FUNCTION__));
×
54
        }
55

56
        return match (true) {
57
            $value instanceof BackedEnum => $value->value,
20✔
58
            $value instanceof UnitEnum => $value->name,
16✔
59

60
            default => $value ?? value($default),
20✔
61
        };
62
    }
63
}
64

65
if (! \function_exists('Orchestra\Sidekick\once')) {
66
    /**
67
     * Run callback only once.
68
     *
69
     * @api
70
     *
71
     * @param  mixed  $callback
72
     * @return \Closure():mixed
73
     */
74
    function once($callback): Closure
75
    {
76
        $response = new UndefinedValue;
5✔
77

78
        return function () use ($callback, &$response) {
5✔
79
            if ($response instanceof UndefinedValue) {
5✔
80
                $response = value($callback) ?? null;
5✔
81
            }
82

83
            return $response;
5✔
84
        };
5✔
85
    }
86
}
87

88
if (! \function_exists('Orchestra\Sidekick\join_paths')) {
89
    /**
90
     * Join the given paths together.
91
     *
92
     * @param  string|null  $basePath
93
     * @param  string  ...$paths
94
     * @return string
95
     */
96
    function join_paths(?string $basePath, string ...$paths): string
97
    {
98
        foreach ($paths as $index => $path) {
1✔
99
            if (empty($path) && $path !== '0') {
1✔
100
                unset($paths[$index]);
1✔
101
            } else {
102
                $paths[$index] = DIRECTORY_SEPARATOR.ltrim($path, DIRECTORY_SEPARATOR);
1✔
103
            }
104
        }
105

106
        return $basePath.implode('', $paths);
1✔
107
    }
108
}
109

110
if (! \function_exists('Orchestra\Sidekick\is_safe_callable')) {
111
    /**
112
     * Determine if the value is a callable and not a string matching an available function name.
113
     *
114
     * @param  mixed  $value
115
     * @return bool
116
     */
117
    function is_safe_callable(mixed $value): bool
118
    {
119
        if ($value instanceof Closure) {
5✔
120
            return true;
1✔
121
        }
122

123
        if (! \is_callable($value)) {
4✔
124
            return false;
1✔
125
        }
126

127
        if (\is_array($value)) {
3✔
128
            return \count($value) === 2 && ! Arr::isAssoc($value) && method_exists(...$value);
1✔
129
        }
130

131
        return ! \is_string($value);
2✔
132
    }
133
}
134

135
if (! \function_exists('Orchestra\Sidekick\is_symlink')) {
136
    /**
137
     * Determine if path is symlink for both Unix and Windows environment.
138
     *
139
     * @api
140
     *
141
     * @param  string  $path
142
     * @return bool
143
     */
144
    function is_symlink(string $path): bool
145
    {
146
        if (windows_os() && is_dir($path) && readlink($path) !== $path) {
1✔
UNCOV
147
            return true;
×
148
        } elseif (is_link($path)) {
1✔
UNCOV
149
            return true;
×
150
        }
151

152
        return false;
1✔
153
    }
154
}
155

156
if (! \function_exists('Orchestra\Sidekick\transform_relative_path')) {
157
    /**
158
     * Transform relative path.
159
     *
160
     * @api
161
     *
162
     * @param  string  $path
163
     * @param  string  $workingPath
164
     * @return string
165
     */
166
    function transform_relative_path(string $path, string $workingPath): string
167
    {
168
        return str_starts_with($path, './')
1✔
169
            ? rtrim($workingPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.mb_substr($path, 2)
1✔
170
            : $path;
1✔
171
    }
172
}
173

174
if (! \function_exists('Orchestra\Sidekick\laravel_version_compare')) {
175
    /**
176
     * Laravel version compare.
177
     *
178
     * @api
179
     *
180
     * @template TOperator of string|null
181
     *
182
     * @param  string  $version
183
     * @param  string|null  $operator
184
     * @return int|bool
185
     *
186
     * @phpstan-param  TOperator  $operator
187
     *
188
     * @phpstan-return (TOperator is null ? int : bool)
189
     *
190
     * @codeCoverageIgnore
191
     */
192
    function laravel_version_compare(string $version, ?string $operator = null): int|bool
193
    {
194
        if (! class_exists(Application::class)) {
195
            throw new RuntimeException('Unable to verify Laravel Framework version');
196
        }
197

198
        /** @var string $laravel */
199
        $laravel = transform(
200
            Application::VERSION,
201
            fn (string $version) => match ($version) {
202
                '13.x-dev' => '13.0.0',
203
                default => $version,
204
            }
205
        );
206

207
        if (\is_null($operator)) {
208
            return version_compare($laravel, $version);
209
        }
210

211
        return version_compare($laravel, $version, $operator);
212
    }
213
}
214

215
if (! \function_exists('Orchestra\Sidekick\phpunit_version_compare')) {
216
    /**
217
     * PHPUnit version compare.
218
     *
219
     * @api
220
     *
221
     * @template TOperator of string|null
222
     *
223
     * @param  string  $version
224
     * @param  string|null  $operator
225
     * @return int|bool
226
     *
227
     * @throws \RuntimeException
228
     *
229
     * @phpstan-param  TOperator  $operator
230
     *
231
     * @phpstan-return (TOperator is null ? int : bool)
232
     *
233
     * @codeCoverageIgnore
234
     */
235
    function phpunit_version_compare(string $version, ?string $operator = null): int|bool
236
    {
237
        if (! class_exists(Version::class)) {
238
            throw new RuntimeException('Unable to verify PHPUnit version');
239
        }
240

241
        /** @var string $phpunit */
242
        $phpunit = transform(
243
            Version::id(),
244
            fn (string $version) => match (true) {
245
                str_starts_with($version, '12.2-') => '12.2.0',
246
                default => $version,
247
            }
248
        );
249

250
        if (\is_null($operator)) {
251
            return version_compare($phpunit, $version);
252
        }
253

254
        return version_compare($phpunit, $version, $operator);
255
    }
256
}
257

258
if (! \function_exists('Orchestra\Sidekick\php_binary')) {
259
    /**
260
     * Determine the PHP Binary.
261
     *
262
     * @api
263
     *
264
     * @return string
265
     *
266
     * @codeCoverageIgnore
267
     */
268
    function php_binary(): string
269
    {
270
        return (new PhpExecutableFinder)->find(false) ?: 'php';
271
    }
272
}
273

274
if (! \function_exists('Orchestra\Sidekick\windows_os')) {
275
    /**
276
     * Determine whether the current environment is Windows based.
277
     *
278
     * @api
279
     *
280
     * @return bool
281
     *
282
     * @codeCoverageIgnore
283
     */
284
    function windows_os(): bool
285
    {
286
        return PHP_OS_FAMILY === 'Windows';
287
    }
288
}
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