Lucas
Lucas

Reputation: 10646

Making a method in PHP automatically run some code after being called. Possible?

I'm not really sure if what I am looking for has a name, so it has been a bit difficult for me to search for, so I apologise if what I am asking has already been answered here before.

The situation is that I have an abstract class which has, of course, many other classes which extend it.

The abstract method in the abstract class is called run() and all of the extending classes define this method.

My problem is that I want to call some common code after the run() method has been called, but I want to know if there is a better way to do this.

I could of course just paste my common code into each and every extending class's run() method, but if I do that then it would be a lot of work for me to make simple changes from that point onward.

I could also put my common code into a method in the parent class and then call if from the extending class's run() method with $this.

But my question is, is there a better way to do this, or do I have to either use the $this keyword or paste in the code into each class's?

Here is a small example of what I want to do with my current idea:

abstract class Parent_Class {
    public abstract function run();

    protected function common_code() {
        // Common code here
    }
}

class Child_Class {
    public function run() {
        // Code here
        // I want some common code to run after the run method has been called

        $this->common_code(); // Is this the best way to do it?
    }
}

Or is it possible to somehow tell the class that when the run() method has been called to automatically run the common_code() method?

Upvotes: 3

Views: 6878

Answers (3)

nulll
nulll

Reputation: 1603

Based on Bradley Forster answer, you can define the method as protected, so when it's called from outside the class, you can intercept the event with php magic __call metod because

__call() is triggered when invoking inaccessible methods in an object context.

and then you can execute that method from the __call function

class A {

  public function __call($method, $args){

     if(!method_exists($this, $method))
       throw new Exception("Call to undefined method ".__CLASS__."::$method()");

     echo "doing something BEFORE function 'run' execution\n";

     $retval = call_user_func_array(array($this, $method), $args);

     echo "doing something AFTER function 'run' execution\n";

     return $retval;
  }

  protected function run() {
      echo "function 'run' executed\n" ;
  }
}

$a = new A;
$a->run();

Upvotes: 2

Bradmage
Bradmage

Reputation: 1231

The answer given by Amber is nice and simple. But requires you to put your data in run() but call start(). Here is an alternative that allows you to have your code $a->run() in all your scripts, and $a->run() with your common code, but encapsulated in a namespace.

File hooks.php

<?php
// File: hooks.php
namespace hook;

class processHooks {
    public function __construct() { /* Fatal Error without constructor */ }

    protected function processHooks($_c, $method, $args) {
        /* Swap the next two callbacks to run your custom code after */
        call_user_func_array("\hook\\{$_c}::{$method}", $args);
        return call_user_func_array(array($_c,$method), $args);
    }
}

class A {
    public function foo() {
        echo 'Doing code stuff BEFORE calling child function....<br>';
    }
}

File regular_file.php

<?php
// File: regular_file.php
include "hooks.php";

class A extends \hook\processHooks {

    /* All that is required is this function
     * and the function with common code to be protected
     */
    public function __call($method, $args) {
        self::processHooks(__CLASS__, $method, $args);
    }

    protected function foo() {
        echo 'Method is called....<br>';
    }
}

$a = new A();
$a->foo();

This works beacause method foo of class A in regular_file.php is protected, so it's not callable outside the class, so calling it triggers PHP magic method __call

__call() is triggered when invoking inaccessible methods in an object context.

Upvotes: 1

Amber
Amber

Reputation: 526613

A far simpler way to do this would to simply have a third method which calls run() and then calls common_code(). Subclasses can then override run() all they want.

abstract class Parent_Class {
    public abstract function run();

    protected function common_code() {
        // Common code here
    }

    protected function start() {
        $this->run();
        $this->common_code();
    }

}

class Child_Class {
    public function run() {
        // Code here
    }
}

Upvotes: 2

Related Questions