Baidu Encyclopedia definition: computer science in the Macro, is a batch processing title. Generally speaking, a macro is a rule or pattern, or syntactic substitution, that explains how a particular input (usually a string) is converted to a corresponding output (usually a string) according to predefined rules. This substitution occurs at precompilation time and is called macro expansion.
- I first got to know Macro when I was taking a basic computer course in college, and the teacher told me about Office. At the time, the teacher didn’t pay much attention to macros, only remembering that they were powerful and made everyday tasks much easier.
- Today we’ll talk about macros in Laravel
First complete source code
namespace Illuminate\Support\Traits;
use Closure;
use ReflectionClass;
use ReflectionMethod;
use BadMethodCallException;
trait Macroable
{
/**
* The registered string macros.
*
* @var array
*/
protected static $macros = [];
/**
* Register a custom macro.
*
* @param string $name
* @param object|callable $macro
*
* @return void
*/
public static function macro($name, $macro)
{
static::$macros[$name] = $macro;
}
/**
* Mix another object into the class.
*
* @param object $mixin
* @return void
*/
public static function mixin($mixin)
{
$methods = (new ReflectionClass($mixin))->getMethods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);
foreach ($methods as $method) {
$method->setAccessible(true);
static::macro($method->name, $method->invoke($mixin)); }}/**
* Checks if macro is registered.
*
* @param string $name
* @return bool
*/
public static function hasMacro($name)
{
return isset(static::$macros[$name]);
}
/**
* Dynamically handle calls to the class.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public static function __callStatic($method, $parameters)
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
if (static::$macros[$method] instanceof Closure) {
return call_user_func_array(Closure::bind(static::$macros[$method], null.static::class), $parameters);
}
return call_user_func_array(static::$macros[$method], $parameters);
}
/**
* Dynamically handle calls to the class.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
$macro = static::$macros[$method];
if ($macro instanceof Closure) {
return call_user_func_array($macro->bindTo($this.static::class), $parameters);
}
returncall_user_func_array($macro, $parameters); }}Copy the code
Macroable::macro
methods
public static function macro($name, $macro)
{
static::$macros[$name] = $macro;
}
Copy the code
$macro can pass a closure or an object, according to the comments on the parameter, thanks to the magic method in PHP
class Father
{
// We can use the object as a closure by adding the magic method **__invoke**.
public function __invoke(a)
{
echo __CLASS__; }}class Child
{
use \Illuminate\Support\Traits\Macroable;
}
// After adding the macro directive, we can call methods that do not exist in the Child object
Child::macro('show'.new Father);
/ / output: Father
(new Child)->show();
Copy the code
Macroable::mixin
Method This method injects the return result of an object’s method into the original object
public static function mixin($mixin)
{
// Get all public and protected methods in the object by reflection
$methods = (new ReflectionClass($mixin))->getMethods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);
foreach ($methods as $method) {
// Set methods are accessible because protected ones cannot be called externally
$method->setAccessible(true);
// Call macro to batch create macro directives
static::macro($method->name, $method->invoke($mixin)); }}// Actual use
class Father
{
public function say(a)
{
return function (a) {
echo 'say';
};
}
public function show(a)
{
return function (a) {
echo 'show';
};
}
protected function eat(a)
{
return function (a) {
echo 'eat'; }; }}class Child
{
use \Illuminate\Support\Traits\Macroable;
}
// Batch bind macroinstructions
Child::mixin(new Father);
$child = new Child;
/ / output: say
$child->say();
/ / output: the show
$child->show();
/ / output: eat
$child->eat();
Copy the code
As you can see from the above code, mixins can bind a class’s methods to a macro class. It is important to note that the method must return a closure type.
Macroable::hasMacro
methods
public static function hasMacro($name)
{
return isset(static::$macros[$name]);
}
Copy the code
So this is a very simple method and there’s nothing complicated about it, just to see if there’s a macro instruction. This is usually done before using macros.
Macroable::__call
andMacroable::__callStatic
Methods It is because of these two methods that we can perform macro operations, the code is similar except for the way they are executed. So let’s talk about that__call
public function __call($method, $parameters)
{
// If the macro does not exist, throw an exception
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
// Get the stored macros
$macro = static::$macros[$method];
// Closures do a little bit of special processing
if ($macro instanceof Closure) {
return call_user_func_array($macro->bindTo($this.static::class), $parameters);
}
// When not a closure, such as an object, run directly through this method, but make sure the object has an '__invoke' method
return call_user_func_array($macro, $parameters);
}
class Child
{
use \Illuminate\Support\Traits\Macroable;
protected $name = 'father';
}
// For closure special handling, all you need to do is bind $this, as in
Child::macro('show'.function (a) {
echo $this->name;
});
/ / output: father
(new Child)->show();
Copy the code
The Closure::bindTo method is used in the __call method because the Closure::bindTo method can be called in the Closure through $this when we bind the macro.
Closure::bindTo copies the current Closure object and binds the specified $this object to the class scope.
Add macros to classes in Laravel
Many classes in Laravel use the trait macros
Illuminate\Filesystem\Filesystem::class
- We just need to get to
App\Providers\AppServiceProvider::register
Method to add macros (you can also create a new service provider to handle it)
- Then add a test route to test our new method
- Then open the browser and run it, and you’ll see that our code will run and output the results
The original address