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

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, "\n";
$error_reporting = @ini_get( 'error_reporting' );
echo $error_reporting, "\n";
?>

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

apfelstrudel.jpg

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 :-)

Comments

The only reason I've come across in my six years with PHP thus far is the load* methods of the DomDocument class, because an error (of some level) is emitted each time it encounters a validation issue. It would be nice if there was a way to control this and retrieve any encountered errors after the fact, but the only alternative is to do what @ already does and temporarily modify the error reporting level so such warnings don't flood the application output. I do wholeheartedly agree that use of @ should be avoided as much as possible, though.

Still there are cases where @ is very useful:

if (@$_REQUEST['Something'] === 'SomethingElse')

is (I think) much cooler than:

if (isset($_REQUEST['Something'])) if ($_REQUEST['Something'] === 'SomethingElse')

Hi Derick,

It's exactly what we have to do with try/catch blocks. If you always catch the exception without doing anything, it will be very difficult to know where errors are... My strategy would be to always resend an exception or better, not to catch it.

Looks like reasons why @ operator should be fixed :)

Just to clarify on the strudel issue: The common nickname for the "@" sign in Israel is "Strudel", named after the pastry of course.

I assume the folks who wrote this code were from Israel - because up until now I haven't found any other place on earth which calls the at sign this way. It has funny names in all sorts of cultures - but I haven't encountered anyone who calls it a "Strudel" just yet ;)

For the first point: this is why I always define an own error handler. But I absolutely agree with you, the @ operator is very bad habit.

@Donald: this is not a good solution why your code will generate a NOTICE. I know that a lot of people just ignore NOTICEs, but I think a "well formed" PHP program should be NOTICE free, and able to run with E_ALL, while a NOTICE can point lot of times to a programmer error. And for the mentioned problem I usually like to setup the default values in an array with the + operator ($array += $defaults). This is very convenient.

3 reasons saying "it's slow" and not a single benchmark? If you actually benchmarked it, you'd have found out that it's actually not noticeably slower. The only time it's slow is when there is an error invoked, but in this case it is as slow without the @.

See http://vega.rd.no/article/php-performance-error-suppression

The first point is valid, which is why you should try to avoid using it, but when you are required then just use it. One common example is the use of PEAR modules under a PHP5 strict environment, which will not work properly without @.

So the @ operator also prevents the error from being added to the error_log file? Didn't know that.

@Shahar Evron: if you incorrectly use a double colon (::) you can get another lovely error message: PAMAYIM_NEKUDOTAYIM, which is obviously the Hebrew name for a double colon :)

Dead on. I always cry when i find them in peer code reviews. It's slow and ridiculously hard to debug.

My wish list for PHP6 would be for @ to be deprecated as-is, with silence() taking its place, leaving @ available for something useful.

Like anonymous instances.

Example in ORM context: PHP6: $posts = @Post->find(1); PHP5: $post = new Post; $posts = $post->find(1);

PHP6: $query = @User->join(@Comments, @Articles)->getAll(); PHP5: $user = new User; $comments = new Comments; $articles = new Articles; $query = $user->join($comments, $articles)->getAll();

For me the most important reason, according to my experience, is the first one.

I use set_error_handler to define my own custom error report function, and find it very useful since you can really print the information you need, such as variable values.

The custom function can receive - apart of the error number and description - file name, line number and all variables which are defined in the scope. In this way you can print a very clear message, and even distinguish between different files, variables etc. when you generate the warning/error message (you can also redirect it to a log file, and avoid user see it...)

@Jussi: They would never do that, as it would have conflicts with older scripts. Anyway, the better way of doing what you want there is to use the factory pattern as documented in the PHP5 OO Design Patterns page...

Add Comment

Name:
Email:

Will not be posted. Please leave empty instead of filling in garbage though!
Comment:

Please follow the reStructured Text format. Do not use the comment form to report issues in software, use the relevant issue tracker. I will not answer them here.


All comments are moderated

Life Line