"Best" != "Good"
Web apps are no different than any other kind of app, except they take HTTP requests as input and generate HTTP responses as output. PHP does have some features for speaking HTTP, but that's about it. For the rest of the app, you are on your own.
Let's look at the components of a web app, and see what PHP is missing.
The first thing any application needs is a data model, which is divided into a few tasks -- storing and retrieving the data, and doing something useful with that. For the first half, most people use a database, and talk to it with some library. Obviously PHP apps can talk to the database, but the core support (and Pear DB) are abysmal compared to the DB access layers in other languages (see DBI and JDBC, for example). Most apps these days like to use an ORM, as composing queries with the ORM is easier than writing SQL queries manually. I don't know of any good ORMs for PHP, at least not ones as good as DBIx::Class or Hibernate, so that's one impediment to productivity.
Now that you can get data in and out of your app, you need to do something with it. This is where the real programming happens, and you get annoyed by the fact that a == b but b != a, a !== b but a == b (and the associated "type coercions"), and so on. "Programming language features" alone don't make a language usable, but lack of a sane base makes programming quite difficult. (I could go on for hours here; inconsistent naming conventions, arrays implemented as maps, the lack of lexicals, namespaces, macros and closures, and other features that other languages have had for ages, and so on. This is all well-known, and even the newest PHP newbie knows this stuff is insane or missing.)
You'll also want to use OOP, which PHP does provide, but unfortunately, its take on OO is pathetic. It copies Java flaw-for-flaw, and so is missing key features that make writing readable code so easy, like named initargs. (Java is full of WTFs, but this is the biggest one, IMO. I have to manually initialize every class with a method that can only take positional arguments!?) Its object system has no support for features like roles, and the language doesn't give you any room to implement them yourself. So, I hope you like cutting and pasting, because that's the only way you can share code between classes, unless you think inheritance is an acceptable way to share code, that is... The flaws with the object system go on and on. There is a hack around the type system (interfaces), but there is no type system. WTF? There is no MOP. That's fine for an application developer, but it makes it difficult to write modules. (For what a good MOP can do, see the MooseX:: namespace on CPAN. There are some great time-savers in there; things I haven't seen in any other language, like MooseX::Getopt, and MooseX::AttributeHelpers. I can't live without stuff like this.)
Note to language designers: if you are going to copy a language verbatim, never pick Java. People will laugh at you.
So, uh, now you sort of cobbled together some working app code. Hopefully you didn't couple your domain classes to the database too tightly so that you can write some tests. Tests. What tools do you use for that? I see PHPUnit, but that's about it. There is no framework like Test::Builder (for writing Test modules) and Test::Harness for running those tests. That means there is no way to compose test modules or to write tools to process test output generically. At least you can write tests, though, which I suppose is better than writing your application in bourne shell.
Now you have some tested code that's the core of your application, and can worry about the web side of things. Unfortunately, PHP doesn't do that well here either. The first thing you have to deal with is handling requests. Out of the box, a request for a file on disk results in running that file. This means a nightmarish procedure of manually loading your backend classes, instantiating them, and then doing something to handle the request. Hopefully there are web frameworks that abstract this away. (I've seen web frameworks that dispatch requests, which is nice, but they are still not as good as the dispatchers in other languages' web frameworks. Take a look at Catalyst's Chained dispatch type and find that for PHP. The other issue, component loading, is made nearly impossible by PHP's object system. [No introspection and no type system == no dependency injection.] Java worked around this with annotations, but PHP hasn't come up with anything other than "do it manually" yet.)
At this point, you can run some code when a user visits a URL. Now you need to get data from the user somehow. Usually PHP provides all this data in global (erm, "super" global) variables, so you have the data everywhere in your app all at once. (No need to refactor your code to get at external data, it's all global! Great!) This approach is rather unstructured compared to just getting an instance of a class that has all the data you need in it. It is also not introspectable or composable; as an example, consider REST requests. It's like a regular HTTP request, but with some extra meta-information (for example, if it was a JSON request, you will have some "data" which resulted from parsing the JSON). In Catalyst, the usual HTTP request class has a REST role applied to it. I can introspect the instance, see the role, and know that I can call "data" to get that data. I have no idea how you would implement this in PHP. Perhaps a global variable called $HTTP_REQUEST_DATA that has the data in it. That is nice until someone accidentally overwrites it.
Hopefully there is a framework that abstracts this away, so all your app sees is an HTTP request object. I doubt anyone that uses PHP would think of that, though.
Anyway, I digress. If you manage to deal with getting the data out of the request, you still have to do something and return a result. Doing something is easy because of your well-tested backend classes. Returning the result is not that easy. As far as I know, you are own your own for speaking HTTP, with carefully-timed calls to "headers" and "print". (A framework could fix this with a proper response class, so I am not too worried about this.)
Now you just need to turn the data into HTML. This is supposedly what PHP is best at, but it's still not very good. To start with, all tags start with <?php, whereas most other languages separate the code from markup with <% or [%. (You can use <?, but since that's valid XML, you can't necessarily use it in practice.) Generally, the flaws with PHP are the same as with other templating systems -- processing the template can cause side-effects, your designers can't actually program PHP, etc. (I've used Clearsilver with PHP, which is a pretty nice solution to both problems, but you have to recompile PHP to use it. I don't think that's an option for people that use PHP because it's "easy to install".) Even ignoring that, it's still hard to do simple things with PHP's built-in templating, like filtering. As a result, most PHP programmers tend to use something better, like Smarty (which is really not all that good either, but it's better than nothing).
Anyway, as we've seen, PHP is not good for building applications. It's not good for talking to the database, it's not good for building domain objects that interact with your data, it's not good for testing, it's not good for handling web requests, and it's not good for generating HTML. Considering other languages are good for many of these things, it seems like a no-brainer to use one of those instead. You can use PHP, but you can also just wire together some transistors. The ability to do something doesn't mean you should do it.
I think the problem is that PHP is good enough for tiny web pages that need a bit of dynamic content. The low memory footprint and the ease of getting started definitely make it a favorable choice. The problem comes in considering a web application to be a set of web pages. There is a reason why nobody builds non-web applications in PHP. Unfortunately, web applications are more application than web.