container = new Container();
}
/**
* Initialize the plugin
*/
public function init(): void
{
if ($this->initialized) {
return;
}
try {
// Register service providers first
$this->registerServiceProviders();
// Load helper functions (must be loaded after autoloader)
$this->loadHelperFunctions();
// Initialize cache hooks
if (class_exists('\Yatra\Hooks\CacheHooks')) {
\Yatra\Hooks\CacheHooks::init();
}
if (class_exists('\Yatra\Hooks\AvailabilityInventoryHooks')) {
\Yatra\Hooks\AvailabilityInventoryHooks::init();
}
if (class_exists('\Yatra\Hooks\MigrationAdminNoticeHooks')) {
\Yatra\Hooks\MigrationAdminNoticeHooks::init();
}
// Initialize Setup Wizard Service
if (class_exists('\Yatra\Services\SetupWizardService')) {
\Yatra\Services\SetupWizardService::init();
}
// Centralized notices (React UI + WP admin notices)
if (class_exists('\Yatra\Services\NoticeService')) {
\Yatra\Services\NoticeService::init();
}
if (class_exists('\Yatra\Services\StatsUsage')) {
\Yatra\Services\StatsUsage::instance()->init();
}
// Initialize Dynamic Pricing Service
// DISABLED: Automatic dynamic pricing was adding 15% markup for trips with ≤5 spots
// if (class_exists('\Yatra\Services\DynamicPricingService')) {
// \Yatra\Services\DynamicPricingService::init();
// }
// Initialize SEO Manager
if (class_exists('\Yatra\Managers\SEOManager')) {
\Yatra\Managers\SEOManager::init();
}
// Register Setup Service activation hook
if (class_exists('\Yatra\Services\SetupService')) {
\Yatra\Services\SetupService::registerActivationHook();
}
// Initialize Action Scheduler
$this->initializeActionScheduler();
// Initialize REST API hooks
if (class_exists('\Yatra\Hooks\RestApiHooks')) {
\Yatra\Hooks\RestApiHooks::init();
}
// Initialize core components
$this->initializeCore();
// Set up WordPress hooks
$this->setupWordPressHooks();
// Load text domain
$this->loadTextDomain();
} catch (\Throwable $e) {
error_log('Yatra plugin initialization error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
// Show admin notice if in admin area
if (is_admin()) {
add_action('admin_notices', function() use ($e) {
echo '
Yatra: ' .
esc_html($e->getMessage()) . '
';
});
}
return;
}
$this->initialized = true;
}
/**
* Load helper functions
*/
private function loadHelperFunctions(): void
{
$helpersPath = YATRA_PLUGIN_PATH . 'includes/helpers.php';
if (file_exists($helpersPath)) {
require_once $helpersPath;
}
$seoHelperPath = YATRA_PLUGIN_PATH . 'includes/seo-helper.php';
if (file_exists($seoHelperPath)) {
require_once $seoHelperPath;
}
}
/**
* Register and boot service providers.
*
* Each provider is instantiated once. The same instance is used for both
* register() and boot() so that any hooks added during register() remain
* attached to the object that boot() later acts on.
*/
private function registerServiceProviders(): void
{
// Register third-party compatibility hooks (Elementor, etc.)
// Must run on `plugins_loaded` so that other plugins (Elementor, etc.) are
// guaranteed to have loaded their classes before we check class_exists().
// Calling Compatibility::register() directly here runs before plugins_loaded
// and class_exists('\Elementor\Plugin') will always be false at that point.
if (!is_admin() && class_exists('Yatra\\Compatibility\\Compatibility')) {
add_action('plugins_loaded', ['Yatra\\Compatibility\\Compatibility', 'register'], 20);
}
$providerClasses = [];
// Core providers — always loaded
if (class_exists('Yatra\Providers\AppServiceProvider')) {
$providerClasses[] = 'Yatra\Providers\AppServiceProvider';
}
if (class_exists('Yatra\Providers\RouteServiceProvider')) {
$providerClasses[] = 'Yatra\Providers\RouteServiceProvider';
}
// Admin-only providers
if (is_admin() && class_exists('Yatra\Providers\AdminServiceProvider')) {
$providerClasses[] = 'Yatra\Providers\AdminServiceProvider';
}
// Frontend-only providers
if (!is_admin() && class_exists('Yatra\Providers\FrontendAssetsProvider')) {
$providerClasses[] = 'Yatra\Providers\FrontendAssetsProvider';
}
// Shortcode provider (frontend + admin)
if (class_exists('Yatra\Providers\ShortcodeServiceProvider')) {
$providerClasses[] = 'Yatra\Providers\ShortcodeServiceProvider';
}
// Block provider (Gutenberg)
if (class_exists('Yatra\Providers\BlockServiceProvider')) {
$providerClasses[] = 'Yatra\Providers\BlockServiceProvider';
}
// Instantiate and register all providers, keeping a map of the instances
// so boot() runs on the exact same object that register() did.
$instances = [];
foreach ($providerClasses as $class) {
try {
$instance = new $class($this->container);
if (method_exists($instance, 'register')) {
$instance->register();
}
$instances[$class] = $instance;
} catch (\Throwable $e) {
error_log("Yatra: Failed to register provider {$class}: " . $e->getMessage());
continue;
}
}
// Boot each successfully registered provider
foreach ($instances as $class => $instance) {
try {
if (method_exists($instance, 'boot')) {
$instance->boot();
}
} catch (\Throwable $e) {
error_log("Yatra: Failed to boot provider {$class}: " . $e->getMessage());
continue;
}
}
}
/**
* Initialize Action Scheduler
*/
private function initializeActionScheduler(): void
{
$actionSchedulerPath = YATRA_PLUGIN_PATH . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
if (file_exists($actionSchedulerPath)) {
require_once $actionSchedulerPath;
}
}
/**
* Initialize core components
*/
private function initializeCore(): void
{
// Check and create database tables if they don't exist
$this->ensureDatabaseTables();
}
/**
* Setup WordPress hooks
*/
private function setupWordPressHooks(): void
{
// Register activation/deactivation hooks
register_activation_hook(YATRA_PLUGIN_FILE, [$this, 'activate']);
register_deactivation_hook(YATRA_PLUGIN_FILE, [$this, 'deactivate']);
// Check for plugin upgrades
add_action('admin_init', [$this, 'upgrade']);
}
/**
* Ensure database tables exist
*/
private function ensureDatabaseTables(): void
{
// Register migration routes
add_action('rest_api_init', function() {
// Temporarily manually require migration files until autoloader is fixed
$migrationFiles = [
__DIR__ . '/Migrations/MigrationController.php',
__DIR__ . '/Migrations/MigrationProgress.php',
__DIR__ . '/Migrations/MigrationDetector.php',
];
foreach ($migrationFiles as $file) {
if (file_exists($file)) {
require_once $file;
}
}
if (class_exists('\Yatra\Migration\MigrationController')) {
$migrationController = new \Yatra\Migration\MigrationController();
$migrationController->registerRoutes();
}
});
// Register Action Scheduler hook for background migration processing
add_action('yatra_migrate_data_type', function($dataType, $force = false) {
if (class_exists('\Yatra\Migration\MigrationProgress')) {
$migrationService = new \Yatra\Migration\MigrationProgress();
$result = $migrationService->processMigration($dataType, (bool) $force);
// Log result for debugging
if (isset($result['success']) && $result['success']) {
error_log("Yatra Migration completed for {$dataType}: migrated={$result['migrated']}, skipped={$result['skipped']}, failed={$result['failed']}");
} else {
error_log("Yatra Migration failed for {$dataType}: " . ($result['error'] ?? 'Unknown error'));
}
}
}, 10, 2);
// Register background hook for all data types migration via cron
add_action('yatra_migration_background_run', function($force = false) {
if (class_exists('\Yatra\Migration\MigrationProgress')) {
$migrationService = new \Yatra\Migration\MigrationProgress();
$migrationService->migrateAllDirect((bool) $force);
}
}, 10, 1);
}
/**
* Plugin activation
*/
public function activate(): void
{
// Run centralized installer for all one-time actions (tables + settings)
\Yatra\Services\InstallerService::install();
// Set default options (only version tracking - other defaults handled by InstallerService)
if (get_option('yatra_version') === false) {
add_option('yatra_version', YATRA_VERSION);
}
// Set up setup wizard redirect for first-time activation
if (get_option('yatra_setup_wizard_ran') !== '1') {
set_transient('yatra_setup_wizard_redirect', 1, 30);
}
// Flush rewrite rules
flush_rewrite_rules();
}
/**
* Plugin upgrade logic
*/
public function upgrade(): void
{
$current_version = get_option('yatra_version', '1.0.0');
if (version_compare($current_version, YATRA_VERSION, '<')) {
Database::createTables();
update_option('yatra_version', YATRA_VERSION);
}
\Yatra\Services\InstallerService::maybeBackfillEmailTemplateDefaults();
\Yatra\Services\InstallerService::maybeNormalizeMigratedCouponDiscountStatuses();
}
/**
* Plugin deactivation
*/
public function deactivate(): void
{
// Clean up if needed
flush_rewrite_rules();
}
/**
* Load plugin text domain
*/
public function loadTextDomain(): void
{
$locale = determine_locale();
// Unload any existing text domain
unload_textdomain('yatra');
// Load from WordPress languages directory first (where Loco Translate saves files)
load_textdomain('yatra', WP_LANG_DIR . '/plugins/yatra-' . $locale . '.mo');
// Also check loco subdirectory
if (file_exists(WP_LANG_DIR . '/loco/plugins/yatra-' . $locale . '.mo')) {
load_textdomain('yatra', WP_LANG_DIR . '/loco/plugins/yatra-' . $locale . '.mo');
}
// Load from plugin directory (fallback)
load_plugin_textdomain('yatra', false, 'i18n/languages');
}
/**
* Get container instance
*/
public function getContainer(): Container
{
return $this->container;
}
}