Universal Constructor Sighting "In The Wild"

By | July 9, 2010

For those of you who don’t know, “universal constructor” is the name I give to PHP constructors that always and only take a single parameter. The parameter is an array of key-value pairs, which is then merged with a set of default keys and values. Finally, the array is unmarshalled, usually into object properties.

One benefit of the universal constructor is that it allows you to quickly and easily pass in configuration values from a config file (or other source) when building an object. You don’t have to remember the order of parameters, and you only need to specify the values that override the defaults.

I standardized on a universal constructor in the Solar framework for PHP. As far as I know, Solar was the first to standardize on this pattern and give it a name, and other PHP projects appear to be adopting the idea based on my advocacy. I saw a link today to a universal constructor “in the wild”, not the result of my direct advocacy, here: http://www.jqueryin.com/projects/mongo-session/.

It’s nice to see the idea is getting around.

30 thoughts on “Universal Constructor Sighting "In The Wild"

  1. Elizabeth M Smith

    Really this would be less necessary if PHP had named parameters – but as long as it doesn’t I’m forced to use the “universal constructor” mechanism for proper setup (sigh)

    Reply
  2. Ed Finkler

    I love this, and the approach is used widely in JavaScript. Of course, it’s a lot less painful to do in JS with the object literal notation. Defining a hash in PHP is painfully verbose in comparison, and I really really really really wish someone in Internals would fix this.

    Reply
  3. Corey Ballou

    Hey Paul,

    Thanks for the reference. I was naively unaware of the terminology of the constructor technique. I believe the increasing popularity of the “universal constructor” could correlate with the increasing popularity of jQuery plugin development. The pattern is all too common when developing plugins to override a default set of options. It was only a matter of time before adoption crept out of the wood-works into other languages.

    You were ahead of the curve, my friend!

    Reply
  4. Tyrael

    which is a bad thing, because your IDE can’t typehint your arguments for universal constructors. :(

    Tyrael

    Reply
  5. intel352

    This method is pretty heavily used in Yii Framework. Dunno if it was used in Yii’s predecessor framework (Prado)

    Reply
  6. Bill Karwin

    @Tyrael: If you need to rely on typehints you could make a rule to never use optional parameters in constructors, and make a setter method for anything you would have made an optional parameter.

    $foo = new Foo(‘required’, ‘params’, ‘only’);
    $foo->setBar(new Bar()); // enforces type Bar for optional param

    But this does not satisfy Paul’s requirement to allow a constructor to accept an array of parameters, both required and optional, that were read from a config file or other source.

    Conventions restrict some types of flexibility, but give you other benefits. There are tradeoffs to everything.

    Reply
  7. Ivo

    I don’t understand why this concept should be called ‘universal’? In most OO situations it’s quite a bad idea, it’s no longer possible to make parameters required, IDE’s treat this way of doing constructors horribly (no code completions), you can no longer use typehinting on the parameters…

    don’t get me wrong, there are certainly situations where this makes sense, but calling it ‘universal’, suggesting that it should be used for all constructors everywhere is a bad, bad idea.

    Reply
  8. Nate Abele

    @Ivo: It’s called “universal because” the idea is that all classes which have constructors would use it.

    With respect to IDEs: boo-hoo; use a real editor. IMHO, an IDE is only useful as a crutch, or by Java developers (since it’s simply impossible to fit all the information required to do Java programming in your head at any one time).

    Also, unless you’re using a statically-typed language, static typing is (IMO) not generally helpful.

    Reply
  9. Rene

    @nate I guess you are one of those VI/VIM freaks feeling oh so proud of them selves because they work command line ;).

    A IDE is more then just a fancy text editor and with proper inline documentation of class methods, hard type casting in method parameters can save you hours of useless debugging sessions of a universal parametered inherited code. Because somewhere in a small helper function of a triple extended object some coder tought it was handy to add a non documented parameter called $m to the universal constructor array of already spaghetti zed paramater chunk

    Sure for small projects with little code coverage, or jQuery events this constructor can be useful, but for a serious enterprise application….. nah.. I rather walk a marathon on flip-flops ;)

    Reply
  10. Lukas

    I much prefer symfony’s dependency injection container approach. Also makes it easy to generate an instance out with close to no additional code beyond a config file, but with the upside of being much friendlier when using third party libraries and more importantly when trying to replace framework internal classes with custom implementations.

    For the array merging after years of lobbying I guess I got a little present in 5.3 with a method that does recursive array merging while maintaining the structure (as opposed to what array_merge_recursive() does):
    http://php.net/manual/en/function.array-replace-recursive.php

    I dont know the Solar code base, so I do not know if you already adopted it (maybe PEAR::PHP_Compat already provides a PHP implementation as a fallback for pre 5.3 PHP setups).

    Reply
  11. Josh J

    The Universal Constructor in itself isn’t a bad idea, but it is one that lends itself to developer laziness. A lot of times it is used to save time by the author of the class which then passes on the burden to the developer using the class. As long as the author CLEARLY documents all possible parameters and doesn’t make the consumer pour over miles of comments and source code to find the obscure parameter “m” then it is OK but more often that not, good documentation is nowhere to be found.

    A better idea would be to use the required parameter constructor with option parameters are mutators then do one of the following:

    * A static method that accepts a config array/object that instances the class, sets values, then returns the constructed object

    — or —

    * The first parameter of the constructor could accept a value, or the aforementioned config object and apply the setting magic (@see Zend Framework and Zend_Config)

    Reply
  12. Patrick

    I believe the advantage of being able to throw every single parameter into an array and pass it to the so-called “universal” constructor does not outweigh the advantages of readable, documentable, maintainable and (not in the least) testable code.
    Especially for larger projects with multiple developers, where not every developer knows what parameters are used, which are required and what the default values are, unless every single parameter is well documented. And even then, a proper IDE won’t be able to assist you during development; you’ll be spending a lot more time on searching for the right info yourself.

    Reply
  13. Marcus

    @Patrick
    without documentation, a standard list of arguments is no more legible than an array – and at least you can enforce an array type in the constructor. It just underlines the importance of documentation (and an IDE with decent autocompletion)

    Reply
  14. Steve Clay

    Having constants for the key names could ease some pain:

    $spec[Box::HEIGHT_KEY] = 23;
    $spec[Box::WIDTH_KEY] = 5;
    $box = new Box($spec);

    Unfortunately you can’t:

    $road = new Road(array(Road::LANES_KEY => 4));

    Anyone seen a proposal for named params in PHP?

    function funcName<p>(Foo $foo = null, $bar) {}

    $val = funcName($bar => 23);

    Reply
  15. Devis

    No offence but this is just a new name for an existing bad practice. It might solve some problems but it makes the code less readable and hard to mantain, also hiding the fact you might be using too many parameters. I’ve seen the same approach on a legacy framework 2 years ago and it was just slowing down the development. Honestly I hope no one follow the example.

    Reply
  16. Herman Radtke

    @Rene
    Tests should be catching “a small helper function of a triple extended object some coder tought it was handy to add a non documented parameter called $m to the universal constructor array of already spaghetti zed paramater chunk” not your IDE.

    Reply
  17. Simon Harris

    Marcus – I fear that you’ve fundamentally missed the point of documentation versus self-documenting code. If implemented sensibly, a “standard list of arguments” can *be* documentation.

    Reply
  18. Daniel

    @Steve

    You can have class constants.

    class Box {
    const HEIGHT = ‘height';
    const WIDTH = ‘width';
    }

    So your example of Box::HEIGHT would work. For the IDE folk out there you could prefix all of you class const with something like P_, and then your IDE should auto complete Box::P_ with a list of all the params for that constructor.

    You could also try something like this:

    class UBase {
    //from http://www.php.net/manual/en/function.get-defined-constants.php#84975
    public function get_class_constants()
    {
    $reflect = new ReflectionClass(get_class($this));
    return $reflect->getConstants();
    }
    }

    class Box extends UBase {
    const HEIGHT = ‘height';
    const WIDTH = ‘width';
    }

    $box = new Box();
    print_r($box->get_class_constants());

    You could write the base class to dynamically do the merge and automatically add params via the P_ prefix.

    Reply
  19. Bill Karwin

    @Simon Harris: If code were readable, why would we call it “code”?

    Self-documenting code is a myth, or a rationalization by people who don’t want to write documentation.

    Reply
  20. gerard

    Hmm. I think “universal constructors” may work well in frameworks, or plugins (hence the jQuery popularity), but in general it does not sit well with me.

    If writing a custom app, I’d much prefer the clarity of fewer parameters. So instead of:

    function walkDog(array(‘distance’ => 1, ‘withCat’ => false));

    I’d rather do:

    function walkDogWithCat($distance);
    function walkDogWIthoutCat($distance);

    Or something like that.

    Reply
  21. Michael

    There some instances where a universal constructor is appropriate (classes that implement an interface but need a variable number of arguments in the constructor), but I don’t think using universal constructors for all of your classes is a good solution. Oh, and I’m on team Jacob.

    Reply
  22. Good Time Tribe

    I would not say I’m convinced to use these for my regular programming, for all of the same reasons everyone else already mentioned.

    Reply
  23. Filipe

    @Bill Karwin: I disagree. A well structured class and function, with a well built datasource, can tell you a ton about how an app should work — without requiring you to actually trace the entire route that data takes.

    Reply
  24. Bill Karwin

    @Filipe: Well-structured code is definitely worthwhile, but that’s not the same thing as documentation. Code may be written in a way that helps you understand how the app *does* work — but it tells you nothing about how the app *should* work.

    Reply
  25. Simon Harris

    Bill –

    Clearly I disagree, but even if I didn’t, code that “helps you understand how the app *does* work” is a massive win. It has to be better than throwing your hands in the air and saying “no one’s going to be able to read my code, I might as well not bother making it legible”, surely?

    Reply
  26. Bill Karwin

    @Simon: Yes, as I said, writing good code is worthwhile and we should do it. I didn’t say that documentation can replace good code. Doc supplements code.

    Some information about using a class is not best learned by reading code.

    What are all the keys supported by an $options array of a given class?
    Which of these keys are mandatory and which are optional?
    What are the legitimate values associated with each key?

    For example, suppose a key exists named “status”. Does this mean you’re searching for objects matching this status? Or you want to change the status of the matched object? Or that you as a user of the class have a certain status level that allows you to exercise some privileges? What are the values that are meaningful for status? 0, 1, 2…? “high,” “medium,” “low”?

    You could answer these questions by poring over the code, but wouldn’t you rather read a summary of this information in natural language, in the constructor’s docblock?

    Reply
  27. Simon Harris

    Bill, thanks for engaging :)

    Everything you say seems to agree with my original point. All those questions which you ask are valid questions which a developer will need answered in order to do their job. If you take Paul’s (extraordinarily poorly-named) “universal constructor” approach, the only way to provide those answers is through supplementary, manually-written docs – docs which no one will maintain and no one will ever read.

    Languages provide mechanisms (interfaces, contracts, typehinting) that allow code, and tools, to answer those question automatically and immediately. This is the very essence of productive programming in the 21st century.

    > wouldn’t you rather read a summary of this information in natural language

    No, I really wouldn’t.

    Reply
  28. Pingback: Another Unified Constructor Sighting at Paul M. Jones

  29. Nils Luxton

    I’m afraid I have to agree with Simon Harris here; it’s all well and good saying “THAT is why you fail” but it’s not just about “you”. There are a multitude of other people that will be working on the code, and who says they’re all as disclipined as “you”?

    If your code clearly states its purpose, and you use language mechanisms to clearly show what is expected by that code (in a language that everyone who is working with the code will understand…), then people do not have to maintain documentation in order for other people to understand the code (which makes us more productive, no?)

    I honestly can’t understand why anyone would think otherwise.

    I agree that having documentation is great, when it’s done properly, but what you suggest seems to say “It’s OK that this code is needlessly complicated to understand/use, because I can just write some documentation that explains it.”

    If it’s possible, as it is in the case of the “Universal Constructor”, why not use “self-documenting” code in the first place and save yourself the hassle?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *