What Does the & (ampersand) symbol mean in this circumstance? - php

I'm new to php and mysql. Im following a tutorial from phpacademy on youtube. There is a section that is of form data, but everyone who followed the tutorial (myself included) got undefined index errors. I fixed it with & (ampersand) symbol. But what does it do in the follow circumstances?? Why does putting the & symbol in front of the $ stop the error? Is it the same as # and is it just suppressing the error or did it actually fix it?
$submit = &$_POST['submit'];

It means instead of assigning the value to $submit, assign a reference to the value to $submit.
If you are familiar with C and pointers, it is like a pointer to the value that is automatically dereferenced when you access it. However, PHP doesn't allow things pointer arithmetic or getting the actual memory address (unlike C).
$a = 7;
$b = $a;
$c = &$a;
$c = 5;
echo $a, $b, $c; // 575
CodePad.
To stop the error you mentioned, you can use a pattern such as...
$submit = isset($_POST['submit']) ? $_POST['submit'] : 'default_value';
...or...
$submit = filter_input(INPUT_POST, 'submit');

Related

PHP ignore dollar sign

PHP
<?php
$a = $offer->service_original_price_display;
$b = $offer->service_discounted_price_display;
$c = $a - $b;
?>
However I am getting this error: A non-numeric value encountered since service_original_price_display is '$500' and service_discounted_price_display is '$300'. I assume it is because both contains the dollar sign hence PHP is not able to perform the equation. Is there any solution to this? Thanks!
$a = str_replace("$", "", $offer->service_original_price_display);
$b = str_replace("$", "",$offer->service_discounted_price_display);
$ is string
use trim($a, '$'); to remove both side $ sign
You can use substr, if you know the first place is always occupied by a $ from an error standpoint, removing the $ ( with str_replace ) is probably better. But, as those answers where already posted. I get the scraps ( lol ).
$a = '$500';
$b = '$300';
$c = substr($a,1) - substr($b,1);
echo $c;

Basic Multiplication with PHP

Consider the below code in PHP
$a = 8425996523 * 121212713;
$b = sprintf('%.2f', $a);
$mul = gmp_mul("8425996523", "121212713");
Output is
1.0213378982814E+18
1021337898281396864.00
1021337898281396899
The actual answer is 1021337898281396899. Hence, it is clear that we need to use any libraries like gmp_mul to do arithmetic with large numbers.
My question is, how to identify such errors?
ie, when PHP does a calculation like
8425996523 * 121212713
, how can I identify that the result is not correct?
One way I see is to check for E and assume that whenever we forcefully convert such numbers, the errors exits.
Use
$b = sprintf('%.2d', $a);
Instead of
$b = sprintf('%.2f', $a);
Here's a screenshot of the source code to show the difference:

Why is it assigning value to $b? It should not as per operator precedence rule

I was trying to answer Why is this code an infinite loop?
There I thought the issue could be because of operations precedence, but when I checked http://php.net/manual/en/language.operators.precedence.php , . has more precedence than =
so I tried the following code :
$a.$b = "test";
echo $a;
echo $b;
and I got undefined variable a and test which means it is assigning value to $b, How is it assigning value to $b (Should not as per operations precedence)
I believe the answer is clearly stated in the docs:
Although = has a lower precedence than most other operators, PHP will still allow expressions similar to the following: if (!$a = foo()), in which case the return value of foo() is put into $a.
Expanding on Jon's answer, since you can't assign to an expression, the = takes precedence and the interpreter sees the following statement:
$a.($b = "test");
So they become two separate expressions, each following their own precedence. And therefore test is assigned to $b.
To prove this, add another assignment:
$a = 'my ';
$c = $a.$b = "test";
var_dump($a); // string(3) "my "
var_dump($b); // string(4) "test"
var_dump($c); // string(7) "my test"
This does not make sense:
($a.$b) = "test"; // you can't assign to the result of an expression
This does:
$a.($b = "test");
Without going deeper than this, I believe the PHP compiler interprets the code as if you had written the latter because "that would work".
It wouldn't be the first time that the PHP parser's deficiencies "bleed through" and have surprising effects on the code.
Update: Turns out that this is not a bug, it's a feature.

String concatenation while incrementing

This is my code:
$a = 5;
$b = &$a;
echo ++$a.$b++;
Shouldn't it print 66?
Why does it print 76?
Alright. This is actually pretty straight forward behavior, and it has to do with how references work in PHP. It is not a bug, but unexpected behavior.
PHP internally uses copy-on-write. Which means that the internal variables are copied when you write to them (so $a = $b; doesn't copy memory until you actually change one of them). With references, it never actually copies. That's important for later.
Let's look at those opcodes:
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > ASSIGN !0, 5
3 1 ASSIGN_REF !1, !0
4 2 PRE_INC $2 !0
3 POST_INC ~3 !1
4 CONCAT ~4 $2, ~3
5 ECHO ~4
6 > RETURN 1
The first two should be pretty easy to understand.
ASSIGN - Basically, we're assinging the value of 5 into the compiled variable named !0.
ASSIGN_REF - We're creating a reference from !0 to !1 (the direction doesn't matter)
So far, that's straight forward. Now comes the interesting bit:
PRE_INC - This is the opcode that actually increments the variable. Of note is that it returns its result into a temporary variable named $2.
So let's look at the source code behind PRE_INC when called with a variable:
static int ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval **var_ptr;
SAVE_OPLINE();
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
zend_error_noreturn(E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets");
}
if (IS_VAR == IS_VAR && UNEXPECTED(*var_ptr == &EG(error_zval))) {
if (RETURN_VALUE_USED(opline)) {
PZVAL_LOCK(&EG(uninitialized_zval));
AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
}
if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
if (UNEXPECTED(Z_TYPE_PP(var_ptr) == IS_OBJECT)
&& Z_OBJ_HANDLER_PP(var_ptr, get)
&& Z_OBJ_HANDLER_PP(var_ptr, set)) {
/* proxy object */
zval *val = Z_OBJ_HANDLER_PP(var_ptr, get)(*var_ptr TSRMLS_CC);
Z_ADDREF_P(val);
fast_increment_function(val);
Z_OBJ_HANDLER_PP(var_ptr, set)(var_ptr, val TSRMLS_CC);
zval_ptr_dtor(&val);
} else {
fast_increment_function(*var_ptr);
}
if (RETURN_VALUE_USED(opline)) {
PZVAL_LOCK(*var_ptr);
AI_SET_PTR(&EX_T(opline->result.var), *var_ptr);
}
if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
Now I don't expect you to understand what that's doing right away (this is deep engine voodoo), but let's walk through it.
The first two if statements check to see if the variable is "safe" to increment (the first checks to see if it's an overloaded object, the second checks to see if the variable is the special error variable $php_error).
Next is the really interesting bit for us. Since we're modifying the value, it needs to preform copy-on-write. So it calls:
SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
Now, remember, we already set the variable to be a reference above. So the variable is not separated... Which means everything we do to it here will happen to $b as well...
Next, the variable is incremented (fast_increment_function()).
Finally, it sets the result as itself. This is copy-on-write again. It's not returning the value of the operation, but the actual variable. So what PRE_INC returns is still a reference to $a and $b.
POST_INC - This behaves similarly to PRE_INC, except for one VERY important fact.
Let's check out the source code again:
static int ZEND_FASTCALL ZEND_POST_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
retval = &EX_T(opline->result.var).tmp_var;
ZVAL_COPY_VALUE(retval, *var_ptr);
zendi_zval_copy_ctor(*retval);
SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
fast_increment_function(*var_ptr);
}
This time I cut away all of the non-interesting stuff. So let's look at what it's doing.
First, it gets the return temporary variable (~3 in our code above).
Then it copies the value from its argument (!1 or $b) into the result (and hence the reference is broken).
Then it increments the argument.
Now remember, the argument !1 is the variable $b, which has a reference to !0 ($a) and $2, which if you remember was the result from PRE_INC.
So there you have it. It returns 76 because the reference is maintained in PRE_INC's result.
We can prove this by forcing a copy, by assigning the pre-inc to a temporary variable first (through normal assignment, which will break the reference):
$a = 5;
$b = &$a;
$c = ++$a;
$d = $b++;
echo $c.$d;
Which works as you expected. Proof
And we can reproduce the other behavior (your bug) by introducing a function to maintain the reference:
function &pre_inc(&$a) {
return ++$a;
}
$a = 5;
$b = &$a;
$c = &pre_inc($a);
$d = $b++;
echo $c.$d;
Which works as you're seeing it (76): Proof
Note: the only reason for the separate function here is that PHP's parser doesn't like $c = &++$a;. So we need to add a level of indirection through the function call to do it...
The reason I don't consider this a bug is that it's how references are supposed to work. Pre-incrementing a referenced variable will return that variable. Even a non-referenced variable should return that variable. It may not be what you expect here, but it works quite well in almost every other case...
The Underlying Point
If you're using references, you're doing it wrong about 99% of the time. So don't use references unless you absolutely need them. PHP is a lot smarter than you may think at memory optimizations. And your use of references really hinders how it can work. So while you think you may be writing smart code, you're really going to be writing less efficient and less friendly code the vast majority of the time...
And if you want to know more about References and how variables work in PHP, checkout One Of My YouTube Videos on the subject...
I think the full concatenate line is first executed and than send with the echo function.
By example
$a = 5;
$b = &$a;
echo ++$a.$b++;
// output 76
$a = 5;
$b = &$a;
echo ++$a;
echo $b++;
// output 66
EDIT: Also very important, $b is equal to 7, but echoed before adding:
$a = 5;
$b = &$a;
echo ++$a.$b++; //76
echo $b;
// output 767
EDIT: adding Corbin example: https://eval.in/34067
There's obviously a bug in PHP. If you execute this code:
<?php
{
$a = 5;
echo ++$a.$a++;
}
echo "\n";
{
$a = 5;
$b = &$a;
echo ++$a.$b++;
}
echo "\n";
{
$a = 5;
echo ++$a.$a++;
}
You get:
66 76 76
Which means the same block (1st and 3rd one are identical) of code doesn't always return the same result. Apparently the reference and the increment are putting PHP in a bogus state.
https://eval.in/34023

Why is there no error in PHP if you try to use brackets to get the value of a key on a NULL?

I tried the following code in both facebook's phpsh and the standard crappy php -a abomination for a repl:
$a = NULL;
echo $a['foobar'];
To my regret (I wouldn't call it surprise or disappointment when it concerns PHP) I don't get any errors or warnings or exceptions or anything.
Smarter languages like Ruby and Python both complain when trying to dereference a key from eg None or nil. How is PHP interpreting this situation? Is the only cure inserting is_null checks everywhere? Should I blame Smarty for not doing it for me?
According to PHP source code (Zend/zend_execute.c), only strings, arrays and objects can trigger errors when accessing an offset/index. The rest is more-or-less ignored:
$a = true;
$b = false;
$c = null;
$d = 1;
$e = 1.234;
$f = '';
$g = array();
$h = new stdClass;
echo $a[0]; // NULL
echo $b[0]; // NULL
echo $c[0]; // NULL
echo $d[0]; // NULL
echo $e[0]; // NULL
echo $f[0]; // E_NOTICE: Uninitialized string offset
echo $g[0]; // E_NOTICE: Undefined offset
echo $h[0]; // E_FATAL: Cannot use object as array
None of $a,$b,$c,$d or $e actually spit an error. Most of the times in the code I just see return; or return 0;, which means NULL, instead of a returned zval* (pointer) or zend_error() call. Hence the results above.
Whatever the reason why it has been done like this, it doesn't really matter. You should always check a variable for existence and/or nullity in such cases. The safest ways (slightly different behaviours) are isset and empty:
isset($a['foo']);
!empty($a['foo']);

Categories