views:

241

answers:

5

I'm pretty new to PHP, but I've been programming in similar languages for years. I was flummoxed by the following:

class Foo {
    public $path = array(
        realpath(".")
    );
}

It produced a syntax error: Parse error: syntax error, unexpected '(', expecting ')' in test.php on line 5 which is the realpath call.

But this works fine:

$path = array(
    realpath(".")
);

After banging my head against this for a while, I was told you can't call functions in an attribute default; you have to do it in __construct. My question is: why?! Is this a "feature" or sloppy implementation? What's the rationale?

+12  A: 

My question is: why?! Is this a "feature" or sloppy implementation?

I'd say it's definitely a feature. A class definition is a code blueprint, and not supposed to execute code at the time of is definition. It would break the object's abstraction and encapsulation.

However, this is only my view. I can't say for sure what idea the developers had when defining this.

Pekka
+1 I agree, for example if i say: `public $foo = mktime()` will it save the time from when the class is parsed, constructed, or when its tried to accessed static.
Hannes
As mentioned, it is not defined when the expression will be evaluated. However, you should be able to assign a closure to an attribute - which could return the time without ambiguity - but that yields a syntax error as well.
erisco
So its a bit of BDSM language design in an otherwise very permissive language and implemented as a syntax error?
Schwern
@Schwern ahahahahahahaha! I guess you can put it that way :)
Pekka
Sorry, I tried to edit it to be less argumentative but ran out of time. What I wanted to say ways: I'd like to see a citation for that rationale. That level of BDSM seems wildly out of place in a dynamic language and in PHP in particular. Also, how does executing code at definition time break either abstraction or encapsulation? A class definition doesn't have to be exactly the same every run.
Schwern
@Schwern good points. I don't have a citation for the rationale - as said, it makes architectural sense from my point of view and it's probably safe to say this *was* a conscious decision, seeing as function results can be used almost everywhere else. But I do not know what discussion there was internally when this was decided. If I get around to it, I'll do some digging on the PHP internals mailing list
Pekka
@Hannes That's like removing all the knives and stoves from the kitchen so none of the chefs cut themselves or get burnt. Its very safe, but you can't get much cooking done. Trust your chefs not to be complete idiots.
Schwern
@Schwern I did some searching on the internals mailing list http://marc.info/?l=php-internals but didn't find anything. I bet it's somewhere in there but one has to find the right words....
Pekka
@Pekka Thank you for your efforts.
Schwern
+3  A: 

It's a sloppy parser implementation. I don't have the correct terminology to describe it (I think the term "beta reduction" fits in somehow...), but the PHP language parser is more complex and more complicated than it needs to be, and so all sorts of special-casing is required for different language constructs.

Ignacio Vazquez-Abrams
Do other languages allow this? I'm curious because I honestly don't know. If I remember correctly, Pascal/Delphi doesn't.
Pekka
@Pekka: Static languages usually don't, since a class in them is almost always only a compiler construct. But with dynamic languages, the class is created when the definition is executed so there's no reason that they can't use the return value of the function at that time as the value for the attribute.
Ignacio Vazquez-Abrams
@Ignacio cheers. Okay, that's true. I still think it's a good thing overall, because it enforces good OOP principles.
Pekka
@pekka Perl 6 can do this, here (http://dl.dropbox.com/u/7459288/Perl%206%20Examples/Person.p6 ) is an example.
mfollett
Yes, other dynamic languages allow this. Ruby, Perl 5 (via many means), Perl 6 and Python (I'm pretty sure). Either the PHP language designers got hit on the head and thought they were programming Java, or its an implementation limitation.
Schwern
@pekka What "good OOP principles" are those?
Schwern
+2  A: 

My guess would be that you won't be able to have a correct stack trace if the error does not occur on an executable line... Since there can't be any error with initializing values with constants, there's no problem with that, but function can throw exceptions/errors and need to be called within an executable line, and not a declarative one.

Yanick Rochon
+3  A: 

You can probably achieve something similar like this:

class Foo
{
    public $path = __DIR__;
}

IIRC __DIR__ needs php 5.3+, __FILE__ has been around longer

Robin
Good point. This works because it's a magic constant and will be replaced at the time of parsing
Pekka
Thank you, but the example was only for illustration.
Schwern
+4  A: 
Tim Stone
Thank you! The answer to when to evaluate points out the obvious flaw in PHP's attribute default syntax: you shouldn't be able to assign to it at all, it should be set in the object constructor. Ambiguity resolved. (Do objects try to share that constant?) As for static attributes, there is no ambiguity and they could be allowed any expression. That's how Ruby does it. I suspect they didn't remove object attrib defaults because, lacking a class constructor, there's no good way to set a class attrib. And they didn't want to have separate allowances for object vs class attrib defaults.
Schwern
@Schwern: Happy to help! This is something that I had be curious about in the past but never thought to check out in detail, so this was a good opportunity to figure out what exactly was going on. In regard to the assignment, allowing this kind of assignment avoids forcing you to create a constructor if you don't "need" one...which I feel would be a terrible justification, though in the case of PHP, not a shocking one. I think each instance will replicate default property values on creation, but I might be mistaken, so it's possible that they do try to share.
Tim Stone
In any case, the savings gained by doing so (given the limited data you can assign in the first place) would be minimal, so I'm not sure it would be worth having this setup. As far as your comments about resolving the ambiguity go, I'm inclined to agree.
Tim Stone