PHP method overloading
Tags: php
Published: 2023-05-18 09:29:27

This is a rant about an article on freeCodeCamp named [How Method Overloading Works in PHP](https://www.freecodecamp.org/news/method-overloading-in-php/).

It is more an emotional rant about creating readable and safe code rather than one where I angerly wave my fist at a cloud.

## My freeCodeCamp article takeaways

Many languages have method overloading because it is a part of polymophism. And PHP provides it by using the [__call method](https://www.php.net/manual/en/language.oop5.overloading.php#object.call).

Zubair provides the code, addresses some use cases, so overall it is a good article.

## The code

I'm biased because I find the __call method a garbage magic function. I only use it if there is no other way.

What i don't like about the code:

- mixed functionality, identifying the function overload and the actual code
- no consistent return
- no long term vision (what if you have ten or twenty methods you want to overload?)

You can argue it is an example to demonstrate overloading, and you are right. 
Ravavyr mentioned in the comments that the code should have the same output, and I agree. Method overloading should only handle the input, it should not have an effect on the return of the method. The change in the output is only to demonstrate there is an input difference. 

I just want to cry when I see it, so lets continue to make me happy.

## Fixing the code

```php
class SampleClass
{
	private function add2($numbers) {
		return array_sum($numbers);
	}
	
	private function add3($numbers) {
		return array_sum($numbers) > 10 ? 10 : array_sum($numbers);
	}
	
	public function __call($functionName, $arguments)
	{
		$count = count($arguments);
		$methodName = $functionName . $count;

		return method_exists(__CLASS__, $methodName) ? $this->{$methodName}($arguments) : NULL;
	}
}

# The sample execution works
$sampleObject = new SampleClass;
echo $sampleObject->add(12, 12) . PHP_EOL; // Outputs 24 
echo $sampleObject->add(12, 2, 6) . PHP_EOL; // Outputs 10
```

To clean up the __call method I used the arguments to create method names, and call the methods if they exist. 
So now the only function of the __call method is to find methods in the class.  

Nowhere in polymorphism it is mentioned that you can't use private functions to achieve overloading.

Another thing that makes me cry is not validating arguments. array_sum is a very forgiving function, but what if the code can't be so forgiving?

## Preventing errors

```php
class Numbers {
	private array $collection = [];
	
	public function __construct(...$nums) {
		$this->collection = array_filter($nums, fn($num) => is_numeric($num));
	}
	
	public function toArray() {
		return $this->collection;
	}
	
}

class SampleClass
{
	private function addOverload(Numbers $numbers) {
		$arr = $numbers->toArray();
		
		return match(count($arr)) {
			2 => array_sum($arr),
			3 => array_sum($arr) > 10 ? 10 : array_sum($arr)
		};
	}
	
	private function callMethod($functionName, $arguments) {
		$count = count($arguments);
		$methodName = $functionName . $count;
		
		if(method_exists(__CLASS__, $methodName) === FALSE) {
			return NULL;
		}
		
		return $this->{$methodName}($arguments);
	}
	
	public function __call($functionName, $arguments)
	{
		return match($functionName) {
			'add' => $this->addOverload($arguments[0]),
			'default' => $this->callMethod($functionName, $arguments)
		};
	}
}

$sampleObject = new SampleClass;
$twentyfour = new Numbers(12, 12);
$ten = new Numbers(12, 2, 6);
echo $sampleObject->add($twentyfour) . PHP_EOL; // Outputs 24 
echo $sampleObject->add($ten) . PHP_EOL; // outputs 10
```

I added a Numbers class to allow only numeric input. This change made me refactor the SampleClass. So there is now an addOverload method that switches the code based on the count.

When I look at the code I have now, there are a lot of lines that are just waste. I love the environment, so I'm going to reduce the waste.

## Only in this case

```php
# copy Numbers from previous example

class SampleClass2
{
	public static function add(Numbers $numbers) {
		$arr = $numbers->toArray();
		
		return match(count($arr)) {
			2 => array_sum($arr),
			3 => array_sum($arr) > 10 ? 10 : array_sum($arr)
		};
	}
}

$twentyfour = new Numbers(12, 12);
$ten = new Numbers(12, 2, 6);
echo SampleClass2::add($twentyfour) . PHP_EOL; // Outputs 24 
echo SampleClass2::add($ten) . PHP_EOL; // outputs 10
```

Now I am a happy camper. Bye bye __call method, no overloading because the code deviation is contained by the add method. 

## Conclusion

It is good to know the concept of method overloading, and use it when it is the best solution for the problem you are facing.

Just try to figure out if the code you write makes others and yourself happy in the future.