Nicolas Bouvrette
Nicolas Bouvrette

Reputation: 4777

Dynamic methods in PHP

I found a way to do the following call using dynamic methods in PHP (input and img are just example, this could create anything):

$Form->insert->input(array('type' => 'text', 'name' => 'firstName', 'maxlength' => '100', 'value' => 'First name'))
$Form->insert->img(array('id' => 'banner', 'src' => '/images/banner.svg'))

This trick uses two object (Form and FormInsert). And Form insert uses the _call method which means I could create any HTML object with this method.

While this works well, I was wondering if there is a way to use the following, nicer syntax instead?:

$Form->insert->input->type('text')->name('firstName')->maxlength('100')->value('First name')
$Form->insert->img->id('banner')->src('/images/banner.svg')

The idea would be to create not just inputs but any sort of HTML element.

Upvotes: 0

Views: 105

Answers (2)

Nicolas Bouvrette
Nicolas Bouvrette

Reputation: 4777

This might be useful to others but I managed to get the following 3 classes which can help semantically create HTML objects in PHP while tracking some information that can be reused in PHP (such as IDs within an object or 'names' within a form). The following object is for a form but could be used for a 'body' object as well.

class ElementAttribute {

    protected $parent; // Parent class object

    function __construct(&$parent, $element) {
        $this->parent = &$parent;

        $this->rootElement = &$this->parent;
        if (@is_object($this->rootElement->parent)) {
            $this->rootElement = &$this->rootElement->parent; // Set root element
        }
        $this->rootClassName = strtolower(get_class($this->rootElement)); // Set root class name object
        $this->element = $element; // Define current HTML element
        $this->s = '<'.$this->element; // Initialize HTML string
    }

    public function __call($method, $args) {

        $method = strtolower($method); // User lowercase by convention for HTML5

        if (isset($args[0])) {
            // Add available attributes to root element when created
            if ($this->element == $this->rootClassName) {
                if (@property_exists($this->rootElement, $method)) {
                    $this->rootElement->$method = $args[0];
                }
            }
            // Add available attribute arrays to root element when created
            $a = $method.'s';
            if (@property_exists($this->rootElement, $a)) {
                array_push($this->rootElement->$a, $args[0]);
            }
            $this->s .= ' '.$method.'="'.$args[0].'"';
        }
        return $this;
    }

    public function __toString() {
        $s = $this->s.'>';
        $m = $this->rootElement->lastUsedMethod;
        unset($this->rootElement->$m); // Unset HTML object from root object so that a new object can replace it
        return $s;
    }
}

class HtmlElement {

    protected $parent; // Parent class object

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

    function __get($name) {
        if (!@is_object($this->$name)) {
            $this->$name = new ElementAttribute($this, $name);
        }
        return $this->$name;
    }

}

class Form {

    public $id; // Form Id
    public $name = ''; // Form Name
    public $ids = array(); // List of object Ids within the form
    public $names = array(); // List of object Names within the form
    public $lastUsedMethod; // Track last used dynamic method

    function __get($name) {
        if ($name == 'open') { // Open current HTML element tag
            $this->$name = new ElementAttribute($this, get_class($this));
        } else if ($name == 'insert') { // Insert new HTML element
            $this->$name = new HtmlElement($this);
        }
        $this->lastUsedMethod = $name;
        return $this->$name;
    }

}

Some examples on how to use it:

$Form = new Form();

echo $Form->open->id('signup')->method('post')->autocomplete('off')->autocorrect('off')->action('/toto');

var_dump($Form);

echo $Form->insert->input->type('text')->name('firstName')->maxlength('100')->value('First name');

var_dump($Form);

echo $Form->insert->input->type('text')->name('lastName')->maxlength('100')->value('Last name');

var_dump($Form);

Upvotes: 0

Alejandro Arbiza
Alejandro Arbiza

Reputation: 766

What you ask is common practice in jQuery and you surely can do it in PHP, all you need to do is to return the object every time:

class FormInsert
{
    ...

    public function __call( $method, $args )
    {
        // do the cool stuff 

        return $this;
    }
    ...
}

This way you'd be coming back to the FomrInput object to add details about the tag you are building.


The idea sounds interesting. I see a few problems there though if you limit yourself to just two classes. It would seem to me that the FormInsert class would be huge and full of controls to deal with particular cases and "HTML-tag syntax".

If I think of ways to solve those problems, I end up with a class per HTML tag and no need for the magic method __call()... then again, I haven't gone that deep into the problem.

Upvotes: 1

Related Questions