Original link: learnku.com/laravel/t/4… For discussion, head to the professional Laravel developer forum: learnku.com/Laravel
Laravel takes PHP to a whole new level and provides you with a great development experience (DX) for your next project. Therefore, some people call it “magic”.
Today, I’m going to show you one of Laravel’s techniques, the magic method.
What is magic method?
It is important to understand that the magic method is not unique to Laravel, but can be used in any PHP application. Laravel happens to have some of the most interesting magic method use cases.
Magic methods are methods that can be used in any class declared in PHP and provide methods to implement additional functionality in the class.
Here’s a good definition:
The magic method is never called by the programmer — in fact, PHP will call it in the background. That’s why they’re called “magic” methods — because they’re never called directly, they allow programmers to do some very powerful things.
There are 15 methods of magic:
class MyClass
{
public function __construct() {}
public function __destruct() {}
public function __call() {}
public function __callStatic() {}
public function __get() {}
public function __set() {}
public function __isset() {}
public function __unset() {}
public function __sleep() {}
public function __wakeup() {}
public function __toString() {}
public function __invoke() {}
public function __set_state() {}
public function __clone() {}
public function __debuginfo() {}}Copy the code
If you’ve done some object-oriented programming in PHP, then you know the __construct method, which is a magic method. So you’ve been using magic methods.
You will also notice that all magic methods are prefixed with “__”.
Today, we won’t delve into these methods, just the interesting ones used throughout the Laravel codebase. If others are interested, feel free to check out the documentation below at 👇
PHP: Méthodes magiques – Manual
How does Laravel use the magic method
__get()
The models in Laravel are very special. Instead of storing attribute data as direct attributes of the class, they store it in the protected $Attributes property, which is a related array of all the data held by the model.
Let’s look at the difference between a simple PHP class and a Laravel model that accesses properties.
<? PHP /** * the NormalUser class in PHP (not Laravel) will just be a class with the above attributes */ class NormalUser {public$firstName = 'Alice';
}
$normalUser = new NormalUser;
$normalUser->firstName; / / returns'Alice'
Copy the code
<? php namespace App; use Illuminate\Database\Eloquent\Model; /** * a user class in Laravel */ class LaravelUser extends Model {/** * Note that we store all property data in a single array */ protected$attributes = [
'firstName'= >'Alice',]; }$laravelUser = new LaravelUser;
$laravelUser->firstName; // still return'Alice'
Copy the code
As you can see, the PHP and Laravel classes above behave exactly the same.
In the Laravel example, however, attributes are not stored like normal PHP, but are concentrated in a single attribute called $Attributes. We still manage to access the right data, but how?
All of this is possible because of the _get magic method. Let’s try implementing a simple example for ourselves.
<? PHP class NormalUser {/** * Declare attributes as in Laravel */ protected$attributes = [
'firstName'= >'Alice',]; /** * the __get function takes an argument * it will be the name of the property you want to access * in this case it is$key = "firstName"
*/
public function __get(string $key)
{
return $this->attributes[$key]; }}$normalUser = new NormalUser;
$normalUser->firstName; // will return'Alice'
Copy the code
We did it! 🎉
Note that the magic method _get is called only if no property with a matching name is found in the class. This is a fallback method that is called when PHP cannot find the property it is accessing in the class. Therefore, in the following example, the magic method _get is not called at all.
<? php class NormalUser { public$firstName = 'Bob';
protected $attributes = [
'firstName'= >'Alice',]; publicfunction __get($key)
{
return $this->attributes[$key]; }}$normalUser= new NormalUser; /** * The magic method __get */ is not called in this example because this property is present in the class and will return Bob *$normalUser->firstName;
Copy the code
There’s a lot more going on behind the scenes. If you want to learn more about exactly how Laravel’s model uses __get, you can check out the source code below.
laravel/framework
__set()
The magic method _set is used when the property you are trying to set is not declared in the class. Let’s look again at the differences between the Normal PHP class and the Model model in Laravel.
<? php class NormalUser { public$firstName = 'Alice';
}
$normalUser = new NormalUser;
$normalUser->firstName = 'Bob';
$normalUser->firstName; // Will return 'Bob'
Copy the code
<? php namespace App; use Illuminate\Database\Eloquent\Model; class LaravelUser extends Model { protected$attributes = [
'firstName'= >'Alice',]; }$laravelUser = new LaravelUser;
$laravelUser->firstName = 'Bob';
$laravelUser->firstName; // Will return 'Bob' as well
Copy the code
As we can see, in this example we are still trying to influence Bob’s value, which doesn’t actually exist in the class but is in the $Attributes attribute. Let’s try using the magic method __ set
<? php class NormalUser { public$attributes = [
'firstName'= >'Alice',]; /** * The magic method __set receives the$name you want to affect the value on
* and the value
*/
public function __set($key.$value)
{
$this->attributes[$key] = $value; }}$normalUser = new NormalUser;
$normalUser->firstName = 'Bob';
/**
* As we don't have the __get magic method define in this example for simplicity sake, * we will access the $attributes directly */ $normalUser->attributes['firstName']; // Will return 'Bob'
Copy the code
Now let’s get started! We have successfully implemented the basic usage of the __ get and __ set magic methods in Laravel! They can do it with just a few lines of code!
Keep in mind that these magic methods are as simple as possible without going into too much detail, because there are more to them than just use cases, and if you’re curious about how it works, I invite you to explore some for yourself! (Feel free to contact me on Twitter if you have any questions.)
Again, if you want to dig deeper, link to the source code here
laravel/framework
Let’s move on to the last and most interesting thing! 🙌
__call()
 & __callStatic()
__call() is called when the called method is not found in the class. In Laravel, the magic method method makes macros possible in PHP.
I won’t go into all the details of macros, but if you’re interested, here’s a great article explaining how to use them in Laravel applications 👇
The Magic of Laravel Macros
Let’s try to see how to write a simple macro example.
<? php class NormalUser { public$firstName = 'Alice';
public $lastName = 'Bob';
}
$normalUser = new NormalUser;
$normalUser->fullName(); // Because there is no declaration"fullName"Method, which will throw an errorCopy the code
Using __call, we can define an array of closure functions that can be programmatically added to the application at development time.
<? php class NormalUser { public$firstName = 'Alice';
public $lastName = 'Bob'; /** * initialize our macro to an empty array, then assign */ public static$macros= []; /** * Defines a method to add a new macro * the first argument is the name of the macro we want to define * the second argument is the closure function that will be executed when the macro is called */ public staticfunction addMacro($name.$macro) {
static::$macros[$name] = $macro; } / * * *"__call"Accepts two arguments, *$nameIs the name of the function being called, such as "fullName" *$argumentsIs all the arguments passed to the function. Here we use an empty array because our function does not pass arguments */ publicfunction __call(string $name, array $arguments/** * get macro */ by name$macro = static::$macros[$name]; /** * then execute macros with arguments * Note: Before calling, we need to bind the closure to"$this"So that the macro methods execute */ in the same contextreturn call_user_func_array($macro->bindTo($this, static::class), $arguments); }}$normalUser = new NormalUser;
$normalUser->fullName(); // This is broken because we have neither defined the "fullName" macro nor the "fullName" method. /** * NormalUser::addMacro('fullName'.function () {
return $this->firstName.' '.$this->lastName;
});
$normalUser->fullName(); // Now, return "Alice Bob"Copy the code
Macros are a bit more complicated than that, but we managed to create a simple working version of the macro using the __call magic method.
__callStatic and __call are exactly the same except for static methods.
If you want to dig a little deeper for yourself, here’s the macro feature source code.
laravel/framework
conclusion
So, coders, it’s true that Laravel feels magical when you first use it, but by digging into the source code, you’ll understand how the magic works behind the scenes.
Like magic in real life, things don’t happen for no reason, especially in code. There’s always a line of code running behind the scenes, you just need to find it.
Original link: learnku.com/laravel/t/4… For discussion, head to the professional Laravel developer forum: learnku.com/Laravel