debugging protected functions in OXID eShop

OXID framework has some private and lots of protected functions in it’s classes and often they are hard to debug because of code enryption in PE/EE or hard extendable dataflow inside the functions. Shortly OXID added an extra protection for several core classes to ensure the integrity and safety of the shop, which makes these classes not extendable with modules. Should you ever see an OXID developer crying, this will be the reason. This or the next paypal+ module.

## Here is an example:
There is a function „*oxArticle->getCustomerAlsoBoughtThisProducts()*“. I had to debug this function to find out, why oxid displays inactive articles in crosselling products.

public function getCustomerAlsoBoughtThisProducts()
    // Performance
    $myConfig = $this->getConfig();
    if (!$myConfig->getConfigParam('bl_perfLoadCustomerWhoBoughtThis')) {
    // selecting products that fits
    $sQ = $this->_generateSearchStrForCustomerBought();
    $oArticles = oxNew('oxArticleList');
    $oArticles->setSqlLimit(0, $myConfig->getConfigParam('iNrofCustomerWhoArticles'));
    if ($oArticles->count()) {
        return $oArticles;

I found out, that getCustomerAlsoBoughtThisProducts() receives its sql query for loading „also bought articles“ from another protected function and returns the loaded article list. There is no easy way for you to see the sql query generated by the protected function.

## The common way for debugging:
Inserting var_dump() at the end of *_generateSearchStrForCustomerBought()* is the easiest way to debug this little piece of shop. Oh, you are using encoded PE or EE? Sorry for that. In this case you have to create new module and extend oxArticle e.g. with a custom public function calling *_generateSearchStrForCustomerBought()* and returning its response or extend *_generateSearchStrForCustomerBought()* with a var_dump like you could insert the code in CE.
Approx. time effort: 2 minutes for CE and 20 minutes for PE/EE. You can add additional 15 minutes for unexperienced developers and 25 minutes for tired developers.
Tired unexperienced developers will probably need tranquilizer and a new keyboard.

## Debugging with dev-console:
The idea is the same: we will extend oxArticle but we do not need a module anymore. Its dead simple:

class moped extends oxarticle {
    public function give() {
        return $this->_generateSearchStrForCustomerBought();
$m = oxNew("moped");
echo $m->give();

Thats it! You will see the generated sql query:

select distinct oxv_oxarticles_de.* from (
  select d.oxorderid as suborderid 
  from oxorderarticles as d use index ( oxartid )
  where d.oxartid in (  '05362dd9d08d1fe1bb9c136ff57184e2' , 'd9b70cbd736e7a3f22bc8974c0dce7d4' , 'b69dbd88be95d4d8fcc5f7795ee755b4' , '5a10dafd2729f93e98061a6dce81d24c' , 'b3b8733ffef1470567dbb16d42a15a15' , '7fb34b1287695281e8179634e7143837' , 'fbd9719611a26174862e861f215e26a7' , '4e3bdfc86f435d82ccf9b2b438914857' , '2797dd5cfe51f6d35b62105c7d719e29' , 'ca6089d278a1e91442df4af4829afd8e'  )
  limit 30
) as suborder
left join oxorderarticles force index ( oxorderid ) on suborder.suborderid = oxorderarticles.oxorderid
left join oxv_oxarticles_de on oxv_oxarticles_de.oxid = oxorderarticles.oxartid
where oxv_oxarticles_de.oxid not in (  '05362dd9d08d1fe1bb9c136ff57184e2' , 'd9b70cbd736e7a3f22bc8974c0dce7d4' , 'b69dbd88be95d4d8fcc5f7795ee755b4' , '5a10dafd2729f93e98061a6dce81d24c' , 'b3b8733ffef1470567dbb16d42a15a15' , '7fb34b1287695281e8179634e7143837' , 'fbd9719611a26174862e861f215e26a7' , '4e3bdfc86f435d82ccf9b2b438914857' , '2797dd5cfe51f6d35b62105c7d719e29' , 'ca6089d278a1e91442df4af4829afd8e'  )
and ( oxv_oxarticles_de.oxissearch = 1 or oxv_oxarticles_de.oxparentid <> '' )
and ( 
  ( oxv_oxarticles_de.oxactive = 1  or ( oxv_oxarticles_de.oxactivefrom < '2015-05-08 22:19:56' and oxv_oxarticles_de.oxactiveto > '2015-05-08 22:19:56' ) )
  and ( oxv_oxarticles_de.oxstockflag != 2 or ( oxv_oxarticles_de.oxstock + oxv_oxarticles_de.oxvarstock ) > 0  )
  and IF( oxv_oxarticles_de.oxvarcount = 0, 1, ( 
    select 1 from oxv_oxarticles_de as art 
    where art.oxparentid=oxv_oxarticles_de.oxid 
    and ( art.oxactive = 1  or ( art.oxactivefrom < '2015-05-08 22:19:56' and art.oxactiveto > '2015-05-08 22:19:56' ) )
    and ( art.oxstockflag != 2 or art.oxstock > 0 )
    limit 1

**Short explanation of the query:**
Subquery in rows 2-5 selects all orders contain current product or its variants.
row 7 adds article IDs of other products from the same orders
row 8 adds article data for loaded article IDs
row 9 removes the currect product and its variants from the list
row 10-13 allow only active searchable products or active variants on stock
row 14 checks if a parent article in list still has active variants

At the bottom line we get active+searchable parent articles with active variants and active+searchable variants.
But wait! If your article with variants is sold out or discontinued, would you deactivate the parent article only or also all its variants? People are lazy, nobody deactivates variants, believe me.

At this point we found the problem here: this query does not check if the parent article of the bought variant is active or not. So this function will load and display even variants if their parent isn’t active anymore. But when someone clicks on the article link in the crossselling box, he will be redirected to the start page because of deactivated parent article.

If you have the same problem, check my fix here:

## You can get dev-console here:
For more information and installation instructions visit this page:
You can download devutils package from my github account:

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published.