Creating a Hashing Manager For Our Custom Laravel Hashing Implementations

November 30, 2016 —John Koster

To make it easier to easily work with all the different hashing implementations that we have created in previous articles, we will create a new class called HashManager in some directory that is accessible with the namespace Laravel\Artisan\Hashing\HashManager.

The hashing articles were created in the following articles:

The goal of this hashing manager class is to be able to write code that looks like the following:

1<?php
2 
3use Laravel\Artisan\Hashing\Hasher;
4 
5// Make a hash with the default hasher.
6$hash = Hasher::hash()->make('test');
7 
8// Make a hash with our new hasher implementations.
9$hash = Hasher::hashSha256()->make('test');
10$hash = Hasher::hashSha512()->make('test');
11$hash = Hasher::hashDes()->make('test');
12$hash = Hasher::hashExtDes()->make('test');
13$hash = Hasher::hashMd5()->make('test');
14 
15// We should also be able to work with an instance.
16$hasher = new Hasher;
17$hash = $hasher->hashSha256()->make('test');

With the goals in mind we have a few options. We can explicitly create all the member and static methods, or we can find some way to dynamically call the methods. Luckily, PHP provides __call and __callStatic methods that we can take advantage of to achieve our goals.

The complete code for our new Hasher will look like this:

1<?php
2namespace Laravel\Artisan\Hashing;
3 
4use Illuminate\Support\Str;
5 
6class Hasher
7{
8 
9 /**
10 * Dynamically handle calls to the class.
11 *
12 * @param string $method
13 * @param array $parameters
14 * @return mixed
15 *
16 * @throws \BadMethodCallException
17 */
18 public function __call($method, $parameters)
19 {
20 // We will handle the case of 'hash' specially so that
21 // we can request the default hasher implementation.
22 if ($method == 'hash') {
23 return app('hash');
24 }
25 
26 // Figure out which hasher to get from the service container.
27 // We will use mb_substr to remove the "with" part of the
28 // method name. This should leave us with some form
29 // of the actual hasher implementation name.
30 $hasher = mb_substr($method, 4);
31 
32 // All the hashing implementations were bound to the service
33 // container using the format hash.camelCaseHashName. Now
34 // we just have to use the Str::camel() helper function.
35 $hasher = Str::camel($hasher);
36 
37 // Now that we have the name in the form we can use, we
38 // can request the hasher instance from the service
39 // container and return it the user who wants it.
40 return app('hash.'.$hasher);
41 }
42 
43 /**
44 * Handle dynamic, static calls to the object.
45 *
46 * @param string $method
47 * @param array $args
48 * @return mixed
49 */
50 public static function __callStatic($method, $args)
51 {
52 // Create a new instance and forward the method
53 // call to it. Not the most elegant solution
54 // but it really helps with code re-use.
55 $instance = new static;
56 return $instance->$method();
57 }
58 
59}

We could create a facade for the Hasher, but is just another layer of indirection that is not needed in this particular case.

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.