315
// 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.

u_mulder
  • 54,101
  • 5
  • 48
  • 64
Frank Nwoko
  • 3,816
  • 5
  • 24
  • 33
  • 6
    A better explanation for the reason https://vijayasankarn.wordpress.com/2017/08/28/php-only-variables-should-be-passed-by-reference/ – Anant Sep 20 '18 at 05:44
  • 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. https://onlinephp.io/c/4c871 – Troy Niemeier Jan 06 '23 at 20:35

13 Answers13

648

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.

Oswald
  • 31,254
  • 3
  • 43
  • 68
  • 1
    @Oswald, We can turn the warning off using `error_reporting`. Is it safe to do so? – Pacerier Dec 09 '14 at 11:48
  • 10
    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. – Oswald Dec 10 '14 at 23:08
  • Not working. The answer below - double parenthesis - works. – bbe Oct 29 '16 at 14:38
  • How to turn off these notices however? I tried ignoring E_NOTICE but they still show up – NaturalBornCamper Nov 11 '17 at 15:41
  • Every day I hate PHP a little more... :-( – MarcoS Jul 28 '21 at 14:30
67

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

ryeguy
  • 65,519
  • 58
  • 198
  • 260
59

Php 7 compatible proper usage:

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

echo $fileExtension;
// jpg
Sinan Eldem
  • 5,564
  • 3
  • 36
  • 37
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 .

Floern
  • 33,559
  • 24
  • 104
  • 119
22

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.

Stephan Samuel
  • 648
  • 6
  • 12
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$$$
  $$$$$$$$$$""""           ""$$$$$$$$$$$"
   "$$$$$"                      ""$$$$""
     $$$"                         $$$$"
NVRM
  • 11,480
  • 1
  • 88
  • 87
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.

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
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));
wizard
  • 213
  • 1
  • 4
  • 8
  • 3
    @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. – wizard Dec 04 '16 at 12:57
  • 1
    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. – Your Common Sense Aug 25 '21 at 13:25
  • 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... – Luciano Fantuzzi Feb 26 '22 at 23:38
  • @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. – Lutz Prechelt Sep 16 '22 at 16:12
9

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

Tgr
  • 27,442
  • 12
  • 81
  • 118
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);
jon_darkstar
  • 16,398
  • 7
  • 29
  • 37
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.

evenvi
  • 11
  • 4
  • 3
    Make a quote from official manual, don't rewrite by your own hands. Also, consider to make your answer better than existing one. – VP. Aug 08 '15 at 07:59
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.

sanyassh
  • 8,100
  • 13
  • 36
  • 70
Jailendra Rajawat
  • 101
  • 1
  • 1
  • 9
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