Why strtolower() does not follow the PHP strict Standard - php

If i use
strtolower(end(explode('.',$_FILES['file']['name'])));
it give me error
PHP Strict Standards: Only variables should be passed by reference in
I thought ok I just store the values in a variables first, and then use explode
$filename = $_FILES['file']['name'];
$filearray = explode('.',$filename);
and it works fine
But i have another line
strtolower(end($filearray));
I thought it should give me the same error , i mean i should first have to store end($filearray) in a variable then use that variable in strtolower(),
But this is not giving me any error ,So why strtolower() accepting a function as parameter , and not giving an error , can someone explain why ?

It's not strtolower that gives you the warning - but end function. Quoting the docs:
end() advances array's internal pointer to the last element, and
returns its value. [...] The 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.
In your first example you attempt to end the result of explode call - i.e., not a real variable. While it's possible for PHP to ignore such a use case, it usually means that you've done something by mistake - and E_STRICT warning attempts to notify you about it.
Your third example works fine, because:
1) strtolower actually doesn't care about the reference. It returns a string with all alphabetic characters converted to lowercase instead of modifying the string in place.
2) end has a variable - array - passed in. It returns its last element, while advancing the internal pointer of that array to, well, its end. Have you attempted to employ this internal pointer (with current or some other means), you'd see the difference.
As a sidenote (already mentioned in comments by #DoktorOSwaldo), you can replace the all explode(end() stuff with simple pathinfo call:
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));

Because some functions in php are passed as a reference. end is one of those functions. see doc : http://php.net/manual/en/function.end.php
But the strtolower function gets just a normal parameter.
So why does the end function gets a reference? End will not just return the last element, but will also move the array's internal pointer to the last element. so if you call current function after the end function you will get the last element.
So basically end function will modify the array passed in parameter. And therefore it needs to be a variable that it can modify get as a reference.

Related

PHP Error Was encountered, and I can not debug it myself

I am struggling to debug a PHP error I am getting when uploading photos to my website. It is not WordPress btw. The error message says
"A PHP Error was encountered, Severity: Notice
Message: Only variables should be passed by reference
Filename: admin/vehicles.php
Line Number: 322"
Below is the line of text from that file and line 322. I'm not sure if this is enough info to go off of, but if it's a simple syntax error, I'm sure it is.
$ext = array_pop(explode('.', $_FILES['slider_image']['name']));
Thanks in advance! I can provide more of the code if needed.
array_pop expects an actual variable, not a returned array, because it works with a pointer.
Try this:
$array = explode('.', $_FILES['slider_image']['name']);
$ext = array_pop($array);
Just to expand a bit on that:
If you use explode('.', $_FILES['slider_image']['name']);, you get an array. However, this array doesn't really exist. Its basically homeless. When you assign it to a variable, it gets an actual "address" in the memory.
array_pop only accepts references, not values (this is known as "pass by reference" vs "pass by value"). So you don't give array_pop a value, but the address to the value. These functions usually have a & sign in front of the variable name in the function definition.
https://www.php.net/manual/en/function.array-pop.php

1st argument in array_push only accepts variable

array_push
I have a PHP statement as follow (in a method of a class)
array_push(self::USER_BASIC_DETAIL_FIELDS, 'cname_username');
which gives me error
Cannot pass parameter 1 by reference
Then I tried it assigning it to variable and it all worked fine
$r = self::USER_BASIC_DETAIL_FIELDS;
array_push($r, 'cname_username');
My question is why does PHP throws an error in above case?
I have an answer but I am not sure so asked here. The answer is like:
array_push does not return the modified array but changes the variable given at argument 1. So change are made at the locations in memory where variable (argument 1) is stored.
If we are passing argument 1 as self::USER_BASIC_DETAIL_FIELDS then with the same behavior of array_push it will try to modify constant USER_BASIC_DETAIL_FIELDS of a class which will create mess for developer
Am I right?
The answer is: everything depends on the details of your project.
You can't modify the value of constants.
And the '$r' variable is not a pointer to 'self::USER_BASIC_DETAIL_FIELDS' it is a copy of 'self::USER_BASIC_DETAIL_FIELDS'.
I think than you need a static variable instead of constant in that case.

PHP error - Only variables should be passed by reference [duplicate]

// 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.
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.
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);
Php 7 compatible proper usage:
$fileName = 'long.file.name.jpg';
$tmp = explode('.', $fileName);
$fileExtension = end($tmp);
echo $fileExtension;
// jpg
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 .
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.
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$$$
$$$$$$$$$$"""" ""$$$$$$$$$$$"
"$$$$$" ""$$$$""
$$$" $$$$"
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.
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));
end(...[explode('.', $file_name)]) has worked since PHP 5.6. This is documented in the RFC although not in PHP docs themselves.
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);
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.
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.

500 - Internal Server Error with extra pair of brackets

I have been looking for an error in my code since an hour. This was the error:
Writing:
if(isset(($_POST['to'])))
instead of
if(isset($_POST['to']))
I don't get why is this extra pair of brackets causing an Internal Server Error.
I don't think putting brackets around a variable never changes its value. I mean,
$a = $b;
$c = ($b);
$a==$c; //True
I am curious as to know why is it an error?
Thank you.
EDIT:
The above error was occurring for normal variable also.
This is because isset is not a function but a language construct; as such, its definition can be found in the language parser.
T_ISSET '(' isset_variables ')' { $$ = $3; }
It only expects one pair of braces; passing another pair will cause a parse error.
Im pretty sure it has something to do with the fact that isset can not take a function in parameter. You have to pass it a value. Your extra pair of parenthesis may be evaluated as a 'function' or something that need to be evaluated.
Normally, when you try to pass a function to isset, you get this error :
Can't use method return value in write context
isset:
Warning
isset() only works with variables as passing anything else will result in a parse error. For checking if constants are set use the defined() function.
Information can be passed to functions through arguments. An argument is just like a variable.
Arguments are specified after the function name, inside the parentheses. You can add as many arguments as you want, just seperate them with a comma.
The following example has a function with one argument ($fname). When the familyName() function is called, we also pass along a name (e.g. Jani), and the name is used inside the function, which outputs several different first names, but an equal last name:
<?php
function familyName($fname)
{
echo "$fname Refsnes.<br>";
}
familyName("Jani");
familyName("Hege");
familyName("Stale");
familyName("Kai Jim");
familyName("Borge");
?>

Can I extract function return value?

I noticed that in PHP extract(some_function()); will work just like:
$stuff = some_function();
extract($stuff);
But in the PHP's documentation the extract function argument has the & thingy in front, and from what I know that means you have to pass a variable to it.
If the documentation was right, this would produce a strict standards message:
PHP Strict standards: Only variables should be passed by reference
So I think you just found a bug in the documentation. Congratulations.
EDIT
It still doesn't complain if you use it with EXTR_REFS as a second argument:
~❯ php -a
Interactive shell
php > function a(){return array('pwet'=> 42);}
php > extract(a(), EXTR_REFS);
php > echo $pwet;
42
Which is strange because referencing variables defined inside a function doesn't make much sense to me. I think the & might have been introduced because of this option, but appears only in the doc and is not enforced in the code.
EDIT
It seems I'm right, I found this comment in ext/standard/array.c (branches 5.3 and 5.4):
/* var_array is passed by ref for the needs of EXTR_REFS (needs to
* work on the original array to create refs to its members)
* simulate pass_by_value if EXTR_REFS is not used */
The ampersand passes a variable by reference so that when it is used in a function, you are manipulating the original object -- not a new variable with the same value. The documentation is telling you that if you pass a variable to the extract function, then the original object can be updated in some fashion by that function.
So, the answer is yes, you need to pass a variable to that function.
The reason $var_array parameter of the extract function is passed by reference (most likely) is from a holdover from older versions of PHP. Newer versions automatically pass arrays by reference.
The extract function creates a variable list from the contents of a (potentially large) array and it is not recommended that data of that type be passed by value.
Long story short, assign your array to a variable and pass it in that way.

Categories