2

I've gotten myself into a situation in PHP where I need to access a class's constructor without knowing what the class is. I may be approaching this from the wrong design standpoint, so here is the situation - I am working in Drupal, writing a slight OO layer around their classless data-handling to allow some non-Drupal devs to join me on a project.

In Drupal, all content is considered a node. So, I made an abstract class - Node. Any content must have a specific content type. With OO, this is easy - I make a Person class that extends Node, or an Event class that extends Node, etc. Now comes the tricky part - part of my project allows these nodes to be "included" in other nodes. That is, if Node A includes Node B, then whenever A is displayed, the data from B is displayed. This means that whenever Node A is instantiated, it needs to instantiate Node B as well. BUT...Node is an abstract class. So, I can't instantiate a raw Node. I have to instantiate one of its implementing classes. So, as far as I can tell, I either need to write an abstract, static function that all extending classes must implement that returns the constructor...OR, I need to use reflection in some way to determine type, and somehow call the appropriate class constructor?

Without regard to Drupal, what is the most appropriate way to handle this issue from a PHP/OO programming standpoint?

Here is my code:

<?php

abstract class Node {

    public $title, $short_summary, $full_summary, $body, $uri, $machine_type_name, $included_content;

    public function __construct($node) {

        ##
        ## Set simple values
        $this->title = $node->title;
        $this->body = $node->body['und'][0]['safe_value'];

        ##
        ## Set clean uri if aliased
        if (drupal_lookup_path('alias', 'node/'.$node->nid)) {
            $this->uri = '/'.drupal_lookup_path('alias', 'node/'.$node->nid);
        } else {
            $this->uri = '/node/'.$node->nid;
        }

        ##
        ## Set short summary if exists, else short form of body text
        if(strlen($node->body['und'][0]['safe_summary'])) {
            $this->short_summary = $node->body['und'][0]['safe_summary'];
        } else {
            $this->short_summary = text_summary($node->body['und'][0]['safe_value'], NULL, 100);
        }

        ##
        ## Set full summary to concatenation of body
        $this->full_summary = text_summary($node->body['und'][0]['safe_value'], NULL, 600);

        ##
        ## Add included content if module is enabled
        if (module_exists('content_inclusion')) {
            // is this possible? Is there a better design pattern available?
            $this->included_content = Node::get_constructor(node_load($node->content_inclusion['und'][0]['value']));
        }

    }

    public static abstract function get_all_published();
    public static abstract function get_by_nid($nid);
    public static abstract function get_constructor();
}

?>
3
  • Ryan, meet static. I'm sure you'll become good friends. Commented Dec 10, 2011 at 21:07
  • While I appreciate your help, I clearly have some understanding of the static keyword from my above code. Unless I'm missing something, I don't see how this addresses the problem in my question. Commented Dec 10, 2011 at 21:12
  • 1
    But have you met its identical twin static, used for late static binding? (docs) Commented Dec 10, 2011 at 21:16

1 Answer 1

2

Do you mean this:?

class MySubClass extends Node
{
    public static function get_constructor()
    {
        return new self();
    }
}

// ...

$object = MySubClass::get_constructor();

Or if you have PHP 5.3 you can use late static binding with which you could use return new static(); or $class = get_called_class(); return new $class();. Etc.

Full example:

abstract class Node 
{
    public $test;

    public function __construct()
    {
        $this->test = 'Node';
    }

    public static function get_constructor()
    {
        return new static();
    }
}

class MySubClass extends Node
{
    public function __construct()
    {
        $this->test = __CLASS__ . PHP_EOL;
    }
}

// ...

$object = MySubClass::get_constructor();
echo $object->test; // echoes "MySubClass"
Sign up to request clarification or add additional context in comments.

2 Comments

Node is an abstract class, though, so it cannot be instantiated. I need an unknown class implementing Node to be instantiated.
It should still work though. As long as you call it with your subclass name. (e.g. MySubClass::get_constructor();)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.