Analytics Architecture on osCommerce
osCommerce is a PHP-based e-commerce platform with a straightforward template system. Analytics code is injected through direct PHP template edits. The key integration points are:
includes/header.php-- loaded on every storefront page, output before main contentincludes/footer.php-- loaded on every storefront page, output before</body>includes/application_top.php-- the PHP bootstrap file that initializes sessions, database connections, and globals; runs before any output- Template files (
.tplin some forks) -- individual page layouts inincludes/templates/orincludes/modules/ checkout_success.php-- the order confirmation page where purchase tracking fires
osCommerce does not have a built-in plugin/hook system for analytics in its original release. Most tracking implementations involve directly editing PHP template files. Some community forks (e.g., osCommerce Phoenix) introduced a header_tags module system for cleaner script injection.
Installing Tracking Scripts
Global Header Injection
Edit includes/header.php to add tracking scripts to every page:
<!-- In includes/header.php, before </head> or at top of body -->
<script>
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');
</script>
Footer Script Injection
Edit includes/footer.php to add scripts that should load after page content:
<!-- In includes/footer.php, before </body> -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
Using Header Tags Modules (Phoenix Fork)
If running osCommerce Phoenix, create a module in includes/modules/header_tags/:
<?php
// includes/modules/header_tags/ht_google_analytics.php
class ht_google_analytics {
var $code = 'ht_google_analytics';
var $group = 'header_tags';
var $title;
var $enabled = false;
function __construct() {
$this->title = 'Google Analytics';
$this->enabled = (defined('MODULE_HEADER_TAGS_GOOGLE_ANALYTICS_STATUS')
&& MODULE_HEADER_TAGS_GOOGLE_ANALYTICS_STATUS == 'True');
}
function execute() {
$ga_id = MODULE_HEADER_TAGS_GOOGLE_ANALYTICS_ID;
$output = <<<EOT
<script async src="https://www.googletagmanager.com/gtag/js?id={$ga_id}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{$ga_id}');
</script>
EOT;
return $output;
}
}
Data Layer Implementation
osCommerce makes PHP variables available in templates. Build a dataLayer by echoing PHP data into JavaScript.
Product Page Data Layer
In product_info.php or its template file:
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'view_item',
'ecommerce': {
'items': [{
'item_id': '<?php echo (int)$product_info['products_id']; ?>',
'item_name': '<?php echo addslashes($product_info['products_name']); ?>',
'price': <?php echo number_format($product_info['products_price'], 2, '.', ''); ?>,
'item_category': '<?php echo addslashes($category_name); ?>',
'currency': '<?php echo DEFAULT_CURRENCY; ?>'
}]
}
});
</script>
Category Listing Data Layer
In index.php (when displaying a category), loop through products:
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'view_item_list',
'ecommerce': {
'item_list_name': '<?php echo addslashes($current_category_name); ?>',
'items': [
<?php
$position = 0;
while ($listing = tep_db_fetch_array($listing_query)) {
$position++;
echo " {";
echo "'item_id': '" . (int)$listing['products_id'] . "',";
echo "'item_name': '" . addslashes($listing['products_name']) . "',";
echo "'price': " . number_format($listing['products_price'], 2, '.', '') . ",";
echo "'index': " . $position;
echo "},\n";
}
?>
]
}
});
</script>
E-commerce Tracking
Order Confirmation Page (checkout_success.php)
The checkout_success.php page has access to the most recent order data. Retrieve it and push a purchase event:
<?php
// At the top of checkout_success.php, after the order query
$order_query = tep_db_query("SELECT * FROM orders WHERE customers_id = '"
. (int)$customer_id . "' ORDER BY orders_id DESC LIMIT 1");
$order = tep_db_fetch_array($order_query);
$products_query = tep_db_query("SELECT * FROM orders_products WHERE orders_id = '"
. (int)$order['orders_id'] . "'");
$items_json = [];
while ($product = tep_db_fetch_array($products_query)) {
$items_json[] = [
'item_id' => $product['products_id'],
'item_name' => $product['products_name'],
'price' => (float)$product['final_price'],
'quantity' => (int)$product['products_quantity']
];
}
?>
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'purchase',
'ecommerce': {
'transaction_id': '<?php echo (int)$order['orders_id']; ?>',
'value': <?php echo number_format($order['order_total'], 2, '.', ''); ?>,
'currency': '<?php echo DEFAULT_CURRENCY; ?>',
'items': <?php echo json_encode($items_json); ?>
}
});
</script>
Add to Cart Tracking
osCommerce processes add-to-cart via form POST to shopping_cart.php. To track this event, add JavaScript to the product page form submission:
<script>
document.querySelectorAll('form[action*="shopping_cart"]').forEach(function(form) {
form.addEventListener('submit', function() {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'add_to_cart',
'ecommerce': {
'items': [{
'item_id': form.querySelector('input[name="products_id"]').value,
'quantity': parseInt(form.querySelector('input[name="cart_quantity"]').value) || 1
}]
}
});
});
});
</script>
Common Issues
Scripts duplicated across templates -- osCommerce loads header.php and footer.php on every page, but some pages override the main template. Verify your scripts are not also hardcoded in individual page templates, causing double-firing.
application_top.php output buffering -- If application_top.php sends headers or starts output buffering, scripts injected too early may not render. Place analytics JavaScript in the HTML output portion of templates, not in the PHP bootstrap.
Session-based cart data -- osCommerce stores cart contents in $_SESSION['cart']. This data is not directly accessible from JavaScript. To expose cart state to the data layer, echo it from PHP into a <script> block.
Order total discrepancies -- The orders table stores the raw order total, but shipping, tax, and discounts are in the orders_total table. Query both tables to build an accurate purchase event with itemized totals.
Mixed PHP versions -- Older osCommerce installations may run on PHP 5.x. Functions like json_encode are available from PHP 5.2+. If on an extremely old version, you may need to build JSON strings manually.
Platform-Specific Considerations
No native hook system -- Classic osCommerce has no event hook or observer pattern. Every tracking integration requires direct file edits. Track which files you have modified to simplify upgrades.
Community forks diverge -- osCommerce CE, Phoenix, and the original 2.3.x branch have different file structures. Confirm your version before editing templates. Phoenix uses includes/modules/header_tags/ while classic uses direct includes/header.php edits.
Database-driven pages -- Category and product pages are generated dynamically from the database. There are no static HTML files to edit. All tracking code must be embedded in the PHP template that renders the page.
No REST API by default -- osCommerce does not ship with a REST API. For server-side tracking integrations (Meta CAPI, Google Ads Enhanced Conversions), you must either install a community API module or build a custom endpoint that queries the orders table.
File permissions -- On shared hosting, template files may be owned by the web server user. Ensure write permissions before editing via FTP/SFTP. Changes made through the admin panel (if available) bypass this issue.