New to PHP 5.4: Traits
simas.posterous.com
simas.posterous.com
One of the noteworthy language additions are Traits – a brand new horizontal code reuse mechanism.
Code re-use mechanism? Horizontal? I didn't realize PHP was so geometric. What an interesting concept.
While traits are technically different from mixins – they are both designed to fill the same gap left by a single-parent inheritance model
So let me try and get this straight: mixins fill a gap left by a single-parent inheritance model and so traits are like mixins because PHP has a single-inheritance model but traits are technically different in some way?
This is getting confusing...
Python, Perl, Ruby, and many languages with multiple inheritance have been using mixins for... well a long time. It's pretty straight forward.
Perl5 got "roles" through Moose and Perl6 has them natively. I think they're explanations are a little more clear. (http://search.cpan.org/dist/Moose/lib/Moose/Manual/Roles.pod)
As the original author of the patch Stefan Marr pointed out – traits are nothing more but a compiler assisted copy and paste.
In true PHP fashion! What an interesting way to think about it.
Conflicts and Aliases
Ok, this is pretty cool. It's an edge case, but there's already a solution. Neat.
Oh... wait a second, I think I've found the meat of it:
Trait method definitions support all modifiers just like regular class methods do – that includes visibility modifiers, final, static and abstract. The latter can be used to define abstract methods expressing requirements for this particular trait
So this is how they're slightly different than regular-old classes? By being able to use all the same keywords as regular old classes?
It seems that the meat of it is at the very end:
Traits only take part in the process of building a class and do not add any new run-time semantics. That means that usual PHP object-oriented functionality (like late static bindings) work as expected when combined with traits.
But this next one is a bit of a gotcha:
Traits can be composed from other traits (the same way classes use traits).
Wait... what? Why would you want to do this? You're just getting back into hierarchies again... and pretty much just working around multiple-inheritance without calling it such.
It's good to see PHP is evolving. Most people still think of PHP4 and shudder (and rightfully so). Evolving means it might be catching up to the superior languages it competes with in the web space. (Note the tongue-in-cheek).
I'm still not convinced, but it's a step forward I am sure for PHP programmers everywhere. Just don't actually use trait-inheritance and you should be fine.
It indeed seems like that ;-)
There are four concepts here:
* Multiple inheritance
* Interfaces (abstract types)
* Mixins
* Traits
Multiple inheritance is available in languages like C++ and Python. But multiple inheritance has well known highly problematic side effects that resolve around diamond inheritance and method aliasing. Basically you have a directed graph of parent classes (with diamonds and all) all implementing a foo() method, and then have to pick a method of linearizing that graph to find out which method actually wins if someone calls o.foo(). There are multiple solutions to that, but all are non-intuitive and have caused heaps of trouble. It's just way to hard for a programmer to reason which method will be called in his program.
This is why Java did away with multiple inheritance and just has interfaces. A lot simpler, a lot cleaner, and easy to understand, but sadly lacking in functionality.
The problem is supposed to be fixed by traits and mixins. I think there is no really clean demarcation between the two. My understanding is that traits are more or less mixins, but with the aliasing features and similar stuff you highlight.
The traits are nothing more but a compiler assisted copy and paste is actually a feature, not a bug. This is exactly what you actually want to keep your program understandable: the code/functionality from traits is effectively treated as copied into your class, and there is exactly one "winner" in the race to be the "foo()" method for this class, where the winner is determined by things like aliasing, or order. All the weird situations where parent classes call a method that is overridden in some branch of the inheritance graph just don't occur.
So traits/mixins are IMHO a huge step forward from multiple inheritance or just interfaces. Not that this will help with the mess that is PHP much ...
Another interesting side effect is that even if you don't use a particular trait, but a conflict appears upstream - boom - "Warning: Trait method hello has not been applied, because there are collisions with other trait methods on TraitsTest in %s on line %d"
If I understand it correctly: You will be warned that a collision appeared, NONE of them will be applied and it's only a warning, so it won't stop the execution until you step on the "missing method" trap. Now that's a creative way to shoot yourself in the foot.
So when you say "even if it doesn't use the methods in question" -- well, the compiler has no way to know that.
While odd, and somewhat limiting, there is also some benefit to the static object model that PHP has. It gives some point of certainty in a language that is otherwise very dynamic. In general, it leads to easier-to-reason-about code.
All with the IMHO disclaimer, understood.
This isn't exactly true. PHP objects can have dynamically assigned members without having magic methods defined. e.g.
$foo = new stdClass();
$foo->bar = "baz";
echo $foo->bar; //"baz"
And now, with "closures", those members can be methods.One of the primary purposes of traits is to fix the problem of having to linearize mixins/parents. Aliasing does ease the problems of linearization, but it doesn't solve them all.
If in a mixin-based system like Ruby you include two modules which initially have no conflicting methods, but at some point methods are added such that there is a conflict, the conflict is silently ignored.
Yes, you should pay attention to changes to the mixins you use, but linearization means that whenever a method is added to a mixin, being certain that your module/class has the right method means going through every single class that uses the mixin and looking at all the other mixins that class uses to ensure that they do not conflict. Traits make changing code easier by doing that checking for you. The one disadvantage is that if you want to ignore a conflict you have to say so, but I don't actually think that is a disadvantage: if a Ruby module says "include Foo, Bar" and both Foo and Bar have an explode method, someone reading the code can't tell if the writer of the code was aware of the two explode methods and chose to use the Bar method, or if they were unaware of the conflict and might have either wanted no explode method or the Foo explode method.
To me traits are actually a better solution than mixins via dynamism, since you get syntax checking via the compiler along with the benefit of compiled speed. You also have less confusion due to mixin conflicts.
I am super excited about this. Thanks PHP!
I know this is a single example, but I feel like I've seen bad OOP practices in PHP so many times in the past. This is such a rich feature but it brings with it a lot of potential for misuse.
That class extends SPL ArrayObject class which provides array-like access syntax (operators). It does not mean it has to act like an in-memory array, it could read data from disk, session, database or whatever. Delegating this kind of functionality to a single array would simply not work without further abstraction.
They are global shared state than can be unexpectedly accessed from any place in the program.
Encapsulation provided by singletons makes them even more problematic than global variables. You can temporarily overwrite global variable, run function that uses it, and restore global value. Singletons can be designed to prevent that, which kills testability of code that uses singletons (e.g. when you need to replace global DB connection with mock object).
http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140...
http://www.c2.com/cgi/wiki?SingletonsAreEvil
I don't know the particulars of how PHP's ArrayObject works, but take Ruby for example: instead of inheriting from Array, to make a class that operates like an array, one mixes in the Enumerable module (similar to a trait) and implements an "each" method to handle the low level access. All of the other methods come along with the module. In this way, the lineage of a class isn't important; rather, it's what it actually does that matters. (How meritocratic! :-)
But particulars of any language aside, I think this is an important step for PHP. Once a language gives the ability to compose the behavior of a class through traits, modules, mixins, etc rather than standard inheritance, it becomes easier to separate the data that a class encapsulates from the behaviors that it provides—and that, I believe, leads to more modular code.
In Ruby, no one cares who your parents were, all
they care about is if you know what you are
talking about.
- Logan CapaldoHonestly, I would prefer PHP remove all the bad parts of PHP before adding new things.
All this even though most PHP4 scripts run unaltered in php5.
And now you suggest to drop backwards compatibiliy to get rid of warts? How long do you think it would take before that new language will get any significant deployment? What good will all the new features be if you are not able to use them in the next 10+ years?
Personally, I like to see them add features in a backwards compatible way. Knowing PHP and programming in general, I can stay away of the bad parts but still use all the new features in the foreseeable (2-3 years) future.
Just have a look at python3 of you want a taste if what you were just suggesting.
The fact that PHP broke many websites when going from PHP3 to PHP4, then at each PHP4 minor release is probably largely responsible for this conservatism.
This can happen in a more feasible way, e.g. freeze PHP5 (unlike the current situation), don't add any new stuffs; branch out a subset of PHP (removing bad parts), and continue the development there.
PHP 5.4 will also drop:
- safe mode
- register_globals
- allow_call_time_pass_reference/y2k_compliance and other legacy ini stuff
- "continue 123" syntax (can be replaced with goto). As far as I recall this is done since it's barely used and if removed would allow to implement some opcode performance optimizations.
Dropping some PHP 4 era syntax is obviously out of question due to already mentioned webhosting and backwards compatibility issues.
This has already been done, and the result is called "Python" (or Ruby, or ...). SCNR.
Edit: ok, I realize this was maybe a bit too snarky. But I mean it: if you remove all the somewhat strange parts from PHP, you are breaking backwards compatibility. And at that point, your users have no reason not to move to another, maybe cleaner language (e.g. Python) all together.
So if you want to get a cleaner PHP while breaking backwards compatibility, you might as well just use a different language.
Standardizing things, making libraries consistent, and adding some type of strict typing would be the nicest things to do, I believe.
The fun starts when you hit one of the situations where the whole php stacktrace is `in file "" at 0` (or something similar). I've got 4 php programmers around me and I hear about the "white page" almost once a week, so it's not that uncommon :(
Shutdown functions still run after a fatal error (something I just learned), so you can display the error or redirect to another page:
function shutdown() {
if (($error = error_get_last())) {
ob_clean();
// report the event, send email etc.
// Redirect
header("Location: http://hostname/error");
}
register_shutdown_function('shutdown');
As for the empty stracktrace, I used to encounter those frequently but I don't anymore. Either Zend has been fixing them up, or I'm doing less work inside of various handlers.There are some situations which cause PHP to just die and not log anything, but those are pretty rare.
It's pretty common practice to have display_errors to on for development, and off for production. Are you not doing that already? Or is everyone developing on production systems?
Use SplFileObject (part of SPL that is part of PHP core) for object-oriented way to manipulate files. On a failure (like your permission issue) it throws Exceptions and if not handled this will result in a "hard" error with a backtrace log.
Shudo Maeda, a colleague of Matz, went into greater detail about Refinements in his talk. You can find the slides here: http://slidesha.re/9rbfwN
we are running the system on our own dedicated CentOS server ... and although we are developing with PHP 5.3, the most updated good package repository I've found is Jason Litka's repo, with PHP 5.2.x, he says he still got compatibly problems with the Zend Framework, so he won't update. so i guess using 5.4 on production will take some time. and i'm not talking about other virtual hosting providers who tend to update this things very slowly.
here corrected link to php53-fpm http://dl.iuscommunity.org/pub/ius
nginx latest repo http://centos.alt.ru/pub/repository/centos/5