make(Language::class)->__(index: $index, default: $default); } } if (! function_exists('array_sort')) { /** * sort array of arrqays by value * * @param string|array $sortyBy Column name (string) or multi-sort spec (array) */ function array_sort(array $array, mixed $sortyBy): array { if (is_string($sortyBy)) { $collection = collect($array); $sorted = $collection->sortBy($sortyBy, SORT_NATURAL); return $sorted->values()->all(); } else { return \Illuminate\Support\Collection::make($array)->sortBy($sortyBy)->all(); } } } if (! function_exists('do_once')) { /** * Execute a callback only once. */ function do_once(string $key, Closure $callback, bool $across_requests = false): void { $key = "do_once_{$key}"; if ($across_requests) { if (session()->exists('do_once') === false) { session(['do_once' => []]); } if (session('do_once.'.$key) ?? false) { return; } session(['do_once.'.$key => true]); } else { static $do_once; $do_once ??= []; if ($do_once[$key] ?? false) { return; } $do_once[$key] = true; } $callback(); } } if (! function_exists('build')) { /** * Turns any object into a builder object * **/ function build(object $object): Build { return new Build($object); } } if (! function_exists('format')) { /** * Returns a format object to format string values * * @param string|int|float|DateTime|Carbon|null $value * @param string|int|float|DateTime|null $value2 */ function format(string|int|float|null|\DateTime|\Carbon\CarbonInterface $value, string|int|float|null|\DateTime|\Carbon\CarbonInterface $value2 = null, ?FromFormat $fromFormat = FromFormat::DbDate): Format { return new Format($value, $value2, $fromFormat); } } if (! function_exists('cast')) { /** * Casts a variable to a different type if possible. * * @param mixed $source The object to be cast. * @param string $classOrType The class to which the object should be cast. * @param array $constructParams Optional parameters to pass to the constructor. * @param array $mappings Make sure certain sub properties are casted to specific types. * @return mixed The casted object, or throws an exception on failure. * * @throws \InvalidArgumentException If the class does not exist. * @throws \RuntimeException|ReflectionException On serialization errors. */ function cast(mixed $source, string $classOrType, array $constructParams = [], array $mappings = []): mixed { if (in_array($classOrType, ['int', 'integer', 'float', 'string', 'str', 'bool', 'boolean', 'object', 'stdClass', 'array'])) { return Cast::castSimple($source, $classOrType); } if (enum_exists($classOrType)) { return Cast::castEnum($source, $classOrType); } // Convert string to date if required. if (is_string($source) && is_a($classOrType, CarbonInterface::class, true)) { return Cast::castDateTime($source); } return (new Cast($source))->castTo($classOrType, $constructParams, $mappings); } } if (! function_exists('dtHelper')) { /** * Get a singleton instance of the DateTimeHelper class. * * @throws BindingResolutionException */ function dtHelper(): DateTimeHelper { if (! app()->bound(DateTimeHelper::class)) { app()->singleton(DateTimeHelper::class); } return app()->make(DateTimeHelper::class); } } if (! function_exists('redirect')) { /** * Get an instance of the redirector. * * @param string|null $url * @param int $http_response_code * @param array $headers * @param bool|null $secure * @return \Symfony\Component\HttpFoundation\RedirectResponse */ function redirect($url = null, $http_response_code = 302, $headers = [], $secure = null) { return new RedirectResponse( trim(preg_replace('/\s\s+/', '', strip_tags($url))), $http_response_code ); } } if (! function_exists('currentRoute')) { /** * Get an instance of the redirector. */ function currentRoute() { return app('request')->getCurrentRoute(); } } if (! function_exists('get_domain_key')) { /** * Gets a unique instance key determined by domain */ function get_domain_key() { // Now that we know where the instance is bing called from // Let's add a domain level cache. $host = app('request')->host(); $url = config('app.url'); if (is_string($url) && $url !== '') { $host = $url; } $domainKeyParts = config('app.url').config('app.key'); $slug = \Illuminate\Support\Str::slug($domainKeyParts); $domainCacheName = md5($slug); return $domainCacheName; } } if (! function_exists('mix')) { /** * Get the path to a versioned Mix file. Customized for Leantime. * * * @throws BindingResolutionException */ function mix(string $path = '', string $manifestDirectory = ''): \Leantime\Core\Support\Mix|string { if (! ($app = app())->bound(\Leantime\Core\Support\Mix::class)) { $app->instance(\Leantime\Core\Support\Mix::class, new \Leantime\Core\Support\Mix); } $mix = $app->make(\Leantime\Core\Support\Mix::class); if (empty($path)) { return $mix; } return $mix($path, $manifestDirectory); } } if (! function_exists('base_path')) { /** * Get the path to the base of the install. * * @param string $path * @return string */ function base_path($path = '') { return app()->basePath($path); } } if (! function_exists('safe_unserialize')) { /** * Safely unserialize data without allowing object instantiation. * * Tries json_decode first (forward-compatible with future JSON storage). * Falls back to unserialize with allowed_classes=false to prevent * PHP object instantiation, which mitigates deserialization RCE attacks. * * @param string|null $data The serialized/encoded data * @param mixed $default Default value if data is empty or unparseable * @return mixed The decoded data */ function safe_unserialize(?string $data, mixed $default = []): mixed { if ($data === null || $data === '') { return $default; } // Try JSON first (forward-compatible) $jsonResult = json_decode($data, true); if (json_last_error() === JSON_ERROR_NONE) { return $jsonResult; } // Fall back to PHP unserialize with no class instantiation $result = @unserialize($data, ['allowed_classes' => false]); if ($result !== false) { return $result; } // Handle the edge case where the serialized value is literally boolean false if ($data === 'b:0;') { return false; } return $default; } } if (! function_exists('get_release_version')) { /** * Gets a unique instance key determined by domain */ function get_release_version() { $appSettings = app()->make(\Leantime\Core\Configuration\AppSettings::class); return $appSettings->appVersion; } } if (! function_exists('can')) { /** * Whether the current user holds a `domain.action` permission, optionally scoped to a * project. The permission-era replacement for `$login::userIsAtLeast(...)` in templates. * * In Blade prefer the @can directive (it routes through the same Gate bridge); use this * helper in legacy .tpl.php templates and anywhere a plain boolean is handy: * ... */ function can(string $permission, ?int $projectId = null): bool { return app()->make(\Leantime\Core\Auth\Permissions\PermissionService::class) ->currentUserCan($permission, $projectId); } }