Only variables should be passed by reference
Asked Answered
U

13

321
// Other variables
$MAX_FILENAME_LENGTH = 260;
$file_name = $_FILES[$upload_name]['name'];
//echo "testing-".$file_name."<br>";
//$file_name = strtolower($file_name);
$file_extension = end(explode('.', $file_name)); //ERROR ON THIS LINE
$uploadErrors = array(
    0=>'There is no error, the file uploaded with success',
    1=>'The uploaded file exceeds the upload max filesize allowed.',
    2=>'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
    3=>'The uploaded file was only partially uploaded',
    4=>'No file was uploaded',
    6=>'Missing a temporary folder'
);

Any ideas? After 2 days still stuck.

Uglify answered 8/1, 2011 at 21:10 Comment(2)
A better explanation for the reason vijayasankarn.wordpress.com/2017/08/28/…Analogue
Warning about this: as of PHP 7.4 (perhaps earlier versions, too), assigning a variable inline as a function argument/parameter which is passed by reference results in this notice-level error. E.g. onlinephp.io/c/4c871Wadi
S
658

Assign the result of explode to a variable and pass that variable to end:

$tmp = explode('.', $file_name);
$file_extension = end($tmp);

The problem is, that end requires a reference, because it modifies the internal representation of the array (i.e. it makes the current element pointer point to the last element).

The result of explode('.', $file_name) cannot be turned into a reference. This is a restriction in the PHP language, that probably exists for simplicity reasons.

Spic answered 8/1, 2011 at 21:14 Comment(5)
@Oswald, We can turn the warning off using error_reporting. Is it safe to do so?Vento
It is safe to turn off error_reporting. It is not safe to blindly ignore errors. Turning off error_reporting is a major step towards blindly ignoring errors. In the production environment, turn off display_errors instead, and write errors to a log file.Spic
Not working. The answer below - double parenthesis - works.Pressing
How to turn off these notices however? I tried ignoring E_NOTICE but they still show upAlarick
Every day I hate PHP a little more... :-(Chlorpromazine
D
68

Everyone else has already given you the reason you're getting an error, but here's the best way to do what you want to do: $file_extension = pathinfo($file_name, PATHINFO_EXTENSION);

Deaton answered 8/1, 2011 at 21:21 Comment(2)
I agree. There's no point in using string manipulation to parse file paths when you have appropriate APIs to do so.Borroff
this is the best answer for me, the string manipulations just add little mess in the codeSchramke
T
59

Php 7 compatible proper usage:

$fileName      = 'long.file.name.jpg';
$tmp           = explode('.', $fileName);
$fileExtension = end($tmp);

echo $fileExtension;
// jpg
Tipperary answered 9/1, 2014 at 9:35 Comment(7)
Weird. That works but how? Does it suppress the warning, similar to what the @ prefix does?Scandian
@NigelAlderton No it does not suppress errors like @ does. Try $test = end((explode('.', array('Error!')))); ExampleDemetriusdemeyer
So why does adding an extra parenthesis remove the error?Scandian
I researched this quirk and it seems to be a bug? with the php parser where double parenthesis "(())" causes the reference to be converted to a plain value. More on this link.Hershberger
I like this .. but I don't like it at the same time. Thanks for ruining my day :-)Greeson
In php7 warning will be still issued. php.net/manual/en/…Sneak
So, it works because it's a bug you should not suggest it, no?Thresher
M
31

save the array from explode() to a variable, and then call end() on this variable:

$tmp = explode('.', $file_name);
$file_extension = end($tmp);

btw: I use this code to get the file extension:

$ext = substr( strrchr($file_name, '.'), 1);

where strrchr extracts the string after the last . and substr cuts off the .

Marrin answered 8/1, 2011 at 21:14 Comment(0)
S
24

The answer given elsewhere,

$tmp = explode('.', $fileName);
$file_extension = end($tmp);

is correct and valid. It accomplishes what you are trying to do.

Why?

The end() function does not do quite what you think it does. This is related to how the PHP array data structure works. You don't normally see it, but arrays in PHP contain a pointer to a current element, which is used for iteration (like with foreach).

In order to use end(), you must have an actual array, which has attached to it (normally, invisibly), the current element pointer. The end() function physically modifies that pointer.

The output of explode() is not an actual array. It is a function output. Therefore, you cannot run end(explode()) because you violate language requirements.

Simply setting the output of explode() in a variable creates the array that you're looking for. That created array has a current element pointer. Now, all is once again right in the world.

So what about the parentheses?

This is not a bug. Once again, it's a language requirement.

The extra parentheses (like end((explode()))) do more than just grouping. They create an inline instance variable, just like setting the function output to a variable. You may think of it as a lambda function that is executed immediately.

This is another correct and valid solution. It is perhaps a better solution, as it takes less space. A good reviewer or maintainer should grok what you're trying to do when they see the extra parentheses.

If you use a linter or SCA program like PHPCS, that may dislike the extra parentheses, depending on the linting profile you're using. It's your linter, tell it what you want it to do for you.

Some other answers also list things like the spread operator or array_key_last(), which are also reasonable solutions. They may be perfectly valid but they're more complicated to use and read.

I'll just use the @ prefix

This solution is valid, however incorrect. It is valid because it solves the problem. That's about the end of its merit.

Suppressing errors is always bad practice. There are many reasons why. One very large one is that you are trying to suppress one specific error condition (one that you have created), but the error suppression prefix suppresses all errors.

In this case, you will probably get away with this. However, engaging in bad programming habits is cheating and will likely lead you to cheat more and bigger in the future. You will be responsible for bad code. But I'm not the Code Police and it's your code. It's valid because it solves the problem.

Okay, so what's the best answer?

Do what @ryeguy suggests. Don't do string manipulation to solve a well-defined problem that the platform already solves for you. Use pathinfo().

This has the added benefit that it actually does what you want, which is finding the extension on a file name. There is a subtle difference.

What you are doing is getting the text following the final dot. This is different from finding the file extension. Consider the file name, .gitignore. PHP knows how to handle this. Does your code?

Once again, I'm not the Code Police. Do what suits you best.

Solatium answered 9/7, 2021 at 13:4 Comment(1)
end((explode())) is still giving the notice; not sure if this changed in recent PHP versionsTrace
P
13

Since it raise a flag for over 10 years, but works just fine and return the expected value, a little stfu operator is the goodiest bad practice you are all looking for:

$file_extension = @end(explode('.', $file_name));

But warning, don't use in loops due to a performance hit. Newest version of php 7.3+ offer the method array_key_last() and array_key_first().

https://www.php.net/manual/en/function.array-key-last.php

                 uuuuuuu
             uu$$$$$$$$$$$uu
          uu$$$$$$$$$$$$$$$$$uu
         u$$$$$$$$$$$$$$$$$$$$$u
        u$$$$$$$$$$$$$$$$$$$$$$$u
       u$$$$$$$$$$$$$$$$$$$$$$$$$u
       u$$$$$$$$$$$$$$$$$$$$$$$$$u
       u$$$$$$"   "$$$"   "$$$$$$u
       "$$$$"      u$u       $$$$"
        $$$u       u$u       u$$$
        $$$u      u$$$u      u$$$
         "$$$$uu$$$   $$$uu$$$$"
          "$$$$$$$"   "$$$$$$$"
            u$$$$$$$u$$$$$$$u
             u$"$"$"$"$"$"$u
  uuu        $$u$ $ $ $ $u$$       uuu
 u$$$$        $$$$$u$u$u$$$       u$$$$
  $$$$$uu      "$$$$$$$$$"     uu$$$$$$
u$$$$$$$$$$$uu    """""    uuuu$$$$$$$$$$
$$$$"""$$$$$$$$$$uuu   uu$$$$$$$$$"""$$$"
 """      ""$$$$$$$$$$$uu ""$"""
           uuuu ""$$$$$$$$$$uuu
  u$$$uuu$$$$$$$$$uu ""$$$$$$$$$$$uuu$$$
  $$$$$$$$$$""""           ""$$$$$$$$$$$"
   "$$$$$"                      ""$$$$""
     $$$"                         $$$$"
Phi answered 3/11, 2019 at 19:4 Comment(0)
A
10

Try this:

$parts = explode('.', $file_name);
$file_extension = end($parts);

The reason is that the argument for end is passed by reference, since end modifies the array by advancing its internal pointer to the final element. If you're not passing a variable in, there's nothing for a reference to point to.

See end in the PHP manual for more info.

Algesia answered 8/1, 2011 at 21:16 Comment(0)
H
10

end(...[explode('.', $file_name)]) has worked since PHP 5.6. This is documented in the RFC although not in PHP docs themselves.

Hundredpercenter answered 16/4, 2019 at 0:52 Comment(2)
I like this one line solution.Nunci
this is clever – using extra brackets does not suppress the notice, but the spread conversion does.Trace
M
9

PHP complains because end() expects a reference to something that it wants to change (which can be a variable only). You however pass the result of explode() directly to end() without saving it to a variable first. At the moment when explode() returns your value, it exists only in memory and no variable points to it. You cannot create a reference to something (or to something unknown in the memory), that does not exists.

Or in other words: PHP does not know, if the value you give him is the direct value or just a pointer to the value (a pointer is also a variable (integer), which stores the offset of the memory, where the actual value resides). So PHP expects here a pointer (reference) always.

But since this is still just a notice (not even deprecated) in PHP 7, you can savely ignore notices and use the ignore-operator instead of completely deactivating error reporting for notices:

$file_extension = @end(explode('.', $file_name));
Monostome answered 1/9, 2016 at 17:2 Comment(4)
@OskarCalvo That's also my philosophy. But this is not an error - PHP treats it as a "notice". And it was an alternative "solution" to other answers here, which no one directly mentioned it. A better way would be to save the value of explode to a temporary variable, like others wrote here. But again: This is not an error, so it is ok to use this operator. PHP is generally bad at error handling. Therefore I would suggest to use set_error_handler and set_exception_handler for error handling and as cleanest solution.Monostome
It's a horrible answer. An error could be everywhere - for example, $file_name could be unsuitable for explode or doesn't exist. But @ will suppress them all.Marks
I'm not down voting this since it's algo a valid answer, but it's certainly a very bad advice. You may get mad to debug later if something occurs there...Nunci
@YourCommonSense You may not like the advice given and I agree. But the answer explains the origin of the message, which is too often missing in SO answers. When I understand what's going on, I can make up my mind about the advice. I would not want to use the @ myself, but I think the answer is fine.Tiddlywinks
S
4

Just as you can't index the array immediately, you can't call end on it either. Assign it to a variable first, then call end.

$basenameAndExtension = explode('.', $file_name);
$ext = end($basenameAndExtension);
Stolon answered 8/1, 2011 at 21:17 Comment(0)
S
0

PHP offical Manual : end()

Parameters

array

The array. This array is passed by reference because it is modified by the function. This means you must pass it a real variable and not a function returning an array because only actual variables may be passed by reference.

Supramolecular answered 8/8, 2015 at 7:53 Comment(1)
Make a quote from official manual, don't rewrite by your own hands. Also, consider to make your answer better than existing one.Impatiens
E
0

First, you will have to store the value in a variable like this

$value = explode("/", $string);

Then you can use the end function to get the last index from an array like this

echo end($value);

I hope it will work for you.

Entebbe answered 3/5, 2019 at 9:53 Comment(0)
H
0

assume:

function endSimulator (&$var) {
    echo $var;
}
function explodeSimulator ($var) {
    return $var;
}

now, in controadictory with explodeSimulator which can get "functions output" you can feed endSimulator just by variables otherwise php says "only variables should be passed by reference" you can check this with below code:

  endSimulator(explodeSimulator(":)")); // warnings arise

why?
because when php reachs "& symbol", he looks for a "pointer to sth (a variable in general terms)" after it, any variable is a "pointer" (to a perimitive or reference-see additional information) but functions can be evaluate to values not pointers! (although i think in javascript they point to a variable whith the same name of corresponding function)
in another words, when you use endSimulator(explodeSimulator(":)")), interpreter see something like endSimulator(":)"), as you know you can not send ":)" to a function which is just feeded with pointers
so finally you can solve this like:

  $explodeSimulatorOutput = explodeSimulator(":)");
  endSimulator($explodeSimulatorOutput);

additional information

i think many peaple here have an ambiguius mind about the phrase "pass by reference", please note that "passed by reference" is a general idea and can be used in vast various of situations like:

a=10   // a <-- 10; 
// content of a is passed by a value into it

b="a" // b <-- "a";
// content of b is passed by a value into it

obj = {a,b} // obj <-- {} <-- 10,"a";
// content of obj is passed by a reference (which is {}) into it

function func1 (var) {...}
// the "var" variable is passing by value

function func2 (&var) {...}
// the "var" variable is passing by reference into the function

"passing by reference" in this topic refers to the concept of func2 not a, b or obj

Hero answered 11/8, 2023 at 15:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.