Convert a string to integer with PHP
PHP 8.5 provides a new error message when one tries to convert a string to integer with PHP.
The float-string “%s” is not representable as an int, cast occurred
This is new with this version, and besides the warning itself, it is not even a changed behavior. It is the classic PHP behavior when casting a string into an integer. Let’s see what is happening here.
Convert a string to integer
The most common way to convert a string to integer is to use the (int) cast operator. It takes anything, and then, returns an integer. This is quite safe, and also an impressive feat as the incoming type is really mixed.
<?php $string = "123"; $integer = (int) $string; // int(123) ?>
Convert a string to integer, with a warning
Obviously, there are several situations where the conversion to integer fails. For example, when the string does not contains numbers, it is converted to 0.
<?php $string = "abc"; $integer = (int) $string; // int(0) ?>
Yet, there are no warning produced when the string is unparsable. PHP fails silently, and returns 0. It is actually a good practice to check for 0 after the cast: depending on the situation, it might actually be an error, just like strpos() returning false.
So, where does the warning come from? It happens when PHP manages to convert the string, but the resulting integer is larger that the largest PHP integer.
<?php
$string = str_repeat('1', 30); // 30 ones, one after each other
Warning: The float-string "111111111111111111111111111111" is not representable as an int, cast occurred
$integer = (int) $string; // int(9223372036854775807)
?>
Making sense of the conversion result
It is easy to recognize 9223372036854775807as the value of PHP_INT_MAX. If you thought it was 4294967295, then, you are still on a PHP 32-bits architecture.
When the original string yields a larger than PHP_INT_MAX value, PHP caps its value to the maximum, and emits a warning. This helps distinguish between an error where the string is not an integer, with a case where it is a possible integer, just a bit too big.
And it gets weirder
And then, it gets weirder.
<?php
$string = str_repeat('1', 500); // 500 ones, one after each other
Warning: The float-string "111...111" is not representable as an int, cast occurred
$integer = (int) $string; // int(0)
?>
With 30 1, the string yields a PHP_INT_MAXand with 500 1, it yields a 0.
Now… there could be a limit around 170141183460469231731687303715884105727, which would be PHP_INT_MAX for a 128-bits architecture (Thanks to bcmath for the quick calculation). But a quick check also get 0 with 310 1 in a string.
The limit is actually around another, lesser known constant, which is also a close cousin of PHP_INT_MAX : PHP_FLOAT_MAX. It is 1.7976931348623E+308.
And then, some edge cases
Now, no one will send a 310 character long number via HTTP to PHP, for conversion. This looks suspicious enough to be the work of some intrusion attempt, or a script kiddy, or simply, a bug.
Yet, while looking at the maximum float, one may recognize something we have skipped so far: an integer string is not build only with digits. We can see ., E and + in the number. All those look like special characters…. let’s check them.
Space
Let’s start with space, , which is not listed above but comes immediately to mind. Spaces are automatically trimmed from the beginning and the end of the string. Spaces, and also new line, carriage return, both tabulations, and line feed.
<?php $string = ' 123 '; $integer = (int) $string; // int(123) $string = ' 123 456 '; $integer = (int) $string; // int(123) ?>
Non space, non digit chars
When the space is in the middle of the string, PHP stops the conversion, and returns the integer so far. It is actually the same behavior when the string is partially made of digits. PHP parse what it can, and then, stops at the first weird character.
<?php $string = ' 123456 '; $integer = (int) $string; // int(123) $string = ' 123 456 '; $integer = (int) $string; // int(123) ?>
And here comes the E
One letter that is not weird in a number is E. It is used to build the decimal format, as we saw with PHP_FLOAT_MAX. And it gets a special behavior:
<?php $string = '123e4'; $integer = (int) $string; // int(1230000) $string = '1234'; $integer = (int) $string; // int(1234) $string = '123a4'; $integer = (int) $string; // int(123) ?>
E is part of the decimal format, and represents the separator between the mantis and the exponent. So, PHP reads it as 123 * 10 ^ 4 and ends up with 1230000. So, the string is much larger.
At that point, it is interesting to note that there is no more need for a long string to break the size of integers.
<?php $string = '2E19'; //The float-string "2E19" is not representable as an int, cast occurred $integer = (int) $string; // int(9223372036854775807) ?>
The case of the dot
Now that E has been introduced, the last weird character is dot .. It is also a part of the decimal format, and, when used alone in the string, its trailing characters are abandonned: after all, they are part of the decimal part of the number.
<?php $string = '123.456'; $integer = (int) $string; // int(123) $string = '123.456E2'; $integer = (int) $string; // int(12345) ?>
When the . has a following E, then the exponent is applied. In the example above, it moves the comma two positions to the right, and only the final 6 is abandonned. Note that this is not a rounding, but a truncature: the 6 is not even processed.
Plus and Minus
Finally, plus + and minus - are accepted in the string. They must be at the beginning of the string, and alone (unlike PHP which parses multiple occurrences of them). A second sign may be used with the exponent too.
<?php $string = '-123'; $integer = (int) $string; // int(-123) $string = '123E-2'; $integer = (int) $string; // int(1) // hardcoded PHP integer $integer = -+-123; // int(123) ?>
The use of the (int) warning
The warning that PHP 8.5 emits with the cast to integer does not change anything to the way PHP parses numbers. This process has been for decades, and it is not ready to change.
Now, the emission of the warning leads to more work. Hopefully, it is not displayed directly to the user on the web application. A quick search on your favorite search engine yields quite a few online apps that are displaying it (not naming any culprits, find your own).
A better approach is to use the display_errorsdirective, and set it to 0 (or a very large cast string…) and rather, use log_errors.
Yet, this means that the devops have a new error message polluting their logs, and it needs to be addressed in the code.
The question becomes :
How to check when a string can be converted to integer?
Indeed, it would be nice to have an equivalent of json_validate(): it validates the string, but does not parse the string. That would skip the warning, and allow the code to handle the weird situation immediatly.
But how to do that? Here are several solutions.
Use intval()
intval yields the same warning when the string is too big for an integer.
Use settype()
settype yields the same warning when the string is too big for an integer. Also, nobody uses that function.
<?php
$string = str_repeat('1', 30);
$integer = settype($string, 'integer');
?>
Use is_int()
is_int checks the type of the variable. It is already known that is a string, so this fails. Yet, everyone tries it, at least once.
<?php
$string = str_repeat('1', 30);
var_dump(is_int($string)); // false
?>
Use is_numeric()
is_numeric checks if the string may be converted to a number. That means both integers and decimals. So, if the string is a valid decimal, but not a valid integer, the warning still happens at cast time.
<?php
$string = str_repeat('1', 30);
var_dump(is_numeric($string)); // true
// Warning!
$integer = (int) $string;
$string = str_repeat('1', 3000);
var_dump(is_numeric($string)); // true
?>
Also, is_numeric() does not take into account the limits integers not decimals, so it reports that 3000 ones is a valid number. And it is not.
Big number functions: BCMath and GMP
PHP has, at least, two extensions to handle really large numbers: bccomp and gmp_cmp. They both take in strings, or integers, so this works as an early warning system.
<?php
echo bccomp(str_repeat("1", 30), PHP_INT_MAX); // 1
echo gmp_cmp(str_repeat("1", 30), PHP_INT_MAX); // 1
?>
In both cases, they do not support numeric-strings like PHP, in particular E or any non-digit character. These require another try/catch structure to be safe.
The filter extension
Closer to our use case is the filter extension. This extensions has a few functions to validate incoming data: in this case, filter_var. There is a dedicated option FILTER_VALIDATE_INT for that purpose, with a side option:
<?php
$string = str_repeat('1', 30);
var_dump(filter_var($string, FILTER_VALIDATE_INT); // false
$string = '1e30';
var_dump(filter_var(
$string,
FILTER_VALIDATE_INT,
['flags' =>
['max_range' => PHP_INT_MAX],
]
)
); // false
?>
The max_range option works with its default values, but also, with other values, as long as it is in the valid range of PHP integers (indeed, it is possible to specify max_range as … a string).
The revenge of the noscream
Finally, there is a valid option: @. It doesn’t change anything to the string interpretation, provides backward compatibility, and … hides warnings!
<?php
$string = str_repeat('1', 30);
$integer= @(int) $string;
// extreme usage...
$integer= @(int)@@@(int) $string;
?>
@ is usually slow, but it might be faster than calling a separate function to avoid it. It is also easier to complete the current syntax.
To be honest, I never though about using @ on a cast operator (it works!), nor about recommending using it in 2026. But, there we are.
Converting a string to integer is not obvious
As usual, there is a lot of knowledge to gain by looking at how the code works.
The most interesting part here is how it might be easy to trigger warnings and strange behaviors on application, by feeding them with too large integers. Such integers might be validated, and used in the database as primary key.
The damage might not be important. But it will create strange errors in the logs, which might be difficult to interpret and fix. Hopefully, this post might help understanding the situation, and taking the right steps to mitigate it.
Happy Code Auditing!

