AlkantarClanX12
| Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/lib/ |
| Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/lib/class-inventory-manager.php |
<?php
/**
* Inventory Manager - Request-level caching for inventory data
*
* Ensures inventory.json is only read ONCE per request
*
* CRITICAL PERFORMANCE FIX:
* - Problem: Theme calls get_inventory() 4-720 times per page load
* - Each call reads 1.0MB file from disk, parses 28,291 lines of JSON
* - Impact: Model pages alone = 59.25GB/day waste
*
* Solution: Request-level caching (NOT persistent caching)
* - First call in request: Reads from disk (fresh data)
* - Subsequent calls in SAME request: Returns cached data (no disk read)
* - Next page load: Reads from disk again (always fresh data)
*
* @package LBX
*/
// Exit if accessed directly.
if (!defined('ABSPATH')) {
exit;
}
class LB_Inventory_Manager {
private static $instance = null;
private $inventory_cache = null;
private $vehicle_cache = [];
/**
* Get singleton instance
*
* @return LB_Inventory_Manager
*/
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Private constructor prevents direct instantiation
*/
private function __construct() {
// Private constructor for singleton pattern
}
/**
* Get inventory data (cached for current request only)
*
* IMPORTANT: This is request-level caching, NOT persistent caching.
* - First call: Reads from disk (fresh data)
* - Subsequent calls in SAME request: Returns cached data
* - Next page load: Reads from disk again (always fresh)
*
* @return array|null Inventory data or null on failure
*/
public function get_inventory() {
// Return cached data if already loaded THIS request
if ($this->inventory_cache !== null) {
return $this->inventory_cache;
}
// Read from disk (first call of THIS request)
// Check for idiom-specific inventory file first (matches original logic)
$json_path = wp_get_upload_dir()['basedir'] . '/data/inventory';
$idiom_path = $this->makeUrl($json_path);
if (file_exists($idiom_path)) {
$json = file_get_contents($idiom_path);
if ($json === false) {
leadbox_logger()->error('Failed to read inventory file', array(
'file' => $idiom_path,
));
return null;
}
$data = json_decode($json, true);
if ($data === null) {
leadbox_logger()->error('Failed to parse inventory JSON', array(
'file' => $idiom_path,
'json_error' => json_last_error_msg(),
));
return null;
}
// Cache for remainder of request
$this->inventory_cache = $data;
return $data;
}
// Fallback to default inventory.json
$file = wp_get_upload_dir()['basedir'] . '/data/inventory.json';
if (!file_exists($file)) {
leadbox_logger()->error('Inventory file not found', array(
'file' => $file,
));
return null;
}
$json = file_get_contents($file);
if ($json === false) {
leadbox_logger()->error('Failed to read inventory file', array(
'file' => $file,
));
return null;
}
$data = json_decode($json, true);
if ($data === null) {
leadbox_logger()->error('Failed to parse inventory JSON', array(
'file' => $file,
'json_error' => json_last_error_msg(),
));
return null;
}
// Cache for remainder of request
$this->inventory_cache = $data;
return $data;
}
/**
* Get individual vehicle by ID
*
* Always reads individual vehicle file (has full data including description)
*
* @param string $id Vehicle stock number
* @return array|null Vehicle data or null
*/
public function get_vehicle_by_id($id) {
// Check cache first
if (isset($this->vehicle_cache[$id])) {
return $this->vehicle_cache[$id];
}
// Individual vehicle files have FULL data (description, features, categories)
// Always read individual file, don't use inventory.json
$file = wp_get_upload_dir()['basedir'] . '/data/' . $id . '.json';
if (!file_exists($file)) {
// Fallback: try to find in inventory
$inventory = $this->get_inventory();
if ($inventory && isset($inventory['vehicles'])) {
foreach ($inventory['vehicles'] as $vehicle) {
if ($vehicle['stocknumber'] === $id) {
$this->vehicle_cache[$id] = $vehicle;
return $vehicle;
}
}
}
return null;
}
$json = file_get_contents($file);
if ($json === false) {
return null;
}
$vehicle = json_decode($json, true);
if ($vehicle === null) {
return null;
}
// Cache for remainder of request
$this->vehicle_cache[$id] = $vehicle;
return $vehicle;
}
/**
* Clear cache (for testing or manual refresh)
*/
public function clear_cache() {
$this->inventory_cache = null;
$this->vehicle_cache = [];
}
/**
* Helper function to construct idiom-specific path
* (matches original makeUrl logic from lib/functions.php)
*
* @param string $json_path Base path
* @return string Idiom-specific path
*/
private function makeUrl($json_path) {
// Check if makeUrl function exists globally
if (function_exists('makeUrl')) {
return makeUrl($json_path);
}
// Fallback: try to construct idiom path manually
// This matches common WordPress multilingual patterns
if (function_exists('pll_current_language')) {
$idiom = pll_current_language();
return $json_path . '-' . $idiom . '.json';
}
return $json_path . '.json';
}
}
/**
* Global helper function for backward compatibility
*
* @return array|null Inventory data
*/
function lb_get_inventory() {
return LB_Inventory_Manager::getInstance()->get_inventory();
}
/**
* Global helper function for getting individual vehicles
*
* @param string $id Vehicle stock number
* @return array|null Vehicle data
*/
function lb_get_vehicle_by_id($id) {
return LB_Inventory_Manager::getInstance()->get_vehicle_by_id($id);
}