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

orchestral / sidekick / 14528163710

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

Pull #19

github

web-flow
Merge a1964066d into 3021bef02
Pull Request #19: Add `Orchestra\Sidekick\is_safe_callable()` function

8 of 8 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
     *
22
     * @throws \RuntimeException
23
     */
24
    function enum_name($enum): string
25
    {
26
        if (PHP_VERSION_ID < 80100) {
5✔
UNCOV
27
            throw new RuntimeException(\sprintf('%s requires PHP 8.1 and above', __FUNCTION__));
×
28
        }
29

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

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

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

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

64
if (! \function_exists('Orchestra\Sidekick\join_paths')) {
65
    /**
66
     * Join the given paths together.
67
     *
68
     * @api
69
     */
70
    function join_paths(?string $basePath, string ...$paths): string
71
    {
72
        foreach ($paths as $index => $path) {
1✔
73
            if (empty($path) && $path !== '0') {
1✔
74
                unset($paths[$index]);
1✔
75
            } else {
76
                $paths[$index] = DIRECTORY_SEPARATOR.ltrim($path, DIRECTORY_SEPARATOR);
1✔
77
            }
78
        }
79

80
        return $basePath.implode('', $paths);
1✔
81
    }
82
}
83

84
if (! \function_exists('Orchestra\Sidekick\once')) {
85
    /**
86
     * Run callback only once.
87
     *
88
     * @api
89
     *
90
     * @param  mixed  $callback
91
     * @return \Closure():mixed
92
     */
93
    function once($callback): Closure
94
    {
95
        $response = new UndefinedValue;
5✔
96

97
        return function () use ($callback, &$response) {
5✔
98
            if ($response instanceof UndefinedValue) {
5✔
99
                $response = value($callback) ?? null;
5✔
100
            }
101

102
            return $response;
5✔
103
        };
5✔
104
    }
105
}
106

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

117
        if (! \is_callable($value)) {
4✔
118
            return false;
1✔
119
        }
120

121
        if (\is_array($value)) {
3✔
122
            return \count($value) === 2 && ! Arr::isAssoc($value) && method_exists(...$value);
1✔
123
        }
124

125
        return ! \is_string($value);
2✔
126
    }
127
}
128

129
if (! \function_exists('Orchestra\Sidekick\is_symlink')) {
130
    /**
131
     * Determine if the path is a symlink for both Unix and Windows environments.
132
     *
133
     * @api
134
     */
135
    function is_symlink(string $path): bool
136
    {
137
        if (windows_os() && is_dir($path) && readlink($path) !== $path) {
1✔
UNCOV
138
            return true;
×
139
        } elseif (is_link($path)) {
1✔
UNCOV
140
            return true;
×
141
        }
142

143
        return false;
1✔
144
    }
145
}
146

147
if (! \function_exists('Orchestra\Sidekick\transform_relative_path')) {
148
    /**
149
     * Transform relative path.
150
     *
151
     * @api
152
     */
153
    function transform_relative_path(string $path, string $workingPath): string
154
    {
155
        return str_starts_with($path, './')
1✔
156
            ? rtrim($workingPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.mb_substr($path, 2)
1✔
157
            : $path;
1✔
158
    }
159
}
160

161
if (! \function_exists('Orchestra\Sidekick\laravel_version_compare')) {
162
    /**
163
     * Laravel version compare.
164
     *
165
     * @api
166
     *
167
     * @template TOperator of string|null
168
     *
169
     * @phpstan-param  TOperator  $operator
170
     *
171
     * @phpstan-return (TOperator is null ? int : bool)
172
     *
173
     * @codeCoverageIgnore
174
     */
175
    function laravel_version_compare(string $version, ?string $operator = null): int|bool
176
    {
177
        if (! class_exists(Application::class)) {
178
            throw new RuntimeException('Unable to verify Laravel Framework version');
179
        }
180

181
        /** @var string $laravel */
182
        $laravel = transform(
183
            Application::VERSION,
184
            fn (string $version) => match ($version) {
185
                '13.x-dev' => '13.0.0',
186
                default => $version,
187
            }
188
        );
189

190
        if (\is_null($operator)) {
191
            return version_compare($laravel, $version);
192
        }
193

194
        return version_compare($laravel, $version, $operator);
195
    }
196
}
197

198
if (! \function_exists('Orchestra\Sidekick\phpunit_version_compare')) {
199
    /**
200
     * PHPUnit version compare.
201
     *
202
     * @api
203
     *
204
     * @template TOperator of string|null
205
     *
206
     * @throws \RuntimeException
207
     *
208
     * @phpstan-param  TOperator  $operator
209
     *
210
     * @phpstan-return (TOperator is null ? int : bool)
211
     *
212
     * @codeCoverageIgnore
213
     */
214
    function phpunit_version_compare(string $version, ?string $operator = null): int|bool
215
    {
216
        if (! class_exists(Version::class)) {
217
            throw new RuntimeException('Unable to verify PHPUnit version');
218
        }
219

220
        /** @var string $phpunit */
221
        $phpunit = transform(
222
            Version::id(),
223
            fn (string $version) => match (true) {
224
                str_starts_with($version, '12.2-') => '12.2.0',
225
                default => $version,
226
            }
227
        );
228

229
        if (\is_null($operator)) {
230
            return version_compare($phpunit, $version);
231
        }
232

233
        return version_compare($phpunit, $version, $operator);
234
    }
235
}
236

237
if (! \function_exists('Orchestra\Sidekick\php_binary')) {
238
    /**
239
     * Determine the PHP Binary.
240
     *
241
     * @api
242
     *
243
     * @codeCoverageIgnore
244
     */
245
    function php_binary(): string
246
    {
247
        return (new PhpExecutableFinder)->find(false) ?: 'php';
248
    }
249
}
250

251
if (! \function_exists('Orchestra\Sidekick\windows_os')) {
252
    /**
253
     * Determine whether the current environment is Windows-based.
254
     *
255
     * @api
256
     *
257
     * @codeCoverageIgnore
258
     */
259
    function windows_os(): bool
260
    {
261
        return PHP_OS_FAMILY === 'Windows';
262
    }
263
}
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