4

There is a PHP library of classes (generated from a WSDL) that cannot be changed. These look like the below for simplicity.

class System extends \SoapClient
{
    public function __construct(array $options = array(), $wsdl = 'http://example.com/system?wsdl')
    {
       // Do configuration and include required classes.

       parent::__construct($wsdl, $options);
    }
}

class Report extends \SoapClient
{
    public function __construct(array $options = array(), $wsdl = 'http://example.com/report?wsdl')
    {
       // Do configuration and include required classes.

       parent::__construct($wsdl, $options);
    }
}

Custom code needs to do some configuration (e.g. setting some SOAP headers and the SOAP version) whenever a System or Report object is required. The current implementation is as below.

class mySystem extends System
{
  public function __construct($wsdl, $configuration) {

    $options = array(
      'soap_version' => SOAP_1_2,
    );

    // Do more stuff here.

    parent::__construct($options, $wsdl);
  }
}

class myReport extends mySystem
{
  public function __construct($wsdl, $configuration) {
    parent::__construct($options, $wsdl);
  }
}

The above works but my IDE is screaming at me every time I do anything on myReport (cannot find method etc.) and I think it only works because PHP doesn't care about type very much, and any actual methods are passed to the SoapClient->__doRequest() method. Any over, more concrete, thoughts on why this is bad are welcome.

I want to refactor this to a better design. The first thing is that mySystem correctly extends System but myReport should also extend Report. This means that the constructor code needs to be duplicated in mySystem and myReport. Is there a way around this?

My thoughts so far:

  • Have a "setup" trait that both mySystem and myReport use and call the appropriate "setup" method in their constructors. The major blocker here is PHP version on some systems is 5.3 although there are plans to upgrade these.
  • Have a factory method that can instantiate the required classes. Not 100% sure how this would look though.
2

Personally I would be using factory methods to create each of these objects and would forget about the additional inheritance you have designed.

The form of this would be a ReportFactory class and a SystemFactory class that would have their own creation methods. In the creation methods you would instantiate the required class, and then perform the initialisation specific to the object within the factory method.

Where there is commonality in the configuration requirements (most likely at the SOAP level) then that work can be farmed off to a common class for handling that part of the configuration.

It would appear that the additional level of inheritance you have added provides no real value, and there is no distinction between the inherited value and the base class, apart from the differing initialisation semantics. Therefore I think that approach would need to be justified more than it currently is if you were to continue down that path.

  • Thanks for the thoughts. I need the inheritance as the base classes can't be changed, and mostly don't need to be changed so extending them and overriding what I need makes sense in this case. – Malks Apr 7 '16 at 5:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.