You can write good, well-designed code in any language. Working in a good,
well-designed, PHP codebase is probably as pleasant an experience as working
in a good codebase in Javascript, Ruby, or Python. I would rather write in
a good PHP codebase than in a bad Javascript, Ruby, or Python codebase.
However I believe:
Unhealthy PHP codebases are more painful than unhealthy codebases in other languages
You have to fight harder against PHP to keep your codebase healthy than against other languages.
For these reasons, I would strongly recommend against PHP for new programmers and new projects.
I'll provide a couple concrete examples of how I have experienced this:
a) Testing with mocks is difficult because you can't monkey-patch in PHP.
I've had a lot of difficulty writing unit tests for PHP code that wasn't designed
with unit testing in mind. In order to write a good unit test, you need to mock
out your database calls, but that can be really tricky if you are calling methods
that were not designed to injecting mocks them easy.
In Javascript, this is not as much of a problem because you can literally monkey-patch
anything. Here's an example:
constdb=require('./db')asyncfunctionbanUser(id){constresults=awaitdb.query('SELECT ip_address '+'FROM users '+'WHERE id = ?',[id])constip=results[0].ip_addressreturnawaitdb.query('INSERT INTO banned_ips VALUES (?)',[ip])}asyncfunctiontestBanUser(){// Store the original definition of `query`const$query=db.queryletcalls=[]// Monkey patch `query`db.query=asyncfunction(statement,values){calls.push({statement,values})db.query=asyncfunction(statement,values){calls.push({statement,values})}return[{ip_address:'100.100.100.100'}]}awaitbanUser(2)// Restore the original definitiondb.query=$queryexpect(calls[0]).to.deep.equal({statement:'SELECT ip_address FROM users WHERE id = ?',values:[2]})expect(calls[1]).to.deep.equal({statement:'INSERT INTO banned_ips VALUES (?)',values:['100.100.100.100']})}
The "monkey patching it with a mock that when called replaces itself with
a different mock" is kind of ugly, but it works and you can do it much more
cleanly with a library like "sinon.js" -- an incredible library.
In PHP, trying to monkey patch like this just fails. If you do something like
functiontestBanUser(){$database->query=function($statements,$values){echo"Joke's on you! You can't monkey-patch in PHP!";};}
and then call $database->query, it will still just end up invoking the
original definition.
There's a PHP library called "mockery" that can get you a little further than
this, and gives you tools to inject your mocks in certain cases. But it's complicated.
Mocking static methods gets especially hairy, and mocking final methods is
nigh impossible. Overall you just really, really have to fight for it.
b) Another example of how PHP actively leads the user into writing bad
code is superglobals. If you want information about the HTTP request you
are currently serving, PHP's default way of exposing this information to you
is through the $_REQUEST superglobal. As experienced programmers know, global variables are a code smell, and they rapidly lead to unmaintainable
code. In a healthy codebase, you should encapsulate the superglobals, access
them only in one place, define some sort of wrapper object around them to
control access to HTTP request data, and pass such objects as arguments to
the functions/methods that need to access/operate on http request data. It's
possible to do this sort of thing in PHP, but the language makes it
difficult. The language itself is basically actively encouraging you to do
the wrong thing and use these superglobals everywhere.
Node.js does the right thing here. You instantiate the HTTP server yourself
and you create handlers for HTTP requests that take HTTP requests object
in as parameters. No global variables necessary.
These are just two examples -- I could go on and on and write more, but I
believe they speak to a general theme. It's possible to write good PHP --
if you know what you're doing and have a good design sense. But PHP the
language and PHP the ecosystem is going to be fighting you every
step of the way.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
You can write good, well-designed code in any language. Working in a good,
well-designed, PHP codebase is probably as pleasant an experience as working
in a good codebase in Javascript, Ruby, or Python. I would rather write in
a good PHP codebase than in a bad Javascript, Ruby, or Python codebase.
However I believe:
For these reasons, I would strongly recommend against PHP for new programmers and new projects.
I'll provide a couple concrete examples of how I have experienced this:
a) Testing with mocks is difficult because you can't monkey-patch in PHP.
I've had a lot of difficulty writing unit tests for PHP code that wasn't designed
with unit testing in mind. In order to write a good unit test, you need to mock
out your database calls, but that can be really tricky if you are calling methods
that were not designed to injecting mocks them easy.
In Javascript, this is not as much of a problem because you can literally monkey-patch
anything. Here's an example:
The "monkey patching it with a mock that when called replaces itself with
a different mock" is kind of ugly, but it works and you can do it much more
cleanly with a library like "sinon.js" -- an incredible library.
In PHP, trying to monkey patch like this just fails. If you do something like
and then call
$database->query
, it will still just end up invoking theoriginal definition.
There's a PHP library called "mockery" that can get you a little further than
this, and gives you tools to inject your mocks in certain cases. But it's complicated.
Mocking static methods gets especially hairy, and mocking final methods is
nigh impossible. Overall you just really, really have to fight for it.
b) Another example of how PHP actively leads the user into writing bad
code is superglobals. If you want information about the HTTP request you
are currently serving, PHP's default way of exposing this information to you
is through the $_REQUEST superglobal. As experienced programmers know,
global variables are a code smell, and they rapidly lead to unmaintainable
code. In a healthy codebase, you should encapsulate the superglobals, access
them only in one place, define some sort of wrapper object around them to
control access to HTTP request data, and pass such objects as arguments to
the functions/methods that need to access/operate on http request data. It's
possible to do this sort of thing in PHP, but the language makes it
difficult. The language itself is basically actively encouraging you to do
the wrong thing and use these superglobals everywhere.
Node.js does the right thing here. You instantiate the HTTP server yourself
and you create handlers for HTTP requests that take HTTP requests object
in as parameters. No global variables necessary.
These are just two examples -- I could go on and on and write more, but I
believe they speak to a general theme. It's possible to write good PHP --
if you know what you're doing and have a good design sense. But PHP the
language and PHP the ecosystem is going to be fighting you every
step of the way.