The power of magic methods and late static binding
Tags: webdev, php, laravel, oop
Published: 2023-06-21 14:06:03

### Table of contents

- [Introduction](#introduction)
- [Building the skeleton](#building-the-skeleton)
- [`__call()` and `__callStatic()` magic methods](#call-and-callstatic-magic-methods)
- [PHP's late static binding](#phps-late-static-binding)
- [Let's connect the dots](#lets-connect-the-dots)



## Introduction 

As with every programming language, PHP has its tricks and traps, which can be valuable to acknowledge and master. Lately, while researching with [Donato](https://dev.to/donnierich) ( author of the [second part](https://dev.to/donnierich/laravels-forwardscalls-trait-493i) of this article ) about Laravel's inner mechanics, we found some patterns that really caught our interest, so we decided to go into more detail, and here's the result.

In this article, we will talk about the PHP's `__call()` and `__callStatic()` magic methods and about the **late static binding** feature. We will see how to use them together to make an Object invoke a method from a different class Class, making our OOP coding smoother and without breaking the [SOLID principles](https://en.wikipedia.org/wiki/SOLID) .  

To explain this, we will walk through building some classes, methods, and traits, getting some gaming inspiration from the old-but-gold Minecraft, to have our friendly Steve be able to make some damages thanks to our code ;) 


## Building the skeleton

Before diving into the hard part, let's first write the skeleton of our application. 
Let's start with the Models; first of all, we will need a class called Entity, which is the basis both for our character Steve and for the enemies, and a Weapon class, so we can give our hero some help in fighting the foes ;)


**Entity.php**
```php
class Entity{

    protected $weapon;
    
    public function setWeapon(string $weaponName){
        $this->weapon  = $this->equipWeapon($weaponName);
        return $this;
    }
    
    public function getWeapon() : Weapon
    {
        return $this->weapon ?? $this->equipWeapon();
    }
    
    private function equipWeapon($weaponName = null): Weapon
    {
        return  new Weapon($weaponName);
    }
    
   

}

```


**Weapon.php**
```php

class Weapon
{
    protected $name;

    public function __construct($name = null)
    {
        $this->name = $name ?? 'Bare Hands';
    }

    public function attack():void
    {
        echo $this->name != 'Bare Hands' ? 'Attacked with '.$this->name : 'Punched';
    }
}
```

Now that we have these two classes, we come to the point. We want them to communicate, but we also know they have different tasks, roots, and responsibilities, so we don't want to endanger our structure stability by mixing different things up.
At this point we would simply write:

**index.php**
```php
$entity = new Entity();
$entity->setWeapon('branch'):
$entity->getWeapon()->attack();

//Output: Attacked with branch
```

Easy peasy, but we can do better!

## __call() and __callStatic() magic methods

So, what's so magic in these two methods?

They are triggered when an undefined method is called on the class they're defined. As you can imagine, `__callStatic()` manage undefined static methods, while `__call()` is invoked for non-static ones. This means that by using them, we can better handle wrong method calls or, what we're going to do, redirect calls to the correct class.
This can be useful when you're developing, and you want to make classes communicate easily, having a much cleaner code without mixing their functionalities. If you're building a library or a framework (like Laravel ), this helps to make the code style outstanding. Let's see how! Let's add some code to our **Entity.php** file:


**Entity.php**
```php
class Entity{

// ...
	public function __call($name, $arguments):void
	{
		if($name == 'attack')
		{	            
	            echo 'Entity attacked';
		}
	} 
    
}

```

What's happening here? We defined the `__call` magic method that, as previously said, will be invoked instead of a not defined method. The `$name` parameter is the name of the function called, and the `$arguments` is an array containing the parameters passed to it. So, inside of `__call()`, we can add some logic to control what happens depending on the method name. 
In this first simple step, we just added an `echo`, so now our index.php can evolve this way.

**index.php**
```php
$entity = new Entity();
$entity->setWeapon('branch'):
$entity->attack();

//Output: Entity attacked
```

Though this works nicely, we probably want to call the `attack()` method from our `Weapon` class. So, let's introduce an incredibly helpful Trait which we'll see in a more detailed way in the second part of this article, the `ForwardCallTrait`:

```php
trait ForwardCallTrait
{
    public function forwardCallTo($object, $method, $params)
    {
        return $object->$method($params);
    }
}
```

The trait is a significantly simplified version of a Laravel Trait, which seamlessly allows calling methods from different classes. As I said, you will explore this trait more deeply in the next part; for now, we focus on the fact that it takes both an object and a method as parameters and lets you perform the call. 
So let's make a small arrangement to our `Entity.php`.

**Entity.php**
```php
public function __call($name, $arguments):void
	{

		use ForwardCallTrait;

		if($name == 'attack')
		{
	           $this->forwardCallTo($this->getWeapon(),$name, $arguments);    
		}
	} 
    
```

Now, with `forwardCallTo()`, we are redirecting the call to the Weapon class, so the output from our `index.php` is now the expected "Attacked with branch".

So, what the `__callStatic()` method can do to improve even more our code? Let's add it to our Entity.php and check out what happens:

```php
class Entity{

//...

	public static function __callStatic($name, $arguments): mixed
	{
		return (new static)->$name($arguments);
	}
}
```

When an unknown static method is invoked on an Entity instance, the `__callStatic()` method is triggered. As for `__call()`, `__callStatic()` has the `$name` and `$arguments` parameters available. So what we're doing here is creating a new instance of the Entity class (don't worry about the `(new static)` syntax; we'll talk about it in a minute ;) ), that will be able to call a non-static method: and if it does not belong to the class, we'll end up triggering the `__call()` method, once again.
So what's the advantage of these twists and turns?
If you go back again to our `index.php` file, you can actually replace everything you wrote before with this:

```php
Entity::attack();

//Output: Attacked with bare hands
```

Pretty cool, right? As you will see in the second part, Laravel is filled up with this pattern to improve the Dev Experience and code cleaning. 

So there's only one thing left to explain if you're still wondering ;) 

## PHP's late static binding

What's this `(new static)->$name($arguments);` about? 
Let's first make one step back. We created the `Entity` Class, representing a Player, a Foe, or anything that can perform an action in our ideal Minecraft-like game.
So we will probably extend it. Let's finally make a simple empty class named after our cubey friend Steve:
```php 
class Steve extends Entity {

}
```

Now a `Steve` instance can call all methods from the Entity class. The problem is that no instance is created in a static context, so you can't use `$this` variable either to call non-static methods. That's why we need to create a new instance, and here comes the tricky part. 
If you create a new instance with `(new self)`, which could be the first thing coming to mind, the later calls will result as made from an `Entity` instance instead of  `Steve`  because, at the time of invoking the method, there's no instance created yet. Using the `(new static)` allows this thing to be changed and compute the method using runtime information. So, the result would be that when `__call()` is invoked, the class of `$this` would be `Steve` and not `Entity`.

To make this more apparent, let's make the latest few adjustments to our classes:
**Entity.php**
```php

class Entity{

//...
	 private function equipWeapon($weaponName = null): Weapon
	 {
        return  new Weapon($weaponName, get_class($this));
	 }
//...
}

```

**Weapon.php**
```php
class Weapon
{
    protected $name;
    protected $bearer;
    public function __construct($name = null, $bearer = null)
    {
        $this->name = $name ?? 'Bare Hands';
        $this->bearer = $bearer;
    }
    public function attack():void
    {
        echo $this->name != 'Bare Hands' ? $this->bearer. 'Attacked with '.$this->name : $this->bearer . 'Punched';
    }
}
```

**index.php**

```php

Steve::attack();

echo '<br>';
echo 'Wait... an enemy is approaching';
echo '<br>';
  
$skeleton = new Entity();
$skeleton->setWeapon('Diamond Sword');
$skeleton->attack();


//output:

//Steve Punched  
//Wait... an enemy is approaching  
//Entity Attacked with Diamond Sword

```

If you play around with the code ( fully available on [github](https://github.com/Giuliano1993/MagicMethodsAndLateBindingArticle)) you can see by yourself that changing `(new static)` with `(new self)` would result in both cases above having an Entity instance calling the methods. A pretty peculiar pattern, isn't it? 


## Let's connect the dots

So, we had a short but intense trip in these few lines of code. Knowing PHP magic methods and patterns can be surprisingly helpful, and even with an easy example, we showed the power of using `__call()` and `__callStatic()` methods to handle undefined calls and to address them to other classes. We also learned about PHP's late static binding, which is really worth knowing when coding a lot in OOP, to handle classes and inheritance correctly. In the second part of the article, you'll see how these patterns are applied in Laravel to ease Developer Experience and to create a fully extensible structure, allowing it to be the versatile framework it is.

I hope you enjoyed this first part. Here you can find the [second half](https://dev.to/donnierich/laravels-forwardscalls-trait-493i). ;) 

If this article was helpful or want to start a conversation, feel free to reach out in the comments or here [@gosty93](https://twitter.com/gosty93) and [@donato-riccio-wda](https://www.linkedin.com/in/donato-riccio-wda/)
We'll be happy for any feedback and ideas.
Happy Coding | _ 0