Five reasons why the shut-op operator (@) should be avoided

Skien, Norway

Reason 1: It creates a debugging hell

The @-operator is often used to silence errors in noisy PHP functions—functions that generate warnings that can not be easily prevented. An example might be to silence network errors with stream_socket_client() , or hiding connection errors for mysql_connect() . In those cases, there is no way how to check up-front whether the function call will not issue a warning when being called (unlike fopen() where you could first call file_exists() for example).

Using the @-operator can have annoying side effects however. Years ago I was helping a co-worker debugging a MySQL issue with our software eZ Publish . We could not find out why it would not successfully create a connection to the database through Apache, while it was working fine from the command line with the same code. We started browsing through our code and found that the mysql_connect() call was prepended by the @-operator to hide possible connection warnings/errors from our users. After removing the @ to see what errors we would get, we were surprised to see that the error message was "Fatal error: Call to undefined function mysql_connect()". Turned out that the MySQL extension was a shared object that was only loaded by the php.ini file that the command line client of PHP was using, while the one used by the PHP in Apache did not load the extension. In this case, only after about two hours, we found that the @-operator was hiding slightly more errors than we'd expected.

From then on, we made it our policy that the @-operator should be avoided, and if used, only is allowed with a comment with what error we're supposed to be hiding with them.

Reason 2: It's slow (part 1)

Whenever the @-operator is used, PHP needs to invoke the INI settings mechanism to change the temporary value of the error_reporting setting to 0. That this happens, can be seen in the following example:

<?php $error_reporting = ini_get( 'error_reporting' ); echo $error_reporting, "

"; $error_reporting = @ini_get( 'error_reporting' ); echo $error_reporting, "

"; ?>

The output of this example is:

32767 0

It requires the INI mechanism because it allows a proper clean-up at the end of each request, where every internal value of each INI setting is reset back to its original value. Without this, a call such as @die(); would set error_reporting to 0 and when the script bails out PHP does not get the chance to reset it back to its original value.

Reason 3: It's slow (part 2)

Whenever PHP generates an error message internally, it's processed and formatted all the way up to the fully formatted message that can be outputted straight to the browser. Only just before it is displayed the error_reporting setting is checked. This however, is not related to the @-operator exclusively. The error message is just always fully formatted before error_reporting is checked—or display_errors for that matter.

Reason 4: It's slow (part 3: It generates crappier code)

The reason why I started writing about the @-operator comes from a new feature that I am implementing for Xdebug : the addition of variable assignments in function traces . When writing tests I found out that the Zend compiler generates quite a bit slower code in case the @-operator is used. With VLD we can see this difference clearly. For the code:

<?php $t['a'] += $b; ?>

The compiler creates the following opcodes:

compiled vars: !0 = $tf, !1 = $t, !2 = $b 15 EXT_STMT 16 ASSIGN_ADD !1, 'a' 17 ZEND_OP_DATA !2, $7

But for the code:

<?php @$t['a'] += $b; ?>

The compiler generates:

18 EXT_STMT 19 BEGIN_SILENCE ~8 20 FETCH_R local $11 'b' 21 FETCH_RW local $9 't' 22 ASSIGN_ADD $9, 'a' 23 ZEND_OP_DATA $11, $12 24 END_SILENCE ~8

This shows that when the @-operator is used, the compiler does not generate the

much faster compiled variables that were introduced with PHP 5.1. Instead, it falls back to use the FETCH_* opcodes that look up variables by name. This is much slower as it requires a hash lookup. On top of that, more opcodes are generated as well.

Reason 5: Apfelstrudels were harmed

The last reason is a bit of a silly one. While looking at the implementation of the @-operator, I found the following bits of code in zend_compile.c :

void zend_do_begin_silence(znode *strudel_token TSRMLS_DC)

and

*strudel_token = opline->result;

This last reason is of course not the most important one :-)