I suggest using PHPUnit for unit testing, if you want to have annotations for marking your tests, and data providers, and data driven tests, and so on. Not to mention, it seems to get all the integration love when it comes to things like continuous integration (cruise control, bamboo, hudson, etc...).
PHP 5.3, it's a big jump, and it's throughly worth it in terms of language features. It maybe rough around the edges, but this is a startup and they'll be fixed up releases by the time you launch.
As far as magic methods go __invoke() alone is a big deal, but it doesn't have the reciprocal method for it, even then, paired with array_map, array_reduce, and array_filter, and some wrappers you can do some amazing functional programming.
__get, __set, and __call are really handy as well, I used these and some interface/class naming convention trickery to implement traits prior to 5.3, but now you have traits, as well.
Also have a look at the addendum library, written by derik rethans of ezComponents, and XDebug fame, it allows you to do annotations for php 5+. It's not bad, and performance is a non-issue with caching.
For profiling, you can use xdebug + webcachegrind.
The best IDE is probably the free eclipse PDT, if you use type hinting on parameters, and phpdoc comments for parameters and returns it can figure things out from those and provide you code completion. That should give you decent intellisense.
BTW, it's tempting to do all sorts of crazy string concats, or variable variables, or variable method calls, or variable class creation, do this in more than one place, that's not well documented and easy to search via regex, and you're SCREWED. Forget hard to debug, but refactoring is a major pain. This is something people rarely consider php has NO automated refactoring tools, and refactoring large code bases is VERY hard to do in php.
A few things to caution you, even if you smell the slightest bit of possibility that you might have to deal with multi-byte chars, or 'exotic' character encodings, I strongly urge you to wrap up string handling. In fact, introducing a thin layer of indirection which allows you to shim between or act as seams for testing/injectability between your code and built-ins will make your life easier. Not strictly necessary, but unless you have the benefit of foresight, it's hard to tackle internationalization or such large cross-cutting projects.
autoload, learn it and love it. Run away from hard coded require/includes, or worse, their *_once variants, they tie your hands in terms of injection, instead use an autoloader, simplest thing is to jam all your includes in a array, keyed on the class name, and the value is the file path from some root, it's fast. The wicked thing about this is that it makes testing really easy, as you've implemented a class loader, and so you can do some really neat stuff with it.
PHP 5.3 has name spaces now, jump for joy and use them like a mad man. This alone provides an opportunity to create seams (rare) for testing/injections.
Opcode caches, file accesses are slow, to avoid them, use an opcode cache, it's not just the file access, it's all the parsing, really. If you don't have to parse PER request, it makes a BIG difference. Even doing this for a front controller/interceptor will give you a lot of benefits.
Think different, one of the most troubling things for PHP programmers if they come from Java/.Net is that your application server is spread across PHP/Apache, or whatever web server you're using.
Phing/Ant/PHPMaven early on it seems easy just to jam everything in, but build scripts are still useful in php and they have some great support.
I had trouble with method overloading, and still contend with it. I came up with a pattern to alleviate a certain aspect of it. I often had many things that could fulfill a certain parameter, so when you document it @param mixed(int|array|fooObject) if those were the possibilities, I created a static method called Caster::CastTo($param, $toTypeAsString) that would just run through a case matching the type and trying to convert it to a known type. The rest of the method could then assume that one type, or a failure to convert, and work with that. And since I jammed ALL conversions in one class, it stopped mapping of types from being a cross cutting concern, and since these functions can be individually tested, I could test them once, and rely on them everywhere else.