Asset::enqueue()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Flynt\Utils;
4
5
// TODO: add async & defer (see https://matthewhorne.me/defer-async-wordpress-scripts/); also add to cdn fallback
6
7
class Asset
8
{
9
    const DEFAULT_OPTIONS = [
10
        'dependencies' => [],
11
        'version' => null,
12
        'inFooter' => true,
13
        'media' => 'all',
14
        'cdn' => []
15
    ];
16
17
    protected static $assetManifest;
18
    protected static $loadFromCdn = false;
19
20
    /**
21
     * Gets the asset's url.
22
     *
23
     * @since 0.1.0
24
     *
25
     * @param string $asset The filename of the required asset.
26
     *
27
     * @return string|boolean Returns the url or false if the asset is not found.
28
     */
29
    public static function requireUrl($asset)
30
    {
31
        return self::get('url', $asset);
32
    }
33
34
    /**
35
     * Gets the asset's absolute path.
36
     *
37
     * @since 0.1.0
38
     *
39
     * @param string $asset The filename of the required asset.
40
     *
41
     * @return string|boolean Returns the absolute path or false if the asset is not found.
42
     */
43
    public static function requirePath($asset)
44
    {
45
        return self::get('path', $asset);
46
    }
47
48
    /**
49
     * Registers an asset.
50
     * It can register a script or a style.
51
     *
52
     * If the Asset utility has `loadFromCdn` set to true, you can use the cdn option to load an asset from the CDN,
53
     * the path option will then become the local fallback file, using the 'check' in the 'cdn' array.
54
     *
55
     * @since 0.1.0
56
     * @since 0.2.0 Supports CDN parameter.
57
     *
58
     * @param array $options Options must specify a type, a name and a path.
59
     *              $options = [
60
     *                 'type'          => (string) The type of asset to register (script|style).
61
     *                 'name'          => (string) Should be unique as it is used to identify the script in the whole system.
62
     *                 'path'          => (string) The path.
63
     *                 'dependencies'  => (array) An array of registered script handles this script depends on.
64
     *                 'version'       => (string/boolean) String specifying script version number, if it has one.
65
     *                 'inFooter'      => (boolean) If the type is 'script', whether to enqueue the script before </body> instead of in the <head>. Default 'false'.
66
     *                 'media'         => (string) If the type is 'style', string specifying the media for which this stylesheet has been defined (all/screen/handheld/print).
67
     *                 'cdn'           => [
68
     *                      'url'   => (string) The CDN url.
69
     *                      'check' => (string/boolean) Javascript boolean value that loads the local fallback if it is false.
70
     *                 ]
71
     *              ]
72
     *
73
     * @return boolean
74
     */
75
    public static function register($options)
76
    {
77
        return self::add('register', $options);
78
    }
79
80
    /**
81
     * Enqueues an asset.
82
     * It can enqueue a script or a style.
83
     *
84
     * @since 0.1.0
85
     * @since 0.2.0 Supports CDN parameter.
86
     *
87
     * @param array $options Options must specify a type, a name and a path.
88
     *              $options = [
89
     *                 'type'          => (string) The type of asset to register (script|style).
90
     *                 'name'          => (string) Should be unique as it is used to identify the script in the whole system.
91
     *                 'path'          => (string) The path.
92
     *                 'dependencies'  => (array) An array of registered script handles this script depends on.
93
     *                 'version'       => (string/boolean) String specifying script version number, if it has one.
94
     *                 'inFooter'      => (boolean) If the type is 'script', whether to enqueue the script before </body> instead of in the <head>. Default 'false'.
95
     *                 'media'         => (string) If the type is 'style', string specifying the media for which this stylesheet has been defined (all/screen/handheld/print).
96
     *                 'cdn'           => [
97
     *                      'url'   => (string) The CDN url.
98
     *                      'check' => (string/boolean) Javascript boolean value that loads the local fallback if it is false.
99
     *                 ]
100
     *              ]
101
     *
102
     * @return boolean
103
     */
104
    public static function enqueue($options)
105
    {
106
        return self::add('enqueue', $options);
107
    }
108
109
    /**
110
     * Getter and setter for the loadFromCdn setting.
111
     *
112
     * @param boolean $load (optional) Value to set the parameter to.
113
     *
114
     * @since 0.2.0
115
     */
116
    public static function loadFromCdn($load = null)
117
    {
118
        if (!isset($load)) {
119
            return self::$loadFromCdn;
120
        }
121
        self::$loadFromCdn = (bool) $load;
122
    }
123
124
    /**
125
     * Gets the contents of an asset.
126
     *
127
     * Useful for loading SVGs or other files inline.
128
     *
129
     * @since 0.2.0
130
     *
131
     * @param string $asset Asset path (relative to the theme directory).
132
     * @return string|boolean Returns the file contents or false in case of failure.
133
     */
134
    public static function getContents($asset)
135
    {
136
        $file = self::requirePath($asset);
137
        if (file_exists($file)) {
138
            return file_get_contents($file);
139
        } else {
140
            trigger_error("File not found at: ${asset}", E_USER_WARNING);
141
            return false;
142
        }
143
    }
144
145
    public static function addDependencies($handle, $dependencies, $type = null)
146
    {
147
        if (empty($dependencies)) {
148
            return;
149
        }
150
        if ($type === 'style') {
151
            global $wp_styles;
152
            $repo =& $wp_styles;
153
        } else {
154
            global $wp_scripts;
155
            $repo =& $wp_scripts;
156
        }
157
158
        $asset = $repo->query($handle, 'registered');
159
        if (!$asset) {
160
            return false;
161
        }
162
        $asset->deps = array_unique(array_merge($asset->deps, $dependencies));
163
        return true;
164
    }
165
166
    protected static function get($returnType, $asset)
167
    {
168
        $distPath = get_template_directory() . '/dist';
169
170
        if (!isset(self::$assetManifest)) {
171
            $manifestPath = $distPath . '/rev-manifest.json';
172
            if (is_file($manifestPath)) {
173
                self::$assetManifest = json_decode(file_get_contents($manifestPath), true);
174
            } else {
175
                self::$assetManifest = [];
176
            }
177
        }
178
179
        if (array_key_exists($asset, self::$assetManifest)) {
180
            $assetSuffix = self::$assetManifest[$asset];
181
        } else {
182
            $assetSuffix = $asset;
183
        }
184
185
        if ('path' == $returnType) {
186
            return $distPath . '/' . $assetSuffix;
187
        } elseif ('url' == $returnType) {
188
            $distUrl = get_template_directory_uri() . '/dist';
189
            return $distUrl . '/' . $assetSuffix;
190
        }
191
192
        return false;
193
    }
194
195
    protected static function add($funcType, $options)
196
    {
197
        $options = array_merge(self::DEFAULT_OPTIONS, $options);
198
199
        if (!array_key_exists('name', $options)) {
200
            trigger_error('Cannot add asset: Name not provided!', E_USER_WARNING);
201
            return false;
202
        }
203
204
        if (!array_key_exists('path', $options)) {
205
            trigger_error('Cannot add asset: Path not provided!', E_USER_WARNING);
206
            return false;
207
        }
208
209
        $funcName = "wp_{$funcType}_{$options['type']}";
210
        $lastVar = $options['type'] === 'script' ? $options['inFooter'] : $options['media'];
211
212
        // allow external urls
213
        $path = $options['path'];
214
        if (
215
            !(StringHelpers::startsWith('http://', $path))
216
            && !(StringHelpers::startsWith('https://', $path))
217
            && !(StringHelpers::startsWith('//', $path))
218
        ) {
219
            $fileExists = file_exists(self::requirePath($path));
220
            $path = Asset::requireUrl($path);
221
        } else {
222
            $fileExists = true;
223
        }
224
225
        if (
226
            'script' === $options['type']
227
            && true === self::$loadFromCdn
228
            && !empty($options['cdn'])
229
            && !empty($options['cdn']['check'])
230
            && !empty($options['cdn']['url'])
231
        ) {
232
            // if the script isn't registered or enqueued yet
233
            if (
234
                !wp_script_is($options['name'], 'registered')
235
                && !wp_script_is($options['name'], 'enqueued')
236
            ) {
237
                $localPath = $path;
238
                $path = $options['cdn']['url'];
239
            } else {
240
                // script already registered / enqueued
241
                // get registered script and compare paths
242
                $scriptPath = wp_scripts()->registered[$options['name']]->src;
243
244
                // deregister script and set cdn options to re-register down below
245
                if ($options['cdn']['url'] !== $scriptPath) {
246
                    wp_deregister_script($options['name']);
247
                    $localPath = $path;
248
                    $path = $options['cdn']['url'];
249
                }
250
            }
251
        }
252
253
        if (function_exists($funcName) && $fileExists) {
254
            $funcName(
255
                $options['name'],
256
                $path,
257
                $options['dependencies'],
258
                $options['version'],
259
                $lastVar
260
            );
261
262
            if (isset($localPath)) {
263
                $cdnCheck = $options['cdn']['check'];
264
                wp_add_inline_script(
265
                    $options['name'],
266
                    "${cdnCheck}||document.write(\"<script src=\\\"${localPath}\\\"><\/script>\")"
267
                );
268
            }
269
270
            return true;
271
        }
272
273
        return false;
274
    }
275
}
276