Devon Bessemer
Devon Bessemer

Reputation: 35337

Undefining a method or alternative

With this plugin system, there is a Utility and a Driver. The driver extends the utility, however, some drivers support different functions than others, some don't support certain functions.

My class would be something of the sort:

Class Utility {

  public function MethodOne() {
    # default MethodOne
  }

  public function MethodTwo() {
    # default MethodTwo
  }

}

class Driver extends Utility {
  # MethodTwo not supported in this driver
}

The easy thing about undefining a method would be it would allow the developer using the class to run a method_exists call to see if the driver supports the method.

Is it possible to undefine a method in an extended class or is there a good alternative?

Upvotes: 0

Views: 67

Answers (3)

tacone
tacone

Reputation: 11441

In object orientated languages, you usually cannot change the visibility of a method if not for widen it.

But while PHP does not support restricting the visibility of a method with the classical inheritance, it turns out it allows it when it comes to traits.

<?php

trait Utility {
    public function methodOne() {

    }
    public function methodTwo() {

    }
}

class Driver {
    use Utility;
    protected function methodTwo(){

    }
}

And here we go:

php > $driver = new Driver;
php > $driver->methodTwo();

PHP Fatal error:  Call to protected method Driver::methodTwo() from context '' in php shell code on line 1

Warning:

method_exists will return true no matter what the visibility is. You have to use is_callable:

php > var_dump(is_callable([$driver, 'methodOne']));
bool(true)
php > var_dump(is_callable([$driver, 'methodTwo']));
bool(false)

Good luck with your project!

Upvotes: 1

MegaAppBear
MegaAppBear

Reputation: 1220

How about this:

<?php

class Utility{

    //will hold unimplemented methods foor every child
    protected $unimplementedMethods = array();

    protected function f1(){

        //debug
        echo __FUNCTION__ . "\n";
    }

    protected function f2(){

        //debug
        echo __FUNCTION__ . "\n";
    }

    //intercept the functions
    public function __call($name, $arguments)
    {
        if (method_exists($this,$name) && !in_array($name, $this->unimplementedMethods)){
            echo "Calling method: '$name'\n";
            $this->$name();
        }
        else{
            //return error or throw exception
            echo "Method: '$name' is not implemented for this driver\n";
        }
    }
}

//driver1
class Driver1 extends Utility{

    //set your non-implemented methods
    protected $unimplementedMethods = array ('f2');

}

//driver2
class Driver2 extends Utility{

    //set your non-implemented methods
    protected $unimplementedMethods = array ('f1');
}

//init
$d1 = new Driver1();
$d2 = new Driver2();


$d1->f1(); //the ok method
$d1->f2(); //the nok method


$d2->f1(); //the nok method
$d2->f2(); //the ok method  
?>

Output:

Calling method: 'f1'
Method: 'f2' is not implemented for this driver
Method: 'f1' is not implemented for this driver
Calling method: 'f2'

Upvotes: 1

MasterOdin
MasterOdin

Reputation: 7886

You can't undefine a parent function within a child, and it's not a great way to use inheritance. Any child class should be able to use all the parent functions. (Otherwise, why bother extending?)

Another solution is to just define the functions you need in each specific driver while Utility just contains common functions (which is the easiest) that all drivers would be able to use, and then driver specific functions inside the drivers.

Class Utility {

    public function MethodOne() {
        # default MethodOne
    }

    public function MethodTwo() {
        # default MethodTwo
    }

}

class Driver extends Utility {
    public function MethodThree() {

    }
}

class Driver2 extends Utility {
    public function MethodFour() {

    }
}

This shows that Driver and Driver2 have different implemented functions than each other while still both having the methods available in Utility.

A final solution (if you wanted to force functions to be in the drivers otherwise throw an error) would be to implement multiple interfaces each bundling some number of functions together:

Class Utility {

    public function MethodOne() {
        print "MethodOne";
    }

    public function MethodTwo() {
        print "MethodTwo";
    }

}

interface inter1 {
    public function MethodThree();
}

interface inter2 {
    public function MethodFour();
}

class Driver extends Utility implements inter1 {
    public function MethodThree(){
        print "MethodThree";
    }    
}

class Driver2 extends Utility implements inter2 {
    public function MethodFour() {
        print "MethodFour";
    }
}

Both solutions achieve the same thing, but Driver has to implement MethodThree in the interface solution:

$d = new Driver();
print "Methods for Driver:\n";
foreach (array("MethodOne","MethodTwo","MethodThree","MethodFour") as $k) {
    $p = method_exists($d, $k) ? 'true' : 'false';
    print "\t ".$k.": " . $p ."\n";
}

$d = new Driver2();
print "Methods for Driver2:\n";
foreach (array("MethodOne","MethodTwo","MethodThree","MethodFour") as $k) {
    $p = method_exists($d, $k) ? 'true' : 'false';
    print "\t ".$k.": " . $p ."\n";
}

Which outputs:

Methods for Driver:
    MethodOne: true
     MethodTwo: true
     MethodThree: true
     MethodFour: false
Methods for Driver2:
    MethodOne: true
     MethodTwo: true
     MethodThree: false
     MethodFour: true

If you wanted Driver2 to have MethodThree, then you would write:

class Driver2 extends Utility implements inter2, inter1 {
    public function MethodFour() {
        print "MethodFour";
    }

    public function MethodThree() {
    }
}

Upvotes: 1

Related Questions