Search code examples
phpinterfacefluent-interface

Framework interfaces; limited or broad, sugary method synonyms?


I'm developing a PHP framework; it started off as application-specific, however it has moved into a more agnostic HMVC-style platform which I intend on re-purposing for further projects.

Anyways, while I'm the only developer on the project as it stands, I'm a bit conflicted on my choices of developing the core interfaces.

My main question is (and I understand this could be deemed slightly subjective) is it more appropriate to create a strict interface through which client programmers will work, or permit a bit more flexibility. This is best described through an example:

class MyDataClass{
    public function getData($key){}
    public function setData($key, $value){}
}

This satisfies the requirements (save for implementation details) of a simple data registry. The methods are chain-able by returning $this, and can contain necessary validation logic. Now the alternative PHP approach:

class MyDataClass{
    public function __get($key){}
    public function __set($key, $value){}
}

Sure that works too, and provides a clean implementation through the use of PHP's magic. Now, the approach I've seemed to take most often is:

class MyDataClass{
    public function getData($key){}
    public function setData($key, $value){}
    public function __get($key){ return $this->getData($key); }
    public function __set($key){ $this->setData($key); }
}

Is providing multiple entry points as this example shows a poor choice in design? I always figured it would be a good idea, by centralizing any interaction logic to a single method, but permitting different syntactic choices. I'm now reconsidering this, in favor of a much stricter entry point. A less brief example:

class MySuperDataClass implements ArrayAccess{

    // these do the work
    public function getData($key){}
    public function setData($key, $value){}
    public function getChild($name){}
    public function setChild($name, self $child){}

    // these are just sugary synonyms
    public function offsetGet($key){}
    public function offsetSet($key, $value){}
    public function __get($name){}
    public function __set($name, self $child){}

    // ...

}

Thoughts?


I realized after OZ_'s response that I hadn't justified my choice, for understanding the intent of my approach.

For instance, direct method calls (getData() for example) would be used in controllers, to bind data to a view.

$view->setData('foo', 'bar')
     ->setData('alpha', 'beta')
     ->setData('hello', 'world');

Whereas magic sugar is used in view generation for readability and brevity.

<p><?php echo $view['foo']; ?></p>
<p><?php echo $view['alpha']; ?></p>
<p><?php echo $view['hello']; ?></p>

This is how I came to such a convention. I still think it's a nice approach, but as this question alludes, I'm on the fence now in favor of better practices.


Also: Performance is becoming a concern, as I see the application growing, and these calls are being made iteratively, by using the sugar I'm doubling up my call stack. Sure, micro-optimization I know, but I would think bottleneck potential increases, especially when dealing with PHP magic (if I've been led to understand correctly, especially __call()) The only way to check is to profile, sure; I've always been a premature optimizer, a habit I'd like to break. Breaking poor design habits is important too.


@jgauffin; the View classes implement ArrayAccess, Iterator and Countable for this behavior, as well as making use of __get/__set magic. This is the sugar for the view, whereas the raw methods are called to bind data from the controllers.

<?php if($view->canRender('post')): ?>
<div>
<?php foreach($view->post as $post): ?>
    <p><?php echo $post['text']; ?></p>
    <?php if($post->canRender('comments')): ?>
    <div>
    <?php foreach($post->comments as $comment): ?>
        <p><?php echo $comment['text']; ?>
    <?php endforeach; ?>
    </div>
    <?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>

Solution

  • Thoughts:

    1. While exists at least 1 way, where you can build class without magic methods - use that way, avoid magic methods if you can.
    2. If you can't describe class in interface - class was designed wrong.
    3. Registry is bad pattern. If you need registry - something is wrong in your design.

    Good book about patterns and design