Indirect modification of overloaded element has no effect - php

I have the following Eloquent query:
$item = Item::where('sku', $sku)->first();
After this query comes in I'm adding a variety of elements manually such as:
$item['total'] = $item['subtotal'] + $this->currentInventory();
Statements like the above that modify the object work just fine.
It stops working when I do the following:
$item['fields'] = [];
$fields = DB::table('item_fields')->where('item_id', $item['id'])->get();
foreach ($fields as $f) {
if (!isset($item['fields'][$f->field_group_name]))
$item['fields'][$f->field_group_name] = [];
$item['fields'][$f->field_group_name]['valid_values'] = DB::table('item_field_valid_values')->where('item_field_id', $f->item_field_id);
}
This will cause the line $item['fields'][$f->field_group_name] = []; to produce the error:
Indirect modification of overloaded element of Item has no effect
How can it be that I can assign $item['fields'] = [] but when I try to add an actual element to the $item['fields'] array that I get this error?
PHP version 5.6.0.

First off, you're missing get() in your code, so either:
1 You are iterating over the Query\Builder instead of the array of results, because you never executed the query. (I suppose you forgot it only here, because otherwise you would probably get trying to get property of non-object)
or 2 one of the rows has '' or null value in the field_group_name column.
That's why your code does this:
$item['fields'][NULL] = [];
and that's causing Indirect modification ... error.
So add check for empty value first:
if ($f->field_group_name && ! isset($item['fields'][$f->field_group_name]))
$item['fields'][$f->field_group_name] = [];
you may need to adjust it to your needs, but you get the idea.

Related

One liner to get a reference on a property, and initialize it if it doesn't exist

I'm used to JavaScript where I often write things like this: const ref = obj.arr || (obj.arr = []);.
With one line of code, this gives me a reference on obj.arr after having initialized it to empty array if it didn't exist.
I'm trying to do the same in PHP but I'm struggling with ampersands, since the assignation is done by copy of value by default. Here is my failing attempt which I thought it would work:
$ref= &$obj['arr'] ?? &($obj['arr'] = []);
If I understand your question correctly, a ternary operator should suffice:
$obj['arr'] = isset($obj['arr']) ? $obj['arr'] : [];
$ref = &$obj['arr']);

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();

"Cannot use a scalar value as an array" for undefined values

I have the following piece of code:
function Biz_GetAccountsPerMarket($site = "", $cache = true){
if($site == ""){
$site = $_SESSION["sitedirect_current_site"];
}
$aAccounts = Sys_OptionsGet("sys_site/".$site, "account", $cache);
$aAccountInfo = array();
$aVatAccount = Biz_GetVatAccounts($site, $cache);
$aSalesAccount = Biz_GetSalesAccounts($site, $cache);
foreach((array)$aAccounts as $code => $data){
foreach((array)$data["data"] as $market => $aItem){
$aAccountInfo[$market][$code] = $aItem;
$aAccountInfo[$market][$code]["vat"] = $aVatAccount[$aItem["vat_account_code"]]["value"];
$aAccountInfo[$market][$code]["vat_account"] = $aVatAccount[$aItem["vat_account_code"]]["account"];
$aAccountInfo[$market][$code]["sales_account"] = $aSalesAccount[$aItem["sales_account_code"]]["account"];
}
}
return $aAccountInfo;
}
The four innermost lines in the nested loop generates warnings: "Cannot use a scalar value as an array".
Adding lines that initialises $aAccountInfo[$market] and $aAccountInfo[$market][$code] to empty arrays first silences the errors, but this is far from the only place in our code where nested arrays are initialised in this way; and I can't figure out why it is a problem in the first place.
The following code should reproduce the problem; as far as I can tell; but doesn't:
<?php
ini_set('display_errors', '1');
error_reporting(E_ALL | E_STRICT);
$aTest = [];
$aTest['key']['key'] = 'sausage';
If $aItem was a string or false, or any scalar; I could understand what the problem is; but then the warning should only happen for the last three lines, not all four.
There are other weird things happening there, I hope they're all connected. This is the only one I've managed to isolate enough to ask a question about.
Is it possble to set the default value created by array access somehow?
edit:
I've noticed that many strings that are generates have an extraneous "0". This breaks things, like SQL. If empty array values somehow default to "0" or something, that would explain a lot. I have no idea how that could happen though. I'm currently grepping for "register_tick_function"...
You need to check if arrays have the expected keys. Something like this:
$aAccountInfo[$market][$code]["vat"] = isset($aItem["vat_account_code"]) ? (isset($aVatAccount[$aItem["vat_account_code"]]) ? $aVatAccount[$aItem["vat_account_code"]]["value"] : []) : [];
I am afraid your code is wrong
This line
$aAccountInfo[$market][$code] = $aItem;
creates the new occurance containing a SCALAR value and you then try and add a sub array onto that scalar value, hence the error
If you do this instead
$t = array();
$t['item'] = $aItem;
$t["vat"] = $aVatAccount[$aItem["vat_account_code"]]["value"];
$t["vat_account"] = $aVatAccount[$aItem["vat_account_code"]]["account"];
$t["sales_account"] = $aSalesAccount[$aItem["sales_account_code"]]["account"];
$aAccountInfo[$market][$code] = $t;
Then you will get a sub array created with all the values in it;

PHP array_push error

My code is as below,
$products = array();
for($i=0; $i < sizeof($sales); $i++){
if(!in_array($sales[$i]['Product']['product'], (array)$products)){
$products = array_push((array)$products, $sales[$i]['Product']['product']);
}
}
I'm getting an error called Fatal error: Only variables can be passed by reference...
I'm using php5
You don't use array_push like that, that's your basic problem. You're trying to fix an error you're producing by casting $products to an array, which causes a new error. You use array_push like this:
array_push($products, ...);
You do not assign the return value back to $products, because the return value is the new number of elements in the array, not the new array. So either:
array_push($products, $sales[$i]['Product']['product']);
or:
$products[] = $sales[$i]['Product']['product'];
Not:
$products = array_push($products, $sales[$i]['Product']['product']);
and most certainly not:
$products = array_push((array)$products, $sales[$i]['Product']['product']);
Please RTM: http://php.net/array_push
The first parameter ($products in your case) has to be a reference, therefore a variable has to be passed. You now cast the variable to an array first and the result of that cast cannot be passed by reference since it is not assigned to a variable. You will have to assign it to a variable first or remove the cast.

PHP Fatal Error: "Can't use method return value in write context in.... "

Trying to use a class that expects something like:
$client->firstname = 'bob';
$client->lastname = 'jones';
So I want to pass this data to the script in an array... where the keys and values are set elsewhere. I want to step through the array passing the key and value to the class. Trying to use this:
while($Val = current($CreateClientData)){
$client->key($CreateClientData) = $Val;
next($CreateClientData);
}
getting this:
Fatal error: Can't use method return value in write context in
blahblahpath on line 40.
Line 40 being: $client->key($CreateClientData) = $Val;
How can I do this?
If $client is already an instance of some class, and $CreateClientData is an array, then you probably wan to do something like this:
foreach($CreateClientData as $k => $v) {
$client->{$k} = $v;
}
This assumes of course that every key in the array is a valid member of the $client instance. If not, then you will have to do some additional checking before assigning the value, or you will have to wrap the assignment in a try / catch.
EDIT
The answer as to why your code doesn't work is because PHP doesn't allow for assignment of class properties to certain functions that return values. In your case, key($CreateClientData) returns a key. So you could alter your code and just add
$key = key($CreateClientData);
$client->$key = $Val;
But, the foreach loop is a lot cleaner anyway.
Why don't you use a foreach loop?
foreach($CreateClientData as $key => $val) {
$client->$key = $val;
}

Categories