简体   繁体   中英

PHP/OOP - Using parent class to run child functions

I'm looking for a way to have a single base class that can be extended by several child classes, only one of which would be active at a time. A very basic example:

class API_Base {

    public $context;

    public function __construct() {
        $this->init()
    }
}

class Mailchimp_API extends API_Base {

    public function init() {
        $this->context = 'mailchimp';
        $this->enabled = false; 
    }

    public function add_contact($email_address) {
        // mailchimp API for adding contact
    }
}

class Infusionsoft_API extends API_Base {

    public function init() {
        $this->context = 'infusionsoft';
        $this->enabled = true;
    }

    public function add_contact($email_address) {
        // infusionsoft API for adding contact
    }

}

Each child initializes itself and registers as an option for the user to select. After the user has chosen which integration to use, this is saved to the database. I'd like future access to the API_Base to look something like:

$api = new API_Base();
$api->context; // should be "infusionsoft"
$api->add_contact($email_address);

So when $api->add_contact() is run, it only runs the add_contact() function for the active API integration.

Eventually I'd like to somehow use get_class_methods(); to return the capabilities of just the active API, so functions accessing the API can know what is possible (ie some API's support email lists while others don't, or support creating custom fields, etc.).

I've had some success with calling parent::set_context($context); from the enabled class, but I still can't figure out how to get the parent to only execute the methods in the "enabled" child class.

This is not how inheritance works. Child subclasses inherit from their parent class.

To solve your problem you can add a factory method to API_Base which will create API implementation by its type:

class API_Base {
    public static function createByType($type)
    {
        switch ($type) {
            case 'mailchimp': return new Mailchimp_API();
            case 'infusionsoft': return new Infusionsoft_API();
            default: throw new \InvalidArgumentException(spintf('Invalid API type "%s"', $type));
        } 
    }
    // other methods
}

and use it like this:

$api = API_Base::createByType($user->selectedApi);
$api->context; // should be "infusionsoft"
$api->add_contact($email_address);

You can consider Abstract Class Implementation . The abstract class works as the , who ever is extending the abstract class can execute the methods it have .

abstract class Something{
       function __construct(){
        // some stuff
       }

       function my_func(){
         $this->myTest ;
       }

      abstract function my_func(); 

      }

    class Some extends Something{

      function __construct(){
        parent::__construct() ;
      }

      function my_test(){
           echo "Voila" ;
      }

    }

I got it working in a way works perfectly for me, thanks to Ihor's advice. Here's what I ended up doing:

In the main plugin file, there's a filterable function where other devs can add new integrations if they need. The first parameter is the slug (for my autoloader) and the second is the class name.

public function get_apis() {

    return apply_filters( 'custom_apis', array(
        'infusionsoft-isdk'         => 'MYPLUGIN_Infusionsoft_iSDK',
        'infusionsoft-oauth'        => 'MYPLUGIN_Infusionsoft_oAuth',
        'activecampaign'            => 'MYPLUGIN_ActiveCampaign'
    ) );

}

Each integration contains the slug and the class name. Then in my API_Base class I have this in the constructor:

class API_Base {

    public $available_apis = array();

    public $api;

    public function __construct() {

        $configured_apis = main_plugin()->get_apis();

        foreach( $configured_apis as $slug => $classname ) {

            if(class_exists($classname)) {

                $api = new $classname();
                $api->init();

                if($api->active == true)
                    $this->api = $api;

                $this->available_apis[$slug] = array( 'name' => $api->name );

                if(isset($api->menu_name)) {
                    $this->available_apis[$slug]['menu_name'] = $api->menu_name;
                } else {
                    $this->available_apis[$slug]['menu_name'] = $api->name;
                }

            }

        }

    }
}

And in my main file, after all the includes, I run:

self::$instance->api_base       = new API_Base();
self::$instance->api            = self::$instance->api_base->api;

Now I can call self::$instance->api->add_contact($email); and it will trigger whichever is the current active API.

It seems to be the best approach as this way I can spin up the API only once when the plugin loads, instead of having to create a new instance each time I want to use it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM