I have the following error while running this code below:
Code:
<?php
$a = array(00001, 00008, 00009, 00012);
print_r($a);
?>
Error:
Parse error: Invalid numeric literal.
Why this issue occurred and how do i solve this?
This comes from the changes made to how integers, specifically octals, are handled in PHP7 (as oppsoed to PHP5).
From the documentation (from PHP7 migration)
Invalid octal literals
Previously, octal literals that contained invalid numbers were silently truncated (0128 was taken as 012). Now, an invalid octal literal will cause a parse error.
From the documentation of integers
Prior to PHP 7, if an invalid digit was given in an octal integer (i.e. 8 or 9), the rest of the number was ignored. Since PHP 7, a parse error is emitted.
Either use them as strings, or actual integers
$a = array(1, 8, 9, 12); // Integers
$a = array("00001", "00008", "00009", "00012"); // Strings
Example of PHP7 vs PHP5: https://3v4l.org/GRZF2
http://php.net/manual/en/language.types.integer.php
Manual: http://php.net/manual/en/migration70.incompatible.php
This is because all numbers starting with 0 is considered octal values, which has an upper limit of 8 digits per position (0-7). As stated in the PHP manual, instead of silently dropping the invalid digits they now (7.x) produce the above warning.
Why are you writing your numbers like that though? If the leading zeroes are significant, then it's not a number you have but a string. Should you need to do calculations on those as if they were numbers, then you need to add the leading zeroes when outputting the values to the client.
This can be done with printf() or sprintf() like this:
$number = 5;
printf ("%05$1d", $number);
Please see the manual for more examples.
Sometime an apparently valid numeric literal is being detected as an invalid numeric literal.
This is a regression since php5.4
You can be fix this by changing the array to:
$a =array(1,8,9,12);
$a = array('0001','0008','0009','0012'); //alternative method for fix
Reference: https://bugs.php.net/bug.php?id=70193
decimal : [1-9][0-9]*(_[0-9]+)*|0
octal : 0[0-7]+(_[0-7]+)*
from document regex for consider value as decimal and octal are given above
In this scenario with value is 00008, 00009 not pass validation for octal or decimal as well. since it is given error, parse parameter to string is solving problem partially.
Note: In PHP any number starts from zero considered as octal but 8 and
9 will not used in octal number resulting throw this error.
Related
I am trying to understand numeric strings in PHP.
I have the following code:
var_dump(5 * "10 abc");
var_dump(is_numeric("10 abc"));
Which gives me the output:
int(50)
bool(false)
This confuses me as the string "10 abc" seems to be interpreted as a numeric string in the first expression (hence the int(50) output and no warnings about using a non-numeric value), but when run through the is_numeric() function it returns false, suggesting that it is in fact not a numeric string.
I have spent some time looking through the documentation to understand this behaviour but can't find any concrete answers, can somebody please help to explain what is causing this behaviour?
I am aware PHP 8.0.0 made some changes to what is considered a numeric string, but this is PHP 7.1.33 I am trying to understand right now.
RFC author of the "Saner numeric string" RFC which got accepted for PHP 8.0 here.
"10 abc" is not a numeric string, but a leading-numeric string, meaning that the beginning of the string looks like a number but it isn't one because gibberish exists at some point in the string (and this includes white-spaces).
Because is_numeric() checks that a value is considered numeric per PHP's definition (which prior to PHP 8.0 meant leading white-spaces followed by a + or - sign and any of an integer, a normal decimal number, or a number in exponential notation), it will return false on strings which are just considered leading-numeric.
However, arithmetic operation try to convert their operands to a proper number type (int or float) and as such "10 abc" gets converted to 10 because PHP will convert the leading-numeric string to it's leading numeric value.
Many more "fun" details and edge cases can be found in the technical background section of the PHP RFC.
I think the easiest way to understand the behaviour you describe is that just because a string isn't numeric, that does not mean it cannot be coerced or treated as a number.
Your first line code
var_dump(5 * "10 abc");
Treats the string as a number, and once it comes across an invalid character it just ignores everything else after that.
Your other line of code
var_dump(is_numeric("10 abc"));
Actually behaves more intelligent, and asks itself, just like a human might, are we dealing with a numeric string here; the answer to which is no (because of those same invalid characters).
Let's suppose I have a code which outputs $i as
$i = 016;
echo $i / 2;
//ans will be 7
I know that the leading zero indicates an octal number in PHP, but how is it interpreted, how can it be executed? Can somebody share its execution step by step? What is the role of parser here? I have researched a lot and read all the previous answers but none are having any deep explanation.
When you preceed integers with zero in PHP, in that instance, 029.
It becomes octal.
So when you echo that, it will convert to its decimal form.
Which results to:
echo 016; //14 (decimal) valid octal
echo 029; // 2 (decimal) - Invalid octal
Actually, its here stated in the manual
Valid octal:
octal : 0[0-7]+
Note: Prior to PHP 7, if an invalid digit was given in an octal integer (i.e. 8 or 9), the rest of the number was ignored. Since PHP 7, a parse error is emitted.
The octal numeral system, or oct for short, is the base-8 number system, and uses the digits 0 to 7.
Octal numerals can be made from binary numerals by grouping consecutive binary digits into groups of three (starting from the right).
For example, the binary representation for decimal 74 is 1001010. Two zeroes can be added at the left: (00)1 001 010, corresponding the octal digits 1 1 2, yielding the octal representation 112.
In your question $i = 016; is calculated by the interpreter and produces $i = 14;(which is the equilevant decimal number)
Then you simply divide it by 2, which outputs 7.
Using PHP 5.3.5. Not sure how this works on other versions.
I'm confused about using strings that hold numbers, e.g., '0x4B0' or '1.2e3'. The way how PHP works with such strings seems inconsistent to me. Is it only me? Or is it a bug? Or undocumented feature? Or am I just missing some magic sentence in docs?
<?php
echo $str = '0x4B0', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1 -> ", var_dump($str * 1); // int(1200)
echo "(int) -> ", var_dump((int)$str); // int(0)
echo "(float) -> ", var_dump((float)$str); // float(0)
echo PHP_EOL;
echo $str = '1.2e3', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1 -> ", var_dump($str * 1); // float(1200)
echo "(int) -> ", var_dump((int)$str); // int(1)
echo "(float) -> ", var_dump((float)$str); // float(1200)
echo PHP_EOL;
In both cases, is_numeric() returns true. Also, in both cases, $str * 1 parses string and returns valid number (integer in one case, float in another case).
Casting with (int)$str and (float)$str gives unexpected results.
(int)$str in any case is able to parse only digits, with optional "+" or "-" in front of them.
(float)$str is more advanced and can parse something like ^[+-]?\d*(\.\d*)?(e[+-]?\d*)?, i.e., optional "+" or "-", followed by optional digits, followed by optional decimal point with optional digits, followed by optional exponent which consists of "e" with optional "+" or "-" followed by optional digits. Fails on hex data though.
Related docs:
is_numeric() - states that "Hexadecimal notation (0xFF) is allowed too but only without sign, decimal and exponential part". If function, meant to test if a string holds numeric data, returns true, I expect PHP to be able to convert such string to a number. This seems to work with $str * 1, but not with casting. Why?
Converting to integer - states that "in most cases the cast is not needed, since a value will be automatically converted if an operator, function or control structure requires an integer argument". After such statement, I expect both $s * 10 and (int)$s * 10 expressions to work the same way and to return the same result. Though, as shown in example, those expressions are evaluated differently.
String conversion to numbers - states that "Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent". "Exponent" is "e" or "E", followed by digits, e.g., 1.2e3 is valid numeric data. Sign ("+" or "-") is not mentioned. It does not mention hexidecimal values. This conflicts with definition of "numeric data" used in is_numeric(). Then, there is suggestion "For more information on this conversion, see the Unix manual page for strtod(3)", and man strtod describes additional numeric values (including HEX notation). So, after reading this, is hexidecimal data supposed to be valid or invalid numeric data?
So...
Is there (or, rather, should there be) any relation between is_numeric() and the way how PHP treats strings when they are used as numbers?
Why do (int)$s, (float)$s and $s * 1 work differently, i.e,. give completely different results, when $s is 0x4B0 or 1.2e3?
Is there any way to convert a string to a number and keep its value, if it is written as 0x4B0 or as 1.2e3? floatval() does not work with HEX at all, intval() needs $base to be set to 16 to work with HEX, typecasting with (int)$str and (float)$str sometimes works, sometimes does not work, so these are not valid options. I'm also not considering $n *= 1;, as it looks more like data manipulation rather than converting. Self-written functions also are not considered in this case, as I'm looking for native solution.
The direct casts (int)$str and (float)$str don't really work differently at all: They both read as many characters from the string as they can interpret as a number of the respective type.
For "0x4B0", the int-conversion reads "0" (OK), then "x" and stops, because it cannot convert "x" into an integer. Likewise for the float-conversion.
For "1.2e3", the int-conversion reads "1" (OK), then "." and stops. The float-conversion recognises the entire string as valid float notation.
The automatic type recognition for an expression like $str * 1 is simply more flexible than the explicit casts. The explicit casts require the integers and floats to be in the format produced by %i and %f in printf, essentially.
Perhaps you can use intval and floatval rather than explicit casts-to-int for more flexibility, though.
Finally, your question "is hexidecimal data supposed to be valid or invalid numeric data?" is awkward. There is no such thing as "hexadecimal data". Hexadecimal is just a number base. What you can do is take a string like "4B0" and use strtoul etc. to parse it as an integer in any number base between 2 and 36.[Sorry, that was BS. There's no strtoul in PHP. But intval has the equivalent functionality, see above.]
intval uses strtol which recognizes oct/hex prefixes when the base parameter is zero, so
var_dump(intval('0xef')); // int(0)
var_dump(intval('0xff', 0)); // int(255)
Is there (or, rather, should there be) any relation between is_numeric() and the way how PHP treats strings when they are used as numbers?
There is no datatype called numeric in PHP, the is_numeric() function is more of a test for something that can be interpreted as number by PHP.
As far as such number interpreting is concerned, adding a + in front of the value will actually make PHP to convert it into a number:
$int = +'0x4B0';
$float = +'1.2e3';
You find this explained in the manual for string, look for the section String conversion to numbers.
As it's triggered by an operator, I don't see any need why there should be a function in PHP that does the same. That would be superfluous.
Internally PHP uses a function called zendi_convert_scalar_to_number for the add operator (assumable +) that will make use of is_numeric_string to obtain the number.
The exact same function is called internally by is_numeric() when used with strings.
So to trigger the native conversion function, I would just use the + operator. This will ensure that you'll get back the numeric pseudo-type (int or float).
Ref: /Zend/zend_operators.c; /ext/standard/type.c
Why is it that following results in 34?
It doesn't seem to have anything to do with octal numbers.
intval(042);
but a leading 0 does indicate octal in many languages, as is the case here.
It does have to do with octal numbers, 042 is interpreted as the octal number 42 which is 4 * 8 + 2 = 34.
Please be aware that the octal interpretation happens when the number literal is parsed while loading the PHP script. It has nothing to do with intval(), which doesn't do anything here because the value is already integer.
Octal interpretation happens only with number literals, not when casting a string to integer:
intval(042) // == 34
intval('042') // == 42
(int)'042' // == 42
In PHP a number with leading 0 is read as an octal number.
So 042 is read as an octal number.
The intval() function converts it into decimal number, which is 34.
So the browser output is 34.
It's simply how the function is defined. The leading zero is an instruction parse it as an octal number, similarly as to how 0x as a prefix means hex. See the documentation for more information.
Be careful when passing this function a string value with a leading "0". If you give it "042" then, it will treat it as BASE 8 - 9 and convert it to decimal value, which is by default base.
Please go through this
Why is the output 'in'?
<?php
if (1=='1, 3')
{
echo "in";
}
?>
The == operator does type conversion on the two values to try to get them to be the same type. In your example it will convert the second value from a string into an integer, which will be equal to 1. This is then obviously equal to the value you're matching.
If your first value had been a string - ie '1' in quotes, rather than an integer, then the match would have failed because both sides are strings, so it would have done a string comparison, and they're different strings.
If you need an exact match operator that doesn't do type conversion, PHP also offers a tripple-equal operator, ===, which may be what you're looking for instead.
Hope that helps.
Because PHP is doing type conversion, it's turning a string into an integer, and it's methods of doing so work such that it counts all numbers up until a non-numeric value. In your case that's the substring ('1') (because , is the first non-numeric character). If you string started with anything but a number, you'd get 0.
You are comparing a string and an integer. The string must be converted to an integer first, and PHP converts numeric strings to integers. Since the start of that string is '1', it compares the number one, with the number one, these are equal.
What functionality did you intend?
If you're trying to check if 1 is equal to 1 or 3, then I would definitely do it this way:
if (1 == 1 || 1 == 3)
Please refer to the PHP documentation:
http://php.net/manual/en/language.operators.comparison.php
The output should be:
in
From PHP's documentation:
When converting from a string to an
integer, PHP analyzes the string one
character at a time until it finds a
non-digit character. (The number may,
optionally, start with a + or - sign.)
The resulting number is parsed as a
decimal number (base-10). A failure to
parse a valid decimal number returns
the value 0.
I'm guessing you want to know whether a variable is in a range of values.
You can use in_array:
if (in_array(1, array(1, 3, 5, 6)))
echo "in";
if(in_array(1, array(1,3)) {
echo "in";
}