1

I'm a PHP developer, and what do PHP developers do? They create frameworks, so I'm having my way with my little mvc framework project and I've stumbled across a design problem I'd like some opinions on.

For my models I've chosen to take the data objects path (if that's the correct terminology), not a big fan of active record. Essentially there is a gateway pattern that handles persistence and the model is just there to hold data.

Problem is, how do I get this data into the model.

Warning: this involves PHP internals and my performance obsession

This is what I initially had.

abstract class Model {
    public function __construct(array $data = []) {
        foreach($data as $key => $value) $this->$key = $value;
    }    
}

After writing that code my performance related disorder started kicking in and I thought that this iteration is completely unnecessary and I could replace it with an assignment.

abstract class Model {
    public $props;
    public function __construct(array $data = []) {
        $this->props = $data;
    }    
}

Running benchmarks on both cases justified my reasoning as the first one turned out to be up to 20 times slower (depending on $data size), so naturally I decided I'm going to stick with the single assignment.

It wasn't long before this introduced a new problem - how the hell am I going to access the properties. I can't go calling $model->props['property'] for a bunch of reasons - first it's ugly, second I can't get code completion on that and further more the code is open to errors like someone (a dumber me in future) calling $model->props = 'not so smart assignment' and then I'll start getting errors all over the place. So I figured I must protect this property and give proper access to it, like with getters and setters:

abstract class Model {
    protected $props;
    public function __construct(array $data = []) {
        $this->props = $data;
    }

    public function __get($name) { ... }
    public function __set($name, $value) { ... }
}

But it doesn't take a genius (obviously, me) to realize that calling those functions directly will be times faster than having PHP figure out what I'm trying to do and call them for me, so I dropped the magic part and left just get() and set() methods. Now I solved the problem regarding the access to props but I didn't solve the problem regarding code completion and boy that would be a pain to refactor, if I ever have to, in future. Additionally I decided to benchmark what would be the difference between accessing an object's property directly as opposed to getting it from props with get(), results: calling function is 6 times slower than accessing directly.

Basically how I see the decision I have to take now is I have to chose between slow model data assignment vs slow model data retrieval.

Is there anything I'm missing, I'm open to any kind of suggestions.

  • I think you will run into far bigger performance issues due to the N+1 query problem, and trying to figure out the right balance of lazy loading and retrieving data using joins, rather than this. Your main question is like saying "should I build a car with a manual transmission or an automatic?" and fretting about performance only to discover the real slowdown is rush hour traffic. – Greg Burghardt Mar 5 '17 at 21:39
1

First, the model you are talking about is not the M in the MVC. Model is not a database model representing a table, model is the entire infrastructure holding (in most cases) your business logic, model is:

  • service classes,
  • entities,
  • domain objets,
  • helpers...

Knowing that PHP objects are really not objects but hash maps, similarly to PHP arrays, which really are not arrays at all, your micro optimization makes little to no sense. The problem with today's applications is rarely the 1e-6 s it takes to retrieve a property, the problems occur on a much larger scale:

  • poorly written SQL,
  • excesive I/O,
  • unnecessary trips to supplementing services (cloud, database,...),
  • etc.

Using the magic __get and __set methods has such a low impact that it really is not necessary to provide custom methods doing the same thing.

But what about the IDE autocompletion and refactoring?

The almighty phpDoc gave us two amazing options:

  • @property,
  • @property-read.

Both of these are prepended to a class, having the following syntax:

@property/@property-read <data_type> <property_name>
eg. @property int $Number

Modern IDEs, such as PHPStorm or NetBeans, do support these annotations and will correctly provide you autocompletion if you correctly declare the annotations.

As long as you do not access a property which has not been declared using the either @property or @property-read annotation, ie. you should force yourself to declare such property before accessing it, you can safely refactor your code using IDE's refactoring tools.

And if that does not satisfy your needs, completely ditch the Model class, providing at the moment absolutely nothing anyway, and use regular classes instead. It will make the code's intent much clearer, which any new developer coming to the project is going to appreciate much more than a bunch of abstraction-language-bending magic, just because PHP allows you to do that.


My personal input, if the framework you are creating is for nothing but a learning purpose, you are better off using something that has already been created and proven to be functional.

I too have fallen into the hellhole of creating a framework, thinking I need a framework for this, what if I need it in another project, basically copying what someone else had already done and using the framework I had created in approximately exactly one project.

  • Thanks for this, can you explain what you mean by use regular classes instead? – php_nub_qq Mar 5 '17 at 22:34
  • @php_nub_qq What I meant by regular class are just good old classes with statically defined properties and methods, instead of having them defined through magic methods. – Andy Mar 6 '17 at 7:57

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.