-1

I know I cannot extend from two classes, but what is the alternative for my case?

I am using a base class, parser, that parser the page of my CMS. This class contains all the basic functions needed to filter the data retrieved from the database and rendering it into a HTML page.

All the other classes NEED the parser, because without it they don't work.

I have 2 modes:

  1. Inside the CMS
  2. Outside the CMS

Inside CMS

If inside the CMS, userdata and other additional data is loaded into the class.

Outside CMS

If outside the CMS, only the necessary data to render the page is loaded, this is the default way for displaying pages to people who visit the site.


Modules

A page can be used to display default data/elements, but it can also be used to display data from a module (e.g. a calender page). If this is the case, additional data needs to be loaded into the parser object, and thus I have 4 different use cases:

  1. parser mode
  2. cmsParser mode (inside CMS)
  3. moduleParser mode (parser with module data loaded)
  4. cmsModuleParser mode (both)

I have the following [extremely simplified] classes:

class parser {
   protected $oDataSource1;
   protected $oDataSource2;
   protected $oDataSource3;

   //... 
   public function filterData() {
       //.. Search through the data sources and return filtered data
   }
}

class cmsParser extends parser {
   protected $sUser_name;
   protected $iUser_id;
   protected $sUserLanguage;

   ///.. some functions here that are called only within the CMS
}

class moduleParser extends parser {
    protected $mModuleData;
    //.. Do something with this moduleData;
}

class cmsModuleParser extends ?? {
    //... Get functions from the cmsParser + module functions
}

The only solution I can come up with is using a trait that the moduleParser and the cmsModuleParser both use? This is not optimal IMO, because I still have to add duplicate variables etc.

I don't want duplicate code, of course, so how do I solve this puzzle?

PIDZB
  • 903
  • 1
  • 15
  • 38
  • Any reason why the parser loads the data into itself and doesn't just return a data-object that can be used by other classes? That would allow you to just pass a parser-instance to any class that needs it, and let it parse as many things as that class needs parsed. – Erik Jul 18 '16 at 07:58
  • The main reason for using the parser class is to encapsulate the data and have a central point to get my data on later points in the parsing process. You can include PHP snippets in a page, and by keeping all the data in a parser class, I immediately know what I can or cannot use. – PIDZB Jul 18 '16 at 08:02
  • You can extend once class, but implement multiple class. Maybe you should try using interfaces. – Ajit Kumar Singh Jul 18 '16 at 08:04
  • That might be the only workable solution I guess – PIDZB Jul 18 '16 at 08:05
  • If you feel like that's the only solution then I will post it as an answer!! – Ajit Kumar Singh Jul 18 '16 at 08:10
  • I would split the parser data into a separate class. That will also solve your problem I think. – Erik Jul 18 '16 at 08:11
  • @AjitKumarSingh I am trying to use interfaces now, but there is one big issue: interface functions and variables can only be defined public... I rather not want to do that.. – PIDZB Jul 18 '16 at 08:11
  • You can create private variables in abstract class. Here is the link for more details http://stackoverflow.com/questions/28540620/can-you-declare-an-attribute-private-within-an-abstract-class – Ajit Kumar Singh Jul 18 '16 at 08:14
  • @Erik, The parser is made to retreive multiple data objects and output the data after filtering it on certain aspects. So if I use a different class to encapsulate the data, another layer is added to the system, which complicates the getting and setting of data – PIDZB Jul 18 '16 at 08:15
  • @AjitKumarSingh, this creates the same issue, I can only extend one abstract class. – PIDZB Jul 18 '16 at 08:16
  • @AjitKumarSingh http://php.net/manual/en/language.oop5.abstract.php – PIDZB Jul 18 '16 at 08:19
  • @Abayob Well then, i have no other solution. Unless it was C++. With PHP you can extend one and implement Many. Only if the security for that piece of code is not a priority, I would suggest Interfaces. – Ajit Kumar Singh Jul 18 '16 at 08:24
  • Well, thank you all for your comments, I can work with this. I am going to create a module class that gets instanciated on construction and an interface to access the module object from outside the class. Thanks for the input all!! – PIDZB Jul 18 '16 at 08:33
  • I think you have your relationship between classes a bit wrong. By inheriting from your parser you're implying that your subclasses are parsers too (is_a relationship). But if you think about it, a CMS page is not a parser. It might need a parser to provide it with a service (has_a relationship). If you make the parser a stand-alone class and inject an instance of it into your CMS class then you will break the is_a relationship. – GordonM Jul 18 '16 at 08:33
  • @GordonM, well it is definitely an extension of the parser, but I guess that my example is to basic to describe this to you. The CMS parser takes over some parser functions and adds validation and data to it. – PIDZB Jul 18 '16 at 08:35

2 Answers2

1

Have you heard the phrase favor composition over inheritance? Sometimes, Composition would pay more than Inheritance. In this case, the cmsModuleParser extends cmsParser and then injects the moduleParser via the constructor as a Dependency. However, you also have a getter and a setter for the $moduleParser Property. So if you don't need it to be injected via the Constructor, you may still remove it from the constructor and use the setModuleParser() Accessor Method instead. Alternatively, you may also Program To Interface. Below is a code that would illustrate both concepts:

COMPOSITION

    <?php
        class parser {
            protected $mData;

            //...
            public function filterData() {
                //.. filter the data here and return it
            }
        }

        class cmsParser extends parser {
            protected $sUser_name;
            protected $iUser_id;
            protected $sUserLanguage;

            ///.. some functions here that are called only within the CMS
        }

        class moduleParser extends parser {
            protected $mModuleData;
            //.. Do something with this moduleData;
        }

        // HERE YOU EXTEND THE cmsParser 
        // AND THEN USING DI, INCLUDE THE moduleParser
        class cmsModuleParser extends cmsParser {
            /**
             * @var ModuleParser
             */
            protected $moduleParser;
            //... Get functions from the cmsParser + module functions

            public function __construct(moduleParser $moduleParser) {
            }

            /**
             * @return moduleParser
             */
            public function getModuleParser() {
                return $this->moduleParser;
            }

            /**
             * @param moduleParser $moduleParser
             * @return cmsModuleParser
             */
            public function setModuleParser($moduleParser) {
                $this->moduleParser = $moduleParser;

                return $this;
            }

        }

PROGRAMMING TO INTERFACE

    <?php
        interface iParser{
            public function filterData();
            public function renderView();
            public function saveData();

        }

        class parser implements iParser{
            protected $mData;

            //...
            public function filterData() {
                //.. filter the data here and return it
            }

            public function renderView(){}
            public function saveData(){}
        }

        class cmsParser extends parser {
            protected $sUser_name;
            protected $iUser_id;
            protected $sUserLanguage;

            ///.. some functions here that are called only within the CMS
        }

        class moduleParser extends parser {
            protected $mModuleData;
            //.. Do something with this moduleData;
        }


        class cmsModuleParser implements iParser {
            //... Get functions from the cmsParser + module functions

            public function __construct() {
            }

            public function filterData(){}
            public function renderView(){}
            public function saveData(){}

        }
Poiz
  • 7,611
  • 2
  • 15
  • 17
  • But the moduleParser and the cmsParser both are extensions of the parser class. Doesn't this load in duplicate code into the memory? – PIDZB Jul 18 '16 at 08:14
  • @Abayob In that case, you may simply remove the constructor-based injection and then manually call `setModuleParser` only when it is necessary. Here is a typical proof of the phrase: *favor composition over inheritance*.... Alternatively, you may decide not to extend no class but then use composition for the `moduleParser` and `cmsParser`. That way your `cmsModuleParser` class is **composed** of both classes yet inheriting from neither. Another way is to `Program to Interfaces` – Poiz Jul 18 '16 at 08:19
0

First of all you have to Know the fact that all classes extends a single class considered as a bad design and indicated that you have bad understanding about OO concepts.

Inheritence is somethig like A is a B. That is, all your classes IS A cmsParser. This of course is wrong and will cause problems like these you are reffering to. There are many examples where inheritence is usefull like

  • Woman is a human
  • Toyota is a Car
  • PageController is a Controller (more common scenario at MVC applications)

But inheritance like

  • Car is an Engine
  • Womans is a Leg
  • cmsModuleParser is Parser

is not a good design and the issue is obvious.

So how you adress your problem? Lets stick to Car case. First define the Car class

class Car extends Engine{

    public function reverse(){
    $this->startEngine();//Parent's method.
    .....
    }

instead of the above you can do something like this

class Car{
protected $Engine;
protected $Driver;

public function __construct($Engine, $Driver){
$this->Engine = $Engine;
$this->Driver = $Driver;
}

public function reverse(){
$this->Engine->start();
$this->Driver->LookBehindYou();
...
}

} 

With that way using contstructor injection you have loose coupling between your depedencies and you have a better design.

Search at google about Depedency Injection and Composition over inheritance consepts.

dios231
  • 714
  • 1
  • 9
  • 21