phpphp-7

Call function before each function


Is it possible to create a function that going be automatically called when each function are called? I want that the result do this:

before_functions()
function_1()
before_functions()
function_2()
before_functions()
function_3()

But i want a file wit the function:

function before_functions(){} -> call before each function

and another file where I call functions:

function_1()
function_2()
function_3()

But I won't call the before_functions in each function..


Solution

  • There are different approaches to solve your problem. Some of them are mentioned in the comments already. Let us take the simplest approaches for solving your issue.

    The magic method __call()

    As lovelace said in the comments, there is already a simple solution for your problem stated in another stack overflow article. It uses PHPs own magic method __call(). Let 's have a look at a simple example.

    class Foo
    {
        protected function before() : void
        {
            echo "before";
        }
    
        protected function after() : void
        {
            echo "after";
        }
    
        public function __call($method, $arguments)
        {
            if (method_exists($this, $method)) {
                $this->before();
                return call_user_func_array(array($this, $method), $arguments);
            }
        }
    }
    
    // Testing
    $class = new Foo();
    $class->after(); // echoes "before->after"
    

    As you can see the magic method __call provides proper handling for your purpose. First it checks, if the called method exists and after that it executes the before method before the called method is executed. The before method is called automatically, when you call a class method, that exists.

    The callback approach

    As also mentioned in the comments a callback function could be a possible solution without handling class instances. Let 's have a look at the callback example.

    $callback = function()
    {
        echo "before->";
    }
    
    function foo(callable $callback, $bla)
    {
        $callback();
        echo $bla;
    }
    
    // example code
    foo($callback, 'go and make some coffee');
    // output: "before->go and make some coffee"
    

    This approach is even simpler as using the __call method, because you need just a callable function as parameter for your functions. Easy, hm?

    The observer pattern

    The observer pattern came with the standard php library in php5 and is more complex. I guess way too complex for your use case. To keep it complete, here 's a short example, how the observer pattern could be a usable solution to your issue.

    class Group implements SplSubject
    {
        /**
         * persons in this group
         * @var \SplObjectStorage
         */
        protected $persons;
    
        /**
         * observer active in this group
         * @var \SplObjectStorage
         */
        protected $observers;
    
        /**
         * the person, which actually speaks
         * @var Person
         */
        protected $speaker;
    
        /**
         * Initializes our class members and sets an observer for this group
         */
        public function __construct() 
        {
            $this->persons = new \SplObjectStorage();
            $this->observers = new \SplObjectStorage();
    
            $onSpeakObserver = new OnSpeakObserver($who, $what);
            $this->attach($onSpeakObserver);
        }
    
        public function add(Person $person) {
             $this->persons->attach($person);
        }
    
        public function speak(Person $who, $what) {
            echo $who . " says: " . $what . "<br>";
    
            $this->speaker = $who;  
            $this->notify();
        } 
        
        public function getSpeaker() {
            return $this->speaker;
        }
    
        public function getGroup() {
            return $this->persons;
        }
    
        public function attach(\SplObserver $observer) {
            $this->observers->attach($observer);
        }
    
        public function detach(\SplObserver $observer) {
            $this->observers->attach($observer);
        }
    
        public function notify() {
            foreach ($this->observers as $observer) {
                 $observer->update($this);
            }
        }
    }
    

    This is our basic class called group, which should be observed. A class, which should be observed, is always called the "subject". A subject takes one ore more observers, which are called by the notify method of the subject. A group consists of several people and a speaker. There is always one speaker and the other persons are listeners, which can react, when the speaker says something. For the reaction of the listeners we need an observer. This observer listens, if the speaker says something. The observer is added directly in the constructor of the group.

    This class implements the \SplSubject interface, which brings us the methods attach, detach and notify for handling the observer, we attach to the group. Next we need the classes for a person and the observer itself.

    class Person 
    {
        protected $name = '';
    
        public function __construct(string $name) : void
        {
            $this->name = $name;
        }
    
        public function __toString() : string
        {
            return $this->name;
        }
    }
    

    A simple person with a name.

    class OnSpeakObserver implements \SplObserver 
    {
        public function update(\SplSubject $subject) 
        {
            foreach ($subject->getGroup() as $person) {
                if ($person !== $subject->getSpeaker()) {
                    echo $person . " says: me!<br>";
                }
            }
        }
    }
    

    This is our observer, which implements the native \SplObserver interface, which forces us to use the update method. This method is called every time, when a person in the group speaks.

    With this the classes, we have a simple observer pattern. In this simple example the observer forces a reaction every time a person in a group says something.

    // open a group (the subject, which is observed)
    $friends = new Group();
    
    // add some persons to our group
    $sarah = new Person('Sarah');
    $friends->add($sarah);    
    
    $nicole = new Person('Nicole');
    $friends->add($nicole);
    
    $marcel = new Person('Marcel');
    $friends->add($marcel);
    
    $steffen = new Person('Steffen');
    $friends->add($steffen);
    
    // Marcel says ...
    $friends->speak($marcel, 'Who likes the observer pattern?');
    
    // result:
    // Marcel says: Who likes the observer pattern?
    // Sarah says: me!
    // Nicole says: me!
    // Steffen says: me!
    

    You could transfer this little example to solve your problem. An observer could listen on the execution of your functions and every time one of your functions is called, the observer could execute another function before. As shown in this example, the observer does nothing more than executing, after a person in a group has said something. Same goes for your issue. It all depends on when the notify method of the subject is called.

    If you have any questions feel free to ask.