# symfony/password-hasher keeps hashing behind a factory and a name

> The PasswordHasher component gives PHP applications a small, MIT licensed abstraction for hashing and verifying passwords, with a factory that holds several named hashers so different algorithms can coexist. It is a Symfony component with its own release line, and the repository carries a legacy hasher interface for older hashes.

**symfony/password-hasher** — Provides password hashing utilities

- Repository: https://github.com/symfony/password-hasher
- Website: https://symfony.com/password-hasher
- Stars: 807 · Forks: 4
- Language: PHP
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/symfony-password-hasher

## A component, not a framework, that does one job

The PasswordHasher component provides secure password hashing utilities. That is the entire description, and it is unusually direct for a Symfony component README.

The component is installed the way every Symfony component is installed:

```bash
composer require symfony/password-hasher
```

The package is MIT licensed, its homepage is symfony.com/password-hasher, and it is versioned independently of the framework. The repository layout shows a conventional component: a Hasher directory, an Exception directory, a Command directory, a Tests directory with a phpunit.xml.dist, a composer.json, a CHANGELOG.md, and the two interfaces at the root, PasswordHasherInterface and LegacyPasswordHasherInterface.

Two things in that listing tell you most of what you need to know about the design. First, the root interface is small, so the contract between the component and your application is narrow. Second, there is a separate legacy interface, which means the component does not only hash new passwords but also knows about password formats that predate its own preferences.

The audience is a Symfony application that stores user passwords. The value it adds over the platform's own hashing primitives is not the hashing, it is the management: which algorithm is in use, how a stored hash from an older scheme is still accepted, and how all of that is configured in one place instead of being hard-coded in the code that touches the user table.

## The factory holds named hashers, and you pick one by name

The core of the component is PasswordHasherFactory, and the README's example shows how it is configured:

```php
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory;

// Configure different password hashers via the factory
$factory = new PasswordHasherFactory([
    'common' => ['algorithm' => 'bcrypt'],
    'memory-hard' => ['algorithm' => 'sodium'],
]);

// Retrieve the right password hasher by its name
$passwordHasher = $factory->getPasswordHasher('common');
```

Each entry in the constructor is a named configuration with an algorithm. The names are yours to choose, and in the example they describe intent rather than the algorithm, with common pointing at bcrypt and memory-hard pointing at sodium.

The retrieval call takes that name and returns a hasher. From that point the interface is two operations:

```php
// Hash a plain password
$hash = $passwordHasher->hash('plain'); // returns a bcrypt hash

// Verify that a given plain password matches the hash
$passwordHasher->verify($hash, 'wrong'); // returns false
$passwordHasher->verify($hash, 'plain'); // returns true (valid)
```

The naming scheme is the interesting part. Naming a hasher common and another memory-hard means the choice of algorithm is a configuration decision with a stated reason attached, and it means you can add a third later without rewriting call sites. It also means the place where a hash is produced and the place where it is verified can legitimately be different, which is exactly what an algorithm migration needs.

## The legacy interface exists because stored hashes outlive algorithms

The presence of LegacyPasswordHasherInterface at the repository root is the most telling line in the file listing, because it addresses the problem that password hashing libraries usually leave to the application.

When you change the algorithm you use for new passwords, the hashes already in your database do not change. They stay what they are. If the verification path only understands the current algorithm, every existing user is locked out at the moment you upgrade.

So the component separates the two concerns. The modern interface covers the hasher you configure and use for new passwords, and the legacy interface covers recognition of older stored hashes. The README's example does not show the legacy path, and the repository listing shows no separate legacy factory class at the root, so the exact wiring is something to confirm in the documentation rather than in the README.

The idea itself is what matters for an evaluation. A hasher component that only hashes is a convenience. A hasher component that also recognises the formats you used before is the piece that lets an application change algorithms without a password reset campaign, and the naming scheme of the factory, with the hasher chosen by name rather than by construction, is what makes that possible at all.

The other thing worth noticing is that the interfaces live at the root of the component rather than inside the Hasher directory, which is how Symfony signals that they are the public contract of the package. Anything your application type-hints against is one of those two interfaces.

## A console command exists, but the README does not document it

The repository contains a Command directory, which in a Symfony component is where console commands live. That means this package ships a command you can run.

The README does not say what it does, so it would be wrong to guess. What can be said is that the component is built to be operated rather than only called, and that the command is discoverable from the source and from the Symfony console when the component is registered.

This is worth flagging for a different reason. A password hashing component with a console command is a component you can use to rehash or migrate stored passwords without writing your own script, and the presence of the legacy interface alongside it suggests the command exists for that kind of work. But the README gives no usage, no arguments and no examples, so the documentation for it lives elsewhere, in the Symfony documentation on hashing passwords that the README links.

That is a small observation about this component's documentation style, and it repeats elsewhere. The README is a getting started snippet: composer require, one factory configuration, one hasher retrieved by name, two calls. Everything beyond that is delegated to the documentation site. For a component this small that is a reasonable choice, but it does mean you should treat the README as proof that the API is small, not as proof that the feature set is small.

## The alternative is a direct call to the platform hashing API

The obvious alternative is to skip the abstraction and call the platform's own password hashing function where you need a hash. PHP has had a standard library password hashing interface for a long time, and for a single algorithm with no legacy hashes to worry about, that call is shorter than everything above.

The difference in approach is worth being precise about. A direct call gives you one algorithm and one configuration site, in exchange for you choosing the algorithm at every call site, or in every place you wrap it yourself. It also leaves you to deal with old stored hashes on your own.

The factory approach moves the choice to one place, gives each choice a name that records why it was made, and adds a distinct interface for hashes the component did not produce. When an application has one algorithm, has never changed it, and will not need to change it, the direct call is genuinely enough, and this component would be overhead.

When an application has two algorithms running side by side, or a legacy hash format in the database, or a plan to change algorithms later, the named factory is not overhead. It is the minimum amount of machinery that keeps one code path working across the transition, and that is the case this component is built for.

One gap to be aware of when you compare the two: the README does not describe how to configure the cost parameters for the chosen algorithms. If your security policy fixes a bcrypt cost or a sodium operations limit, that configuration has to come from the Symfony documentation and the component's own configuration, not from the README.

## Maintenance, versioning and the branch ahead of the tag

The component is not archived and the last push was on 2026-10-18, so it is receiving work.

The release list shows v8.1.0 on 2026-05-29, preceded by the v8.1.0 betas, BETA1 on 2026-05-06 and BETA3 on 2026-05-20. The default branch is 8.2, which is a branch that is ahead of the newest tag. That is the normal shape of a Symfony component, where the next minor branch is developed in parallel with the current release line, and it means that if you want to be on the versioned release you pin v8.1.0, and if you want the in-development line you follow the 8.2 branch.

For a component this small, that is a comfortable upgrade cadence. The code surface your application touches is the factory, the two interfaces and the hash and verify calls, and the CHANGELOG.md at the repository root is where a change to any of that would be recorded.

The licence is MIT, which is the usual Symfony component licence and means you can take the component into a commercial application without obligations beyond the notice. The homepage, symfony.com/password-hasher, and the documentation link to the security section of the current Symfony documentation are the two places to read further.

The package is also looking for a backer, which the README says in its sponsor section. That is a funding statement rather than a technical one, but it does indicate that the component is maintained as a standalone project with its own support surface rather than as a leftover in the monorepo.

## Conclusion

Symfony applications that need to hash passwords should use this component rather than calling a platform hashing function directly, because the factory lets several algorithms coexist under names and the component ships a legacy hasher interface for older stored hashes. It is the wrong choice if you want one algorithm and one line of code, since the factory and the naming exist for the case where you have more than one, and it is the wrong choice if you need to choose cost parameters, which the README does not describe. Before adopting, read the hashing documentation linked from the component, check the legacy hasher interface against the hash formats your database already contains, and note that the default branch is 8.2 while the newest tag is v8.1.0.

## FAQ

### How do I install symfony/password-hasher?

Run composer require symfony/password-hasher. The package is MIT licensed and versioned independently of the framework, with its own tag line and changelog.

### How do I hash and verify a password with the PasswordHasher component?

Create a PasswordHasherFactory with named algorithm configurations, call getPasswordHasher with one of those names, then use hash on the plain password and verify to compare a plain password against the stored hash.

### Why does the component have a LegacyPasswordHasherInterface?

It exists so that stored hashes produced by an older algorithm or format can still be recognised after you change the algorithm used for new passwords. The repository ships the interface at the root of the package, and the README does not show the wiring for it.

### Can I configure the cost of bcrypt or sodium hashing through the factory?

The README does not say. It shows only the algorithm key for each named hasher, so the cost parameters have to be confirmed in the Symfony security documentation rather than assumed from the README example.

## Sources

- [License: MIT](https://github.com/symfony/password-hasher/blob/8.2/LICENSE)
- [Project website](https://symfony.com/password-hasher)
- [README](https://github.com/symfony/password-hasher/blob/8.2/README.md)
- [Releases](https://github.com/symfony/password-hasher/releases)
- [symfony/password-hasher on GitHub](https://github.com/symfony/password-hasher)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/symfony-password-hasher
