Message: Attempt to increment/decrement property of non-object - php

$order_temp = $this->mdl_admin->get_latest_order_id($_POST['parent_id']);
if ($order_temp) {
$order = (string)$order_temp->order++;
var_dump($order);
die();
}
This code above produce this error:
Message: Attempt to increment/decrement property of non-object
And the vardump is string '' (length=0)
If I do just something like this to vardump the variable:
$order_temp = $this->mdl_admin->get_latest_order_id($_POST['parent_id']);
if ($order_temp) {
var_dump($order_temp);
die();
}
the output is :
array (size=1)
0 =>
object(stdClass)[28]
public 'id' => string '16' (length=2)
public 'name' => string 'sssssssssssssss' (length=15)
public 'slug' => string 'aaaaaaaaa' (length=9)
public 'title' => string 'aaaaaa' (length=6)
public 'body' => string '<p>asdas asd asd </p>' (length=21)
public 'order' => string '1' (length=1)
public 'parent_id' => string '5' (length=1)
I just want to add 1 to the $order_temp->order so if e.g. the $order_temp->order = 2 the result $order = 3.
What am I doing wrong?

$order_temp is not an object, as the error message says. When you try to access (or in this case, increment) a property of something that isn't an object (and so does not have properties), you get this error.
Not knowing what $this->mdl_admin->get_latest_order_id does, I can only go so far, BUT, I can tell you to use explicit comparisons in if statements, and that your type coercion isn't doing what you expect.
Use explicit comparison:
if (is_object($order_temp) === true)
.. that might not be the cause of your bug, but that's good practice.
Your use of ((string)) in assignment coerces the presumed object variable to a string, which you then try to access property of. This is not the droid you are looking for. It appears, from PHP's perspective, that you want to do this:
"0"->order++;
...which doesn't make sense.
PHP uses type juggling[doc]. Whether your object's property order started out as a string or not, as soon as you use the increment operator ++, PHP juggles the type to become an integer. If you want a string, all you have to do is start treating it like a string:
$order_temp->order++; // <-- variable is an integer, $order_temp->order === 1
$order_temp->order .= " and bob's your uncle"; // <-- variable is a string, $order_temp->order === "1 and bob's your uncle"
For that reason, type coercion is seldom necessary in PHP.
Finally, you can do this exactly in the way that you're trying, but you'll have to understand the way the increment ++ operator works.
$number = 0;
$output = $number++;
echo "Output: ".$output; // Output: 0
$number = 0;
$output = ++$number;
echo "Output: ".$output; // Output: 1
... by placing the increment operator at the FRONT of the assignment, the return will be the incremented variable after the increment operation. If you put the operator at the end, then the return is the variable before the increment operation.
That means you could do this:
$order_number_as_string = (string) (++$one_order->order);
By enclosing the increment operator in parenthesis and type casting the outside, plus putting the increment operator as a prefix, you will get the expected results.
Documentation
Increment/Decrement operators - http://php.net/manual/en/language.operators.increment.php
Type juggling - http://php.net/manual/en/language.types.type-juggling.php
settype - http://php.net/manual/en/function.settype.php

<?php
class Test
{
public $order = 10;
}
$order_temp = new Test();
$order = (string)$order_temp->order++;
var_dump($order);
//string(2) "10"
$order = (string)$order_temp->order;
var_dump($order);
//string(2) "11"
?>
Your $order_temp is not an object. So that is what is causing the problems. Also note that you might not get the expected results, even if it works.
Your $order will contain the OLD value. See my test.
UPDATE
The var_dump of your $order_temp shows that its an array. You could use
$order_temp[0]->order++;
$order = $order_temp[0]->order
var_dump($order);

Related

PHP 7 Upgrade Uncaught exception 'Error' with message 'Cannot use string offset as an array'

I recently upgraded to PHP 7.2 and i've been running some old legacy code for a stupid record keeping system. Basically it holds an array of contest_id's and the entries to each contest.
/*
#Use: Adds entries into the active contest for a given member.
#Param: $user_id - INT
#Param: $entries - INT
*/
function add_vip_entries($user_id, $entries) {
$user_data = get_user_meta( $user_id, 'all_contests', true );
$contest_id = get_active_contest();
if ($contest_id !== 0) {
if (isset($user_data['all_contests'][$contest_id]['entries'])) {
$user_data['all_contests'][$contest_id]['entries'] = intval($user_data['all_contests'][$contest_id]['entries'] + $entries);
} else {
$user_data['all_contests'][$contest_id]['entries'] = $entries;
}
update_user_meta( $user_id, 'all_contests', $user_data );
}
}
This used to work fine but now if it's the first time the user gain entries to a given contest I get the following error.
Uncaught exception 'Error' with message 'Cannot use string offset as an array'
And it triggers on this exact line:
$user_data['all_contests'][$contest_id]['entries'] = $entries;
How Can I replicate the behavior it had in PHP7.0? It used to simply push create the data structure or if it was a brand new contest push a new contest ID and set of entries. Now it errors. I tried to edit into this
$user_data = array('all_contests' => array($contest_id => array('entries' => $entries)));
But this causes an issue where if a new contest ID is introduced it will set the data structure to only contain the contest ID and entry pair being set.
The issue here is that you can't successfully play with a STRING variable using ARRAY keys unless you are simply trying to return the Nth character of the string using $myString[n].
Repro:
$x = 'hello'
echo $x[1]; // returns 'e', i.e. the 1st char (0 based) of 'hello'
$x[4] = 'x';
echo $x; // returns 'hellx';
$x['my_key'] = 5; // Error illegal string offset
I.e. you can use array keys to access the character of a string (i.e. the string offset, but it will only allow you to use a valid offset within the length of the string. You can't use a random key on a variable already initialized as a string.
You need to make get_user_data return an array at all times. If it is empty, return [].
$user_data = get_user_meta( $user_id, 'all_contests', true ) ?: [];
maybe
$user_data = array('all_contests' => array($contest_id));

PHP references not working as I expect them to

Basically how I understand references work is
$a = 5;
$b = &$a;
$a = 10;
echo $b; // 10;
However in this bit of code I'm getting unexpected (for me, which probably has an explanation) result
class Room {
private $users = array();
public function addUser(&$user){
$this->users[] = $user;
}
}
$users = array(
1 => 'Tom',
2 => 'Hank',
3 => 'Sam',
4 => 'John'
);
$room = new Room();
$room->addUser($users[1]);
$room->addUser($users[3]);
unset($users[3]);
echo "<pre>" . print_r($room, true) . "</pre>";
echo "<pre>" . print_r($users, true) . "</pre>";
I expect, after unsetting $users[3], the only user inside of $room to be Tom, but that is not the case, both Tom and Sam are present in the object. Why is unset not affecting the object's property?
EDIT:
Even if I take things a step further with the example and create a class User the effect is still the same
class Room {
private $users = array();
public function addUser(&$user){
$this->users[] = $user;
}
}
class User {
public $name;
function __construct($name){
$this->name = $name;
}
}
$users = array(
1 => new User('Tom'),
2 => new User('Hank'),
3 => new User('Sam'),
4 => new User('John')
);
$room = new Room();
$room->addUser($users[1]);
$room->addUser($users[3]);
unset($users[3]);
echo "<pre>" . print_r($room, true) . "</pre>";
echo "<pre>" . print_r($users, true) . "</pre>";
Unset operates on symbols, not reference targets.
That is why using unset on an undefined variable doesn't raise any kind of error.
$a = 10;
$b = &$a;
unset($b); // forget the name "$b" exists.
echo $a; // 10
If you want to unset it in both places, you have to assign null to one of the variables. This is a "hard unset", as opposed to a "soft unset" which is what you are currently doing.
Also you are not assigning a reference, you're assigning a copy.
$this->users[] = &$user;
Reference Counting Basics :
A PHP variable is stored in a container called a "zval". A zval
container contains, besides the variable's type and value, two
additional bits of information. The first is called "is_ref" and is a
boolean value indicating whether or not the variable is part of a
"reference set". (...) Since PHP allows user-land references, as
created by the & operator, a zval container also has an internal
reference counting mechanism to optimize memory usage. This second
piece of additional information, called "refcount", contains how many
variable names (also called symbols) point to this one zval container.
(...)
Variable containers get destroyed when the "refcount" reaches zero.
The "refcount" gets decreased by one when any symbol linked to the
variable container leaves the scope (e.g. when the function ends) or
when unset() is called on a symbol.
Example with arrays:
<?php
$a = array(
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
);
debug_zval_dump($a);
// ... string(3) "bbb" refcount(1) ...
$b = array();
$b[0] = &$a[0];
$b[1] = &$a[1];
$a[1] = 'ddd';
debug_zval_dump($a);
// ... &string(3) "bbb" refcount(2) ...
debug_zval_dump($b);
// ... &string(3) "bbb" refcount(2) ...
unset($a[1]);
debug_zval_dump($a);
/*
array(2) refcount(2){
[0]=>
&string(3) "aaa" refcount(2)
[1]=>
&string(3) "ddd" refcount(2)
}
*/
debug_zval_dump($b);
// ... string(3) "ddd" refcount(1) ...
var_dump($a);
/*
array (size=2)
0 => &string 'aaa' (length=3)
2 => string 'ccc' (length=3)
*/
var_dump($b);
/*
array (size=2)
0 => &string 'aaa' (length=3)
1 => string 'ddd' (length=3)
*/
I think there's a slight logical problem between your desired effect and the way you try to do it.
If I understand correctly, you want to assign users to a container, then unsetting one of those user in a way that it will also be unsetted in your container. This
unset($users[3]);
unsets the value of the fourth element of your users array.
if we did $user[3] = 'foo'; the value contained in the corresponding container's entry will be set to 'foo' as well, but the container's index key itself will not get unset, or affected by the reference, because it is not part of the referenced value
If you want to unset the user, either you keep track of which index key is assigned to which user in your container and then delete users with this index key, or you set the value of $users[3] to null (or whatever suits your needs) and skip the null values when dealing with your container
You can change a value of arrays, like this:
CODE:
private $users = array();
public function addUser(&$user){
$this->users[] = &$user;
}
}
$users = array(
1 => 'Tom',
2 => 'Hank',
3 => 'Sam',
4 => 'John'
);
$room = new Room();
$room->addUser($users[1]);
$room->addUser($users[3]);
$users[3] = "AAA123";
echo "<pre>" . print_r($room, true) . "</pre>";
echo "<pre>" . print_r($users, true) . "</pre>";
OUTPUT:
Room Object
(
[users:Room:private] => Array
(
[0] => Tom
[1] => AAA123
)
)
Array
(
[1] => Tom
[2] => Hank
[3] => AAA123
[4] => John
)
But delete it's not possible this way... I don't know how to explain, so just give example:
$a = 10;
$b = &$a;
unset($a);
echo $b; // 10
Then you deleting variable name, you not delete zval(container), until refcount reach 0... then "Garbage Collection" do all work and delete zval...
So method unset() remove variable name only in this case...
Be careful.
You are passing to addUser() a reference to the string 'Tom' allocated while building the array $users.
First, addUser() should read $this->users[] =& $user;, otherwise you will be copying the value into $this->users[] instead of sharing the reference.
Now, both $users and Room::$users share the same objects, however unset($users[3]) removes the element mapped by the index 3 from the array, it does not destroy the mapped object.

Pass Dynamic Values from one function to another function in class

Hi all i have a function in a class which will print all the variable pass for this function
Class Code :
<?php
class MyPrintClass {
public function printVaraible() {
var_dump(func_get_args())
}
}
$printVar = new MyPrintClass;
?>
if i use like below its correctly working and printing all the values pass.
$print->printVaraible('value1','Value2',20);
The output i got for above cmd
array (size=1)
0 => string 'value1' (length=6)
1 => string 'value2' (length=6)
2 => string 'value3' (length=6)
if i use like blow its showing as a single array.. i want the value seperated.. how its possible ?
function getVal(){
global $printVar;
$print->printVaraible(func_get_args());
}
I need to pass the value like below
getVal('value1','Value2',20);
and i need the output as
array (size=1)
0 => string 'value1' (length=6)
1 => string 'value2' (length=6)
2 => string 'value3' (length=6)
Currently i get NULL as the output
Updated Question Based On the Answer Given By deceze
** I Also had a small change in my code**
Class Code :
<?php
class MyPrintClass {
public function printVaraible($tag,$value) {
echo $tag.' == '.$value;
var_dump(func_get_args());
}
}
$printVar = new MyPrintClass;
?>
Converted Class To Function By
<?php
function getVal($tag,$value) {
global $printVar;
call_user_func_array([$printVar, 'printVariable'], $tag,$value,func_get_args());
}
?>
If i try to use it as below i get error
<?php getVal('first','second','third,'fourth'); ?>
Warning: call_user_func_array() expects exactly 2 parameters, 4 given
call_user_func_array([$print, 'printVariable'], func_get_args());
This calls the function with separate parameters from an array. I'd question the usefulness of this though, since you'll just end up with an array from func_get_args anyway. Why not pass arrays around in the first place?
Your getting null in the return because you never return anything, from the functions above.
if you want to call a class method I suggest you look into Refection or you can also do
$a = 'printVaraible';
$class->$a(1,3); //
besides using call_user_func_array, I would also suggest looking into closures (php > 5.3 ), which will let you write them this way.
$a = function foo(){ echo 'foo'};
$a();
Last thing I would generally avoid using global, as it obscures in your code where the values come from. It is always better to inject values into the function scope and not mix that with the global, imaging hunting that global down in a large project, I just avoid them at all cost and forget they exist.

Weird php mysql variable type issue

Now I admit I am slightly new to laravel still, but to me this just does not make sense. The model that goes along with this table contains only 2 functions, both containing a relationship statement.
I am using Laravel4, mysql, php 5.5
Any ideas are welcome :D
The database record-definitions are for both DATETIME, allow null and no default value (changed that after the screenshots)
the $challenge variable is part of the data I pass on to the view like so:
$challenges = Auth::user()->challenges;
$data['challenges'] = $challenges;
return View::make("challenges/topic", $data);
and in the view I use
#foreach($challenges as $challenge)
read the challenge values (I am aware I cant echo like that without php tags or {{ }}, just easier to explain)
echo gettype($challenge->deadline) // results in string
echo gettype($challenge->created_at) // results in object
Depends on how you access it, if you do:
Route::any('test', ['as' => 'test', function()
{
$a = Article::first();
var_dump( gettype($a->created_at) );
$a = DB::table('articles')->first();
var_dump( gettype($a->created_at) );
}]);
You will get:
string 'object' (length=6) /// This is Eloquent
string 'string' (length=6) /// This is the QueryBuilder directly accessing your table

$this->$array[$key] returning nothing when there is a value in place

I'm attempting to dynamically use a value in an array within an object.
In my particular case I have an array like this.
$this->customer = array(
[dealerId] => 4
[billFirstName] => Joe
[billLastName] => Blo
[billAddress1] => 1010s
[billAddress2] => 1020s
[billCity] => Anytown
[billState] => ST
[billCountry] => USA
[billPostalCode] => 11111
[dEmail] => emailaddress
[billPhone] => 8008008888
[password] => password
[distPrice] => 5
[distCost] => 20);
$result = $this->keyCheck('dealerId', 'customer');
The method I'm using:
protected function keyCheck($key, $array, $type = false)
{
if(array_key_exists($key, $this->$array) && $this->$array[$key]):
return $this->$array[$key];
else:
return $type;
endif;
}
The first check works (array_key_exists($key, $this->$array)). But the second check fails ($this->$array[$key]) even though there is a value held in that index of the array. I've proven that the array exists inside the keyCheck() method by using, print_r($this->$array); inside the method. And I know the value I'm looking for is available inside the method by using, print $this->$array['dealerId'];
Don't get hung up on the names, or the methodology I'm using, what I'm really interested in is finding out how to address a value held in an array that is dynamically addressed in this way.
It's probably so easy that I'll be slapping my head once it's revealed...
You are running into the dreaded treat string as an array trap, i.e.:
$str = 'customer'; echo $str[0]; // prints c
The $array[$key] portion of $this->$array[$key] is being interpreted as show me the nth index of string $array (if you pass customer it will try to access $this->c, which doesn't exist). Assign the array to a variable first. Here is an example:
class A
{
public $customer = array('dealerId'=>4, 'billFirstName'=>'Joe');
public function keyCheck($key, $arrayName, $type = false)
{
$array = $this->$arrayName;
if(array_key_exists($key, $array) && $array[$key]) {
return $array[$key];
} else {
return $type;
}
}
}
$a = new A();
echo $a->keyCheck('billFirstName', 'customer'); // Joe
Alternatively, you could use the complex syntax: $this->{$arrayName}[$key] as suggested in Artjom's answer.
PHP 5.4 addresses this gotcha:
[In PHP 5.4] Non-numeric string offsets - e.g. $a['foo'] where $a is a
string - now return false on isset() and true on empty(), and produce
a E_WARNING if you try to use them.
This E_WARNING should help developers track down the cause much more quickly.
Well u have to use
$this->{$array}[$key];

Categories