Running following in Terminal on macOS Mojave
php -r 'foo();' 2>/dev/null
Produces
Fatal error: Uncaught Error: Call to undefined function foo() in Command line code on line 1
Error: Call to undefined function foo() in Command line code on line 1
Call Stack:
0.0002 390016 1. {main}() Command line code:0
But on Ubuntu Xenial and FreeBSD it produces no output, which is expected, as I am silencing the error. I tried with zsh 5.7.1, default bash 3.2.57 and bash 5.0.11. I'm interested why is this the case on macOS.
Another interesting thing here is that when redirecting both stdout and stderr, then there is finally no output. But redirecting neither stdout nor stderr has the silence effect.
Some more info:
➜ ~ type php
php is /usr/local/bin/php
➜ ~ file $(type -p php)
php: cannot open `php' (No such file or directory)
is: cannot open `is' (No such file or directory)
/usr/local/bin/php: Mach-O 64-bit executable x86_64
➜ ~
Best Answer
This particular behaviour have nothing at all to do with any "impossibility" of redirecting subprocess stderr on macOS. It is not impossible, and works like you would expect - i.e. similar to Linux and FreeBSD.
The gotcha here is that you're using different PHP configurations on those setups. It seems you're not using the stock supplied PHP, but rather something else - but I'll explain according to the stock PHP considering that the same will apply to a custom install.
The way PHP works is that it outputs these types of errors messages according to the setting of the "display_errors" configuration directive. You can choose to either mute it completely (i.e. never output the error), output to stdout (the default) or output to stderr.
You'll commonly set the value of display_error in /etc/php.ini - if you haven't got a php.ini file, or display_errors is not set in it, it will default to stdout. If you're using a custom built php, the location of the php.ini file will vary.
You can test this out by performing the following in the Terminal:
Produces
However running:
Produces no output.
You can also use
1>/dev/null
to redirect stdout, and then you'll see that the behaviour reverses between those two examples.Note also that the setting "on" for display_errors is the same as "stdout".