init() or __contruct()

:D because I’m afraid I still don’t get your point, so I think maybe I should look the other way around, rather than keeping defending without knowing what to defend. It is also a good opportunity to learn from you. Thanks for the discussion.

It can be a requirement to call parent implementation at top of the constructor. It is even logical to assume that parent constructor probably better knows what the very first things are to do.

This is not related with invocation of parent constructor. Take a look at YiiBase::createComponent(). A set of initial property values can be applied to an object after it is constructed (but before init()). This is used everywhere in yii (e.g. filters, controllerMap, app components).

Okay, no problem. In that case I’ll look at those with preinit(), init() and run().

Man I think I bargained for more than I can chew haha. This is definitely a learning experience.

I’m just going to use my calendar class application as an example because it’s really lightweight.

It has 7 methods aside from the construct and init I setup.

As it’s setup, the construct assigns 3 values (either by user input passing variables to the class construct or with default values) and then it calls init method.

Of those 7 methods, 3 are called within the init function, a setTitle, setControlMenu and drawCalendar. These three methods are the only ones that do any real coding as the other 4 methods are used to print out the calendar, thus they just return an attribute (1 for the calendar, 1 for the control menu, and 2 for start of form and end of <form> with control menu)

Right now everything is public, but I set it up so I could make the internal methods private for better security functionality (since there’s no need to access them past initializing the class - it draws out the menus and the calendar and that’s it - you call it with different functions when you need / want it).

So ideally, I should be placing my variable assignments in the construct (like I have) and as well put in my private methods in the construct (and forget about the init) instead of calling init and having the private methods in the init. Reasonable and makes sense.

Since the application is small there’s no need to have an init to extend the class anyway at this point - if any developer wants to extend the class they would probably just build their own methods into the class itself. And if they really did want to extend it via inheritance in a new class then they would probably design their own methods to setup the calling process anyway.

Because this calendar application is so light weight, this isn’t much of an issue but my next project will be a search feature application and this will surely look for lots of extensibility on any developers part so understanding the approach I should take for that is always welcome and helpful.

Thanks a lot for the discussion so far guys, I hope this is not only helping me but the Yii development in it’s core (as clearly we have hit an interesting debate in its design).

As I know, when __construct is executed object is already constructed. So you can apply configuration as well.

I reread your entry several times, still I can’t see why it’s irrelevant. If I misunderstood what you’re saying, please provide a bit more details.

I believe that passing configuration array (or even better: object) to the constructor would result in the following advantages:

  • Specific configuration. As config is passed as it is, only the component should know how to interpret and apply it. Still it could be iterated through and passed to setters, but since it is done internally, it can be customized.

  • Simplified initialization. The object still accepts external config, but handles it right off the bat. It is not forbidden to preinit or postinit the object just by moving the call to parent implementation in subclasses. All initialization in one single (semantic) method.

  • Less function means less documentation, less code to maintain, et cetera.

A signature could be like this in subclasses:




public function __construct($configuration=null)

{

    ... preinit ...

    parent::__construct($configuration);

    ... init or post init ...

}



I wrote all this with constructive intentions in mind. I hope I’m still on topic.

Next instalment:

I’ve had a look at the first chunk of Yii classes with init() methods, those that subclass CApplicationComponent.

CApplicationComponent is abstract and subclasses the base class CComponent.

CComponent has no __contruct(), and no init().

CApplicationComponent has no __construct(), but it implements init() so:




	public function init()

	{

		$this->attachBehaviors($this->behaviors);

		$this->_initialized=true;

	}



Thus, you are encouraged to instantiate subclasses of CApplicationComponent, so:




	$a = new CAssetManager;

	$a->behaviors = array('name1'=>'behavior1', 'name2'=>'behavior2');

	$a->init();



If you are not attaching bahaviors, then you must do this:




	$a = new CAssetManager;

	$a->init();



although it is not at all clear from the docs whether you should or should not always call init().

For example, see CDbConection where init() is not mentioned in the examples at all. So what does $db->isInitialized() tell me?

In all the cases I looked at (I didn’t look at all seventeen subclasses of CApplicationComponent), init() could be removed and the work done in the constructor. This would make the instantiation:




	$a = new CAssetManager;

	$a->attachBehaviors = array('name1'=>'behavior1', 'name2'=>'behavior2');



If you are not attaching bahaviors, then you would do this:




	$a = new CAssetManager;



Surely this is good, honest simplification.

As an aside, CDbConnection is interesting, since it has a constructor and init(). If we were following convention then we should do:




	$db = new CDbConnection;

	$db->behaviors = array('name1'=>'behavior1', 'name2'=>'behavior2');

	$db->init();

	$db->active = true;



But if $db->autoConnect is true, which is the default, then $db->init() has already set $db->active = true. But does the following make it clear whether or not you have an open db?




	$db = new CDbConnection;

	$db->behaviors = array('name1'=>'behavior1', 'name2'=>'behavior2');

	$db->init();



But autoConnect is presumably being set by main.php, which means that we have:




	$db = new CDbConnection;

	$db->behaviors = array('name1'=>'behavior1', 'name2'=>'behavior2');


lots of stuff happens here


	$db->init();

	$db->active = true;



Well, probably, but that’s my point: who knows what init() is doing and who knows when it should be called? Without trawling through the code?

Surely, it’s much simpler to say:




	$db = new CDbConnection;



And at some point simply call $db->active = true. What use isInitialized() might be I have no idea.

I don’t see the need for init() in the subclasses of CApplicationComponent at all, and if I missed something in a class I didn’t look at, I’m sure there’ll be a clearer way to implement that too.

Onward to the next bunch of classes…

Yes, when you require the configuration to be passed as a parameter of constructor, the init() and the constructor() do look as an integrated part. However, this is a strong requirement. It means any descendant class must keep the same constructor signature as well as calling the parent implementation at appropriate place. As you know, you cannot specify constructor in PHP interfaces. So theoretically, if one descendant class breaks this signature, any further descendant class won’t be able to recover this capability.

@auxbuss: you did that long. ;)

First, let me clarify that an application component should mainly be instantiated by the application, not as you do using new operator. Similar classes also include filters, actions and widgets (by controller).

If we move the code from init() to the constructor, then those code would execute without the configuration (specified in app config, filters(), actions(), etc.) being applied. Behaviors can be specified as some special initial properties.

The problem may be solved using pestaa’s approach, i.e., to pass configuration via the constructor. However, this is at the cost of a very strict constructor signature. And PHP interfaces don’t allow specifying constructor. Also, applying configuration within the constructor makes the constructor a big black box, which is not any better than init().

It seems acceptable to me. If the first descendant breaks the signature, maybe it does it with a reason (wants to parse configuration in different way for example). If it doesn’t have a reason, then we’re talking about a bug.

And I’m quite sure that Yii core dev team won’t commit such mistakes in released packages.

In terms of class and methods design - what would you choose?

@pestaa: constructors really should be kept simple, and, imo, be free from risk of failure – Hey, I’ve done some Symbian work, so I know about two-phase constructors. If things are not clear, then the common pattern is to replace the constructor with factory methods. So:




class Employee {

    private $_type;


    private function __constuct($type) {

        $this->_type = $type;

    }


    static function create($type) {

        return new Employee($type);

    }

}



You then create factory methods for the different "things" that you are creating. Of course, you can also create objects of different classes and use ye olde polymorphism again.

With PHP, it also gets around the lack of overloading methods to some extent.

Here’s a random link with some examples.

@pestaa: Let’s use another example. Assume class A has a read-only property called parent. This property can only be set via constructor, i.e., new A($parent). Now what would you do with configuration as parameter? Of course, there are still some workarounds. But mostly, they are not straightforward. I am using this example mainly to show it is a strong requirement to ask for __construct($config).

I don’t quite understand your last question.

But I’m not saying move the init() stuff to the constructor. Doing so without due consideration would be silly. But in the cases I looked at, there was no problem doing so. Please give an example, of the above cases, where this wouldn’t work.

As I showed, I’m saying do what it necessary in the constructor – and certainly in a lot of the cases above the init() stuff can go there – and then explicitly do whatever else is required – because that makes the code clear and easier to comprehend. At the moment things are obfuscated and whether or not to do something, and where, seemingly random. The cause seems to be this init() thing.

No, I’m not suggesting building huge constructors either. That is counter to everything I have said. I’m saying “get this stuff out of here and put it where it belongs”.

Anyway, as I say in my reply to pestaa, you can replace constructors with factory methods, and in complex cases – and there don’t appear to be many of them – that’s a good solution, imo.

So what is your suggestion? I have yet to see any concrete one yet.

Do you mean merging init() into the constructor for application component? As I already said, this means the constructor then must use the signature that pestaa suggested, which is too strong. Theoretically, any component can be an application component, as long as it implements IApplicationComponent. So asking for such strict constructor means this interface is incomplete.

I don’t think factory has anything to do here, and I don’t think using a lot of factory methods is a good thing. Factory itself has a lot of limitations.

http://php.net/manual/en/language.oop5.interfaces.php#85859

The documentation does not state anything against this statement.

They can be implemented inappropriately, of course, but I don’t think they are restrictive by definition.

Thanks for the pointer about constructor. I didn’t know this (it wasn’t allowed before). In general, I’m against posing a requirement on the constructor signature because it doesn’t follow the spirit of interface.

I don’t mean factory is useless. Factory is not good when subclassing is needed.

I think we should stop discussing on this. I realized this is more like personal preference, and it is hard for one to convince the other. As long as we use init() in a consistent way, I don’t see big problems.

It’s a shame, I was hoping to contribute to Yii both with the documentation and the code.

But you don’t! As I demonstrated. And there seems to be no way of persuading you about anything once you have you mind set, which is kind of limiting.

Many FOSS projects work well with a closed approach to development, and I’m happy to use them, but I’ve found it’s not fun to try to work that way. Different strokes.

I’ll still use Yii, I’ll just remove the obvious flaws and procedural code.

Right! I’m off to hack ActiveRecord now I’ve got init() out of the way (mostly).

I am open to your suggestions. But, please, give some concrete ones. Having typed in so many words and spent so much time, I have yet to see any from you yet (pestaa gave one, but it’s not convincing enough to make the change). That’s why I call for stopping discussing on this.