views:

1096

answers:

5

The "Question":
Is there a way in PHP to overwrite a method declared by one interface in an interface extending that interface?

The "Example":
I'm probably doing something wrong, but here is what I have:

interface iVendor{
    public function __construct($vendors_no = null);
    public function getName();
    public function getVendors_no();
    public function getZip();
    public function getCountryCode();
    public function setName($name);
    public function setVendors_no($vendors_no);
    public function setZip($zip);
    public function setCountryCode($countryCode);
}

interface iShipper extends iVendor{
    public function __construct($vendors_no = null, $shipment = null);
    public function getTransitTime($shipment = null);
    public function getTransitCost($shipment = null);
    public function getCurrentShipment();
    public function setCurrentShipment($shipment);
    public function getStatus($shipment = null);
}

Normally in PHP, when you extend something, you can overwrite any method contained therein (right?). However, when one interface extends another, it won't let you. Unless I'm thinking about this wrong... When I implement the iShipper interface, I don't have to make the Shipper object extend the Vendor object (that implements the iVendor interface). I just say:

class FedEx implements iShipper{}

and make FedEx implement all of the methods from iVendor and iShipper. However, I need the _construct functions in iVendor and iShipper to be unique. I know I could take out the $shipment = null, but then it wouldn't be as convenient to create Shippers (by just passing in the vendorsno and the shipment while instantiating).

Anyone know how to make this work? My fallback is to have to set the shipment by calling $shipper->setShipment($shipment); on the Shipper after I instantiate it, but I'm hoping for a way to get around having to do that...

A little more explanation for the curious:
The FedEx Object has methods that go to the FedEx site (using cURL) and gets an estimate for the Shipment in question. I have a UPS Object, a BAXGlobal Object, a Conway Object, etc. Each one has COMPLETELY different methods for actually getting the shipping estimate, but all the system needs to know is that they are a "shipper" and that the methods listed in the interface are callable on them (so it can treat them all exactly the same, and loop through them in a "shippers" array calling getTransitX() to find the best shipper for a shipment).

Each "Shipper" is also a "Vendor" though, and is treated as such in other parts of the system (getting and putting in the DB, etc. Our data design is a pile of crap, so FedEx is stored right alongside companies like Dunder Mifflin in the "Vendors" table, which means it gets to have all the properties of every other Vendor, but needs the extra properties and methods supplied by iShipper).

+2  A: 

My object-oriented PHP experience is limited, but do you really need to define the constructor in the interface definitions? That is generally not the practice in Java, anyway.

Ryan Ahearn
A: 

@Ryan: Hrm... How would you do this in Java? Would you just drop the constructor from the interface definitions and keep the rest? That's not a bad idea since I'm not technically enforcing them passing anything through in the constructor method since it defaults to null if they don't...

cmcculloh
+3  A: 

@cmcculloh Yeah, in Java you don't define constructors in Interfaces. This allows you to both extend interfaces and also have a class that implements multiple interfaces (both allowed, and very useful in many cases) without worrying about having to satisfy a particular constructor.

EDIT:

Here's my new model:

A. Each interface no longer has a constructor method.
B. All Shippers (UPS, FedEx, etc) now implement iShipper (which extends iVendor) and extend the abstract class Shipper (which has all common non-abstract methods for shippers defined in it, getName(), getZip() etc).
C. Each Shipper has it's own unique _construct method which overwrites the abstract __construct($vendors_no = null, $shipment = null) method contained in Shipper (I don't remember why I'm allowing those to be optional now though. I'd have to go back through my documentation...).

So:

interface iVendor{
    public function getName();
    public function getVendors_no();
    public function getZip();
    public function getCountryCode();
    public function setName($name);
    public function setVendors_no($vendors_no);
    public function setZip($zip);
    public function setCountryCode($countryCode);
}

interface iShipper extends iVendor{
    public function getTransitTime($shipment = null);
    public function getTransitCost($shipment = null);
    public function getCurrentShipment();
    public function setCurrentShipment($shipment);
    public function getStatus($shipment = null);
}

abstract class Shipper implements iShipper{  
    abstract public function __construct($vendors_no = null, $shipment = null);  
    //a bunch of non-abstract common methods...  
}

class FedEx extends Shipper implements iShipper{  
    public function __construct($vendors_no = null, $shipment = null){
        //a bunch of setup code...
    }
    //all my FedEx specific methods...
}

Thanks for the help!
ps. since I have now added this to "your" answer, if there is something about it you don't like/think should be different, feel free to change it...

Ryan Ahearn
+1  A: 

My first thoughts where the same as Ryans, I don't think interfaces should have constructors.

Dave Marshall
A: 

You could drop off the constructor and just put them in each individual class. Then what you have is each class has its own __construct, which is probably the same depending on if it is a shipper or vendor. If you want to only have those constructs defined once I don't think you want to go down that route.

What I think you want to do is make an abstract class that implements vendor, and one that implements shipper. There you could define the constructors differently.

abstract class Vendor implements iVendor {
    public function __construct() {
        whatever();
    }
}

abstract class Shipper implements iShipper {
    public function __construct() {
        something();
    }
}
mk