-2

EDIT: As requested by @hakre, here is a simplified version of the parent class. See below that for the full, original question.

class WebsitePage {
    protected $name;
    protected $slug;
    protected $heading;
    protected $intro;
    protected $content;

    // constructor and other unrelated functions, including name()
    // which just spits out a simple string passed into the constructor,
    // and heading(), which wraps that in a <h1> tag.
    ...

    public function slug() {
        return isset($this->slug) ? $this->slug : strtolower(
            str_replace(array(' ', '/'), '-', $this->name));
    }

    public function content() {
        return isset($this->content) ? $this->
            content : $this->get_template();
    }

    protected function get_template() {
        ob_start();
        include(__DIR__ . '/templates/' . (!empty($this->
            slug()) ? $this->slug() : $this->name) . '.php');

        $content = ob_get_contents();
        ob_end_clean();

        return $this->intro() . $content;
    }
}

ORIGINAL: Similar to this question, I am having trouble getting class inheritance (specifically function overriding) to work the way that every PHP resource specifies.

Parent class:

class WebsitePage {
    protected $name;
    protected $slug;
    protected $heading;
    protected $intro;
    protected $content;

    public function __construct($name, $slug = null, $heading = null, $intro = null, $content = null) {
        $this->name = $name;
        $this->slug = $slug;
        $this->heading = $heading;
        $this->intro = $intro;
        $this->content = $content;
    }

    public function name() {
        return $this->name;
    }

    public function slug() {
        return isset($this->slug) ? $this->slug : strtolower(
            str_replace(array(' ', '/'), '-', $this->name));
    }

    public function heading() {
        return isset($this->heading) ? $this->
            heading : "<h1>$this->name</h1>";
    }

    public function intro() {
        return '<div class="page-intro">' . (!isset($this->intro) ? $this->
            heading : "$this->heading<p>$this->intro</p>") . '</div>';
    }

    public function content() {
        return isset($this->content) ? $this->
            content : $this->get_template();
    }

    protected function get_template() {
        ob_start();
        include(__DIR__ . '/templates/' . (!empty($this->
            slug()) ? $this->slug() : $this->name) . '.php');

        $content = ob_get_contents();
        ob_end_clean();

        return $this->intro() . $content;
    }
}

Child class:

class WebsiteServicePage extends WebsitePage {
    public function __construct($name, $slug = null, $heading = null, $intro = null, $content = null) {
        parent::__construct($name, $slug, $heading, $intro, $content);
    }

    public function slug() {
        return 'our-services/' . parent::slug();
    }

    public function heading() {
        return isset($this->heading) ? $this->
            heading : "<h2>$this->name</h2>";
    }
}

Instantiation:

$servicePages = array(
    new WebsiteServicePage('Tree Lopping'),
    new WebsiteServicePage('Land Clearing')
    ...more service page instantiations
);

foreach ($servicePages as $servicePage) {
    echo $servicePage->content();
}

Which on each iteration of the loop results in the parent class' content() being called, which calls its own get_template(), which calls its own slug() and therefore omits /our-services from the final page slug. This means that get_template() can't find the correct template files, since they exist in the our-services directory.

I could simply put all templates in the same dir to get around this issue, but I feel as though I must be misunderstanding something fundamental about PHP class inheritance. Although as mentioned before, every single PHP resource that I've been able to find has suggested that what I have done should work!

So what gives? MTIA! :-)

P.S. StackOverflow is suggesting that I should add some more non-code content to my question, so for those who care, this is for a website that I'm building for a client who runs a tree lopping/arborism business. They provide all kinds of tree-related services, so the child WebsiteServicePage class is very important. I'd also like to reuse this model (with some obvious modifications) on any sites that I dev in the future, so I really need to know what I'm doing wrong here! Thanks again :-)

Kenny83
  • 769
  • 12
  • 38
  • What's up with `$servicePages` instantiation? That's a wrong syntax. – Aniket Sahrawat Apr 22 '18 at 07:42
  • Could you simplify the example to the minimum to demonstrate it and ask about it? Looks a lot of code for what you ask about, so it is not easy to read and quite hard to help. – hakre Apr 22 '18 at 07:44
  • @AniketSahrawat How so? Please elaborate as that is not the part of the code that fails (or at least I don't think so lol). – Kenny83 Apr 22 '18 at 07:44
  • My apologies @hakre, will do. I was afraid that including the whole class might cause that problem, but was also afraid that people might ask for more info on the class if I didn't provide it lol! – Kenny83 Apr 22 '18 at 07:45
  • 1
    Well, array elements are separated by `,` not `;`. That's syntactically wrong. – Aniket Sahrawat Apr 22 '18 at 07:46
  • What you write in your question looks a bit unprecise to me, can you verify not that I'm missing something: You write get_template() would call it's own slug(), but that must not be necessarily true: it calles $this->slug() which is the objects slug() method, not explicitly WebsitePage::slug() method. – hakre Apr 22 '18 at 07:52
  • @AniketSahrawat LMAO sorry mate, that's a silly typo on my part! Rest assured, the actual code files do indeed use commas! – Kenny83 Apr 22 '18 at 07:53
  • @hakre Exactly! That's the way it **should** work, but for some reason it's not! – Kenny83 Apr 22 '18 at 07:54
  • If I am reading your question right, it sounds like you think $this->get_template() in the parent will call the get_template in the parent. It won't - it calls the one in the child. $this always seeks the most child-like method... – wordragon Apr 22 '18 at 07:54
  • @wordragon No it's the complete opposite...I expect the well-defined, "most child-like" behaviour as you put it, but I'm getting the parent's method :S – Kenny83 Apr 22 '18 at 07:55
  • 1
    In your WebsiteServicePage get_template method - you aren't returning the value - should be `return parent::get_template();` – Nigel Ren Apr 22 '18 at 07:58
  • @NigelRen Thanks mate, that is definitely something that would be causing a **different** issue if that function was ever being called, but it's not, hence the problem. I've verified this by stepping through the code line by line using XDebug. – Kenny83 Apr 22 '18 at 08:03
  • I've copied your code and the only problem I get is the above. Didn't post it as an answer because you don't seem to even get to this code, which I can't replicate. Have you tried the old `echo "here";` debug method in the WebsiteServicePage get_template method? – Nigel Ren Apr 22 '18 at 08:07
  • I did the same things as nigel - also worked after changed get_template to return the value.. – wordragon Apr 22 '18 at 08:10
  • @NigelRen I can go 100 times better: XDebug lets you step through the code line by line, like a C# or other desktop project in Visual Studio. So like my previous comment states, I have verified that WebsiteServicePage::get_template() is never called for some weird reason :S If you can replicate the code but not the problem, that makes it even weirder :S But thanks for trying to help! :) – Kenny83 Apr 22 '18 at 08:12
  • I use XDebug a lot, but also find that it doesn't always work as well as you may think. One final thing (I have to go out) is that as the method itself does nothing more than call the parent method - there is no point in having it in your sub class. – Nigel Ren Apr 22 '18 at 08:16
  • At ant rate, you should correct the child to "return parent::...", verify the code still doesn't work, edit your post here to show the corrected version and some input and output. -- perhaps just listing the files, instead of including them. – wordragon Apr 22 '18 at 08:19
  • @NigelRen Hmm OK, I'll try the good 'ol fashioned "`echo` and `var_dump`" debugging method and see if that gets me anywhere. And yeah I initially didn't override `get_template()` coz I didn't think I had to, but in trying to solve the problem myself, I added it. Thanks for the advice though; good to know that I can definitely delete it from `WebsiteServicePage`. – Kenny83 Apr 22 '18 at 08:21
  • @Kenny83: I see. Can you please not override get_template() in the child class? Just b/c it is not necessary for the code you give (just calling the parent method is the default behaviour so it looks a bit misconcepted), and could you please share which PHP version you're using? I can't reproduce your example btw.. Perhaps make it self-contained and link it demonstrating it on a code-pad like 3v4l.org? – hakre Apr 22 '18 at 08:21
  • @hakre No worries mate, will do. Thanks for the suggestion and your continued effort on this :D Oh and PHP version is 7.1. – Kenny83 Apr 22 '18 at 08:26
  • 1
    Could you create a one-code-chunk example script with the minimum code to demonstrate the issue and provide it as a link to 3v4l.org? Just as I saw you did the edit b/c I would have requested it, that simplified (as little as necessary) code-example that demonstrates the issue running is what I would request (if me could request anything from you at all). – hakre Apr 22 '18 at 08:31
  • @hakre, Mate, if it helps you help me, you can request anything you like! :P Sorry for the delayed response; in the middle of making dinner as well lol. But I'll update the question and post a 3v4l.org link ASAP :) – Kenny83 Apr 22 '18 at 08:49
  • btw, I really have problems to reproduce what you describe even if I try to make this complicated: https://3v4l.org/44eQ6 – hakre Apr 22 '18 at 08:50
  • I understand the need for a SSCCE, but I think the entire reason for this error is the code's complexity. It's not **really** that complex IMHO, but obviously complex enough to break one of the most fundamental concepts of OOP lol. Anyway, I digress. I will **try** to make the code as short as possible while still producing the error, although I doubt I'll be able to! – Kenny83 Apr 22 '18 at 08:57

1 Answers1

1

It turns out that I had simply instantiated the child objects wrongly, by copying the instantiation of the parent objects and stupidly forgetting to change WebsitePage to WebsiteServicePage! Even though I managed to remember to do this when I wrote the question! slaps forehead

Many, many thanks go out to those who tried to help in the comments; if not for your wise words (especially @hakre's) I wouldn't have figured this out!

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
Kenny83
  • 769
  • 12
  • 38