PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.9
Yatra – Travel Booking & Tour Operator Software v3.0.2.9
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.9, at app/Bootstrap.php

380 lines 12.0 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\Services\StatsUsage')) {
80 \Yatra\Services\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 // Show admin notice if in admin area
118 if (is_admin()) {
119 add_action('admin_notices', function() use ($e) {
120 echo '<div class="notice notice-error"><p><strong>Yatra:</strong> ' .
121 esc_html($e->getMessage()) . '</p></div>';
122 });
123 }
124
125 return;
126 }
127
128 $this->initialized = true;
129 }
130
131 /**
132 * Load helper functions
133 */
134 private function loadHelperFunctions(): void
135 {
136 $helpersPath = YATRA_PLUGIN_PATH . 'includes/helpers.php';
137 if (file_exists($helpersPath)) {
138 require_once $helpersPath;
139 }
140
141 $seoHelperPath = YATRA_PLUGIN_PATH . 'includes/seo-helper.php';
142 if (file_exists($seoHelperPath)) {
143 require_once $seoHelperPath;
144 }
145 }
146
147 /**
148 * Register and boot service providers.
149 *
150 * Each provider is instantiated once. The same instance is used for both
151 * register() and boot() so that any hooks added during register() remain
152 * attached to the object that boot() later acts on.
153 */
154 private function registerServiceProviders(): void
155 {
156 // Register third-party compatibility hooks (Elementor, etc.)
157 // Must run on `plugins_loaded` so that other plugins (Elementor, etc.) are
158 // guaranteed to have loaded their classes before we check class_exists().
159 // Calling Compatibility::register() directly here runs before plugins_loaded
160 // and class_exists('\Elementor\Plugin') will always be false at that point.
161 if (!is_admin() && class_exists('Yatra\\Compatibility\\Compatibility')) {
162 add_action('plugins_loaded', ['Yatra\\Compatibility\\Compatibility', 'register'], 20);
163 }
164
165 $providerClasses = [];
166
167 // Core providers — always loaded
168 if (class_exists('Yatra\Providers\AppServiceProvider')) {
169 $providerClasses[] = 'Yatra\Providers\AppServiceProvider';
170 }
171
172 if (class_exists('Yatra\Providers\RouteServiceProvider')) {
173 $providerClasses[] = 'Yatra\Providers\RouteServiceProvider';
174 }
175
176 // Admin-only providers
177 if (is_admin() && class_exists('Yatra\Providers\AdminServiceProvider')) {
178 $providerClasses[] = 'Yatra\Providers\AdminServiceProvider';
179 }
180
181 // Frontend-only providers
182 if (!is_admin() && class_exists('Yatra\Providers\FrontendAssetsProvider')) {
183 $providerClasses[] = 'Yatra\Providers\FrontendAssetsProvider';
184 }
185
186 // Shortcode provider (frontend + admin)
187 if (class_exists('Yatra\Providers\ShortcodeServiceProvider')) {
188 $providerClasses[] = 'Yatra\Providers\ShortcodeServiceProvider';
189 }
190
191 // Block provider (Gutenberg)
192 if (class_exists('Yatra\Providers\BlockServiceProvider')) {
193 $providerClasses[] = 'Yatra\Providers\BlockServiceProvider';
194 }
195
196 // Instantiate and register all providers, keeping a map of the instances
197 // so boot() runs on the exact same object that register() did.
198 $instances = [];
199
200 foreach ($providerClasses as $class) {
201 try {
202 $instance = new $class($this->container);
203 if (method_exists($instance, 'register')) {
204 $instance->register();
205 }
206 $instances[$class] = $instance;
207 } catch (\Throwable $e) {
208 continue;
209 }
210 }
211
212 // Boot each successfully registered provider
213 foreach ($instances as $class => $instance) {
214 try {
215 if (method_exists($instance, 'boot')) {
216 $instance->boot();
217 }
218 } catch (\Throwable $e) {
219 continue;
220 }
221 }
222 }
223
224 /**
225 * Initialize Action Scheduler
226 */
227 private function initializeActionScheduler(): void
228 {
229 $actionSchedulerPath = YATRA_PLUGIN_PATH . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
230 if (file_exists($actionSchedulerPath)) {
231 require_once $actionSchedulerPath;
232 }
233 }
234
235 /**
236 * Initialize core components
237 */
238 private function initializeCore(): void
239 {
240 // Check and create database tables if they don't exist
241 $this->ensureDatabaseTables();
242 }
243
244 /**
245 * Setup WordPress hooks
246 */
247 private function setupWordPressHooks(): void
248 {
249 // Register activation/deactivation hooks
250 register_activation_hook(YATRA_PLUGIN_FILE, [$this, 'activate']);
251 register_deactivation_hook(YATRA_PLUGIN_FILE, [$this, 'deactivate']);
252
253 // Check for plugin upgrades
254 add_action('admin_init', [$this, 'upgrade']);
255 }
256
257 /**
258 * Ensure database tables exist
259 */
260 private function ensureDatabaseTables(): void
261 {
262 // Register migration routes
263 add_action('rest_api_init', function() {
264 // Temporarily manually require migration files until autoloader is fixed
265 $migrationFiles = [
266 __DIR__ . '/Migrations/MigrationController.php',
267 __DIR__ . '/Migrations/MigrationProgress.php',
268 __DIR__ . '/Migrations/MigrationDetector.php',
269 ];
270
271 foreach ($migrationFiles as $file) {
272 if (file_exists($file)) {
273 require_once $file;
274 }
275 }
276
277 if (class_exists('\Yatra\Migration\MigrationController')) {
278 $migrationController = new \Yatra\Migration\MigrationController();
279 $migrationController->registerRoutes();
280 }
281 });
282
283 // Register Action Scheduler hook for background migration processing
284 add_action('yatra_migrate_data_type', function($dataType, $force = false) {
285 if (class_exists('\Yatra\Migration\MigrationProgress')) {
286 $migrationService = new \Yatra\Migration\MigrationProgress();
287 $result = $migrationService->processMigration($dataType, (bool) $force);
288
289
290 }
291 }, 10, 2);
292
293 // Register background hook for all data types migration via cron
294 add_action('yatra_migration_background_run', function($force = false) {
295 if (class_exists('\Yatra\Migration\MigrationProgress')) {
296 $migrationService = new \Yatra\Migration\MigrationProgress();
297 $migrationService->migrateAllDirect((bool) $force);
298 }
299 }, 10, 1);
300 }
301
302 /**
303 * Plugin activation
304 */
305 public function activate(): void
306 {
307 // Run centralized installer for all one-time actions (tables + settings)
308 \Yatra\Services\InstallerService::install();
309
310 // Set default options (only version tracking - other defaults handled by InstallerService)
311 if (get_option('yatra_version') === false) {
312 add_option('yatra_version', YATRA_VERSION);
313 }
314
315 // Set up setup wizard redirect for first-time activation
316 if (get_option('yatra_setup_wizard_ran') !== '1') {
317 set_transient('yatra_setup_wizard_redirect', 1, 30);
318 }
319
320 // Flush rewrite rules
321 flush_rewrite_rules();
322 }
323
324 /**
325 * Plugin upgrade logic
326 */
327 public function upgrade(): void
328 {
329 $current_version = get_option('yatra_version', '1.0.0');
330
331 if (version_compare($current_version, YATRA_VERSION, '<')) {
332 Database::createTables();
333 update_option('yatra_version', YATRA_VERSION);
334 }
335
336 \Yatra\Services\InstallerService::maybeBackfillEmailTemplateDefaults();
337 \Yatra\Services\InstallerService::maybeNormalizeMigratedCouponDiscountStatuses();
338 }
339
340 /**
341 * Plugin deactivation
342 */
343 public function deactivate(): void
344 {
345 // Clean up if needed
346 flush_rewrite_rules();
347 }
348
349 /**
350 * Load plugin text domain
351 */
352 public function loadTextDomain(): void
353 {
354 $locale = determine_locale();
355
356 // Unload any existing text domain
357 unload_textdomain('yatra');
358
359 // Load from WordPress languages directory first (where Loco Translate saves files)
360 load_textdomain('yatra', WP_LANG_DIR . '/plugins/yatra-' . $locale . '.mo');
361
362 // Also check loco subdirectory
363 if (file_exists(WP_LANG_DIR . '/loco/plugins/yatra-' . $locale . '.mo')) {
364 load_textdomain('yatra', WP_LANG_DIR . '/loco/plugins/yatra-' . $locale . '.mo');
365 }
366
367 // Load from plugin directory (fallback)
368 load_plugin_textdomain('yatra', false, 'i18n/languages');
369 }
370
371 /**
372 * Get container instance
373 */
374 public function getContainer(): Container
375 {
376 return $this->container;
377 }
378 }
379
380