Completed
Push — devel ( 000362...7d44b1 )
by Miguel
03:56
created

Abstract   D

Complexity

Total Complexity 113

Size/Duplication

Total Lines 996
Duplicated Lines 10.24 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 15
Bugs 8 Features 4
Metric Value
wmc 113
c 15
b 8
f 4
lcom 1
cbo 2
dl 102
loc 996
rs 4.4444

54 Methods

Rating   Name   Duplication   Size   Complexity  
B getFromSocket() 0 27 5
A __construct() 0 3 1
A save() 0 17 4
A _getVclTemplateFilename() 0 4 1
A _getVclFilename() 0 5 1
A _getCustomIncludeFilename() 0 5 1
A _formatTemplate() 0 8 1
A _vcl_call() 0 3 1
A _getAdminFrontname() 0 8 2
A _getNormalizeHostTarget() 0 14 3
A getAllowedHostsRegex() 0 11 2
A _vcl_sub_allowed_hosts_regex() 0 10 1
A getBaseUrlPathRegex() 0 6 1
A _getBaseUrlPaths() 0 19 3
A _getUrlExcludes() 0 5 1
A _getDefaultBackend() 15 15 2
A _getAdminBackend() 15 15 2
A _getGetParamExcludes() 0 4 1
A _getIgnoreGetParameters() 0 7 1
A _sendUnModifiedUrlToBackend() 0 4 1
A _getGenerateSessionStart() 0 4 2
A _getGenerateSessionEnd() 0 4 2
A _getGenerateSession() 0 4 2
A _getGenerateSessionExpires() 0 4 2
A _getSimpleHashStatic() 0 4 2
A _getStaticExtensions() 0 4 1
A _getUrlTtls() 0 21 4
A _getDebugIps() 0 4 1
A _getCrawlerIps() 0 4 1
A _getCrawlerUserAgents() 0 6 1
B _getAdvancedSessionValidationTargets() 0 19 6
A _cleanVcl() 0 9 1
A _cleanVclHelper() 0 6 4
A _vcl_backend() 0 19 2
B _vcl_director() 0 28 6
B _vcl_director_backend() 0 27 3
A _vcl_get_probe() 0 21 2
A _vcl_acl() 0 13 1
A _vcl_sub_normalize_host() 0 8 1
A _getNormalizeCookieTarget() 0 4 1
A _getNormalizeCookieRegex() 0 4 1
B _vcl_sub_maintenance_allowed_ips() 38 38 4
B _vcl_sub_synth() 34 34 4
F _getTemplateVars() 0 80 12
generate() 0 1 ?
A _getDefaultTtl() 0 3 1
A _getGracePeriod() 0 3 1
A _getEnableDebugHeaders() 0 4 2
A _getForceCacheStatic() 0 4 2
A _getStaticTtl() 0 3 1
A _getEnableCaching() 0 4 2
A _getLruFactor() 0 3 1
B _vcl_sub_normalize_user_agent() 0 25 1
A _vcl_sub_normalize_encoding() 0 16 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Abstract often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Abstract, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Nexcess.net Turpentine Extension for Magento
5
 * Copyright (C) 2012  Nexcess.net L.L.C.
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License along
18
 * with this program; if not, write to the Free Software Foundation, Inc.,
19
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
 */
21
22
abstract class Nexcessnet_Turpentine_Model_Varnish_Configurator_Abstract {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
23
24
    const VCL_CUSTOM_C_CODE_FILE = 'uuid.c';
25
26
    /**
27
     * Get the correct version of a configurator from a socket
28
     *
29
     * @param  Nexcessnet_Turpentine_Model_Varnish_Admin_Socket $socket
30
     * @return Nexcessnet_Turpentine_Model_Varnish_Configurator_Abstract
31
     */
32
    static public function getFromSocket($socket) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
33
        try {
34
            $version = $socket->getVersion();
35
        } catch (Mage_Core_Exception $e) {
36
            Mage::getSingleton('core/session')
37
                ->addError('Error determining Varnish version: '.
38
                    $e->getMessage());
39
            return null;
40
        }
41
        switch ($version) {
42
            case '4.0':
43
                return Mage::getModel(
44
                    'turpentine/varnish_configurator_version4',
45
                    array('socket' => $socket) );
46
47
            case '3.0':
48
                return Mage::getModel(
49
                    'turpentine/varnish_configurator_version3',
50
                        array('socket' => $socket) );
51
            case '2.1':
52
                return Mage::getModel(
53
                    'turpentine/varnish_configurator_version2',
54
                        array('socket' => $socket) );
55
            default:
56
                Mage::throwException('Unsupported Varnish version');
57
        }
58
    }
59
60
    /**
61
     * The socket this configurator is based on
62
     *
63
     * @var Nexcessnet_Turpentine_Model_Varnish_Admin_Socket
64
     */
65
    protected $_socket = null;
66
    /**
67
     * options array
68
     *
69
     * @var array
70
     */
71
    protected $_options = array(
72
        'vcl_template'  => null,
73
    );
74
75
    public function __construct($options = array()) {
76
        $this->_options = array_merge($this->_options, $options);
77
    }
78
79
    abstract public function generate($doClean = true);
80
    // abstract protected function _getTemplateVars();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
81
82
    /**
83
     * Save the generated config to the file specified in Magento config
84
     *
85
     * @param  string $generatedConfig config generated by @generate
86
     * @return null
87
     */
88
    public function save($generatedConfig) {
89
        $filename = $this->_getVclFilename();
90
        $dir = dirname($filename);
91
        if ( ! is_dir($dir)) {
92
            // this umask is probably redundant, but just in case...
93
            if ( ! mkdir($dir, 0777 & ~umask(), true)) {
94
                $err = error_get_last();
95
                return array(false, $err);
96
            }
97
        }
98
        if (strlen($generatedConfig) !==
99
                file_put_contents($filename, $generatedConfig)) {
100
            $err = error_get_last();
101
            return array(false, $err);
102
        }
103
        return array(true, null);
104
    }
105
106
    /**
107
     * Get the full path for a given template filename
108
     *
109
     * @param  string $baseFilename
110
     * @return string
111
     */
112
    protected function _getVclTemplateFilename($baseFilename) {
113
        $extensionDir = Mage::getModuleDir('', 'Nexcessnet_Turpentine');
114
        return sprintf('%s/misc/%s', $extensionDir, $baseFilename);
115
    }
116
117
    /**
118
     * Get the name of the file to save the VCL to
119
     *
120
     * @return string
121
     */
122
    protected function _getVclFilename() {
123
        return $this->_formatTemplate(
124
            Mage::getStoreConfig('turpentine_varnish/servers/config_file'),
125
            array('root_dir' => Mage::getBaseDir()) );
126
    }
127
128
    /**
129
     * Get the name of the custom include VCL file
130
     *
131
     * @return string
132
     */
133
    protected function _getCustomIncludeFilename() {
134
        return $this->_formatTemplate(
135
            Mage::getStoreConfig('turpentine_varnish/servers/custom_include_file'),
136
            array('root_dir' => Mage::getBaseDir()) );
137
    }
138
139
    /**
140
     * Format a template string, replacing {{keys}} with the appropriate values
141
     * and remove unspecified keys
142
     *
143
     * @param  string $template template string to operate on
144
     * @param  array  $vars     array of key => value replacements
145
     * @return string
146
     */
147
    protected function _formatTemplate($template, array $vars) {
148
        $needles = array_map(create_function('$k', 'return "{{".$k."}}";'),
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
149
            array_keys($vars));
150
        $replacements = array_values($vars);
151
        // do replacements, then delete unused template vars
152
        return preg_replace('~{{[^}]+}}~', '',
153
            str_replace($needles, $replacements, $template));
154
    }
155
156
    /**
157
     * Format a VCL subroutine call
158
     *
159
     * @param  string $subroutine subroutine name
160
     * @return string
161
     */
162
    protected function _vcl_call($subroutine) {
163
        return sprintf('call %s;', $subroutine);
164
    }
165
166
    /**
167
     * Get the Magento admin frontname
168
     *
169
     * This is just the plain string, not in URL format. ex:
170
     * http://example.com/magento/admin -> admin
171
     *
172
     * @return string
173
     */
174
    protected function _getAdminFrontname() {
175
        if (Mage::getStoreConfig('admin/url/use_custom_path')) {
176
            return Mage::getStoreConfig('admin/url/custom_path');
177
        } else {
178
            return (string) Mage::getConfig()->getNode(
179
                'admin/routers/adminhtml/args/frontName' );
180
        }
181
    }
182
183
    /**
184
     * Get the hostname for host normalization from Magento's base URL
185
     *
186
     * @return string
187
     */
188
    protected function _getNormalizeHostTarget() {
189
        $configHost = trim(Mage::getStoreConfig(
190
            'turpentine_vcl/normalization/host_target' ));
191
        if ($configHost) {
192
            return $configHost;
193
        } else {
194
            $baseUrl = parse_url(Mage::getBaseUrl());
195
            if (isset($baseUrl['port'])) {
196
                return sprintf('%s:%d', $baseUrl['host'], $baseUrl['port']);
197
            } else {
198
                return $baseUrl['host'];
199
            }
200
        }
201
    }
202
203
    /**
204
     * Get hosts as regex
205
     *
206
     * ex: base_url: example.com
207
     *     path_regex: (example.com|example.net)
208
     *
209
     * @return string
210
     */
211
    public function getAllowedHostsRegex() {
212
        $hosts = array();
213
        foreach (Mage::app()->getStores() as $store) {
214
            $hosts[] = parse_url($store->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB, false), PHP_URL_HOST);
215
        }
216
217
        $hosts = array_values(array_unique($hosts));
218
219
        $pattern = '('.implode('|', array_map("preg_quote", $hosts)).')';
220
        return $pattern;
221
    }
222
223
    /**
224
     * Get the Host normalization sub routine
225
     *
226
     * @return string
227
     */
228
    protected function _vcl_sub_allowed_hosts_regex() {
229
        $tpl = <<<EOS
230
# if host is not allowed in magento pass to backend
231
        if (req.http.host !~ "{{allowed_hosts_regex}}") {
232
            return (pass);
233
        }
234
EOS;
235
        return $this->_formatTemplate($tpl, array(
236
            'allowed_hosts_regex' => $this->getAllowedHostsRegex() ));
237
    }
238
239
    /**
240
     * Get the base url path regex
241
     *
242
     * ex: base_url: http://example.com/magento/
243
     *     path_regex: /magento/(?:(?:index|litespeed)\.php/)?
244
     *
245
     * @return string
246
     */
247
    public function getBaseUrlPathRegex() {
248
        $pattern = '^(%s)(?:(?:index|litespeed)\\.php/)?';
249
        return sprintf($pattern, implode('|',
250
            array_map(create_function('$x', 'return preg_quote($x,"|");'),
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
251
                $this->_getBaseUrlPaths())));
252
    }
253
254
    /**
255
     * Get the path part of each store's base URL and static file URLs
256
     *
257
     * @return array
258
     */
259
    protected function _getBaseUrlPaths() {
260
        $paths = array();
261
        $linkTypes = array(Mage_Core_Model_Store::URL_TYPE_LINK,
262
                            Mage_Core_Model_Store::URL_TYPE_JS,
263
                            Mage_Core_Model_Store::URL_TYPE_SKIN,
264
                            Mage_Core_Model_Store::URL_TYPE_MEDIA);
265
        foreach (Mage::app()->getStores() as $store) {
266
            foreach ($linkTypes as $linkType) {
267
                $paths[] = parse_url($store->getBaseUrl($linkType, false),
268
                    PHP_URL_PATH);
269
                $paths[] = parse_url($store->getBaseUrl($linkType, true),
270
                    PHP_URL_PATH);
271
            }
272
        }
273
        $paths = array_unique($paths);
274
        usort($paths, create_function('$a, $b',
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
275
            'return strlen( $b ) - strlen( $a );'));
276
        return array_values($paths);
277
    }
278
279
    /**
280
     * Format the URL exclusions for insertion in a regex. Admin frontname and
281
     * API are automatically added.
282
     *
283
     * @return string
284
     */
285
    protected function _getUrlExcludes() {
286
        $urls = Mage::getStoreConfig('turpentine_vcl/urls/url_blacklist');
287
        return implode('|', array_merge(array($this->_getAdminFrontname(), 'api'),
288
            Mage::helper('turpentine/data')->cleanExplode(PHP_EOL, $urls)));
289
    }
290
291
    /**
292
     * Get the default cache TTL from Magento config
293
     *
294
     * @return string
295
     */
296
    protected function _getDefaultTtl() {
297
        return Mage::helper('turpentine/varnish')->getDefaultTtl();
298
    }
299
300
    /**
301
     * Get the default backend configuration string
302
     *
303
     * @return string
304
     */
305 View Code Duplication
    protected function _getDefaultBackend() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
        $timeout = Mage::getStoreConfig('turpentine_vcl/backend/frontend_timeout');
307
        $default_options = array(
308
            'first_byte_timeout'    => $timeout.'s',
309
            'between_bytes_timeout' => $timeout.'s',
310
        );
311
        if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') {
312
            return $this->_vcl_director('default', $default_options);
313
        } else {
314
            return $this->_vcl_backend('default',
315
                Mage::getStoreConfig('turpentine_vcl/backend/backend_host'),
316
                Mage::getStoreConfig('turpentine_vcl/backend/backend_port'),
317
                $default_options);
318
        }
319
    }
320
321
    /**
322
     * Get the admin backend configuration string
323
     *
324
     * @return string
325
     */
326 View Code Duplication
    protected function _getAdminBackend() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
327
        $timeout = Mage::getStoreConfig('turpentine_vcl/backend/admin_timeout');
328
        $admin_options = array(
329
            'first_byte_timeout'    => $timeout.'s',
330
            'between_bytes_timeout' => $timeout.'s',
331
        );
332
        if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') {
333
            return $this->_vcl_director('admin', $admin_options);
334
        } else {
335
            return $this->_vcl_backend('admin',
336
                Mage::getStoreConfig('turpentine_vcl/backend/backend_host'),
337
                Mage::getStoreConfig('turpentine_vcl/backend/backend_port'),
338
                $admin_options);
339
        }
340
    }
341
342
    /**
343
     * Get the grace period for vcl_fetch
344
     *
345
     * This is curently hardcoded to 15 seconds, will be configurable at some
346
     * point
347
     *
348
     * @return string
349
     */
350
    protected function _getGracePeriod() {
351
        return Mage::getStoreConfig('turpentine_vcl/ttls/grace_period');
352
    }
353
354
    /**
355
     * Get whether debug headers should be enabled or not
356
     *
357
     * @return string
358
     */
359
    protected function _getEnableDebugHeaders() {
360
        return Mage::getStoreConfig('turpentine_varnish/general/varnish_debug')
361
            ? 'true' : 'false';
362
    }
363
364
    /**
365
     * Format the GET variable excludes for insertion in a regex
366
     *
367
     * @return string
368
     */
369
    protected function _getGetParamExcludes() {
370
        return implode('|', Mage::helper('turpentine/data')->cleanExplode(',',
371
            Mage::getStoreConfig('turpentine_vcl/params/get_params')));
372
    }
373
374
    protected function _getIgnoreGetParameters()
375
    {
376
        /** @var Nexcessnet_Turpentine_Helper_Data $helper */
377
        $helper = Mage::helper('turpentine');
378
        $ignoredParameters = $helper->cleanExplode(',', Mage::getStoreConfig('turpentine_vcl/params/ignore_get_params'));
379
        return implode('|', $ignoredParameters);
380
    }
381
382
    /**
383
     * @return boolean
384
     */
385
    protected function _sendUnModifiedUrlToBackend()
386
    {
387
        return Mage::getStoreConfigFlag('turpentine_vcl/params/transfer_unmodified_url');
388
    }
389
390
    /**
391
     * Get the Generate Session
392
     *
393
     * @return string
394
     */
395
    protected function _getGenerateSessionStart() {
396
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
397
            ? '/* -- REMOVED' : '';
398
    }
399
400
    /**
401
     * Get the Generate Session
402
     *
403
     * @return string
404
     */
405
    protected function _getGenerateSessionEnd() {
406
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
407
            ? '-- */' : '';
408
    }
409
410
411
    /**
412
     * Get the Generate Session
413
     *
414
     * @return string
415
     */
416
    protected function _getGenerateSession() {
417
        return Mage::getStoreConfigFlag('turpentine_varnish/general/vcl_fix')
418
            ? 'return (pipe);' : 'call generate_session;';
419
    }
420
421
422
    /**
423
     * Get the Generate Session Expires
424
     *
425
     * @return string
426
     */
427
    protected function _getGenerateSessionExpires() {
428
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
429
            ? '# call generate_session_expires' : 'call generate_session_expires;';
430
    }
431
432
    /**
433
     * Get the Force Static Caching option
434
     *
435
     * @return string
436
     */
437
    protected function _getForceCacheStatic() {
438
        return Mage::getStoreConfig('turpentine_vcl/static/force_static')
439
            ? 'true' : 'false';
440
    }
441
442
    /**
443
     * Get the Force Static Caching option
444
     *
445
     * @return string
446
     */
447
    protected function _getSimpleHashStatic() {
448
        return Mage::getStoreConfig('turpentine_vcl/static/simple_hash')
449
            ? 'true' : 'false';
450
    }
451
452
    /**
453
     * Format the list of static cache extensions
454
     *
455
     * @return string
456
     */
457
    protected function _getStaticExtensions() {
458
        return implode('|', Mage::helper('turpentine/data')->cleanExplode(',',
459
            Mage::getStoreConfig('turpentine_vcl/static/exts')));
460
    }
461
462
    /**
463
     * Get the static caching TTL
464
     *
465
     * @return string
466
     */
467
    protected function _getStaticTtl() {
468
        return Mage::getStoreConfig('turpentine_vcl/ttls/static_ttl');
469
    }
470
471
    /**
472
     * Format the by-url TTL value list
473
     *
474
     * @return string
475
     */
476
    protected function _getUrlTtls() {
477
        $str = array();
478
        $configTtls = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
479
            Mage::getStoreConfig('turpentine_vcl/ttls/url_ttls'));
480
        $ttls = array();
481
        foreach ($configTtls as $line) {
482
            $ttls[] = explode(',', trim($line));
483
        }
484
        foreach ($ttls as $ttl) {
485
            $str[] = sprintf('if (bereq.url ~ "%s%s") { set beresp.ttl = %ds; }',
486
                $this->getBaseUrlPathRegex(), $ttl[0], $ttl[1]);
487
        }
488
        $str = implode(' else ', $str);
489
        if ($str) {
490
            $str .= sprintf(' else { set beresp.ttl = %ds; }',
491
                $this->_getDefaultTtl());
492
        } else {
493
            $str = sprintf('set beresp.ttl = %ds;', $this->_getDefaultTtl());
494
        }
495
        return $str;
496
    }
497
498
    /**
499
     * Get the Enable Caching value
500
     *
501
     * @return string
502
     */
503
    protected function _getEnableCaching() {
504
        return Mage::helper('turpentine/varnish')->getVarnishEnabled() ?
505
            'true' : 'false';
506
    }
507
508
    /**
509
     * Get the list of allowed debug IPs
510
     *
511
     * @return array
512
     */
513
    protected function _getDebugIps() {
514
        return Mage::helper('turpentine/data')->cleanExplode(',',
515
            Mage::getStoreConfig('dev/restrict/allow_ips'));
516
    }
517
518
    /**
519
     * Get the list of crawler IPs
520
     *
521
     * @return array
522
     */
523
    protected function _getCrawlerIps() {
524
        return Mage::helper('turpentine/data')->cleanExplode(',',
525
            Mage::getStoreConfig('turpentine_vcl/backend/crawlers'));
526
    }
527
528
    /**
529
     * Get the regex formatted list of crawler user agents
530
     *
531
     * @return string
532
     */
533
    protected function _getCrawlerUserAgents() {
534
        return implode('|', Mage::helper('turpentine/data')
535
            ->cleanExplode(',',
536
                Mage::getStoreConfig(
537
                    'turpentine_vcl/backend/crawler_user_agents' )));
538
    }
539
540
    /**
541
     * Get the time to increase a cached objects TTL on cache hit (in seconds).
542
     *
543
     * This should be set very low since it gets added to every hit.
544
     *
545
     * @return string
546
     */
547
    protected function _getLruFactor() {
548
        return Mage::getStoreConfig('turpentine_vcl/ttls/lru_factor');
549
    }
550
551
    /**
552
     * Get the advanced session validation restrictions
553
     *
554
     * Note that if User-Agent Normalization is on then the normalized user-agent
555
     * is used for user-agent validation instead of the full user-agent
556
     *
557
     * @return string
558
     */
559
    protected function _getAdvancedSessionValidationTargets() {
560
        $validation = array();
561
        if (Mage::getStoreConfig('web/session/use_remote_addr')) {
562
            $validation[] = 'client.ip';
563
        }
564
        if (Mage::getStoreConfig('web/session/use_http_via')) {
565
            $validation[] = 'req.http.Via';
566
        }
567
        if (Mage::getStoreConfig('web/session/use_http_x_forwarded_for')) {
568
            $validation[] = 'req.http.X-Forwarded-For';
569
        }
570
        if (Mage::getStoreConfig(
571
                    'web/session/use_http_user_agent' ) &&
572
                ! Mage::getStoreConfig(
573
                    'turpentine_vcl/normalization/user_agent' )) {
574
            $validation[] = 'req.http.User-Agent';
575
        }
576
        return $validation;
577
    }
578
579
    /**
580
     * Remove empty and commented out lines from the generated VCL
581
     *
582
     * @param  string $dirtyVcl generated vcl
583
     * @return string
584
     */
585
    protected function _cleanVcl($dirtyVcl) {
586
        return implode(PHP_EOL,
587
            array_filter(
588
                Mage::helper('turpentine/data')
589
                    ->cleanExplode(PHP_EOL, $dirtyVcl),
590
                array($this, '_cleanVclHelper')
591
            )
592
        );
593
    }
594
595
    /**
596
     * Helper to filter out blank/commented lines for VCL cleaning
597
     *
598
     * @param  string $line
599
     * @return bool
600
     */
601
    protected function _cleanVclHelper($line) {
602
        return $line &&
603
            ((substr($line, 0, 1) != '#' &&
604
            substr($line, 0, 2) != '//') ||
605
            substr($line, 0, 8) == '#include');
606
    }
607
608
    /**
609
     * Format a VCL backend declaration
610
     *
611
     * @param  string $name    name of the backend
612
     * @param  string $host    backend host
613
     * @param  string $port    backend port
614
     * @param  array  $options options
615
     * @return string
616
     */
617
    protected function _vcl_backend($name, $host, $port, $options = array()) {
618
        $tpl = <<<EOS
619
backend {{name}} {
620
    .host = "{{host}}";
621
    .port = "{{port}}";
622
623
EOS;
624
        $vars = array(
625
            'host'  => $host,
626
            'port'  => $port,
627
            'name'  => $name,
628
        );
629
        $str = $this->_formatTemplate($tpl, $vars);
630
        foreach ($options as $key => $value) {
631
            $str .= sprintf('   .%s = %s;', $key, $value).PHP_EOL;
632
        }
633
        $str .= '}'.PHP_EOL;
634
        return $str;
635
    }
636
637
    /**
638
     * Format a VCL director declaration, for load balancing
639
     *
640
     * @param string $name           name of the director, also used to select config settings
641
     * @param array  $backendOptions options for each backend
642
     * @return string
643
     */
644
    protected function _vcl_director($name, $backendOptions) {
645
        $tpl = <<<EOS
646
director {{name}} round-robin {
647
{{backends}}
648
}
649
EOS;
650
        if ('admin' == $name && 'yes_admin' == Mage::getStoreConfig('turpentine_vcl/backend/load_balancing')) {
651
            $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
652
                Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes_admin'));
653
            $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url_admin');
654
        } else {
655
            $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
656
                Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes'));
657
            $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url');
658
        }
659
        $backends = '';
660
        foreach ($backendNodes as $backendNode) {
661
            $parts = explode(':', $backendNode, 2);
662
            $host = (empty($parts[0])) ? '127.0.0.1' : $parts[0];
663
            $port = (empty($parts[1])) ? '80' : $parts[1];
664
            $backends .= $this->_vcl_director_backend($host, $port, $probeUrl, $backendOptions);
665
        }
666
        $vars = array(
667
            'name' => $name,
668
            'backends' => $backends
669
        );
670
        return $this->_formatTemplate($tpl, $vars);
671
    }
672
673
    /**
674
     * Format a VCL backend declaration to put inside director
675
     *
676
     * @param string $host     backend host
677
     * @param string $port     backend port
678
     * @param string $probeUrl URL to check if backend is up
679
     * @param array  $options  extra options for backend
680
     * @return string
681
     */
682
    protected function _vcl_director_backend($host, $port, $probeUrl = '', $options = array()) {
683
        $tpl = <<<EOS
684
    {
685
        .backend = {
686
            .host = "{{host}}";
687
            .port = "{{port}}";
688
{{probe}}
689
690
EOS;
691
        $vars = array(
692
            'host'  => $host,
693
            'port'  => $port,
694
            'probe' => ''
695
        );
696
        if ( ! empty($probeUrl)) {
697
            $vars['probe'] = $this->_vcl_get_probe($probeUrl);
698
        }
699
        $str = $this->_formatTemplate($tpl, $vars);
700
        foreach ($options as $key => $value) {
701
            $str .= sprintf('            .%s = %s;', $key, $value).PHP_EOL;
702
        }
703
        $str .= <<<EOS
704
        }
705
    }
706
EOS;
707
        return $str;
708
    }
709
710
    /**
711
     * Format a VCL probe declaration to put in backend which is in director
712
     *
713
     * @param string $probeUrl URL to check if backend is up
714
     * @return string
715
     */
716
    protected function _vcl_get_probe($probeUrl) {
717
        $urlParts = parse_url($probeUrl);
718
        if (empty($urlParts)) {
719
            // Malformed URL
720
            return '';
721
        } else {
722
            $tpl = <<<EOS
723
            .probe = {
724
                .request =
725
                    "GET {{probe_path}} HTTP/1.1"
726
                    "Host: {{probe_host}}"
727
                    "Connection: close";
728
            }
729
EOS;
730
            $vars = array(
731
                'probe_host' => $urlParts['host'],
732
                'probe_path' => $urlParts['path']
733
            );
734
            return $this->_formatTemplate($tpl, $vars);
735
        }
736
    }
737
738
    /**
739
     * Format a VCL ACL declaration
740
     *
741
     * @param  string $name  ACL name
742
     * @param  array  $hosts list of hosts to add to the ACL
743
     * @return string
744
     */
745
    protected function _vcl_acl($name, array $hosts) {
746
        $tpl = <<<EOS
747
acl {{name}} {
748
    {{hosts}}
749
}
750
EOS;
751
        $fmtHost = create_function('$h', 'return sprintf(\'"%s";\',$h);');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
752
        $vars = array(
753
            'name'  => $name,
754
            'hosts' => implode("\n    ", array_map($fmtHost, $hosts)),
755
        );
756
        return $this->_formatTemplate($tpl, $vars);
757
    }
758
759
    /**
760
     * Get the User-Agent normalization sub routine
761
     *
762
     * @return string
763
     */
764
    protected function _vcl_sub_normalize_user_agent() {
765
        /**
766
         * Mobile regex from
767
         * @link http://magebase.com/magento-tutorials/magento-design-exceptions-explained/
768
         */
769
        $tpl = <<<EOS
770
if (req.http.User-Agent ~ "iP(?:hone|ad|od)|BlackBerry|Palm|Googlebot-Mobile|Mobile|mobile|mobi|Windows Mobile|Safari Mobile|Android|Opera (?:Mini|Mobi)") {
771
        set req.http.X-Normalized-User-Agent = "mobile";
772
    } else if (req.http.User-Agent ~ "MSIE") {
773
        set req.http.X-Normalized-User-Agent = "msie";
774
    } else if (req.http.User-Agent ~ "Firefox") {
775
        set req.http.X-Normalized-User-Agent = "firefox";
776
    } else if (req.http.User-Agent ~ "Chrome") {
777
        set req.http.X-Normalized-User-Agent = "chrome";
778
    } else if (req.http.User-Agent ~ "Safari") {
779
        set req.http.X-Normalized-User-Agent = "safari";
780
    } else if (req.http.User-Agent ~ "Opera") {
781
        set req.http.X-Normalized-User-Agent = "opera";
782
    } else {
783
        set req.http.X-Normalized-User-Agent = "other";
784
    }
785
786
EOS;
787
        return $tpl;
788
    }
789
790
    /**
791
     * Get the Accept-Encoding normalization sub routine
792
     *
793
     * @return string
794
     */
795
    protected function _vcl_sub_normalize_encoding() {
796
        $tpl = <<<EOS
797
if (req.http.Accept-Encoding) {
798
        if (req.http.Accept-Encoding ~ "gzip") {
799
            set req.http.Accept-Encoding = "gzip";
800
        } else if (req.http.Accept-Encoding ~ "deflate") {
801
            set req.http.Accept-Encoding = "deflate";
802
        } else {
803
            # unknown algorithm
804
            unset req.http.Accept-Encoding;
805
        }
806
    }
807
808
EOS;
809
        return $tpl;
810
    }
811
812
    /**
813
     * Get the Host normalization sub routine
814
     *
815
     * @return string
816
     */
817
    protected function _vcl_sub_normalize_host() {
818
        $tpl = <<<EOS
819
set req.http.Host = "{{normalize_host_target}}";
820
821
EOS;
822
        return $this->_formatTemplate($tpl, array(
823
            'normalize_host_target' => $this->_getNormalizeHostTarget() ));
824
    }
825
826
    /**
827
     * Get the hostname for cookie normalization
828
     *
829
     * @return string
830
     */
831
    protected function _getNormalizeCookieTarget() {
832
        return trim(Mage::getStoreConfig(
833
            'turpentine_vcl/normalization/cookie_target' ));
834
    }
835
836
    /**
837
     * Get the regex for cookie normalization
838
     *
839
     * @return string
840
     */
841
    protected function _getNormalizeCookieRegex() {
842
        return trim(Mage::getStoreConfig(
843
            'turpentine_vcl/normalization/cookie_regex' ));
844
    }
845
846
    /**
847
     * Get the allowed IPs when in maintenance mode
848
     *
849
     * @return string
850
     */
851 View Code Duplication
    protected function _vcl_sub_maintenance_allowed_ips() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
852
        if (( ! $this->_getDebugIps()) || ! Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')) {
853
            return false;
854
        }
855
856
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
857
            case 4.0:
858
                $tpl = <<<EOS
859
if (req.http.X-Forwarded-For) {
860
    if (req.http.X-Forwarded-For !~ "{{debug_ips}}") {
861
        return (synth(999, "Maintenance mode"));
862
    }
863
}
864
else {
865
    if (client.ip !~ debug_acl) {
866
        return (synth(999, "Maintenance mode"));
867
    }
868
}
869
870
EOS;
871
                break;
872
            default:
873
                $tpl = <<<EOS
874
if (req.http.X-Forwarded-For) {
875
    if(req.http.X-Forwarded-For !~ "{{debug_ips}}") {
876
        error 503;
877
    }
878
} else {
879
    if (client.ip !~ debug_acl) {
880
        error 503;
881
    }
882
}
883
EOS;
884
        }
885
886
        return $this->_formatTemplate($tpl, array(
887
            'debug_ips' => Mage::getStoreConfig('dev/restrict/allow_ips') ));
888
    }
889
890
    /**
891
     * Get the allowed IPs when in maintenance mode
892
     *
893
     * @return string
894
     */
895 View Code Duplication
    protected function _vcl_sub_synth()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
896
    {
897
        if (( ! $this->_getDebugIps()) || ! Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')) {
898
            return false;
899
        }
900
901
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
902
            case 4.0:
903
                $tpl = <<<EOS
904
sub vcl_synth {
905
    if (resp.status == 999) {
906
        set resp.status = 404;
907
        set resp.http.Content-Type = "text/html; charset=utf-8";
908
        synthetic({"{{vcl_synth_content}}"});
909
        return (deliver);
910
    }
911
    return (deliver);
912
}
913
914
EOS;
915
                break;
916
            default:
917
                $tpl = <<<EOS
918
sub vcl_error {
919
    set obj.http.Content-Type = "text/html; charset=utf-8";
920
    synthetic {"{{vcl_synth_content}}"};
921
    return (deliver);
922
}
923
EOS;
924
        }
925
926
        return $this->_formatTemplate($tpl, array(
927
            'vcl_synth_content' => Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')));
928
    }
929
930
931
932
    /**
933
     * Build the list of template variables to apply to the VCL template
934
     *
935
     * @return array
936
     */
937
    protected function _getTemplateVars() {
938
        $vars = array(
939
            'default_backend'   => $this->_getDefaultBackend(),
940
            'admin_backend'     => $this->_getAdminBackend(),
941
            'admin_frontname'   => $this->_getAdminFrontname(),
942
            'normalize_host_target' => $this->_getNormalizeHostTarget(),
943
            'url_base_regex'    => $this->getBaseUrlPathRegex(),
944
            'allowed_hosts_regex'   => $this->getAllowedHostsRegex(),
945
            'url_excludes'  => $this->_getUrlExcludes(),
946
            'get_param_excludes'    => $this->_getGetParamExcludes(),
947
            'get_param_ignored' => $this->_getIgnoreGetParameters(),
948
            'default_ttl'   => $this->_getDefaultTtl(),
949
            'enable_get_excludes'   => ($this->_getGetParamExcludes() ? 'true' : 'false'),
950
            'enable_get_ignored' => ($this->_getIgnoreGetParameters() ? 'true' : 'false'),
951
            'send_unmodified_url' => ($this->_sendUnModifiedUrlToBackend() ? 'true' : 'false'),
952
            'debug_headers' => $this->_getEnableDebugHeaders(),
953
            'grace_period'  => $this->_getGracePeriod(),
954
            'force_cache_static'    => $this->_getForceCacheStatic(),
955
            'simple_hash_static'    => $this->_getSimpleHashStatic(),
956
            'generate_session_expires'    => $this->_getGenerateSessionExpires(),
957
            'generate_session'    => $this->_getGenerateSession(),
958
            'generate_session_start'    => $this->_getGenerateSessionStart(),
959
            'generate_session_end'    => $this->_getGenerateSessionEnd(),
960
            'static_extensions' => $this->_getStaticExtensions(),
961
            'static_ttl'    => $this->_getStaticTtl(),
962
            'url_ttls'      => $this->_getUrlTtls(),
963
            'enable_caching'    => $this->_getEnableCaching(),
964
            'crawler_acl'   => $this->_vcl_acl('crawler_acl',
965
                $this->_getCrawlerIps()),
966
            'esi_cache_type_param'  =>
967
                Mage::helper('turpentine/esi')->getEsiCacheTypeParam(),
968
            'esi_method_param'  =>
969
                Mage::helper('turpentine/esi')->getEsiMethodParam(),
970
            'esi_ttl_param' => Mage::helper('turpentine/esi')->getEsiTtlParam(),
971
            'secret_handshake'  => Mage::helper('turpentine/varnish')
972
                ->getSecretHandshake(),
973
            'crawler_user_agent_regex'  => $this->_getCrawlerUserAgents(),
974
            // 'lru_factor'    => $this->_getLruFactor(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
975
            'debug_acl'     => $this->_vcl_acl('debug_acl',
976
                $this->_getDebugIps()),
977
            'custom_c_code' => file_get_contents(
978
                $this->_getVclTemplateFilename(self::VCL_CUSTOM_C_CODE_FILE) ),
979
            'esi_private_ttl'   => Mage::helper('turpentine/esi')
980
                ->getDefaultEsiTtl(),
981
        );
982
983
        if ((bool) Mage::getStoreConfig('turpentine_vcl/urls/bypass_cache_store_url')) {
984
            $vars['allowed_hosts'] = $this->_vcl_sub_allowed_hosts_regex();
985
        }
986
987
        if (Mage::getStoreConfig('turpentine_vcl/normalization/encoding')) {
988
            $vars['normalize_encoding'] = $this->_vcl_sub_normalize_encoding();
989
        }
990
        if (Mage::getStoreConfig('turpentine_vcl/normalization/user_agent')) {
991
            $vars['normalize_user_agent'] = $this->_vcl_sub_normalize_user_agent();
992
        }
993
        if (Mage::getStoreConfig('turpentine_vcl/normalization/host')) {
994
            $vars['normalize_host'] = $this->_vcl_sub_normalize_host();
995
        }
996
        if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_regex')) {
997
            $vars['normalize_cookie_regex'] = $this->_getNormalizeCookieRegex();
998
        }
999
        if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_target')) {
1000
            $vars['normalize_cookie_target'] = $this->_getNormalizeCookieTarget();
1001
        }
1002
1003
        if (Mage::getStoreConfig('turpentine_vcl/maintenance/enable')) {
1004
            // in vcl_recv set the allowed IPs otherwise load the vcl_error (v3)/vcl_synth (v4)
1005
            $vars['maintenance_allowed_ips'] = $this->_vcl_sub_maintenance_allowed_ips();
1006
            // set the vcl_error from Magento database
1007
            $vars['vcl_synth'] = $this->_vcl_sub_synth();
1008
        }
1009
1010
        $customIncludeFile = $this->_getCustomIncludeFilename();
1011
        if (is_readable($customIncludeFile)) {
1012
            $vars['custom_vcl_include'] = file_get_contents($customIncludeFile);
1013
        }
1014
1015
        return $vars;
1016
    }
1017
}
1018