We need a coding standard for when you are calling a class constructor that has no arguments.

Should it be:

$x = new MyClass();

//or

$x = new MyClass;

I don't have a good answer as to what the PHP community thinks the standard should be.

http://www.php.net/manual/en/language.oop5.decon.php
The examples here show () in every case.

http://www.php.net/manual/en/language.oop5.references.php
The examples here don't show () at all.

http://drupal.org/coding-standards
has absolutely nothing about any coding standard for this right now.

CommentFileSizeAuthor
#35 no-arg.patch14.26 KBberdir

Comments

dawehner’s picture

I would vote for the first suggestion because

  • Its obvious that the constructor has no arguments
  • When you add a variable to the construction, it's less to change.
jbrown’s picture

I like the first one because it's consistent with when a constructor is getting arguments.

jhodgdon’s picture

Both of those sound like reasonable arguments to me. +1 for suggestion #1.

Crell’s picture

I am going to "me too" dereine's points in #1. A function if called with no parameters requires (), a method if called with no params still requires (), I don't see why calling the constructor of a new object (which is what happens when you create an object) shouldn't require one.

berdir’s picture

Agree with #1 and #4. A quick grep gave me 17 instances (vs 63 with ()) without ():

$ grep -R "= new " modules/ includes/ | grep -v "("
modules/system/system.module:  $modules[$profile] = new stdClass;
modules/block/block.module:    $block = new stdClass;
modules/shortcut/shortcut.test:    $set = new stdClass;
modules/filter/filter.admin.inc:      $filters[$name] = new stdClass;
modules/filter/filter.test:    $format = new stdClass;
modules/filter/filter.test:    $format = new stdClass;
modules/filter/filter.test:    $filter = new stdClass;
modules/filter/filter.test:    $filter = new stdClass;
modules/filter/filter.test:    $filter = new stdClass;
modules/openid/tests/openid_test.module:  $association = new stdClass;
modules/openid/openid.test:    $association = new stdClass;
modules/simpletest/tests/common.test:    $entity = new stdClass;
modules/dashboard/dashboard.module:        $block = new stdClass;
includes/mail.inc:      $instances[$class] = new $class;
includes/file.inc:    $instance = new $class;
includes/file.inc:    $instance = new $class;
includes/file.inc:          $file = new StdClass;

I guess it would be easier if we wait for #780154: There is no listing API for field API to get commited before we work on a patch for this as it started this issue and misses some () too.

jhodgdon’s picture

Let's also give it 24 hours to see if anyone objects, before adding it as a standard to the coding standards page. And I guess then someone should alert Coder module?

jhodgdon’s picture

And who are the current coding standards gurus?

stella’s picture

+1 for using the () Though I'm pretty sure the coding standards used to say to do "new stdClass;" without the (), but can't find it there now.

morbus iff’s picture

I vote for ().

morbus iff’s picture

For two reasons:

  • it looks the same as functions with no-args, and why bother learning two things?
  • a class and a class(arg=1) make it look like two different things, but they're not.
chx’s picture

I vote very very strongly against () because new $foo() is one of the biggest undocumented WTFs in PHP, observe

class a { function __construct() { echo "\nclass a\n"; } }
class b { function __construct() { echo "\nclass b\n"; } }
function a() { return 'b'; }
$foo = 'a';
print $foo(); // Prints b.
new $foo(); //  Prints class a because new is stronger than ().

it's bad enough to figure out what's going in if the constructor gets arguments, let's not throw that WTF around for every variable-class new.

chx’s picture

Also it's not a function, why would it get parentheses which means call-a-function? Just new class is fine.

morbus iff’s picture

$request = new HTTP_Request2($url, HTTP_Request2::METHOD_GET); i argue that
"new HTTP_Request2" is more confusing than a paramless construction: "new HTTP_Request2()"

morbus iff’s picture

chx's code isn't confusing to me at all, really. $foo is a placeholder for "a". if you use "a" in different ways - by throwing 'new' in front of it - then, yeah, it'll do different things. the inclusion, or exclusion, of "new" "casts" the result to an expectation. chx's argument is not too different from:

$foo = 'monkey';
print $foo;
print (int)$foo;
chx’s picture

But the problem is not with $foo standalone bu that $foo() behaves totally differently in two contexts. As new $foo and new $foo() are the same and so new $foo is way clearer in what it does because you do not need to guess whether the relevant information $foo() or just $foo. That's widely different from an explicit cast.

The confusion arises from the fact that () itself changes meaning between print $foo(); and new $foo(); because in the first it is a function-call-operator the second is nothing. Don't tell me that's not confusing.

Crell’s picture

chx: Valid point, but that remains an issue regardless of whether it's an empty constructor or not. To wit:

print $foo($a, $b); // Prints the result of function call $foo with params $a and $b

new $foo($a, $b); // Creates a new instance of class $foo with constructor params $a and $b

And we certainly can't omit the () on the second one, naturally. It's only a serious WTF if we have a function and class with the same name, which should never happen anyway and if it does then someone needs to change their code because that's its own wtf. :-) If you don't share names, you'll get a fatal error if you get it wrong which is quite easy to spot and fix.

That said, if we wanted to go with something like "always include the () unless it's a variable-named class", I'd be OK with that.

chx’s picture

That said, if we wanted to go with something like "always include the () unless it's a variable-named class", I'd be OK with that.

Me too.

dave reid’s picture

$function = 'mymodule_do_stuff';
$function();

So we'd have a different standard for variable functions with empty parameters as variable classes with empty constructor. I'd much rather prefer the same thing.

jhodgdon’s picture

OK.
I finally get it.

If you call

$a = new $foo();

This is somewhat ambigous to read. You don't know if it is going to evaluate $foo as a class name, and then call

new WhateverTheClassName();

OR it could be that it will call $foo() first and evaluate that (make the function call), and then call

new ThisOtherClassName;

So I agree with the proposal in #16/#17.

jhodgdon’s picture

Status: Active » Needs review

Proposed wording for coding standards:

When calling class constructors with no arguments, always include parentheses:
$foo = new MyClassName();
This is to maintain consistency with constructors that have arguments:
$foo = new MyClassName($arg1, $arg2);

The only exception to this is in the rare case that you need to use a variable for the class name. In that case, to avoid ambiguity over whether "new $classname()" means "evaluate variable $classname and then call that class's no-arg constructor", or "evaluate function $classname() and then call that class's no-arg constructor", we leave the parentheses out:
$foo = new $classname;

Island Usurper’s picture

I suppose it's an even rarer case that we would have

  $foo = new $classname($arg1, $arg2);

That's the only syntax that works here, so if people look at this and don't know what it does, they need to be told that "new" makes $classname evaluate to a class name and not a function. Semantic variable names go a long way towards that, I think.

Crell’s picture

Island: The DB layer does that quite a bit, actually, and it was never a problem.

Island Usurper’s picture

Crell, that's actually my point. I guess it's not as rare as I thought.

I don't see how leaving off the parentheses for the one case of variable class name with no constructor arguments really helps the confusion. Class names can still look like function names except for our casing convention, which PHP doesn't care about.

I knew when I first saw this issue it would turn into a bikeshed. :)

jhodgdon’s picture

OK....

So it sounds like whether you do new $foo(), new $foo($a, $b), or new $foo [no parens], you get the same behavior: $foo evaluates as a variable to the class name, then the constructor for that class is called. Correct?

In which case, and if we're using the new $foo($a, $b) syntax all over the code, then I think I agree with Island Usurper that saying not to do new $foo() is not any worse confusion.

morbus iff’s picture

I knew that someone would run around trotting out lame coding tropes within 30 comments.

neclimdul’s picture

I'm with the should have () crowd. I'm really of the opinion we should expect our developers to understand their language. And if we expect people to understand what happening when they call new $foo($bar, $baz); then why can't we expect then to understand what's happening in new $foo();. Its just overall more consistent.

merlinofchaos’s picture

I agree that the use of () would be more consistent than dropping it. I like having the ().

jhodgdon’s picture

New proposed standard:

When calling class constructors with no arguments, always include parentheses:
$foo = new MyClassName();
This is to maintain consistency with constructors that have arguments:
$foo = new MyClassName($arg1, $arg2);
Note that if the class name is a variable, the variable will be evaluated first to get the class name, and then the constructor will be called. Use the same syntax:
$bar = 'MyClassName';
$foo = new $bar();
$foo = new $bar($arg1, $arg2);

Can we adopt this?

Crell’s picture

I'm cool with #28.

morbus iff’s picture

Same.

jbrown’s picture

#28++

chx’s picture

Sure. We can do #28

jhodgdon’s picture

Title: Decide on a coding standard for no-arg constructors » Apply coding standard for no-arg constructors
Status: Needs review » Active

OK. I think everyone is agreed then. I can't find any comments above that disagree with #28, aside from arguments from the people above who explicitly said #28 is OK.

I'm going to add this to the coding standards page, in that case. Next step: A core patch to bring it into compliance with this standard. And then also to add this to Coder?

stella’s picture

#28++

berdir’s picture

Status: Active » Needs review
StatusFileSize
new14.26 KB

These should be all. I also changed 3 instances from "StdClass" to "stdClass".

jhodgdon’s picture

Status: Needs review » Reviewed & tested by the community

That patch is fine. Thanks!

dries’s picture

Status: Reviewed & tested by the community » Fixed

Alright, committed to CVS HEAD. Thanks!

jhodgdon’s picture

We need to (a) confirm that all new className() and new $classnamevar() have parens after this patch, and (b) file a Coder issue to get it checking for this, correct?

berdir’s picture

I'm quite sure that I found all of them:

grep -R "= new " includes/ modules/ | grep -v "("

This simply looks for lines with "= new " and then removes those which have a "(". Not perfect since it will not find those that aren't assigned to a variable but I don't get any results for this now.

jhodgdon’s picture

That's reasonable. After expanding the grep to also look for lines like throw new FooException; and looking in the profiles and themes directories as well, I agree you probably got them all.

So is there a Coder issue, or should we file one?

Status: Fixed » Closed (fixed)
Issue tags: -Coding standards

Automatically closed -- issue fixed for 2 weeks with no activity.