Not a bug [E_WARNING] array_merge(): Argument #2 is not an array in src/XF/Admin/Controller/PaymentProfile.php at line 150

truonglv

Well-known member
Affected version
Latest
Code:
ErrorException: [E_WARNING] array_merge(): Argument #2 is not an array in src/XF/Admin/Controller/PaymentProfile.php at line 150
XF::handlePhpError()
array_merge() in src/XF/Admin/Controller/PaymentProfile.php at line 150
XF\Admin\Controller\PaymentProfile->actionDelete() in src/XF/Mvc/Dispatcher.php at line 249
XF\Mvc\Dispatcher->dispatchClass() in src/XF/Mvc/Dispatcher.php at line 88
XF\Mvc\Dispatcher->dispatchLoop() in src/XF/Mvc/Dispatcher.php at line 41
XF\Mvc\Dispatcher->run() in src/XF/App.php at line 1891
XF\App->run() in src/XF.php at line 328
XF::runApp() in admin.php at line 13

When i tries to delete an payment profile.
 
I can't reproduce that here on 2.0.4.

Do you have any custom purchasable handlers? What do they return in the getPurchasablesByProfileId method?
 
Ok. I found 2 issues.
1: Of XenForo. It's does not check while add-on disabled. You have fetch all purchasable records. I have not sure it's as designed or not.
2: The main issues refer to @DragonByte Tech add-on. (I will report to their support thread).
 
I can't reproduce that here on 2.0.4.

Do you have any custom purchasable handlers? What do they return in the getPurchasablesByProfileId method?
Given that abstract public function getPurchasablesByProfileId($profileId); is phpdoc'd to return either an array or an ArrayCollection, I'm assuming that either of the following are valid:

Donate:
PHP:
    public function getPurchasablesByProfileId($profileId)
    {
        $drives = [];

        foreach (\DBTech\Donate\Application\App::em()->findCached('DBTech\Donate:Drive') as $key => $drive)
        {
            if (!in_array($profileId, $drive->payment_profile_ids))
            {
                // Skip this
                continue;
            }

            $drives['dbtech_donate_donation' . $key] = [
                'title' => $drive->getTitle() . ' #' . $drive->driveid,
                'link' => \XF::app()->router('admin')->buildLink('dbtech-donate/drive', [], ['action' => 'modify', 'driveid' => $drive->driveid])
            ];
        }

        return new ArrayCollection($drives);
    }


Credits:
PHP:
    public function getPurchasablesByProfileId($profileId)
    {
        $events = [];

        foreach (\DBTech\Credits\Application\App::em()->findCached('DBTech\Credits:Event') as $key => $event)
        {
            if (!in_array($profileId, $event->settings['payment_profile_ids']))
            {
                // Skip this
                continue;
            }

            $events['dbtech_credits_currency' . $key] = [
                'title' => $event->getTitle() . ' #' . $event->eventid,
                'link' => \XF::app()->router('admin')->buildLink('dbtech-credits/event', [], ['action' => 'modify', 'eventid' => $event->eventid])
            ];
        }

        return new ArrayCollection($events);
    }


eCommerce:
PHP:
    /**
     * @param $profileId
     *
     * @return array|\XF\Mvc\Entity\ArrayCollection
     */
    public function getPurchasablesByProfileId($profileId)
    {
        return [];
    }

If this is not correct, can you please advise?


Fillip
 
I think the docs is wrong at this point. I have see almost default Purchasable type return array not ArrayCollection.
 
Yeah the doc is wrong, I think it was auto-generated by phpStorm based on the concrete version in the UserUpgrade handler. It returns the result of a pluck on a collection. That can return a collection (on empty), but you'll notice we ask it not to:
PHP:
return $upgrades->pluck(function(\XF\Entity\UserUpgrade $upgrade, $key) use ($router)
{
   return ['user_upgrade_' . $key, [
      'title' => $this->getTitle() . ': ' . $upgrade->title,
      'link' => $router->buildLink('user-upgrades/edit', $upgrade)
   ]];
}, false);
Technically, that should always return an array. I've adjusted the docs but indeed you'll have to adjust the code to return an array rather than a collection.
 
Yeah the doc is wrong, I think it was auto-generated by phpStorm based on the concrete version in the UserUpgrade handler. It returns the result of a pluck on a collection. That can return a collection (on empty), but you'll notice we ask it not to:
PHP:
return $upgrades->pluck(function(\XF\Entity\UserUpgrade $upgrade, $key) use ($router)
{
   return ['user_upgrade_' . $key, [
      'title' => $this->getTitle() . ': ' . $upgrade->title,
      'link' => $router->buildLink('user-upgrades/edit', $upgrade)
   ]];
}, false);
Technically, that should always return an array. I've adjusted the docs but indeed you'll have to adjust the code to return an array rather than a collection.
How about for the issue #1 i have replied before?
 
IIRC that was a conscious decision.

The whole idea behind this method, btw, is we prevent payment profiles being deleted if they are in use by any purchasable item. The significance there is that if a payment profile was deleted accidentally while it's still in use then it would cause payments to fail (significantly, recurring payments).

So if you imagine that an add-on could be disabled temporarily, then someone comes along and deletes the payment profile, then re-enables the add-on, it'd likely cause problems.

So I think the way it works now is right.
 
BTW, with the above in mind about needing an array, and the potential problem of certain add-on code not being available, it might be advisable to find the in-use purchasables using the DB adapter queries instead.
 
The whole idea behind this method, btw, is we prevent payment profiles being deleted if they are in use by any purchasable item. The significance there is that if a payment profile was deleted accidentally while it's still in use then it would cause payments to fail (significantly, recurring payments).
Informative :)

Looks like I have work to do for the eCommerce mod to make sure that isn't a gotcha. Thanks for the explanation, much appreciated.


Fillip
 
BTW, with the above in mind about needing an array, and the potential problem of certain add-on code not being available, it might be advisable to find the in-use purchasables using the DB adapter queries instead.
For me personally, AFAIK the Donate and Credits code wouldn't be affected by the lack of add-on code availability as the App class is part of a platform-agnostic framework.

Looking at the UserUpgrade code, it uses a Finder - are custom Finders not available if the addon is disabled? From a cursory glance, it seems like they should still work.

I know Finders themselves can potentially call code that may not be available, but if we're only using the standard ->where('payment_processor_id', $profileId) it should be safe, unless I'm missing something? 🤔


Fillip
 
It should mostly be safe, I think. The exception being if it ends up touching anything that isn't enabled when the add-on is disabled, such as anything that instantiates content type handlers or touches class extensions.
 
Top Bottom