Creating default object from empty value in PHP? - php

I see this error only after upgrading my PHP environment to PHP 5.4 and beyond. The error points to this line of code:
Error:
Creating default object from empty value
Code:
$res->success = false;
Do I first need to declare my $res object?

Your new environment may have E_STRICT warnings enabled in error_reporting for PHP versions <= 5.3.x, or simply have error_reporting set to at least E_WARNING with PHP versions >= 5.4. That error is triggered when $res is NULL or not yet initialized:
$res = NULL;
$res->success = false; // Warning: Creating default object from empty value
PHP will report a different error message if $res is already initialized to some value but is not an object:
$res = 33;
$res->success = false; // Warning: Attempt to assign property of non-object
In order to comply with E_STRICT standards prior to PHP 5.4, or the normal E_WARNING error level in PHP >= 5.4, assuming you are trying to create a generic object and assign the property success, you need to declare $res as an object of stdClass in the global namespace:
$res = new \stdClass();
$res->success = false;

This message has been E_STRICT for PHP <= 5.3. Since PHP 5.4, it was unluckilly changed to E_WARNING. Since E_WARNING messages are useful, you don't want to disable them completely.
To get rid of this warning, you must use this code:
if (!isset($res))
$res = new stdClass();
$res->success = false;
This is fully equivalent replacement. It assures exactly the same thing which PHP is silently doing - unfortunatelly with warning now - implicit object creation. You should always check if the object already exists, unless you are absolutely sure that it doesn't. The code provided by Michael is no good in general, because in some contexts the object might sometimes be already defined at the same place in code, depending on circumstances.

Simply,
$res = (object)array("success"=>false); // $res->success = bool(false);
Or you could instantiate classes with:
$res = (object)array(); // object(stdClass) -> recommended
$res = (object)[]; // object(stdClass) -> works too
$res = new \stdClass(); // object(stdClass) -> old method
and fill values with:
$res->success = !!0; // bool(false)
$res->success = false; // bool(false)
$res->success = (bool)0; // bool(false)
More infos:
https://www.php.net/manual/en/language.types.object.php#language.types.object.casting

If you put "#" character begin of the line then PHP doesn't show any warning/notice for this line. For example:
$unknownVar[$someStringVariable]->totalcall = 10; // shows a warning message that contains: Creating default object from empty value
For preventing this warning for this line you must put "#" character begin of the line like this:
#$unknownVar[$someStringVariable]->totalcall += 10; // no problem. created a stdClass object that name is $unknownVar[$someStringVariable] and created a properti that name is totalcall, and it's default value is 0.
$unknownVar[$someStringVariable]->totalcall += 10; // you don't need to # character anymore.
echo $unknownVar[$someStringVariable]->totalcall; // 20
I'm using this trick when developing. I don't like disable all warning messages becouse if you don't handle warnings correctly then they will become a big error in future.

Try this if you have array and add objects to it.
$product_details = array();
foreach ($products_in_store as $key => $objects) {
$product_details[$key] = new stdClass(); //the magic
$product_details[$key]->product_id = $objects->id;
//see new object member created on the fly without warning.
}
This sends ARRAY of Objects for later use~!

In PHP 7 anonymous objects can be created this way:
$res = new class {
public $success = false;
};
https://www.php.net/manual/en/language.oop5.anonymous.php
http://sandbox.onlinephpfunctions.com/code/ab774707a8219c0f35bdba49cc84228b580b52ee

First think you should create object
$res = new \stdClass();
then assign object with key and value thay
$res->success = false;

Try this:
ini_set('error_reporting', E_STRICT);

I had similar problem and this seem to solve the problem. You just need to initialize the $res object to a class . Suppose here the class name is test.
class test
{
//You can keep the class empty or declare your success variable here
}
$res = new test();
$res->success = false;

Starting from PHP 7 you can use a null coalescing operator to create a object when the variable is null.
$res = $res ?? new \stdClass();
$res->success = false;

A simple way to get this error is to type (a) below, meaning to type (b)
(a) $this->my->variable
(b) $this->my_variable
Trivial, but very easily overlooked and hard to spot if you are not looking for it.

You may need to check if variable declared and has correct type.
if (!isset($res) || !is_object($res)) {
$res = new \stdClass();
// With php7 you also can create an object in several ways.
// Object that implements some interface.
$res = new class implements MyInterface {};
// Object that extends some object.
$res = new class extends MyClass {};
}
$res->success = true;
See PHP anonymous classes.

Try using:
$user = (object) null;

I had a similar problem while trying to add a variable to an object returned from an API. I was iterating through the data with a foreach loop.
foreach ( $results as $data ) {
$data->direction = 0;
}
This threw the "Creating default object from empty value" Exception in Laravel.
I fixed it with a very small change.
foreach ( $results as &$data ) {
$data->direction = 0;
}
By simply making $data a reference.
I hope that helps somebody a it was annoying the hell out of me!

no you do not .. it will create it when you add the success value to the object.the default class is inherited if you do not specify one.

This problem is caused because your are assigning to an instance of object which is not initiated. For eg:
Your case:
$user->email = 'exy#gmail.com';
Solution:
$user = new User;
$user->email = 'exy#gmail.com';

This is a warning which I faced in PHP 7, the easy fix to this is by initializing the variable before using it
$myObj=new \stdClass();
Once you have intialized it then you can use it for objects
$myObj->mesg ="Welcome back - ".$c_user;

I put the following at the top of the faulting PHP file and the error was no longer display:
error_reporting(E_ERROR | E_PARSE);

Related

PHP 7 - Expected an object instance when accessing an instance property

I am using PHP Phan to scan through some code and ensure PHP 7 compatibility before upgrade. It is reporting an issue with the following...
if(!empty($values->custom_field['section'][0]['value'])):
Expected an object instance when accessing an instance property, but saw an expression $values with type null
I am not sure what is causing the error. How can I resolve it?
$values is an object, since it needs to have a custom_field property. For some reason your tool believes $values to be null in that instance.
Why that should be the case, I do not know. I have seen this happen with code such as,
$values = null;
if (SOMETHING THAT SHOULD ALWAYS HAPPEN) {
$values = functionReturningObject();
}
/*
if (null === $values) {
throw new RuntimeException('Impossible error!');
}
*/
if (empty($values->property)) {
...
}
In the above case, sometimes explicitly testing the object for nullity or objectness is enough to remove the warning.
In some cases, the warning is actually correct:
$values = null;
switch ($flag) {
case 0:
$values = ...;
break;
case 1:
$values = ...;
break;
case 2:
$values = ...;
break;
}
In the above case, the tool is warning you that in the unforeseen default case, however unlikely that might be, $values will be null.
The function empty() doesn't care whether or not its parameter is defined or makes sense. empty returns false if $values is not defined or null, but also when it's not an object or when it is an object without the custom_field property. And if that property exists but it's not an array it also returns false. If it is an array but doesn't have the key section it again returns false.
PhpStan shows this message because it thinks that $values is null. It's possible that this is correct, you would have to check the code to find where $value is defined/assigned. But since it is used in empty it will not cause runtime problems. If $values is always null then the check just makes your code more complex.

PHP 7 Execute object method as array element

I am a bit stuck with question why in PHP 7 dropped functionality of accesing object methods using array elements as method name.
E.g.:
$carObj = new Car();
$array = ['method'=>'getMilage', 'object'=>$carObj];
// FATAL HERE:
$mileage = $array['object']->$array['method']();
// WORKS OK:
$objName = $array['object'];
$metName = $array['method'];
$mileage = $objName->$metName();
This code works on PHP 5.6 for sure, however when switched to PHP 7.1 it throws now fatal. Failed to find anything re this in release notes and SO topics.
P.S. Originally found this in Magento 1.14.2.0 version upon PHP upgrade, as Varien library uses this code:
File: Varien/File/Uploader.php
//run validate callbacks
foreach ($this->_validateCallbacks as $params) {
if (is_object($params['object']) && method_exists($params['object'], $params['method'])) {
$params['object']->$params['method']($this->_file['tmp_name']);
}
}
Gives this:
Fatal error: Uncaught Error: Function name must be a string in
/var/www/html/lib/Varien/File/Uploader.php on line 274
--
EDIT #1:
You can TEST it here:
http://sandbox.onlinephpfunctions.com/code/d1d2d36f96a1b66ed7d740db328cd1f14cc2d7d8
(Note: I'm assuming the 'object'=>'carObj' declaration is supposed to be 'object'=>$carObj here - there's no way this code works in any version of PHP otherwise.)
The clue is in the Notice: Array to string conversion in... notice raised before the fatal error.
In PHP 5, the following statement:
$array['object']->$array['method']();
is evaluated like this:
$array['object']->{$array['method']}();
where $array['method'] is evaluated before calling it on the object.
In PHP 7, it's evaluated like this
($array['object']->$array)['method']();
where the $array property is looked up on the object first. Since it doesn't exist (obviously, since it's an array), a notice is thrown, and then a subsequent fatal error when the method itself can't be called.
If you want to preserve the PHP 5 behaviour, wrap some {} around the method name lookup:
$carObj = new Car();
$array = ['method'=>'getMilage', 'object'=>$carObj];
$mileage = $array['object']->{$array['method']}();
See https://3v4l.org/Is5lX
This is explained in a bit more detail here: http://php.net/manual/en/migration70.incompatible.php
Your code works for sure not in PHP5.6
The content of $array['object'] is an string and using the -> member operator on an string always throws an error
Call to a member function getMilage() on string in [...][...] on line [...]
The only way to get this to work is resolve the var with $ or - even better store the object inside the array and not simply the var.
$mileage = ${$array['object']}->$array['method']();
better solution
$array = ['method' => 'getMilage', 'object' => $carObj];
$mileage = $array['object']->{$array['method']}();
Sidenote: in php7 they changed the evaluation order in case of ambiguities - so you have to add explicitly {} around $array['method']. To prevent this, one would normaly extract first the object and method and then simply call it without the array dereferencing.
Btw. the Magento Varien code you posted also expects $params['object'] to be an array. There is even a is_object test to ensure, you couldn't pass just var names.
//run validate callbacks - even in php7
foreach ($this->_validateCallbacks as $params) {
if (is_object($params['object']) && method_exists($params['object'], $params['method'])) {
$object = $params['object'];
$method = $params['method'];
$object->$method($this->_file['tmp_name']);
}
}
You must add $ before the object variable
$carObj = new Car();
$array = ['method'=>'getMilage', 'object'=>'carObj'];
$object = $array['object'];
$method = $array['method'];
// FATAL HERE:
$mileage = $$object->$method();
// WORKS OK:
$objName = $array['object'];
$metName = $array['method'];
$mileage = $$objName->$metName();

Parse error: syntax error, unexpected 'new' (T_NEW) in .../test6_2.php on line 20

Just trying to save and fix sources from PHPBench.com
and hit this error (the site is down and the author didn't respond to questions). This is the source:
<?php
// Initial Configuration
class SomeClass {
function f() {
}
}
$i = 0; //fix for Notice: Undefined variable i error
// Test Source
function Test6_2() {
//global $aHash; //we don't need that in this test
global $i; //fix for Notice: Undefined variable i error
/* The Test */
$t = microtime(true);
while($i < 1000) {
$obj =& new SomeClass();
++$i;
}
usleep(100); //sleep or you'll return 0 microseconds at every run!!!
return (microtime(true) - $t);
}
?>
Is it a valid syntax or not? Correct me if I'm wrong but think it creates a reference to SomeClass, so we can call new $obj() ... Thanks in advance for the help
Objects are always stored by reference anyway. You don't need =& and as Charlotte commented, it's deprecated syntax.
Correct me if I'm wrong but think it creates a reference to SomeClass, so we can call new $obj() .
No, this is not correct. The new operator always creates an instance of the class, not a reference to the class as a type.
You can create a variable object instantiation simply by creating a string variable with the name of the class, and using that.
$class = "MyClass";
$obj = new $class();
Functions like get_class() or ReflectionClass::getName() return the class name as a string. There is no "reference to the class" concept in PHP like there is in Java.
The closest thing you're thinking of is ReflectionClass::newInstance() but this is an unnecessary way of creating an object dynamically. In almost every case, it's better to just use new $class().

Is this php syntax valid? $class->{'something'}()

I was reading a code example of something, and noticed a syntax that isn't familiar to me.
$response = $controller->{'home'}();
Is that a valid php syntax?
Yes.
$controller->{'home'}
// same as
$controller->home
// used to access a property
And
$controller->{'home'}()
// same as
$controller->home()
// used to call a method
The main benefit is that, by calling ->{stuff}, you can access properties with different (or strange) names.
Example:
$a = new stdClass();
$a->{'#4'} = 4;
print_r($a);
// stdClass Object
// (
// [#4] => 4
// )
You can't do $a->#4, but can do $a->{'#4'}
See this, for instance: https://3v4l.org/PaOF1
Yes it is. The cool thing about it is that you can call object's methods based on values stored in variables:
$whatToExecute = 'home';
$response = $controller->{$whatToExecute}();
Good luck!!

php delete element from object(stdClass) [duplicate]

If I have an stdObject say, $a.
Sure there's no problem to assign a new property, $a,
$a->new_property = $xyz;
But then I want to remove it, so unset is of no help here.
So,
$a->new_property = null;
is kind of it. But is there a more 'elegant' way?
unset($a->new_property);
This works for array elements, variables, and object attributes.
Example:
$a = new stdClass();
$a->new_property = 'foo';
var_export($a); // -> stdClass::__set_state(array('new_property' => 'foo'))
unset($a->new_property);
var_export($a); // -> stdClass::__set_state(array())
This also works specially if you are looping over an object.
unset($object[$key])
Update
Newer versions of PHP throw fatal error Fatal error: Cannot use object of type Object as array as mentioned by #CXJ . In that case you can use brackets instead
unset($object->{$key})
This also works if you are looping over an object.
unset($object->$key);
No need to use brackets.
This code is working fine for me in a loop
$remove = array(
"market_value",
"sector_id"
);
foreach($remove as $key){
unset($obj_name->$key);
}
Set an element to null just set the value of the element to null the element still exists
unset an element means remove the element
it works for array, stdClass objects user defined classes and also for any variable
<?php
$a = new stdClass();
$a->one = 1;
$a->two = 2;
var_export($a);
unset($a->one);
var_export($a);
class myClass
{
public $one = 1;
public $two = 2;
}
$instance = new myClass();
var_export($instance);
unset($instance->one);
var_export($instance);
$anyvariable = 'anyValue';
var_export($anyvariable);
unset($anyvariable);
var_export($anyvariable);

Categories