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 $method13 * @param array $parameters14 * @return mixed15 *16 * @throws \BadMethodCallException17 */18 public function __call($method, $parameters)19 {20 // We will handle the case of 'hash' specially so that21 // 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 the28 // method name. This should leave us with some form29 // of the actual hasher implementation name.30 $hasher = mb_substr($method, 4);31 32 // All the hashing implementations were bound to the service33 // container using the format hash.camelCaseHashName. Now34 // 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, we38 // can request the hasher instance from the service39 // 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 $method47 * @param array $args48 * @return mixed49 */50 public static function __callStatic($method, $args)51 {52 // Create a new instance and forward the method53 // call to it. Not the most elegant solution54 // 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.
∎
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.