Features when intercepting method calls using __call () and __callStatic () in PHP

Prologue


My experiments with the Object-Oriented Paradigm helped write this article.
Actually, this article is unlikely to seem interesting to the php guru, but I’m hoping that I’m just getting to know the language and will help to bypass the pitfalls. The article does not claim to be a manual on OOP, but merely clarifies some points.

What are __call () and __callStatic ()?


Let's start with a simple one: you have a class that describes the methods and properties of an object (which, in principle, is logical). Imagine that you decided to turn to a non-existent method of this object. What do you get? That's right - a fatal mistake! Below is the simplest code.

DynamicMethod(); #Получаем Fatal error: Call to undefined method OurClass::DynamicMethod()
?>


In a static context, we observe a similar behavior:



So, sometimes it becomes necessary to either execute some code in the absence of the method we need, or find out which method we tried to call, or use another API to call the method we need. For this purpose, there are __call () and __callStatic () methods - they intercept the call to a nonexistent method in the context of the object and in the static context, respectively.
We rewrite our examples using "magic methods." Note: Each of these wizards takes two parameters: the first is the name of the method we are trying to call, the second is a list containing the parameters of the called method. The key is the parameter number of the called method, the value is the parameter itself:

'.$name.', но его не существует, и сейчас выполняется '.__METHOD__.'()
' .PHP_EOL; return; } public static function __callStatic($name,array $params) { echo 'Вы хотели вызвать '.__CLASS__.'::'.$name.', но его не существует, и сейчас выполняется '.__METHOD__.'()'; return; } } $Object=new OurClass; #Вы хотели вызвать $Object->DynamicMethod, но его не существует, и сейчас выполняется OurClass::__call() $Object->DynamicMethod(); #Вы хотели вызвать OurClass::StaticMethod, но его не существует, и сейчас выполняется OurClass::__callStatic() OurClass::StaticMethod(); ?>


The practical application of these two comrades depends only on your imagination. As an example, I’ll give a sketch of the implementation of the Fluent Interface programming technique (some consider this a design pattern, but the essence does not change from the name). In short, the fluent interface allows you to make chains of calls to objects (it looks something like jQuery). On a habr there are a couple of articles about the implementation of such algorithms. In a broken Russian translation, fluent interfaces sound like “fluid interfaces”:

content_storage;
	}
	public function __call($name,array $params)
	{
		$this->content_storage.=$this->_GetObject($name,$params).'
'.PHP_EOL; return $this; } } abstract class EntryClass { public static function Launch() { return new FluentInterface; } } class FluentInterface extends Manager { public function __construct() { /** * Что-нибудь инициализируем */ } public static function _GetObject($n,array $params) { return $n; } } echo $FI=EntryClass::Launch() ->First() ->Second() ->Third(); /* Выведет First Second Third */ ?>


Do you seem to want to tell us something about the interception features?


I will definitely tell you. Sitting yesterday at the computer, I decided to systematize my knowledge of PHP.
I sketched something like this piece of code (in the original it was slightly different, but reduced it for the article, because the rest did not carry a semantic load for this problem):



Updated the page. My surprise knew no bounds. I immediately ran on php.net to watch the manual.
Here is an excerpt from the documentation
public mixed __call ( string $name , array $arguments )
public static mixed __callStatic ( string $name , array $arguments )

In the context of an object, when invoking inaccessible methods, the __call () method is called.
In a static context, invoking inaccessible methods calls the __callStatic () method.

For a long time I could not understand what the problem was. PHP Version: 5.4.13. That is, those times when calls to nonexistent methods from any context led to __call () were long gone. Why, instead of the logical Fatal Error, do I get a __call () call? I went on to explore further. Added the __callStatic () method to the abstract Base class. Refreshed the page again. The call was still addressed in __call (). Tormented for half a day, I still realized what was the problem. It turns out that PHP perceives a static context inside a class and outside it in different ways. Not understood? I will try to illustrate. Take the previous example and add one line to it:



That is, a static context - a static context of discord.
Miracles and more. When I studied "magic methods", I did not think that the name should be taken so literally.

Well, here everything becomes clear: if we add the __callStatic () method to the Base class of the above example, then instead of displaying a fatal error, PHP will execute __callStatic ().

Summary


If everything is not yet clear to you: we are talking about the fact that the call to the static method inside the class instance and the call to the static method outside the class instance are interpreted differently by the interpreter. If you change self :: Launch () to Main :: Launch (), the call context will not change. The behavior in this case will be the same. Again, I will illustrate:



The result of the article is simple: be careful (however, as always) and check the behavior when calling methods.

Postscript


I looked at the bugtracker, it turns out that I have not one problem . But the developers apparently decided that such a bug was not a bug, so they left it as it is.

Also popular now: