Recently I had the opportunity to work on a decent sized e-commerce project based on OpenCart. I haven’t used it before but I had heard its a very simple and easy to use. So the following is just a quick overview of all the fundamental flaws and issues with OpenCart and why you should NEVER use it as a platform of choice for your projects, unless you want constant headaches, ugly code and incredible amount of code repetition.

The obvious

MyIsam

Code Repetition

So lets start with the obvious… A quick peak at the installation sql schema reveals something mind-boggling. The engine of choice for ALL tables, including orders is, which first of all shouldn’t even be a consideration in any MySql 5+ setup really, especially for web purposes and second and more important maybe. Simple logic dictates that transactions are a fundamental part of e-commerce, but apparently the team behind OpenCart decided thats not that case!? I am already stunned but lets not stop here… MyIsam doesn’t support foreign keys either, so all integrity and constraint checks are done with PHP, nasty…

The system is FULL of code like the following:

$this->language->load('account/account'); $this->data['heading_title'] = $this->language->get('heading_title'); $this->data['text_my_account'] = $this->language->get('text_my_account'); $this->data['text_my_orders'] = $this->language->get('text_my_orders'); $this->data['text_my_newsletter'] = $this->language->get('text_my_newsletter'); $this->data['text_edit'] = $this->language->get('text_edit'); $this->data['text_password'] = $this->language->get('text_password'); $this->data['text_address'] = $this->language->get('text_address'); $this->data['text_wishlist'] = $this->language->get('text_wishlist'); $this->data['text_order'] = $this->language->get('text_order'); $this->data['text_download'] = $this->language->get('text_download'); $this->data['text_reward'] = $this->language->get('text_reward'); $this->data['text_return'] = $this->language->get('text_return'); $this->data['text_transaction'] = $this->language->get('text_transaction'); $this->data['text_newsletter'] = $this->language->get('text_newsletter');

So, we load the language file and manually map ALL the entries from the simple array that each language file is, to a new array with the exact same key as the language string. That usually makes a 10 lines controller into 50 or 60 lines one… Some heavier controllers have in excess of 150 lines just to map the languages !? Then we move on to the form handling. Well there isn’t a form handling framework to speak of, inputs are handled like so:

if (isset($this->request->post['model'])) { $this->data['model'] = $this->request->post['model']; } elseif (!empty($product_info)) { $this->data['model'] = $product_info['model']; } else { $this->data['model'] = ''; } if (isset($this->request->post['sku'])) { $this->data['sku'] = $this->request->post['sku']; } elseif (!empty($product_info)) { $this->data['sku'] = $product_info['sku']; } else { $this->data['sku'] = ''; } if (isset($this->request->post['upc'])) { $this->data['upc'] = $this->request->post['upc']; } elseif (!empty($product_info)) { $this->data['upc'] = $product_info['upc']; } else { $this->data['upc'] = ''; } if (isset($this->request->post['ean'])) { $this->data['ean'] = $this->request->post['ean']; } elseif (!empty($product_info)) { $this->data['ean'] = $product_info['ean']; } else { $this->data['ean'] = ''; }

And again for a form with 50 fields we have 50 blocks like the following. Of course that code can be replaced with 3 lines that do the same, something like:

if(!empty($this->request->post)){ foreach($this->request->post as $k => $v){ if (!empty($this->request->post[$k])) { $this->data[$k] = $this->request->post[$k]; } } }

Because there is no validation framework, each Controller invokes a validate method for each entity it processes, where validation is done with good old PHP functions manually for every form field. I mention that last, since its kinda ok for a PHP app from 10 years ago, but nowadays it doesn’t make sense.

Database

I already explained the usage of MyIsam, lack of transactions and lack of foreign keys but lets elaborate on the database a bit. Because of all the previous items, each controller has a validate method where different constraints are checked and validated (instead of using the MySQL engine to do that). Same goes for the deleting of entities, so intead of having Cascading foreign keys, we manually first find all related entries and then delete them.

The madness doesn’t end there. The query to get an actual product is fairly heavy, joining 8 or 9 tables to gather all the product info. What boggles the mind is that this query is called in multiple locations inside of loops with no caching whatsoever. Same goes for the getProducts function which represents the heaviest query in the entire system. No sign of caching…

In the entire system there is almost no caching anywhere, what’s funnier is that there is caching in the backend at certain places but not in the frontend. For the places where there is cache, the default and only supported one out of the box is file, which if we have proper caching on the mysql server is probably even slower, to open ,read the file and deserialize the result. The list goes on and on and on, but the bottom line is that is a mess.

Templates

Templates you might think are good old PHP so what could go wrong, eh? Wrong… The way the system works is that it treats every single content block on a page as a module, so in order to add something, even as simple as

<p>asdasd</P>

well, you need to create a module. So creating a module consists of adding a backend module(necessary step), which involves copying some dummy existing module, refactoring the names, changing all the 50 language lines and so on, then creating the frontend view and controller for it, and voilaaa 2 hours later you can render something. If simplicity is its core selling point, thats everything but simple (or correct me its simple but so tedious and time consuming you can call it complex).

Seo URLs

Again mind boggling. Take a look at this snippet(SEO url handler):

foreach ($parts as $part) { $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "url_alias WHERE keyword = '" . $this->db->escape($part) . "'"); if ($query->num_rows) { $url = explode('=', $query->row['query']); if ($url[0] == 'product_id') { $this->request->get['product_id'] = $url[1]; } if ($url[0] == 'category_id') { if (!isset($this->request->get['path'])) { $this->request->get['path'] = $url[1]; } else { $this->request->get['path'] .= '_' . $url[1]; } } if ($url[0] == 'manufacturer_id') { $this->request->get['manufacturer_id'] = $url[1]; } if ($url[0] == 'information_id') { $this->request->get['information_id'] = $url[1]; } } else { $this->request->get['route'] = 'error/not_found'; } }

The url_alias mapping table contains two field entiti_id=id and keyword for that entity id. What happens if you have a manufacturer and a category with the same name one might ask… Well its up to the gods of OpenCart to decide which one you will get back. Of course they could have prefixed categories with category/ manufacturers with manufacturer/ and so forth and added an entity type to the rewrite table, but why bother. You cannot add your own parsing and rewriting logic if you want to extend the system (thats due to architectural issues which we will get to soon), so that means you go in that if statement and add your logic(making sure to add something to prevent the same key linking to different entities lol. The rewrite method is the same thing essentially. By far the worst routing system ever.

Security

I havent seen any major security issues yet, everything is filtered pretty well, even though in a really stupid and repetitive way as everything else in the system, but this probably needs some looking into: http://www.opencart.com/index.php?route=common/home/__construct

Architecture

Let my start by pointing out that this is not an OOP software. It uses PHP OOP syntax but the way things are done is completely procedural. There is no abstraction, no separation of concerns, methods call eachother all over the place in some cases such as the tax and shipping system passing reference of one variable through 10 different tax classes with no clarity at all. That is not object oriented programming or a properly structured application. Of course you cannot extend any of the core services, you have to modify the core itself.

There is a frontend and admin apps which share the same core, the same functionality, same entities, but yet they are 2 separate code bases, repeating eachother almost 1 for 1. Any change in an admin Model should be manually done in a frontend Model as well…again retarted way of doing it. So because of the poor architecture, the guys decided to come up with this thing:

https://code.google.com/p/vqmod/

Don’t ever use that please, after installing a couple of modules, you end up working with more xml containing PHP, than actually PHP code. Biggest mess ever.

Conclusion

That was my experience with OpenCart and I shall say by far the worst programming effort ever. Its a system that somehow works, barely hanging in the balance with stuff going all over the place. I tough its good to share it with you, so if you are doing some research about what to base you next e-commerce project off of, you should’t even consider OpenCart. Just FYI, extensions are not much better either, they are as a big as a mess as the core itself. If you are doing a serious project go with Magento or a good Java alternative would be BroadLeaf. If you need something with a shorter learning curve, try Prestashop.

EDIT:

After posting this article it has gained some popularity and people have expressed both agreement and disagreement with my views which of course is fine. However most of the feedback I received in the comments from OC supporters was limited to trash talk, personal offences, absolutely technically flawed ideas (like explanations on how code repetition is OK if the code is “simple”) and all other sorts of nonsense ideas (envy of how much the OC author makes, etc, etc), but very little if any real answers to my points were provided (including from the OC author himself, who mostly resorted to trash talk). I would advice people who read the article to quickly gaze over the comments too, so that way they will gain a better idea of not only the software flaws of OC, but the kind of vibe and technical skill level that goes around in the OC community…