Force an interface on all controller classes

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Force an interface on all controller classes

Post by Chris Corbyn »

Not sure if there's a good way tackle this. Basically, we have each module (and each part of a module if needs be) with it's own controller class. Each controller needs to extend the templateEngine class in order to work with the templates (there are exceptions where we'll make static calls). Now I have need for an interface on *all* of these controller classes. All contollers *must* contains a public method called filter() which will always be called before the resultant HTML code is displayed. The purpose of the function is to allow the controller to do some last minute parsing of the template based up conditions (remove warning, add images and what not).

Question: How would you tackle to implementation of the interface that puts this constraint in place? Would you simply do:

Code: Select all

class controller extends templateEngine implements iController
{
    function __construct()
    {
        //
    }

    public function filter($source)
    {
        //Play with it, filter stuff if needs be
        return $source;
    }
}
Or is there some clever way to force this on all classes called "controller" without having to explictly implement it? (in the __autoload() or something).

The more I think about it the stupider it sounds to even ask... 8O
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Code: Select all

<?php

class a
{
	public function inA()
	{
		echo get_class().'.'.__FUNCTION__;
	}
}

abstract class b extends a
{
	abstract public function inB();
}

class c extends b
{
	public function __construct()
	{
		echo ReflectionClass::export($this);
	}
	
	public function inB()
	{
		echo get_class().'.'.__FUNCTION__;
	}
}

$c = new c;

?>
outputs

Code: Select all

Class [ <user> class c extends b ] {
  @@ classes.php 16-27

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [0] {
  }

  - Methods [3] {
    Method [ <user, ctor> public method __construct ] {
      @@ classes.php 18 - 21
    }

    Method [ <user, overwrites b, prototype b> public method inB ] {
      @@ classes.php 23 - 26
    }

    Method [ <user, inherits a> public method inA ] {
      @@ classes.php 5 - 8
    }
  }
}
If we comment out inB's implementation...

Code: Select all

Fatal error: Class c contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (b::inB) in classes.php on line 29
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Awesome! Nice one feyd 8) I always wondered what abstract was for :oops:
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Force an interface on all controller classes

Post by Christopher »

Although you might be better off using composition. It is usually more flexible than inheritance and is used more often these days. And the template class and controller classes are then indepenently testable.

Code: Select all

class controller extends templateEngine implements iController
{
    function __construct($template)
    {
        $this->template = $template;
    }

    public function filter($source)
    {
        //Play with it, filter stuff if needs be
        $this->template->set(...);
        return $source;
    }
}
(#10850)
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Force an interface on all controller classes

Post by Chris Corbyn »

arborint wrote:Although you might be better off using composition. It is usually more flexible than inheritance and is used more often these days. And the template class and controller classes are then indepenently testable.

Code: Select all

class controller extends templateEngine implements iController
{
    function __construct($template)
    {
        $this->template = $template;
    }

    public function filter($source)
    {
        //Play with it, filter stuff if needs be
        $this->template->set(...);
        return $source;
    }
}
True... I'll bear it in mind but in this case there's less need for flexibility... well, in fact part of the reason I wanted to force this was to reduce the flexbility and ensure things are more clear in this very large app. Each module has a controller, we don't use a single front controller. Each controller has to contain a filter() method because it *will* be executed automatically before finally sending the resultant template output back to the browser. In this case I've gone with feyd's suggestion of using an abstract class :)

Code: Select all

<?php

abstract class templateEngine
{
    abstract public function filter($source);

    function __construct()
    {
        echo "templateEngine::__construct() called...<br />\n";
    }

    public function sayFoo()
    {
        echo "templateEngine says foo...<br />\n";
    }
}

class controller extends templateEngine
{
    function __construct()
    {
        echo "controller::__construct() called...<br />\n";
        $this->sayFoo();
    }

    public function filter($source)
    {
        echo "controller::filter() called...<br />\n";
    }
}

$CON = new controller();
templateEngine::sayFoo();

?>
We can now still make static calls to the template engine if we need to but when instantiating it (for the controllers) the abstract method *must* be implemented. I didn't put this in a separate class like you did though feyd... is this gonna cause problems?

Cheers,

d11
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

nah, it shouldn't cause an issue. I only stuck it into a separate class because that was your object hierarchy at the time and I didn't know if you'd have this or that in there.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

feyd wrote:I only stuck it into a separate class because that was your object hierarchy at the time and I didn't know if you'd have this or that in there.
Luckily the templateEngine is right on the top of the hierarchy (or is that the bottom?)... it inherits from a lot of other things but the only things that inherit from it are the controller classes.

The places where static calls are made is certain parts of the classes that manage the module itself but this is rare since it's not perfectly clean.
Post Reply