PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.7
Yatra – Travel Booking & Tour Operator Software v3.0.2.7
3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 All 83 releases
yatra / app / Bootstrap.php

Bootstrap.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.7, at app/Bootstrap.php

389 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra;
6
7 use Yatra\Core\Container;
8 use Yatra\Core\Database;
9 use Yatra\Providers\AppServiceProvider;
10 use Yatra\Providers\RouteServiceProvider;
11 use Yatra\Providers\AdminServiceProvider;
12 use Yatra\Providers\FrontendAssetsProvider;
13 use Yatra\Compatibility\Compatibility;
14 use Yatra\Providers\BlockServiceProvider;
15
16 /**
17 * Main Bootstrap class for Yatra plugin
18 */
19 class Bootstrap
20 {
21
22 /**
23 * @var Container
24 */
25 private Container $container;
26
27 /**
28 * @var bool
29 */
30 private bool $initialized = false;
31
32 /**
33 * Bootstrap constructor
34 */
35 public function __construct()
36 {
37 $this->container = new Container();
38 }
39
40 /**
41 * Initialize the plugin
42 */
43 public function init(): void
44 {
45 if ($this->initialized) {
46 return;
47 }
48
49 try {
50 // Register service providers first
51 $this->registerServiceProviders();
52
53 // Load helper functions (must be loaded after autoloader)
54 $this->loadHelperFunctions();
55
56 // Initialize cache hooks
57 if (class_exists('\Yatra\Hooks\CacheHooks')) {
58 \Yatra\Hooks\CacheHooks::init();
59 }
60
61 if (class_exists('\Yatra\Hooks\AvailabilityInventoryHooks')) {
62 \Yatra\Hooks\AvailabilityInventoryHooks::init();
63 }
64
65 if (class_exists('\Yatra\Hooks\MigrationAdminNoticeHooks')) {
66 \Yatra\Hooks\MigrationAdminNoticeHooks::init();
67 }
68
69 // Initialize Setup Wizard Service
70 if (class_exists('\Yatra\Services\SetupWizardService')) {
71 \Yatra\Services\SetupWizardService::init();
72 }
73
74 // Centralized notices (React UI + WP admin notices)
75 if (class_exists('\Yatra\Services\NoticeService')) {
76 \Yatra\Services\NoticeService::init();
77 }
78
79 if (class_exists('\Yatra\Admin\StatsUsage')) {
80 \Yatra\Admin\StatsUsage::instance()->init();
81 }
82
83 // Initialize Dynamic Pricing Service
84 // DISABLED: Automatic dynamic pricing was adding 15% markup for trips with ≤5 spots
85 // if (class_exists('\Yatra\Services\DynamicPricingService')) {
86 // \Yatra\Services\DynamicPricingService::init();
87 // }
88
89 // Initialize SEO Manager
90 if (class_exists('\Yatra\Managers\SEOManager')) {
91 \Yatra\Managers\SEOManager::init();
92 }
93
94 // Register Setup Service activation hook
95 if (class_exists('\Yatra\Services\SetupService')) {
96 \Yatra\Services\SetupService::registerActivationHook();
97 }
98
99 // Initialize Action Scheduler
100 $this->initializeActionScheduler();
101
102 // Initialize REST API hooks
103 if (class_exists('\Yatra\Hooks\RestApiHooks')) {
104 \Yatra\Hooks\RestApiHooks::init();
105 }
106
107 // Initialize core components
108 $this->initializeCore();
109
110 // Set up WordPress hooks
111 $this->setupWordPressHooks();
112
113 // Load text domain
114 $this->loadTextDomain();
115
116 } catch (\Throwable $e) {
117 error_log('Yatra plugin initialization error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
118
119 // Show admin notice if in admin area
120 if (is_admin()) {
121 add_action('admin_notices', function() use ($e) {
122 echo '<div class="notice notice-error"><p><strong>Yatra:</strong> ' .
123 esc_html($e->getMessage()) . '</p></div>';
124 });
125 }
126
127 return;
128 }
129
130 $this->initialized = true;
131 }
132
133 /**
134 * Load helper functions
135 */
136 private function loadHelperFunctions(): void
137 {
138 $helpersPath = YATRA_PLUGIN_PATH . 'includes/helpers.php';
139 if (file_exists($helpersPath)) {
140 require_once $helpersPath;
141 }
142
143 $seoHelperPath = YATRA_PLUGIN_PATH . 'includes/seo-helper.php';
144 if (file_exists($seoHelperPath)) {
145 require_once $seoHelperPath;
146 }
147 }
148
149 /**
150 * Register and boot service providers.
151 *
152 * Each provider is instantiated once. The same instance is used for both
153 * register() and boot() so that any hooks added during register() remain
154 * attached to the object that boot() later acts on.
155 */
156 private function registerServiceProviders(): void
157 {
158 // Register third-party compatibility hooks (Elementor, etc.)
159 // Must run on `plugins_loaded` so that other plugins (Elementor, etc.) are
160 // guaranteed to have loaded their classes before we check class_exists().
161 // Calling Compatibility::register() directly here runs before plugins_loaded
162 // and class_exists('\Elementor\Plugin') will always be false at that point.
163 if (!is_admin() && class_exists('Yatra\\Compatibility\\Compatibility')) {
164 add_action('plugins_loaded', ['Yatra\\Compatibility\\Compatibility', 'register'], 20);
165 }
166
167 $providerClasses = [];
168
169 // Core providers — always loaded
170 if (class_exists('Yatra\Providers\AppServiceProvider')) {
171 $providerClasses[] = 'Yatra\Providers\AppServiceProvider';
172 }
173
174 if (class_exists('Yatra\Providers\RouteServiceProvider')) {
175 $providerClasses[] = 'Yatra\Providers\RouteServiceProvider';
176 }
177
178 // Admin-only providers
179 if (is_admin() && class_exists('Yatra\Providers\AdminServiceProvider')) {
180 $providerClasses[] = 'Yatra\Providers\AdminServiceProvider';
181 }
182
183 // Frontend-only providers
184 if (!is_admin() && class_exists('Yatra\Providers\FrontendAssetsProvider')) {
185 $providerClasses[] = 'Yatra\Providers\FrontendAssetsProvider';
186 }
187
188 // Shortcode provider (frontend + admin)
189 if (class_exists('Yatra\Providers\ShortcodeServiceProvider')) {
190 $providerClasses[] = 'Yatra\Providers\ShortcodeServiceProvider';
191 }
192
193 // Block provider (Gutenberg)
194 if (class_exists('Yatra\Providers\BlockServiceProvider')) {
195 $providerClasses[] = 'Yatra\Providers\BlockServiceProvider';
196 }
197
198 // Instantiate and register all providers, keeping a map of the instances
199 // so boot() runs on the exact same object that register() did.
200 $instances = [];
201
202 foreach ($providerClasses as $class) {
203 try {
204 $instance = new $class($this->container);
205 if (method_exists($instance, 'register')) {
206 $instance->register();
207 }
208 $instances[$class] = $instance;
209 } catch (\Throwable $e) {
210 error_log("Yatra: Failed to register provider {$class}: " . $e->getMessage());
211 continue;
212 }
213 }
214
215 // Boot each successfully registered provider
216 foreach ($instances as $class => $instance) {
217 try {
218 if (method_exists($instance, 'boot')) {
219 $instance->boot();
220 }
221 } catch (\Throwable $e) {
222 error_log("Yatra: Failed to boot provider {$class}: " . $e->getMessage());
223 continue;
224 }
225 }
226 }
227
228 /**
229 * Initialize Action Scheduler
230 */
231 private function initializeActionScheduler(): void
232 {
233 $actionSchedulerPath = YATRA_PLUGIN_PATH . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
234 if (file_exists($actionSchedulerPath)) {
235 require_once $actionSchedulerPath;
236 }
237 }
238
239 /**
240 * Initialize core components
241 */
242 private function initializeCore(): void
243 {
244 // Check and create database tables if they don't exist
245 $this->ensureDatabaseTables();
246 }
247
248 /**
249 * Setup WordPress hooks
250 */
251 private function setupWordPressHooks(): void
252 {
253 // Register activation/deactivation hooks
254 register_activation_hook(YATRA_PLUGIN_FILE, [$this, 'activate']);
255 register_deactivation_hook(YATRA_PLUGIN_FILE, [$this, 'deactivate']);
256
257 // Check for plugin upgrades
258 add_action('admin_init', [$this, 'upgrade']);
259 }
260
261 /**
262 * Ensure database tables exist
263 */
264 private function ensureDatabaseTables(): void
265 {
266 // Register migration routes
267 add_action('rest_api_init', function() {
268 // Temporarily manually require migration files until autoloader is fixed
269 $migrationFiles = [
270 __DIR__ . '/Migrations/MigrationController.php',
271 __DIR__ . '/Migrations/MigrationProgress.php',
272 __DIR__ . '/Migrations/MigrationDetector.php',
273 ];
274
275 foreach ($migrationFiles as $file) {
276 if (file_exists($file)) {
277 require_once $file;
278 }
279 }
280
281 if (class_exists('\Yatra\Migration\MigrationController')) {
282 $migrationController = new \Yatra\Migration\MigrationController();
283 $migrationController->registerRoutes();
284 }
285 });
286
287 // Register Action Scheduler hook for background migration processing
288 add_action('yatra_migrate_data_type', function($dataType, $force = false) {
289 if (class_exists('\Yatra\Migration\MigrationProgress')) {
290 $migrationService = new \Yatra\Migration\MigrationProgress();
291 $result = $migrationService->processMigration($dataType, (bool) $force);
292
293 // Log result for debugging
294 if (isset($result['success']) && $result['success']) {
295 error_log("Yatra Migration completed for {$dataType}: migrated={$result['migrated']}, skipped={$result['skipped']}, failed={$result['failed']}");
296 } else {
297 error_log("Yatra Migration failed for {$dataType}: " . ($result['error'] ?? 'Unknown error'));
298 }
299 }
300 }, 10, 2);
301
302 // Register background hook for all data types migration via cron
303 add_action('yatra_migration_background_run', function($force = false) {
304 if (class_exists('\Yatra\Migration\MigrationProgress')) {
305 $migrationService = new \Yatra\Migration\MigrationProgress();
306 $migrationService->migrateAllDirect((bool) $force);
307 }
308 }, 10, 1);
309 }
310
311 /**
312 * Plugin activation
313 */
314 public function activate(): void
315 {
316 // Run centralized installer for all one-time actions (tables + settings)
317 \Yatra\Services\InstallerService::install();
318
319 // Set default options (only version tracking - other defaults handled by InstallerService)
320 if (get_option('yatra_version') === false) {
321 add_option('yatra_version', YATRA_VERSION);
322 }
323
324 // Set up setup wizard redirect for first-time activation
325 if (get_option('yatra_setup_wizard_ran') !== '1') {
326 set_transient('yatra_setup_wizard_redirect', 1, 30);
327 }
328
329 // Flush rewrite rules
330 flush_rewrite_rules();
331 }
332
333 /**
334 * Plugin upgrade logic
335 */
336 public function upgrade(): void
337 {
338 $current_version = get_option('yatra_version', '1.0.0');
339
340 if (version_compare($current_version, YATRA_VERSION, '<')) {
341 Database::createTables();
342 update_option('yatra_version', YATRA_VERSION);
343 }
344
345 \Yatra\Services\InstallerService::maybeBackfillEmailTemplateDefaults();
346 \Yatra\Services\InstallerService::maybeNormalizeMigratedCouponDiscountStatuses();
347 }
348
349 /**
350 * Plugin deactivation
351 */
352 public function deactivate(): void
353 {
354 // Clean up if needed
355 flush_rewrite_rules();
356 }
357
358 /**
359 * Load plugin text domain
360 */
361 public function loadTextDomain(): void
362 {
363 $locale = determine_locale();
364
365 // Unload any existing text domain
366 unload_textdomain('yatra');
367
368 // Load from WordPress languages directory first (where Loco Translate saves files)
369 load_textdomain('yatra', WP_LANG_DIR . '/plugins/yatra-' . $locale . '.mo');
370
371 // Also check loco subdirectory
372 if (file_exists(WP_LANG_DIR . '/loco/plugins/yatra-' . $locale . '.mo')) {
373 load_textdomain('yatra', WP_LANG_DIR . '/loco/plugins/yatra-' . $locale . '.mo');
374 }
375
376 // Load from plugin directory (fallback)
377 load_plugin_textdomain('yatra', false, 'i18n/languages');
378 }
379
380 /**
381 * Get container instance
382 */
383 public function getContainer(): Container
384 {
385 return $this->container;
386 }
387 }
388
389