Can't use function return value in write context

I’m testing some form elements to make sure they have been filled in, just for the absence of a blank string essentially …

I usually test for an empty string which always makes me cringe a wee bit just in case I should find myself writing similar thing in my sql statements (instead of testing for null and so on) and thereby invoking the wrath of the sql gods.

I have also embraced NULL objects, they are clever things too.

Anyhow, I usually do something like this: (given $x is an array of form elements).


if( trim($x['type']) == '' ){
// book 'em Danno
}

So I thought to myself try this, which seems more explicit:


if( empty(trim($x['type'])) ) {
// book 'em Danno*
}

Then blimey, up jumps this fatal error which is unfamiliar to me!

PHP Fatal error: Can’t use function return value in write context

Yeah, I have read it, but can someone walk me through exactly why it appears? What was doing the writing?

Edit:

Garh! passed the 5000 posts mark (again) and didn’t celebrate it …

*

From what I remember (I have encountered it too) It has to do with the way empty works…

empty checks if a variable is empty. Since trim returns just a value, empty can not deal with it… its internal workings of empty() Im afraid…


$trimmed = trim($x['type'];

if( empty($trimmed)) ) {
// book 'em Danno*
} 


EDIT:

doh!
http://php.net/manual/en/function.empty.php

gives trim even as an example… stupid me. so I guess that you knew what I was saying

EDIT2:

http://php.net/manual/en/language.types.boolean.php:
FALSE: the empty string, and the string “0”

so you could also do:


if( ! trim($x['type'] ) ) {
// book 'em Danno*
} 


I gave it another thought :slight_smile:

My guess is that the return value of a function tries to “write” that value into a variable. (or thin air in some cases :smiley: )
Probably empty() was not designed to accept the “writing” action of the function, hence the parse error.

The function tries to write its value to a place where it doesnt get accepted :slight_smile:

If I’m not mistaken it used to give that error when you tried


function foo() {
  foreach(func_get_args() as $arg) {
   // do something
  }
}

in php4, where it didn’t like the func_get_args() within the foreach. That works now in PHP5 but the error always stumped me. Assign to a var and loop over that var wasn’t a problem and I’m guessing that will get rid of the error in your case too. Indeed it’s not really clear what’s actually going wrong, although what fristi just said sounds plausible :slight_smile:

I agree with this. trim() returns a value. empty() doesn’t like that. If you put in another function that returns a value, you get the same error.

If you have such conditions for checking your variables, why not write up a custom function to check them?

Thanks for the replies.

@Fristi - actually no I had not seen that line in the manual. :blush:

Your hypothesis is the best (only?) yet, so shall try and internalise it in the hope of one day really understanding it.

Meanwhile I shall treat empty() with a bit more respect - I wonder if there are other PHP functions which behave similarly?

To reiterate, I know how to check my variables, when experimenting around trying to ‘improve’ my tests I fell upon this new (to me) error.

The full context is : In an Ajax framework whereupon an action is taken do a cursory check to make sure the user did not leave a text field empty. It is not supposed to replace a proper filtering or validation operation - just whack up an alert box.

I have little doubt that you know better what you are doing than me. You seem quite on top of things. I meant no offense, I was just throwing the option out there.

Hi Cups :slight_smile:

I’ll give that a shot.

[color=darkred]empty()[/color] and [color=darkred]isset()[/color] are treated in very much the same way by PHP. They’re both language constructs, which means they operate at a much lower level than other things like functions. Because of this, the process is much simpler than with a function.

During the stage where the PHP script is parsed and converted into a list of operations for the zend vm to execute, the parser can spot the [color=darkred]empty(…)[/color] statement (the token sequence that the parser recognises is [color=darkred]T_EMPTY '(' variable ')'[/color]). The [color=darkred]variable[/color] token there is pretty forgiving in what it will accept, including things like function/method calls as well as variables in their many guises. Due to this forgiving statement (which is a good thing!), code like your [color=darkred]empty(trim(…))[/color] will happily get consumed as (at this stage) a valid call to [color=darkred]empty()[/color].

Upon seeing the [color=darkred]empty(…)[/color] statement, the parser calls the engine function [color=darkred]zend_do_isset_or_isempty()[/color]. This function examines the statement and decides which opcodes need to be recorded for later (there are different opcodes for checking whether variables/array items/object properties are set or empty). But before that decision is made, a call to a function called [color=darkred]zend_check_writable_variable()[/color] is made. This is the part of the whole procedure which is most relevant to the thread here. Its job is simply to check that whatever was seen in place of the [color=darkred]variable[/color] token earlier is a “writable variable”, or not.

The source code for the function is very simple so, rather than describe it verbosely, here it is:

zend_check_writable_variable(const znode *variable) /* {{{ */
{
    zend_uint type = variable->EA;

    if (type & ZEND_PARSED_METHOD_CALL) {
        zend_error(E_COMPILE_ERROR, "Can't use method return value in write context");
    }
    if (type == ZEND_PARSED_FUNCTION_CALL) {
        zend_error(E_COMPILE_ERROR, "Can't use function return value in write context");
    }
}

If you don’t read C, don’t be too put off. All that the function does is trigger a familiar error message if the [color=darkred]variable[/color] was a function/method call.

That’s pretty much it for how/when the “Can’t use function return value in write context” error message gets triggered with [color=darkred]empty()[/color]. As for why it needs to be a variable, well as the manual succinctly puts it, [color=darkred]empty()[/color]'s purpose is to (emphasis mine) “determine whether a variable is considered to be empty.” It is really that simple a reason.

Not functions, however the same “writable variable” check is made…
(might not be a complete list, emphasis denotes what is passed to [color=darkred]zend_check_writable_variable()[/color])

  • with [color=darkred]isset()[/color]
    isset([color=darkgreen][b]pi()[/b][/color])
  • with [color=darkred]unset()[/color]
    unset([color=darkgreen][b]pi()[/b][/color])
  • on the left side of an assignment
    [color=darkgreen][b]pi()[/b][/color] = "cake"
  • unary operations
    [color=darkgreen][b]pi()[/b][/color]++
  • array/function references
    trim( &[color=darkgreen][b]pi()[/b][/color] )
    array( "apple" => &[color=darkgreen][b]pi()[/b][/color] )
  • with [color=darkred]list()[/color]
    list([color=darkgreen][b]pi()[/b][/color]) = array('apple')
  • with [color=darkred]foreach()[/color]
    foreach ([color=darkgreen][b]pi()[/b][/color] as $a)
    foreach ($a as [color=darkgreen][b]pi()[/b][/color])
    foreach ($a as $b => [color=darkgreen][b]pi()[/b][/color])

As you can see, all of those occasions are in situations where PHP, for one reason or another, expects to be able to write to (i.e. manipulate) a variable.

Hopefully that helps to clarify things a little. (:

Thank you very much for walking me through what is going on in the background and exposing the need for this to have access to a piece of writable memory.

We are indebted to you for your excellent and well thought out reply. .

Great reply. Isn’t it something that could be trivial to implement though - checking the return values of functions etc - seems like a change that would be useful. Is there a reason it only operates on variables (other than the manual saying so)?

You’re welcome, I don’t post much but like to help when I can.

Presumably, because using [COLOR="Sienna"]![/COLOR] is good enough for non-variables.

Are they equivalent then? Looks like they are, but I had it in my head that empty() returned different results on some data types than comparing to false… I guess not then!

Reposting this old chestnut, the variable tests grid - for any casual onlookers who stumbled across this thread and wondering what is the issue between different comparisons being aired here. Showing its age a bit now, but still a good reference.

Ooh, that’s a handy list.

A little off-topic, but I used to get into trouble with count() myself. I really don’t like it that it returns 1 if you pass it a variable with false as value.


$stmt = $db->prepare('select nothing from some table that has no rows');
$rows = $stmt->fetchAll();

echo "There are " . count($rows) . " result(s).";

But, if there aren’t any rows in the result set, $rows will end up being false.


There are 1 result(s).

:frowning:

Can’t you use $stmt->num_rows for that?

Certainly could do that, but in the past I’ve had a situation where the rows were passed around (and counted) and not the statement itself.

I learned the hard way to not count(false) :slight_smile: