toDateTimeString(); } } if (!function_exists('local_date')) { function local_date(string $utcDateTime, string $format = 'Y-m-d H:i:s'): string { try { $carbon = CarbonImmutable::parse($utcDateTime)->setTimezone(config('app.timezone')); return $carbon->format($format); } catch (\Throwable $e) { return CarbonImmutable::parse($utcDateTime)->format($format); } } } if (!function_exists('user_timezone')) { function user_timezone(): string { return (string) (config('School')->attendance['timezone'] ?? config('app.timezone', 'America/New_York')); } } if (!function_exists('pbkdf2_hash')) { function pbkdf2_hash(string $password, string $algo = 'sha256', int $iterations = 100000, int $length = 64): string { $salt = random_bytes(16); $saltHex = bin2hex($salt); $derived = hash_pbkdf2($algo, $password, $salt, $iterations, $length, true); $derivedHex = bin2hex($derived); return implode('$', [$algo, $iterations, $saltHex, $derivedHex]); } } if (!function_exists('pbkdf2_verify')) { function pbkdf2_verify(string $password, string $storedHash): bool { $delim = strpos($storedHash, '$') !== false ? '$' : ':'; $parts = explode($delim, $storedHash); if (count($parts) !== 4) { return false; } [$algo, $iterations, $saltHex, $derivedHex] = $parts; if ($saltHex === '' || $derivedHex === '') { return false; } $salt = ctype_xdigit($saltHex) ? hex2bin($saltHex) : false; $derived = ctype_xdigit($derivedHex) ? hex2bin($derivedHex) : false; if ($salt === false || $derived === false) { return false; } $calc = hash_pbkdf2($algo, $password, $salt, (int) $iterations, strlen($derived), true); return hash_equals($derived, $calc); } } if (!function_exists('verify_stored_password')) { function verify_stored_password(string $password, ?string $storedHash): bool { if (!$storedHash) { return false; } $hashInfo = password_get_info($storedHash); if (($hashInfo['algo'] ?? null) !== null) { return Hash::check($password, $storedHash); } $parts = preg_split('/[:$]/', $storedHash); if (is_array($parts) && count($parts) === 4) { return pbkdf2_verify($password, $storedHash); } return Hash::check($password, $storedHash); } }