0

So I want to get the saved database option and then update it with the API return and then save the option in the database again, but I'm having some issues.

So I have the following method:

public static function refresh_access_token(string $user_id, string $access_token): bool
{
    if (!$authenticated_users = get_option('instagram_authenticated_users')) {
        return false;
    }

    // The code for the cURL request is here (It's not needed for this question).

    $array = json_decode($curl_exec);

    foreach ($authenticated_users as $user) {
        if ($user['user_id'] === (int) $user_id) {
            $user['access_token'] = $array->access_token;
            $user['access_token_expiration'] = time() + $array->expires_in;
            $user['last_updated'] = (string) time();
        }
    }

    return update_option('instagram_authenticated_users', $user);
}

The cURL response $array gives me the following:

$array = {stdClass} [3]
   access_token = "IGQVJVV1YzUEdMb1lDajNZAcmRxakR3V0dkOXUyWHdtSGM0ZAHFYempkX0VHYVlKYTVYMkNxYVVpNVFuclZAlWVRsbmktNjN2cG1ISEJ4T2VJWUd0M2JBMGcyUlFXOWFlTjdEVDhKaEJB"
   token_type = "bearer"
   expires_in = {int} 5099901

Now within $authenticated_users, I have an array of arrays which outputs:

$authenticated_users = {array} [2]
 [0] = {array} [5]
    username = "semi_test1"
    user_id = {int} 17841449642220098
    access_token = "IGQVJWMDJnblBnUGMtMTVFa1NpUGdValNBVUZAyZAWM2OTdSSkRFdmNUbnVOQXJqeFhwbDVmT0c3aXJfamFYdnZANSlpXc3Mwc05PS0tMSzNsbXhES0tkTzNoOEY3RFRIb3dsblBiTXN3"
    access_token_expiration = {int} 1651281005
    last_updated = {int} 1646181108
 [1] = {array} [5]
    username = "semi_test2"
    user_id = {int} 17841400835712753
    access_token = "IGQVJVN3VOaUJrU2NzdGxWVTlwaXFLT2h1bnpFU3FKaEpOUGNPeWh2SkpjdHpnRXkyaGJ3NDZArXzJvRWFHdVRqZAEFfN0RodjV4cHQ2YTliSmhyVThUSjlCc1paLV9Fd2dqbzI1b25B"
    access_token_expiration = {int} 1651281136
    last_updated = {int} 1646181136

Now I'm looking at my $user_id param and using the foreach to compare them to the user_id inside an array, and if it matches, update the values inside the array and then update the options, but also retain the other array that hasn't been changed.

I've tried everything and it doesn't seem like it's working and the update_option('instagram_authenticated_users') doesn't update the values and it retains the same data.

So I want get_option('instagram_authenticated_userss') when called after the update to be the following with the new data (The second index should have been updated):

$authenticated_users = {array} [2]
 [0] = {array} [5]
    username = "semi_test1"
    user_id = {int} 17841449642220098
    access_token = "IGQVJWMDJnblBnUGMtMTVFa1NpUGdValNBVUZAyZAWM2OTdSSkRFdmNUbnVOQXJqeFhwbDVmT0c3aXJfamFYdnZANSlpXc3Mwc05PS0tMSzNsbXhES0tkTzNoOEY3RFRIb3dsblBiTXN3"
    access_token_expiration = {int} 1651281005
    last_updated = {int} 1646181108
 [1] = {array} [5]
    username = "semi_test2"
    user_id = {int} 17841400835712753
    access_token = "IGQVJYOEdqQ0hpbmZAHWlFsdDdMNHdUN1hmenhlV2ZAYOTBtMTJiaFhtSjhyUW9DVm9UREtLZAlFQVHhuVE1XUUFBNUF5SHoxdWRJNXd5dUF6ZAkNKeEtNYmVzRzNTWXdGSmhldG9ILTdn" (NEW VALUE)
    access_token_expiration = {int} 1651282134 (NEW VALUE)
    last_updated = {int} 1646181136 (NEW VALUE)

Can someone spot that I might be doing wrong?

SLE
  • 17
  • 4
  • Where's your `update_option()` code? – GetSet Mar 02 '22 at 02:22
  • @GetSet `update_option()` is just native WordPress updating of the option table in the database, with `$authenticated_users` passed to it as the value, is the same value as the original `$authenticated_users`, it doesn't have the new values from `$array` as shown in the last code piece. – SLE Mar 02 '22 at 02:24
  • Basically to break it down, I want to `get_option('test')` which is an array of arrays, loop through and if we have a user_id match, update access_token and access_token_exp and last_updated with new values on let's say index [1], so when I call `get_option('test')` next time, it has the original array index [0], but also index [1] with the new values. – SLE Mar 02 '22 at 02:27
  • Why are you doing this: `$user[$key]['access_token'] = $array->access_token;`? $key is the key from the authenticated users array. It's going to be 0, then 1. Those array elements don't even exist in a user sub_array. You want to do `$user['access_token'] = $array->access_token;` – gview Mar 02 '22 at 02:32
  • @gview, I thought that's what needed to be made, but I literally had a downvote and a question closed with pointing this answer (https://stackoverflow.com/questions/10121483/modify-array-values-in-foreach-loop) before by someone, as they told me this was the right way. Should I change it back? – SLE Mar 02 '22 at 02:34
  • That goes for all the variables you are updating ;) – gview Mar 02 '22 at 02:34
  • That general question has nothing to do with your code here. I read your code, and explained the problem. I'd suggest you adopt my edits and continue your debugging. – gview Mar 02 '22 at 02:36
  • @gview, So I changed it back and debugged and tested and it seems like the array with the new `$array` values aren't updated still, can you spot anything off in my code? – SLE Mar 02 '22 at 02:36
  • `$user` will have those keys if they are already present in `$authenticated_users` via the foreach. – GetSet Mar 02 '22 at 02:36
  • Thanks for the explanation! I was not 100% sure if the keys were needed. I just updated the code. – SLE Mar 02 '22 at 02:37
  • Also another problem you go through the 2 users, either one which could be changed, but you only call `return update_option('instagram_authenticated_users', $user);` after you left the foreach. If either user can be changed you should be updating both. – gview Mar 02 '22 at 02:38
  • What do you see if you *var_dump()* or *print_r()* `$authenticated_users` after your foreach code? Are the desired changes in the array? – GetSet Mar 02 '22 at 02:38
  • @GetSet, they are not, the values remain the same, the new data from `$array` are not included in `$authenticated_users` after the foreach. I can see the debugger finding an `user_id` match and entering the if statement, but it seems like the new values don't get set. – SLE Mar 02 '22 at 02:39
  • @GetSet, what does $authenticated_users have to do with anything? It is the original values. They are compared, and then changed if appropriate and written back to the options storage. – gview Mar 02 '22 at 02:40
  • Ok the problem is your foreach is not creating references. You have to explicitly tell it to. I'll provide an example as an answer below. – GetSet Mar 02 '22 at 02:50
  • I [boiled down the foreach and key/val here](https://3v4l.org/s4e5U). In your code you could omit $key entirely. It is not needed or used. It's an option for foreach, and as I said, in your 2 element array, the values will be 0 and 1, but they are the indexes of the array being foreached through. – gview Mar 02 '22 at 02:50
  • @gview thanks for the input, as I'm learning new things, I wasn't 100% clear on if the key was needed or not, but this explains things better. – SLE Mar 02 '22 at 02:53
  • @GetSet, thank you! All leads will help me further this little hiccup I'm having. – SLE Mar 02 '22 at 02:53
  • @GetSet, that is not the problem at all, because the original array does not need to be updated based on the code presented. A completely independent $user variable is being used, and that $user variable is being used to update via `update_option('instagram_authenticated_users', $user);` – gview Mar 02 '22 at 02:54
  • @gview, should I be calling `update_option('instagram_authenticated_users', $authenticated_users)` instead of `update_option('instagram_authenticated_users', $user)`? I still want to preserve $authenticated_users, but just have it be updated. – SLE Mar 02 '22 at 02:56
  • If that is what you needed, then you can do what GetSet demonstrated. It works (or not) depending on the datatypes employed. Because these were simple arrays they are passed by reference. If they are objects they are passed by value. See this: https://3v4l.org/v2fBe In general it is referred to as mutation, and frowned upon. The alternative would be to build a new array inside your function. Then you aren't dealing with this issue. – gview Mar 02 '22 at 03:37

2 Answers2

0

The problem is your foreach is not creating references. You have to explicitly tell it to on which variables.

This solution doesn't try to add in the code you need where you want it to retain both the previous and the current access tokens. But this solution does you show how to change an array "in-place" using foreach, which will correct (or help) in correcting the problem of having the changes stored to the db.

Replace your foreach statement with:

foreach ($authenticated_users as &$user) {

That ampersand on &$user will make the difference: it will create $user by reference. Any changes to it will change $authenticated_users too.

Example:

$a = array("1","apple","3","pear");

foreach ($a as $p) {
    if ($p == "apple") $p = "sauce";
}

print_r($a);

/* Outputs:
Array
(
    [0] => 1
    [1] => apple
    [2] => 3
    [3] => pear
)
*/

foreach ($a as &$p) {
    if ($p == "apple") $p = "sauce";
}

print_r($a);

/* Outputs:
Array
(
    [0] => 1
    [1] => sauce
    [2] => 3
    [3] => pear
)
*/

The second foreach changes the source array because of the ampersand sign on $p in the foreach statement.

In this way, the same approach could work for you.

GetSet
  • 1,511
  • 2
  • 10
  • 13
  • The `&` ampersand worked!!! – SLE Mar 02 '22 at 02:58
  • Now it successfully updates the values for the specific array! I'll have to learn more about the creating references part, this is the first time I'm hearing about it. – SLE Mar 02 '22 at 02:59
0

So this is an alternative to using the '&' to pass things by reference, and mutate the original array.

public static function refresh_access_token(string $user_id, string $access_token): bool
{
    if (!$authenticated_users = get_option('instagram_authenticated_users')) {
        return false;
    }

    // The code for the cURL request is here (It's not needed for this question).

    $array = json_decode($curl_exec);
    $newAuthenticatedUsers = [];

    foreach ($authenticated_users as $user) {
        if ($user['user_id'] === (int) $user_id) {
            $user['access_token'] = $array->access_token;
            $user['access_token_expiration'] = time() + $array->expires_in;
            $user['last_updated'] = (string) time();
        }
        $newAuthenticatedUser[] = $user;
    }

    return update_option('instagram_authenticated_users', $newAuthenticatedUser);
}

If you understand what you are doing, and why in this circumstance it works, then great. You also could just as easily use a for loop and update the original array without a foreach at all:

$sizeOf = count($authenticated_users);
for ($i=0; $i < sizeOf; $i++) {
    if ($authenticated_users[$i]['user_id'] === (int)$user_id) {
       $authenticated_users[$i]['access_token'] = $array->access_token;
       $authenticated_users[$i]['access_token_expiration'] = time() + $array->expires_in;
       $authenticated_users[$i]['last_updated'] = (string) time();            
    }
}
return update_option('instagram_authenticated_users', $authenticated_users); 

Keep in mind, per my example in the comments, that an array of objects (which is very common when dealing with ORM's and database fetching libraries) is already passed by reference, and you don't want to use a reference in a foreach.

gview
  • 14,876
  • 3
  • 46
  • 51