PHP has an absolutely enormous library, which makes just about anything possible in a webapp without needing much in the way of external libraries. Templating is a core feature of the language, and with a small bit of planning and a knowledge of how to employ PHP's strengths, you can get a lot done really quickly. Perhaps the biggest stumbling block is inconsistent naming and parameter schemes for the core library, but even that is just a simple lookup in the excellent php.net documentation.
If you're fighting your tools in their strongest domains, you're not using the tools correctly...
"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
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
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.
Did it ever occur to you that some of us prefer it that way?
I really don't mind rolling my own, especially if I'm going to use it thousands of times.
What I do mind is getting "painted into a corner" because the high level tool I'm using never accounted for something I want to do.
Everything I want to do I can do with php. It's stable, popular, and everywhere. It may not be the best, but I don't care. I'd rather spend my extra mental cycles on my customer's problems, not mine.
Delicious isn't tiny.
Digg isn't tiny.
Shit, even my little startup, massify.com isn't tiny.
I think probably the reason that people don't write non-web applications in PHP is because that's not PHP's expressed purpose.
And re: catalyst, that sure is setting the web dev world on fire. /sarcasm