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:
- Laravel: Implementing a CRYPT_STD_DES Hasher
- Laravel: Implementing a CRYPT_EXT_DES Hasher
- Laravel: Implementing a MD5 Hasher
- Laravel: Implementing a CRYPT_SHA256 Hasher
- Laravel: Implementing a CRYPT_SHA512 Hasher
- Creating a Service Provider For Our Custom laravel Hashing Implementations
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.
∎