PHP missing notices - php

here are some X-files.
Let's check this script: https://admin.laysoft.tk/test.php
We tested it on different machines with different version of PHP.
Let's see this:
$tomb = 666;
var_dump($tomb);
$a = $tomb['akarmi'];
var_dump($a);
Result of this is:
int(666)
NULL
($tomb means array)
As you see, we initialized the $tomb as an integer.
Why $a = $tomb['akarmi']; does not drop a notice, that no key like this?
UPDATE
I've reported it, I am so curious.
https://bugs.php.net/bug.php?id=74579
UPDATE2
Ok, this bug is exists from years. There are a lot of issue about this:
https://bugs.php.net/bug.php?id=37676

Because of PHP's type juggling feature, which will implicitly convert between types, depending on how you attempt to access a variable.
The conversion to arrays however is not implemented, not even defined how it should work, as the manual says:
The behaviour of an automatic conversion to array is currently undefined.
As for why it has remained that way through the years ... nobody could really answer that question.

Related

Should my PHP functions actively return the "right" type?

I have made various functions such as "get_current_balance_from_my_bank_account()". It technically returns a string, but it's always a full integer number (as a string) which has never caused problems when its return variable is used directly in calculations.
However, it feels wrong.
Should I be doing something like this?
return (int)$amount_as_a_string;
Instead of the current:
return $amount_as_a_string;
? Or is (int) some archaic/legacy way of doing this? Should I be using some other, better method?
Example of the context:
$my_balance = get_current_balance_from_my_bank_account();
$previous_balance = load_last_balance();
echo 'I have ' . format_money_prettily($my_balance - $previous_balance) . '!' . PHP_EOL;
Again, I rarely if ever run into issues with this because it understands the "real" type. It does still feel wrong that I'm technically returning and sending around strings which in theory could be causing problems sooner or later -- perhaps catastrophic ones in production!
PHP will implicitly type cast in many situations, but not all. Take for example this:
echo json_encode(['balance' => get_current_balance_from_my_bank_account()]);
Now your type propagates to some other system via JSON, where it may cause actual issues if that system isn't so lenient about types. You're making somebody else deal with your incorrect type.
So, yes, your function should always return the type that it claims it returns. PHP implicitly "helping" you when you don't stick to your own type declarations is just sweeping the problem in the rug, but the problem is still there and may eventually cause actual issues.
I don't think that is something you have to worry to much, considering that php has automatic type conversion.
From the docs:
PHP does not require (or support) explicit type definition in variable declaration; a variable's type is determined by the context
in which the variable is used.
Just because the function returns the right data type does not mean that the value is correct or what you expect.
If you want to make sure that the function returns ok value, validating it before returning it much more helpful than simply typecasting it to correct type.
If you want to be more explicit about typing in PHP, have a look at the following from the manual:
default
By default, PHP will coerce values of the wrong type into the expected scalar type if possible.
For example, a function that is given an integer for a parameter that expects a string will get a variable of type string.
Strict mode
It is possible to enable strict mode on a per-file basis. In strict mode, only a variable of exact type of the type declaration will be accepted, or a TypeError will be thrown.
The only exception to this rule is that an integer may be given to a function expecting a float.
Function calls from within internal functions will not be affected by the strict_types declaration.
To enable strict mode, the declare statement is used with the strict_types declaration:
<?php
declare(strict_types=1); // strict type declaration MUST be the very first statement in your script
function sum(int $a, int $b) {
return $a + $b;
}
var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5));
?>
output
int(3)
Fatal error: Uncaught TypeError: Argument 1 passed to sum() must be of the type integer, float given
Note: Enabling strict mode will also affect return type declarations.
source: php.net
This will never be required in PHP, as it does the type conversion by itself. However you can do this if you want. This depends from project to project, but sometimes a decision is made for a certain project to use explicit types where possible. Also this is a good practice if you are working on a team where some of the people are comfortable with more strict languages.
In your case, depending on the number, there might be a good side in keeping it as a string, as if you convert it to int via casting and the number is bigger than the max_int_size, it will overflow.
As for the other question, casting as int (int) or (integer) is a perfectly good way to do it, even in newer versions. It is not a legacy way whatsoever. This does not mean there aren't other good ways to do that, though. You can also use something like intval() or settype(). In my opinion there isn't one right way to do it. You can decide for yourself on how to do that.

Is array error possibly a PHP version issue?

I work on a website that, since last Oct, has had the following lines of code that work just fine:
if(empty($post_types))
{
$post_types[] = 'post';
$post_types[] = 'product-list';
}
I had not seen this construct in PHP before, (and since I started my programming work in C, it's a little irritating), but it works.
We started a second site with the same technology and basic setup that threw the following error at the same lines of code,
"Uncaught Error: [] operator not supported for strings ..."
The sites are hosted at the same place, but I noticed that they are using different 7.x versions of PHP. I did a bit of research to see if the behavior is due to a change in PHP 7.3, but I didn't find an answer.
Please note that my question is on whether this situation is possibly a PHP version issue, not how to solve the array problem, which I changed with
$post_types = array('post', 'product-list');
I found your answer in the PHP docs Creating/modifying with square bracket syntax:
$arr[key] = value;
$arr[] = value;
// key may be an integer or string
// value may be any value of any type
If $arr doesn't exist yet, it will be created, so this is also an
alternative way to create an array. This practice is however
discouraged because if $arr already contains some value (e.g. string
from request variable) then this value will stay in the place and []
may actually stand for string access operator. It is always better to
initialize a variable by a direct assignment.
Note: As of PHP 7.1.0, applying the empty index operator on a string throws a fatal error. Formerly, the string was silently converted to
an array.
So it appears there is a change in PHP 7.1.0 matching with the problem you described.
In your code, your $post_type variable must be initialized as a string [EDIT : I could only reproduce the problem with an empty string], and was previously ( PHP < 7.1.0) silently converted to an array.

using array accessor on integer, float or boolean [duplicate]

Edit 2022: This appears to be fixed as of PHP 7.4 which emits a notice.
In PHP, I have error_reporting set to report everything including notices.
Why does the following not throw any notices, errors or anything else?
$myarray = null;
$myvalue = $myarray['banana'];
Troubleshooting steps:
$myarray = array();
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = (array)null;
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = null;
$myvalue = $myarray['banana'];
// no notice or warning thrown, $myvalue is now NULL. ✘ Why?
It's possible it's a bug in PHP, or I'm just not understanding something about how this works.
There are three types which it might be valid to use the array derefence syntax on:
Arrays
Strings (to access the character at the given position)
Object (objects implementing the ArrayAccess interface)
For all other types, PHP just returns the undefined variable.
Array dereference is handled by the FETCH_DIM_R opcode, which uses zend_fetch_dimension_address_read() to fetch the element.
As you can see, there is a special case for NULLs, and a default case, both returning the undefined variable.
Usually, when you try to use a value of one type as if it were another type, either an error or warning gets thrown or "type juggling" takes place. For example, if you try to concatenate two numbers with ., they'll both get coerced to strings and concatenated.
However, as explained on the manual page about type juggling, this isn't the case when treating a non-array like an array:
The behaviour of an automatic conversion to array is currently undefined.
In practice, the behaviour that happens when this "undefined behaviour" is triggered by dereferencing a non-array is that null gets returned, as you've observed. This doesn't just affect nulls - you'll also get null if you try to dereference a number or a resource.
There is an active bug report started at 2006.
And in documentation it is a notice about this in String section.
As of PHP 7.4, this behavior how emits a Notice.
"Trying to access array offset on value of type null"
See the first item in this 7.4 migration page.
https://www.php.net/manual/en/migration74.incompatible.php
This recently struck one of my colleagues in the butt because he neglected to validate the result of a database query before attempting to access column data from the variable.
$results = $this->dbQuery(...)
if($results['columnName'] == 1)
{
// WHEN $results is null, this Notice will be emitted.
}
And I just noticed #Glen's comment, above, citing the relevant RFC.
https://wiki.php.net/rfc/notice-for-non-valid-array-container

Why does PHP not complain when I treat a null value as an array like this?

Edit 2022: This appears to be fixed as of PHP 7.4 which emits a notice.
In PHP, I have error_reporting set to report everything including notices.
Why does the following not throw any notices, errors or anything else?
$myarray = null;
$myvalue = $myarray['banana'];
Troubleshooting steps:
$myarray = array();
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = (array)null;
$myvalue = $myarray['banana'];
// throws a notice, as expected ✔
$myarray = null;
$myvalue = $myarray['banana'];
// no notice or warning thrown, $myvalue is now NULL. ✘ Why?
It's possible it's a bug in PHP, or I'm just not understanding something about how this works.
There are three types which it might be valid to use the array derefence syntax on:
Arrays
Strings (to access the character at the given position)
Object (objects implementing the ArrayAccess interface)
For all other types, PHP just returns the undefined variable.
Array dereference is handled by the FETCH_DIM_R opcode, which uses zend_fetch_dimension_address_read() to fetch the element.
As you can see, there is a special case for NULLs, and a default case, both returning the undefined variable.
Usually, when you try to use a value of one type as if it were another type, either an error or warning gets thrown or "type juggling" takes place. For example, if you try to concatenate two numbers with ., they'll both get coerced to strings and concatenated.
However, as explained on the manual page about type juggling, this isn't the case when treating a non-array like an array:
The behaviour of an automatic conversion to array is currently undefined.
In practice, the behaviour that happens when this "undefined behaviour" is triggered by dereferencing a non-array is that null gets returned, as you've observed. This doesn't just affect nulls - you'll also get null if you try to dereference a number or a resource.
There is an active bug report started at 2006.
And in documentation it is a notice about this in String section.
As of PHP 7.4, this behavior how emits a Notice.
"Trying to access array offset on value of type null"
See the first item in this 7.4 migration page.
https://www.php.net/manual/en/migration74.incompatible.php
This recently struck one of my colleagues in the butt because he neglected to validate the result of a database query before attempting to access column data from the variable.
$results = $this->dbQuery(...)
if($results['columnName'] == 1)
{
// WHEN $results is null, this Notice will be emitted.
}
And I just noticed #Glen's comment, above, citing the relevant RFC.
https://wiki.php.net/rfc/notice-for-non-valid-array-container

Anyone ever used PHP's (unset) casting?

I just noticed PHP has an type casting to (unset), and I'm wondering what it could possibly be used for. It doesn't even really unset the variable, it just casts it to NULL, which means that (unset)$anything should be exactly the same as simply writing NULL.
# Really unsetting the variable results in a notice when accessing it
nadav#shesek:~$ php -r '$foo = 123; unset($foo); echo $foo;'
PHP Notice: Undefined variable: foo in Command line code on line 1
PHP Stack trace:
PHP 1. {main}() Command line code:0
# (unset) just set it to NULL, and it doesn't result in a notice
nadav#shesek:~$ php -r '$foo = 123; $foo=(unset)$foo; echo $foo;'
Anyone ever used it for anything? I can't think of any possible usage for it...
Added:
Main idea of question is:
What is reason to use (unset)$smth instead of just NULL?
As far as I can tell, there's really no point to using
$x = (unset)$y;
over
$x = NULL;
The (unset)$y always evaluates to null, and unlike calling unset($y), the cast doesn't affect $y at all.
The only difference is that using the cast will still generate an "undefined variable" notice if $y is not defined.
There's a PHP bug about a related issue. The bug is actually about a (in my mind) misleading passage elsewhere in the documentation which says:
Casting a variable to null will remove the variable and unset its value.
And that clearly isn't the case.
I’d guess (knowing PHP and it’s notaribly... interesting choices for different things, I may be completely wrong) that it is so that the value does not need setting to a var. For exact reason to use it for a code, I can’t think of an example, but something like this:
$foo = bar((unset) baz());
There you want or need to have null as argument for bar and still needs to call baz() too. Syntax of function has changed and someone did a duck tape fix, like what seems to be hot with PHP.
So I’d say: no reason to use it in well-thought architecture; might be used for solutions that are so obscure that I’d vote against them in first place.
As of PHP 8.0.X, (unset) casting is now removed and cannot be used.
For example it can be used like this
function fallback()
{
// some stuff here
return 'zoo';
}
var_dump(false ? 'foo' : fallback()); // zoo
var_dump(false ? 'foo' : (unset) fallback()); // null
Even if fallback() returns "zoo" (unset) will clear that value.

Categories