PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.21
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.21
1.10.23 1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 All 167 releases
← All changes | includes/Init.php +28 -108 1.10.19 → 1.10.21 View file →
@@ -12,11 +12,9 @@
12 12
13 13 use WCPOS\WooCommercePOS\Admin\Consent;
14 14 use WCPOS\WooCommercePOS\Admin\Menu;
15 15 use WCPOS\WooCommercePOS\Services\Auth as AuthService;
16 -use WCPOS\WooCommercePOS\Services\Extensions;
17 -use WCPOS\WooCommercePOS\Services\Receipt_Snapshot_Store;
18 -use WCPOS\WooCommercePOS\Services\Settings as SettingsService;
16 +use WCPOS\WooCommercePOS\Services\Service_Groups;
19 17
20 18 /**
21 19 * Init class.
22 20 */
@@ -272,8 +270,20 @@
272 270 'args' => 0,
273 271 'reason' => 'Append after lifecycle; REST priority 999 reports the final response status, gated by consent (#1811).',
274 272 'phase' => 'post-latch',
275 273 ),
274 + array(
275 + 'hook' => null,
276 + 'callback' => static function (): void {
277 + add_action( 'wp_logout', array( Services\Auth::class, 'revoke_web_session_on_logout' ), 10, 1 );
278 + add_action( 'password_reset', array( Services\Auth::class, 'revoke_sessions_on_password_reset' ), 10, 1 );
279 + add_action( 'profile_update', array( Services\Auth::class, 'revoke_sessions_on_password_change' ), 10, 2 );
280 + },
281 + 'priority' => 10,
282 + 'args' => 0,
283 + 'reason' => 'Default 10; logout ends this browser\'s web POS session, a password reset or change ends every POS session. Auth is built only when one fires.',
284 + 'phase' => 'post-latch',
285 + ),
276 286 );
277 287
278 288 return array_values(
279 289 array_filter(
@@ -508,142 +518,52 @@
508 518 }
509 519 }
510 520
511 521 /**
512 - * Groups constructed so far in this request (test seam; see constructed_groups()).
513 - *
514 - * @var array<string, bool>
515 - */
516 - private static array $constructed = array();
517 -
518 - /**
519 522 * Common initializations, by request lane.
520 523 *
521 - * Every request gets the services whose hooks WooCommerce consults on a
522 - * plain shopper page BEFORE any order exists: translations, the product
523 - * visibility filters, the order statuses and the read-side order filters
524 - * (My Account renders POS orders), the gateway registration (WooCommerce
525 - * builds its gateway list on cart pages) and the reserved-stock filter
526 - * (POS drafts must reduce online availability at add-to-cart time).
524 + * Every request gets the always group; everything else is constructed only
525 + * on the lanes that use it, and the order group additionally on the first
526 + * order write of ANY request, so the lane classifier is an optimisation
527 + * rather than a correctness gate. {@see Service_Groups} holds the membership
528 + * of each group and the reasons behind it.
527 529 *
528 - * Everything else is constructed only on the lanes that use it, and the
529 - * order-event services additionally on the first order write of ANY request
530 - * ({@see ensure_order_services()}), so the lane classifier is an
531 - * optimisation rather than a correctness gate. Measured 2026-09-03: a
532 - * storefront page loaded ~80 plugin files and 22 objects for hooks that
533 - * never fire there (see .claude/research/2026-09-03-lazy-service-construction-spec.md).
530 + * Measured 2026-09-03: a storefront page loaded ~80 plugin files and 22
531 + * objects for hooks that never fire there (see
532 + * .claude/research/2026-09-03-lazy-service-construction-spec.md).
534 533 */
535 534 private function init_common(): void {
536 - self::$constructed['always'] = true;
535 + Service_Groups::ensure( Service_Groups::ALWAYS );
537 536
538 - // init the Services.
539 - SettingsService::instance();
540 - AuthService::instance();
541 -
542 - // Needed on every lane, including a plain storefront page.
543 - new i18n();
544 - new Gateways();
545 - new Products();
546 - new Orders();
547 - Services\Stock_Validator::instance();
548 - Services\Order_Write_Intent::register();
549 -
550 537 if ( Services\Request_Lane::is_storefront() ) {
551 538 // Order-event services arrive on the first order write, if any.
552 - self::arm_order_services();
539 + Service_Groups::arm_order_group();
553 540 return;
554 541 }
555 542
556 - self::ensure_order_services();
557 - self::construct_pos_services();
543 + Service_Groups::ensure( Service_Groups::ORDER );
544 + Service_Groups::ensure( Service_Groups::POS );
558 545 }
559 546
560 547 /**
561 - * Services only POS, admin, REST, cron and CLI requests use.
562 - */
563 - private static function construct_pos_services(): void {
564 - if ( isset( self::$constructed['pos'] ) ) {
565 - return;
566 - }
567 - self::$constructed['pos'] = true;
568 - Extensions::instance();
569 - new Services\Decimal_Quantities();
570 - new Services\Customer_Meta_Parity();
571 - }
572 -
573 - /**
574 - * Hook the order-event services to the first order write of the request.
575 - *
576 - * Every WooCommerce order write — create, update, status transition,
577 - * `payment_complete()`, refund — goes through `WC_Abstract_Order::save()`,
578 - * which fires `woocommerce_before_order_object_save` before the data store
579 - * writes and before `woocommerce_new_order` / `woocommerce_order_status_changed`
580 - * / `woocommerce_payment_complete` fire. Priority 0 there means every
581 - * observer exists before any order is written — on a webhook, a cron
582 - * spawned from a page view, a third-party plugin creating an order on
583 - * `template_redirect`, or a lane the classifier got wrong. Nothing in the
584 - * order group listens to trash or delete, so those need no arming.
585 - */
586 - private static function arm_order_services(): void {
587 - add_action( 'woocommerce_before_order_object_save', array( self::class, 'ensure_order_services' ), 0, 0 );
588 - }
589 -
590 - /**
591 548 * Construct the order-event services exactly once per request.
592 549 *
593 550 * Idempotent and safe to call after `init`; each service handles its own
594 551 * late registration. Fires `woocommerce_pos_order_services_ready` once so
595 552 * Pro and extensions can construct their own order-event services at the
596 - * same moment on every lane.
553 + * same moment on every lane. Implementation now lives in Service_Groups.
597 554 */
598 555 public static function ensure_order_services(): void {
599 - if ( isset( self::$constructed['order'] ) ) {
600 - return;
601 - }
602 - self::$constructed['order'] = true;
603 -
604 - Receipt_Snapshot_Store::instance();
605 - new Emails();
606 - new Templates();
607 - new Services\Print_Job_Service();
608 - new Services\Cloud_Print_Trigger_Service();
609 - new Services\Cloud_Print_Submit_Service();
610 - new Services\Cloud_Print_Relay_Service();
611 -
612 - /**
613 - * Fires once per request when the POS order-event services exist:
614 - * eagerly on POS, admin, REST, cron and CLI requests (from this
615 - * plugin's `init` callback at priority 10), and on a storefront
616 - * request the moment the first order is about to be written.
617 - *
618 - * Because the eager firing happens at `init` priority 10, a listener
619 - * added later than that (for example from another plugin's `init`
620 - * callback at priority 20) must check `did_action()` first and
621 - * construct immediately when the action has already fired.
622 - *
623 - * @since 1.10.8
624 - */
625 - do_action( 'woocommerce_pos_order_services_ready' );
556 + Service_Groups::ensure( Service_Groups::ORDER );
626 557 }
627 558
628 559 /**
629 - * Which service groups this request has constructed: 'always', 'order', 'pos'.
630 - *
631 - * @internal Test seam.
632 - *
633 - * @return string[]
634 - */
635 - public static function constructed_groups(): array {
636 - return array_keys( self::$constructed );
637 - }
638 -
639 - /**
640 560 * Forget which groups were constructed. Tests only.
641 561 *
642 562 * @internal
643 563 */
644 564 public static function reset_request_state(): void {
645 - self::$constructed = array();
565 + Service_Groups::reset();
646 566 Services\Request_Lane::reset();
647 567 }
648 568
649 569 /**