indigophp/hash-compat
Provides a hash_equals() polyfill for PHP versions older than 5.6, where the native timing-attack-safe string comparison function isn't available. A small compatibility shim for legacy projects.
indigophp/hash-compat is a tiny polyfill package: it backports PHP 5.6’s hash_equals() (and related hash_* helpers where missing) to older PHP runtimes by registering global functions through Composer’s autoload.files mechanism.
Minimal steps:
composer require indigophp/hash-compat
vendor/composer/autoload_files.php — you should see an entry pointing at the package’s bootstrap file.var_dump(function_exists('hash_equals')); // true on PHP 5.3+ once loaded
// before
if ($providedToken === $expectedToken) { ... }
// after
if (hash_equals($expectedToken, $providedToken)) { ... }
Where to look first:
composer.json — the autoload.files entry is the whole integration surface.src/ bootstrap file — it wraps each function definition in if (!function_exists(...)), so it is inert on modern PHP.Because the package is archived (last release 2015), treat “getting started” as “deciding whether you still need it.” On PHP 5.6+ it is dead weight; on PHP 5.3–5.5 it is a legitimate small dependency.
Timing-safe comparisons behind a compatibility boundary. In a legacy codebase targeting multiple PHP versions, centralize sensitive comparisons in one helper so the dependency is swappable:
final class SecureCompare
{
public static function equals($known, $user)
{
return hash_equals($known, $user);
}
}
This keeps call sites clean and makes it trivial to drop the polyfill once the minimum PHP version is bumped.
Composer-only integration. There is nothing to register. Add the requirement, run composer dump-autoload, and the functions exist. No config publishing, no provider, no middleware.
Framework usage (Laravel). Laravel 5.1 still supported PHP 5.5.9, where hash_equals() is not native. If your app or a dependency calls hash_equals() (e.g., MAC verification in encryption/token flows), this package is a valid stopgap. Prefer it over hand-rolled == comparisons.
Conditional loading in a monorepo. If only some services need legacy support, keep the dependency in the specific legacy package’s composer.json rather than the root, so modern services never load the file.
Test matrix pattern. Add PHP 5.4/5.5 and PHP 7.x to CI. The polyfill guards on function_exists, so the same code path should pass on both. Assert hash_equals behavior (equal strings, differing lengths, non-string inputs) rather than the polyfill’s internals.
Migration pattern. When you finally raise the PHP floor to 5.6+, remove the package — the native function wins automatically because the polyfill never defines itself. This makes removal safe in either order: pull the dependency, or leave it until a cleanup pass.
hash_equals conditionally. The function_exists guard usually prevents fatal redeclaration errors, but load order matters: whichever bootstrap runs first wins. If behavior differs, inspect autoload_files.php ordering.hash_equals($known, $user) is not symmetric in intent — always pass the trusted value first and the user-supplied value second, and both must be strings. Passing null or arrays will misbehave or error.hash_equals is “missing”: run composer dump-autoload, confirm autoload.files contains the package, and check for a custom autoloader bypassing Composer.password_hash/password_verify (or a dedicated polyfill for those) rather than assuming it covers crypto needs.Bottom line: a pragmatic one-line backport for hash_equals() in PHP 5.3–5.5 era apps. On any modern Laravel project it should be treated as removable legacy baggage, not a day-to-day tool.
How can I help you explore Laravel packages today?