0

I am building on a CMS where dependencies and files needed for a page get stored in the database. Because this can be very dynamic, I serialize these paths into one Database field into my pages table.

I am trying to store these file-paths on retrieval into an array to use it in a switch and parse it into a web page. I have difficulties trying to decide how to order the data into the array.

I have a few categories in which the files can fall:

  • CSS
  • JavaScript in head
  • JavaScript before end tag of body
  • Meta tags
  • PHP includes
  • Etc...

Lets say I have a few files:

CSS

  1. style.css
  2. custom.css

JavaScript-top

  • jQuery.min.js
  • Bootstrap.min.js

PHP include

  • pages.php

There are two choices here:

Option 1. Use a numeric array with a sub-array as value to contain a include type and the file path:

array( 0 => [
          type => "css",
          file => "/path/to/file"
       ],
       1 => [
          type => "css",
          file => "/path/to/file"
       ],
       2 => [
          type => "js-top",
          file => "/path/to/file"
       ],
       4 => [
          type => "js-top",
          file => "/path/to/file"
       ],
       etc.. );

Option 2. A associate array with a numeric sub-array:

array( css => [ 
           0 => "/path/to/file",
           1 => "/path/to/file"
         ],
       js-top => [ 
           0 => "/path/to/file",
           1 => "/path/to/file"
         ],
       php-include => [ 
           0 => "/path/to/file"
         ],
       etc...);

I would go for option 2, because I don't have to iterate over the array every time I get to a case and have to look for a possible value. While with option 2, I can easily check if this type isset() and go through the switch much faster!

I am asking this question not because of the possible performance increase. I am asking this question because I am trying to design the best systems and I am always looking for a reason why I would make a (simple) choice like this.

Which option would you choose and why?

  • 1
    Personally, I would go for option 3: objects. PHP is an object-oriented language, not an arrays-of-arrays-of-strings-oriented language. (Well, sort of. It's not a particularly good object-oriented language, but it is one.) – Jörg W Mittag May 23 '16 at 19:14
  • So you create a stdClass object and then fill it like option 2? I feel that in PHP there arent much differences between an assoc array and an stdClass object. – PIDZB May 23 '16 at 22:13
  • 3
    No, you should have Css objects, Meta objects, HeaderJs objects, BodyJs objects, PhpInclude objects, etc. and those should implement a common interface (say, PageElement) with a render method and should know how to render themselves. Then you simply call render and don't care what kind of object it is, because it takes care of the rendering itself and there is no switch needed at all. – Jörg W Mittag May 23 '16 at 22:27
  • @JörgWMittag That sounds like an answer to me. – Ixrec May 25 '16 at 10:51
  • Indeed, if you post it like an answer, maybe with a little example(?), I will accept it :D – PIDZB May 25 '16 at 10:59
1

PHP is an object-oriented language, or at least it somewhat reasonably supports object-oriented programming.

The main idea of OOP is that objects send messages to other objects and those objects then respond to those messages. The metaphor of "messaging" is apt: when you send someone a message, all you can see is their response. You cannot see what they do with the message. They may read it, think hard about it and send you an answer back (executing a function). They may read it and send you a pre-canned response back (reading a property). They may enlist the help of other people (send messages themselves). They may even hand off the message to someone else to answer (proxying). You don't know. It is entirely up to the receiver of the message what to do with that message.

And this is what we want to do here. We simply want to send the fragment in question a message that says "render yourself!" and we don't care how the fragment does that. The PHP fragment will do something different than the <meta> fragment.

In your code, you have different "types" (or "classes") of "things" (or "objects"): you have CSS fragments, JavaScript fragments, and so on. And you switch between executing different pieces of code depending on the "class" of the thing you are dealing with. For CSS, you do one thing, for JS, you do a different thing.

However, that functionality is already built into PHP itself! When you write:

$foo->bar();

Then PHP will execute different variants of bar depending on what the class of $foo is. You are duplicating work that PHP already does for you.

You could do something like this: have a common interface for your different object types, which basically just means you decide on a name and signature for the method that you want to use. In PHP, you even have the possibility of documenting this decision by using an interface:

interface PageFragment {
  public function render();
}

You don't actually need this, you could just have a bunch of classes with the same method name, and it would work regardless, but documenting your common interface with an interface is nice, because it allows you to use type hinting later on, i.e. document that some method requires an object of type PageFragment, and it gives you some central place that you can put documentation in, so that you can tell users of the interface what the render() method is supposed to do.

Optionally, we can add some common base class that contains some common functionality for all kinds of page fragments:

abstract class FragmentBase implements PageFragment {
  protected $path;

  function __construct($path) {
    $this->path = $path;
  }

  // Just as an example
  public function __toString() {
    return "Page fragment from file {$this->path}";
  }
}

The real work then is done in different subclasses/implementations of PageFragment, one class for each type of fragment:

class CssFragment extends FragmentBase {
  public function render() {
    print("… do stuff to render CSS from file {$this->path}" . PHP_EOL);
  }
}

class MetaFragment extends FragmentBase {
  public function render() {
    print("… do stuff to render <meta> elements from file {$this->path}" . PHP_EOL);
  }
}

class HeaderJsFragment extends FragmentBase {
  public function render() {
    print("… do stuff to render JavaScript in the header from file {$this->path}" . PHP_EOL);
  }
}

class BodyJsFragment extends FragmentBase {
  public function render() {
    print("… do stuff to render JavaScript in the body from file {$this->path}" . PHP_EOL);
  }
}

class PhpFragment extends FragmentBase {
  public function render() {
    print("… do stuff to include PHP from file {$this->path} on the page" . PHP_EOL);
  }
}

Now, if some part of your framework somewhere else in your codebase creates page fragments:

$fragment1 = new PhpFragment("/path/to/foo.php");
$fragment2 = new CssFragment("/path/to/foo.css");

and hands a bunch of them to you:

$fragments = [$fragment1, $fragment2];

You don't need to know, and you don't care whether they are CSS fragments or PHP fragments or whatever. You don't need to know what they are, because they know what they are, and they know how to handle themselves. You just tell them to do something, and they do the right thing, automatically:

foreach ($fragments as $fragment) $fragment->render();
// … do stuff to include PHP from file /path/to/foo.php on the page
// … do stuff to render CSS from file /path/to/foo.css

As you can see, each fragment did the right thing, and each fragment did a different thing, without there being a single conditional in the entire source code!

This kind of changing the structure of the code from an explicit conditional over some notion of "type" to polymorphic method dispatch over actual types is called the Replace Conditional with Polymorphism Refactoring:

from

function getSpeed() {
  switch ($type) {
    case EUROPEAN:
      return getBaseSpeed();
    case AFRICAN:
      return getBaseSpeed() - getLoadFactor() * $numberOfCoconuts;
    case NORWEGIAN_BLUE:
      return ($isNailed) ? 0 : getBaseSpeed($voltage);
  }
  throw new RuntimeException("Should be unreachable");
}

to

interface Bird {
  public function getSpeed();
}

class European implements Bird {
  public function getSpeed() {
    return $this->getBaseSpeed();
  }
}

class African implements Bird {
  public function getSpeed() {
    return $this->getBaseSpeed() - $this->getLoadFactor() * $this->numberOfCoconuts;
  }
}

class NorwegianBlue implements Bird {
  public function getSpeed($voltage = 0.0) {
      return ($this->isNailed) ? 0 : $this->getBaseSpeed($voltage);
  }
}

You will note that you can further refactor NorwegianBlue like this:

class NorwegianBlue implements Bird {
  public function getSpeed($voltage = 0.0) {
      return $this->getBaseSpeed($voltage);
  }
}

class NailedNorwegianBlue extends NorwegianBlue {
  public function getSpeed($voltage = 0.0) {
      return 0;
  }
}

There is a slightly humorous campaign called the Anti-IF Campaign which advocates programming in this style.

One question you might ask yourself is: does this always work? Can I always replace conditionals with polymorphism? And the answer is: yes, you can! The Replace Conditional with Polymorphism Refactoring only talks about conditionals that switch on some notion of "type", but in reality, every switch can be interpreted as a switch on types, and thus refactored in the same way. There are, in fact, languages that do not even have conditionals, like Smalltalk. And in fact, if PHP didn't have conditionals, you could implement them yourself using nothing but polymorphic method dispatch:

interface Buul {
  public function ifThenElse(callable $thenBranch, callable $elseBranch);
  public function andand(callable $other);
  public function oror(callable $other);
  public function negate();
}

final class TrueClass implements Buul {
  public function ifThenElse(callable $thenBranch, callable $elseBranch) {
    return $thenBranch();
  }
  public function andand(callable $other) { return other(); }
  public function oror(callable $other) {}
  public function negate() { return FalseClass::instance(); }

  // please excuse the rather crude singleton implementation ;-)
  private static $instance;
  public static function instance() {
    if (isset($instance)) return $instance;
    return $instance = new TrueClass();
  }
}

final class FalseClass implements Buul {
  public function ifThenElse(callable $thenBranch, callable $elseBranch) {
    return $elseBranch();
  }
  public function andand(callable $other) {}
  public function oror(callable $other) { return other(); }
  public function negate() { return TrueClass::instance(); }

  private static $instance;
  public static function instance() {
    if (isset($instance)) return $instance;
    return $instance = new FalseClass();
  }
}

function buul($bool) { return $bool ? TrueClass::instance() : FalseClass::instance(); }

buul(4 < 3)->ifThenElse(function () { print("less" . PHP_EOL); }, function () { print("greater" . PHP_EOL); });
// greater
  • 1
    Jörg, thank you very much for the time to explain this concept for me. I am totally changing my design to fit this, because this is so simple to understand, everyone will understand my code! – PIDZB Jun 20 '16 at 19:40
  • 1 question though: I need to call these objects in a particular order depending on their type. Do I need to create an index-array before rendering the objects to figure out which object needs to be rendered somewhere, or is there another option? Looping doesn't seem like an option. – PIDZB Jun 22 '16 at 12:59

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.