Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
LinkPrefetch
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 7
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 get_current_settings
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 add_to_runtime
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 enqueue_scripts
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 add_defer
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 get_settings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 update_settings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace NewfoldLabs\WP\Module\Performance\LinkPrefetch;
4
5use NewfoldLabs\WP\Module\Data\SiteCapabilities;
6use NewfoldLabs\WP\ModuleLoader\Container;
7
8/**
9 * Handles link prefetch functionality.
10 */
11class LinkPrefetch {
12
13    /**
14     * Allowed behavior values.
15     *
16     * @var array
17     */
18    public const VALID_BEHAVIORS = array( 'mouseHover', 'mouseDown' );
19
20    /**
21     * Allowed mobile behavior values.
22     *
23     * @var array
24     */
25    public const VALID_MOBILE_BEHAVIORS = array( 'touchstart', 'viewport' );
26
27    /**
28     * Dependency injection container.
29     *
30     * @var Container
31     */
32    protected $container;
33
34    /**
35     * Option name for link prefetch settings.
36     *
37     * @var string
38     */
39    public static $option_name = 'nfd_link_prefetch_settings';
40
41    /**
42     * Site capabilities for link prefetch Click.
43     *
44     * @var bool
45     */
46    public static $has_link_prefetch_click = false;
47
48    /**
49     * Site capabilities for link prefetch Hover.
50     *
51     * @var bool
52     */
53    public static $has_link_prefetch_hover = false;
54
55    /**
56     * Default settings.
57     *
58     * @var array
59     */
60    public static $default_settings = array(
61        'activeOnDesktop' => false,
62        'behavior'        => 'mouseHover',
63        'hoverDelay'      => 60,
64        'instantClick'    => false,
65        'activeOnMobile'  => false,
66        'mobileBehavior'  => 'touchstart',
67        'ignoreKeywords'  => '#,?',
68    );
69
70    /**
71     * Constructor.
72     *
73     * @param Container $container The dependency injection container.
74     */
75    public function __construct( Container $container ) {
76        $this->container = $container;
77
78        $capabilities = ( new SiteCapabilities() )->all();
79
80        self::$has_link_prefetch_click = array_key_exists( 'hasLinkPrefetchClick', $capabilities ) ? $capabilities['hasLinkPrefetchClick'] : null;
81        self::$has_link_prefetch_hover = array_key_exists( 'hasLinkPrefetchHover', $capabilities ) ? $capabilities['hasLinkPrefetchHover'] : null;
82
83        if ( false === self::$has_link_prefetch_click && false === self::$has_link_prefetch_hover ) {
84            delete_option( self::$option_name );
85            return;
86        }
87
88        add_filter( 'newfold-runtime', array( $this, 'add_to_runtime' ) );
89        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
90        if ( ! is_admin() ) {
91            add_filter( 'script_loader_tag', array( $this, 'add_defer' ), 10, 2 );
92        }
93    }
94
95    /**
96     * Retrieves the current plugin settings from the options table.
97     * If no settings are stored, returns and saves the default settings
98     * based on feature flags.
99     *
100     * @return array The current or default settings.
101     */
102    public function get_current_settings() {
103        $current_settings = get_option( self::$option_name, false );
104        if ( false !== $current_settings ) {
105            return $current_settings;
106        }
107
108        $final_settings = self::$default_settings;
109        if ( self::$has_link_prefetch_click || self::$has_link_prefetch_hover ) {
110            $final_settings['activeOnDesktop'] = true;
111            $final_settings['activeOnMobile']  = true;
112        }
113
114        if ( self::$has_link_prefetch_click ) {
115            $final_settings['behavior']       = 'mouseDown';
116            $final_settings['mobileBehavior'] = 'touchstart';
117        }
118
119        if ( self::$has_link_prefetch_hover ) {
120            $final_settings['behavior']       = 'mouseHover';
121            $final_settings['mobileBehavior'] = 'viewport';
122        }
123
124        update_option( self::$option_name, $final_settings );
125        return $final_settings;
126    }
127
128    /**
129     * Adds values to the runtime object.
130     *
131     * @param array $sdk The runtime object.
132     *
133     * @return array Modified runtime object.
134     */
135    public function add_to_runtime( $sdk ) {
136        $current_settings = $this->get_current_settings();
137
138        return array_merge(
139            $sdk,
140            array( 'linkPrefetch' => array( 'settings' => $current_settings ) )
141        );
142    }
143
144    /**
145     * Enqueues the link prefetch script.
146     *
147     * @return void
148     */
149    public function enqueue_scripts() {
150        $settings             = $this->get_current_settings();
151        $settings['isMobile'] = wp_is_mobile();
152        wp_enqueue_script(
153            'linkprefetcher',
154            NFD_PERFORMANCE_BUILD_URL . '/assets/link-prefetch.min.js',
155            array(),
156            $this->container->plugin()->version,
157            true
158        );
159        wp_add_inline_script(
160            'linkprefetcher',
161            'window.LP_CONFIG = ' . wp_json_encode( $settings ),
162            'before'
163        );
164    }
165
166    /**
167     * Adds a defer attribute to the script tag.
168     *
169     * @param string $tag    The HTML script tag.
170     * @param string $handle The handle of the script.
171     *
172     * @return string Modified HTML script tag.
173     */
174    public function add_defer( $tag, $handle ) {
175        if ( 'linkprefetcher' === $handle && false === strpos( $tag, 'defer' ) ) {
176            $tag = preg_replace( ':(?=></script>):', ' defer', $tag );
177        }
178        return $tag;
179    }
180
181    /**
182     * Retrieves the current link prefetch settings.
183     *
184     * @return array Current settings.
185     */
186    public static function get_settings() {
187        return get_option( self::$option_name, self::$default_settings );
188    }
189
190    /**
191     * Updates the link prefetch settings.
192     *
193     * @param array $settings The settings to update.
194     *
195     * @return boolean
196     */
197    public static function update_settings( $settings ) {
198        return update_option( self::$option_name, $settings );
199    }
200}