Completed
Pull Request — devel (#1216)
by Aric
03:22
created

Abstract   D

Complexity

Total Complexity 114

Size/Duplication

Total Lines 1000
Duplicated Lines 12.9 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 17
Bugs 7 Features 6
Metric Value
wmc 114
c 17
b 7
f 6
lcom 1
cbo 2
dl 129
loc 1000
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
generate() 0 1 ?
A _getAdminFrontname() 0 12 3
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 _getDefaultTtl() 0 3 1
A _getDefaultBackend() 15 15 2
A _getAdminBackend() 15 15 2
A _getGracePeriod() 0 3 1
A _getEnableDebugHeaders() 0 4 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 _getForceCacheStatic() 0 4 2
A _getSimpleHashStatic() 0 4 2
A _getStaticExtensions() 0 4 1
A _getStaticTtl() 0 3 1
A _getUrlTtls() 0 21 4
A _getEnableCaching() 0 4 2
A _getDebugIps() 0 4 1
A _getCrawlerIps() 0 4 1
A _getCrawlerUserAgents() 0 6 1
A _getLruFactor() 0 3 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() 27 27 3
A _vcl_get_probe() 0 21 2
A _vcl_acl() 0 13 1
B _vcl_sub_normalize_user_agent() 0 25 1
A _vcl_sub_normalize_encoding() 0 16 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

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
            if(Mage::getStoreConfig('web/url/use_store')) {
177
                return Mage::getModel('core/store')->load(0)->getCode() . "/" . Mage::getStoreConfig('admin/url/custom_path');
178
            } else {
179
                return Mage::getStoreConfig('admin/url/custom_path');
180
            }
181
        } else {
182
            return (string) Mage::getConfig()->getNode(
183
                'admin/routers/adminhtml/args/frontName' );
184
        }
185
    }
186
187
    /**
188
     * Get the hostname for host normalization from Magento's base URL
189
     *
190
     * @return string
191
     */
192
    protected function _getNormalizeHostTarget() {
193
        $configHost = trim(Mage::getStoreConfig(
194
            'turpentine_vcl/normalization/host_target' ));
195
        if ($configHost) {
196
            return $configHost;
197
        } else {
198
            $baseUrl = parse_url(Mage::getBaseUrl());
199
            if (isset($baseUrl['port'])) {
200
                return sprintf('%s:%d', $baseUrl['host'], $baseUrl['port']);
201
            } else {
202
                return $baseUrl['host'];
203
            }
204
        }
205
    }
206
207
    /**
208
     * Get hosts as regex
209
     *
210
     * ex: base_url: example.com
211
     *     path_regex: (example.com|example.net)
212
     *
213
     * @return string
214
     */
215
    public function getAllowedHostsRegex() {
216
        $hosts = array();
217
        foreach (Mage::app()->getStores() as $store) {
218
            $hosts[] = parse_url($store->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB, false), PHP_URL_HOST);
219
        }
220
221
        $hosts = array_values(array_unique($hosts));
222
223
        $pattern = '('.implode('|', array_map("preg_quote", $hosts)).')';
224
        return $pattern;
225
    }
226
227
    /**
228
     * Get the Host normalization sub routine
229
     *
230
     * @return string
231
     */
232
    protected function _vcl_sub_allowed_hosts_regex() {
233
        $tpl = <<<EOS
234
# if host is not allowed in magento pass to backend
235
        if (req.http.host !~ "{{allowed_hosts_regex}}") {
236
            return (pass);
237
        }
238
EOS;
239
        return $this->_formatTemplate($tpl, array(
240
            'allowed_hosts_regex' => $this->getAllowedHostsRegex() ));
241
    }
242
243
    /**
244
     * Get the base url path regex
245
     *
246
     * ex: base_url: http://example.com/magento/
247
     *     path_regex: /magento/(?:(?:index|litespeed)\.php/)?
248
     *
249
     * @return string
250
     */
251
    public function getBaseUrlPathRegex() {
252
        $pattern = '^(%s)(?:(?:index|litespeed)\\.php/)?';
253
        return sprintf($pattern, implode('|',
254
            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...
255
                $this->_getBaseUrlPaths())));
256
    }
257
258
    /**
259
     * Get the path part of each store's base URL and static file URLs
260
     *
261
     * @return array
262
     */
263
    protected function _getBaseUrlPaths() {
264
        $paths = array();
265
        $linkTypes = array(Mage_Core_Model_Store::URL_TYPE_LINK,
266
                            Mage_Core_Model_Store::URL_TYPE_JS,
267
                            Mage_Core_Model_Store::URL_TYPE_SKIN,
268
                            Mage_Core_Model_Store::URL_TYPE_MEDIA);
269
        foreach (Mage::app()->getStores() as $store) {
270
            foreach ($linkTypes as $linkType) {
271
                $paths[] = parse_url($store->getBaseUrl($linkType, false),
272
                    PHP_URL_PATH);
273
                $paths[] = parse_url($store->getBaseUrl($linkType, true),
274
                    PHP_URL_PATH);
275
            }
276
        }
277
        $paths = array_unique($paths);
278
        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...
279
            'return strlen( $b ) - strlen( $a );'));
280
        return array_values($paths);
281
    }
282
283
    /**
284
     * Format the URL exclusions for insertion in a regex. Admin frontname and
285
     * API are automatically added.
286
     *
287
     * @return string
288
     */
289
    protected function _getUrlExcludes() {
290
        $urls = Mage::getStoreConfig('turpentine_vcl/urls/url_blacklist');
291
        return implode('|', array_merge(array($this->_getAdminFrontname(), 'api'),
292
            Mage::helper('turpentine/data')->cleanExplode(PHP_EOL, $urls)));
293
    }
294
295
    /**
296
     * Get the default cache TTL from Magento config
297
     *
298
     * @return string
299
     */
300
    protected function _getDefaultTtl() {
301
        return Mage::helper('turpentine/varnish')->getDefaultTtl();
302
    }
303
304
    /**
305
     * Get the default backend configuration string
306
     *
307
     * @return string
308
     */
309 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...
310
        $timeout = Mage::getStoreConfig('turpentine_vcl/backend/frontend_timeout');
311
        $default_options = array(
312
            'first_byte_timeout'    => $timeout.'s',
313
            'between_bytes_timeout' => $timeout.'s',
314
        );
315
        if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') {
316
            return $this->_vcl_director('default', $default_options);
317
        } else {
318
            return $this->_vcl_backend('default',
319
                Mage::getStoreConfig('turpentine_vcl/backend/backend_host'),
320
                Mage::getStoreConfig('turpentine_vcl/backend/backend_port'),
321
                $default_options);
322
        }
323
    }
324
325
    /**
326
     * Get the admin backend configuration string
327
     *
328
     * @return string
329
     */
330 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...
331
        $timeout = Mage::getStoreConfig('turpentine_vcl/backend/admin_timeout');
332
        $admin_options = array(
333
            'first_byte_timeout'    => $timeout.'s',
334
            'between_bytes_timeout' => $timeout.'s',
335
        );
336
        if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') {
337
            return $this->_vcl_director('admin', $admin_options);
338
        } else {
339
            return $this->_vcl_backend('admin',
340
                Mage::getStoreConfig('turpentine_vcl/backend/backend_host'),
341
                Mage::getStoreConfig('turpentine_vcl/backend/backend_port'),
342
                $admin_options);
343
        }
344
    }
345
346
    /**
347
     * Get the grace period for vcl_fetch
348
     *
349
     * This is curently hardcoded to 15 seconds, will be configurable at some
350
     * point
351
     *
352
     * @return string
353
     */
354
    protected function _getGracePeriod() {
355
        return Mage::getStoreConfig('turpentine_vcl/ttls/grace_period');
356
    }
357
358
    /**
359
     * Get whether debug headers should be enabled or not
360
     *
361
     * @return string
362
     */
363
    protected function _getEnableDebugHeaders() {
364
        return Mage::getStoreConfig('turpentine_varnish/general/varnish_debug')
365
            ? 'true' : 'false';
366
    }
367
368
    /**
369
     * Format the GET variable excludes for insertion in a regex
370
     *
371
     * @return string
372
     */
373
    protected function _getGetParamExcludes() {
374
        return implode('|', Mage::helper('turpentine/data')->cleanExplode(',',
375
            Mage::getStoreConfig('turpentine_vcl/params/get_params')));
376
    }
377
378
    protected function _getIgnoreGetParameters()
379
    {
380
        /** @var Nexcessnet_Turpentine_Helper_Data $helper */
381
        $helper = Mage::helper('turpentine');
382
        $ignoredParameters = $helper->cleanExplode(',', Mage::getStoreConfig('turpentine_vcl/params/ignore_get_params'));
383
        return implode('|', $ignoredParameters);
384
    }
385
386
    /**
387
     * @return boolean
388
     */
389
    protected function _sendUnModifiedUrlToBackend()
390
    {
391
        return Mage::getStoreConfigFlag('turpentine_vcl/params/transfer_unmodified_url');
392
    }
393
394
    /**
395
     * Get the Generate Session
396
     *
397
     * @return string
398
     */
399
    protected function _getGenerateSessionStart() {
400
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
401
            ? '/* -- REMOVED' : '';
402
    }
403
404
    /**
405
     * Get the Generate Session
406
     *
407
     * @return string
408
     */
409
    protected function _getGenerateSessionEnd() {
410
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
411
            ? '-- */' : '';
412
    }
413
414
415
    /**
416
     * Get the Generate Session
417
     *
418
     * @return string
419
     */
420
    protected function _getGenerateSession() {
421
        return Mage::getStoreConfigFlag('turpentine_varnish/general/vcl_fix')
422
            ? 'return (pipe);' : 'call generate_session;';
423
    }
424
425
426
    /**
427
     * Get the Generate Session Expires
428
     *
429
     * @return string
430
     */
431
    protected function _getGenerateSessionExpires() {
432
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
433
            ? '# call generate_session_expires' : 'call generate_session_expires;';
434
    }
435
436
    /**
437
     * Get the Force Static Caching option
438
     *
439
     * @return string
440
     */
441
    protected function _getForceCacheStatic() {
442
        return Mage::getStoreConfig('turpentine_vcl/static/force_static')
443
            ? 'true' : 'false';
444
    }
445
446
    /**
447
     * Get the Force Static Caching option
448
     *
449
     * @return string
450
     */
451
    protected function _getSimpleHashStatic() {
452
        return Mage::getStoreConfig('turpentine_vcl/static/simple_hash')
453
            ? 'true' : 'false';
454
    }
455
456
    /**
457
     * Format the list of static cache extensions
458
     *
459
     * @return string
460
     */
461
    protected function _getStaticExtensions() {
462
        return implode('|', Mage::helper('turpentine/data')->cleanExplode(',',
463
            Mage::getStoreConfig('turpentine_vcl/static/exts')));
464
    }
465
466
    /**
467
     * Get the static caching TTL
468
     *
469
     * @return string
470
     */
471
    protected function _getStaticTtl() {
472
        return Mage::getStoreConfig('turpentine_vcl/ttls/static_ttl');
473
    }
474
475
    /**
476
     * Format the by-url TTL value list
477
     *
478
     * @return string
479
     */
480
    protected function _getUrlTtls() {
481
        $str = array();
482
        $configTtls = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
483
            Mage::getStoreConfig('turpentine_vcl/ttls/url_ttls'));
484
        $ttls = array();
485
        foreach ($configTtls as $line) {
486
            $ttls[] = explode(',', trim($line));
487
        }
488
        foreach ($ttls as $ttl) {
489
            $str[] = sprintf('if (bereq.url ~ "%s%s") { set beresp.ttl = %ds; }',
490
                $this->getBaseUrlPathRegex(), $ttl[0], $ttl[1]);
491
        }
492
        $str = implode(' else ', $str);
493
        if ($str) {
494
            $str .= sprintf(' else { set beresp.ttl = %ds; }',
495
                $this->_getDefaultTtl());
496
        } else {
497
            $str = sprintf('set beresp.ttl = %ds;', $this->_getDefaultTtl());
498
        }
499
        return $str;
500
    }
501
502
    /**
503
     * Get the Enable Caching value
504
     *
505
     * @return string
506
     */
507
    protected function _getEnableCaching() {
508
        return Mage::helper('turpentine/varnish')->getVarnishEnabled() ?
509
            'true' : 'false';
510
    }
511
512
    /**
513
     * Get the list of allowed debug IPs
514
     *
515
     * @return array
516
     */
517
    protected function _getDebugIps() {
518
        return Mage::helper('turpentine/data')->cleanExplode(',',
519
            Mage::getStoreConfig('dev/restrict/allow_ips'));
520
    }
521
522
    /**
523
     * Get the list of crawler IPs
524
     *
525
     * @return array
526
     */
527
    protected function _getCrawlerIps() {
528
        return Mage::helper('turpentine/data')->cleanExplode(',',
529
            Mage::getStoreConfig('turpentine_vcl/backend/crawlers'));
530
    }
531
532
    /**
533
     * Get the regex formatted list of crawler user agents
534
     *
535
     * @return string
536
     */
537
    protected function _getCrawlerUserAgents() {
538
        return implode('|', Mage::helper('turpentine/data')
539
            ->cleanExplode(',',
540
                Mage::getStoreConfig(
541
                    'turpentine_vcl/backend/crawler_user_agents' )));
542
    }
543
544
    /**
545
     * Get the time to increase a cached objects TTL on cache hit (in seconds).
546
     *
547
     * This should be set very low since it gets added to every hit.
548
     *
549
     * @return string
550
     */
551
    protected function _getLruFactor() {
552
        return Mage::getStoreConfig('turpentine_vcl/ttls/lru_factor');
553
    }
554
555
    /**
556
     * Get the advanced session validation restrictions
557
     *
558
     * Note that if User-Agent Normalization is on then the normalized user-agent
559
     * is used for user-agent validation instead of the full user-agent
560
     *
561
     * @return string
562
     */
563
    protected function _getAdvancedSessionValidationTargets() {
564
        $validation = array();
565
        if (Mage::getStoreConfig('web/session/use_remote_addr')) {
566
            $validation[] = 'client.ip';
567
        }
568
        if (Mage::getStoreConfig('web/session/use_http_via')) {
569
            $validation[] = 'req.http.Via';
570
        }
571
        if (Mage::getStoreConfig('web/session/use_http_x_forwarded_for')) {
572
            $validation[] = 'req.http.X-Forwarded-For';
573
        }
574
        if (Mage::getStoreConfig(
575
                    'web/session/use_http_user_agent' ) &&
576
                ! Mage::getStoreConfig(
577
                    'turpentine_vcl/normalization/user_agent' )) {
578
            $validation[] = 'req.http.User-Agent';
579
        }
580
        return $validation;
581
    }
582
583
    /**
584
     * Remove empty and commented out lines from the generated VCL
585
     *
586
     * @param  string $dirtyVcl generated vcl
587
     * @return string
588
     */
589
    protected function _cleanVcl($dirtyVcl) {
590
        return implode(PHP_EOL,
591
            array_filter(
592
                Mage::helper('turpentine/data')
593
                    ->cleanExplode(PHP_EOL, $dirtyVcl),
594
                array($this, '_cleanVclHelper')
595
            )
596
        );
597
    }
598
599
    /**
600
     * Helper to filter out blank/commented lines for VCL cleaning
601
     *
602
     * @param  string $line
603
     * @return bool
604
     */
605
    protected function _cleanVclHelper($line) {
606
        return $line &&
607
            ((substr($line, 0, 1) != '#' &&
608
            substr($line, 0, 2) != '//') ||
609
            substr($line, 0, 8) == '#include');
610
    }
611
612
    /**
613
     * Format a VCL backend declaration
614
     *
615
     * @param  string $name    name of the backend
616
     * @param  string $host    backend host
617
     * @param  string $port    backend port
618
     * @param  array  $options options
619
     * @return string
620
     */
621
    protected function _vcl_backend($name, $host, $port, $options = array()) {
622
        $tpl = <<<EOS
623
backend {{name}} {
624
    .host = "{{host}}";
625
    .port = "{{port}}";
626
627
EOS;
628
        $vars = array(
629
            'host'  => $host,
630
            'port'  => $port,
631
            'name'  => $name,
632
        );
633
        $str = $this->_formatTemplate($tpl, $vars);
634
        foreach ($options as $key => $value) {
635
            $str .= sprintf('   .%s = %s;', $key, $value).PHP_EOL;
636
        }
637
        $str .= '}'.PHP_EOL;
638
        return $str;
639
    }
640
641
    /**
642
     * Format a VCL director declaration, for load balancing
643
     *
644
     * @param string $name           name of the director, also used to select config settings
645
     * @param array  $backendOptions options for each backend
646
     * @return string
647
     */
648
    protected function _vcl_director($name, $backendOptions) {
649
        $tpl = <<<EOS
650
director {{name}} round-robin {
651
{{backends}}
652
}
653
EOS;
654
        if ('admin' == $name && 'yes_admin' == Mage::getStoreConfig('turpentine_vcl/backend/load_balancing')) {
655
            $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
656
                Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes_admin'));
657
            $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url_admin');
658
        } else {
659
            $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
660
                Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes'));
661
            $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url');
662
        }
663
        $backends = '';
664
        foreach ($backendNodes as $backendNode) {
665
            $parts = explode(':', $backendNode, 2);
666
            $host = (empty($parts[0])) ? '127.0.0.1' : $parts[0];
667
            $port = (empty($parts[1])) ? '80' : $parts[1];
668
            $backends .= $this->_vcl_director_backend($host, $port, $probeUrl, $backendOptions);
669
        }
670
        $vars = array(
671
            'name' => $name,
672
            'backends' => $backends
673
        );
674
        return $this->_formatTemplate($tpl, $vars);
675
    }
676
677
    /**
678
     * Format a VCL backend declaration to put inside director
679
     *
680
     * @param string $host     backend host
681
     * @param string $port     backend port
682
     * @param string $probeUrl URL to check if backend is up
683
     * @param array  $options  extra options for backend
684
     * @return string
685
     */
686 View Code Duplication
    protected function _vcl_director_backend($host, $port, $probeUrl = '', $options = array()) {
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...
687
        $tpl = <<<EOS
688
    {
689
        .backend = {
690
            .host = "{{host}}";
691
            .port = "{{port}}";
692
{{probe}}
693
694
EOS;
695
        $vars = array(
696
            'host'  => $host,
697
            'port'  => $port,
698
            'probe' => ''
699
        );
700
        if ( ! empty($probeUrl)) {
701
            $vars['probe'] = $this->_vcl_get_probe($probeUrl);
702
        }
703
        $str = $this->_formatTemplate($tpl, $vars);
704
        foreach ($options as $key => $value) {
705
            $str .= sprintf('            .%s = %s;', $key, $value).PHP_EOL;
706
        }
707
        $str .= <<<EOS
708
        }
709
    }
710
EOS;
711
        return $str;
712
    }
713
714
    /**
715
     * Format a VCL probe declaration to put in backend which is in director
716
     *
717
     * @param string $probeUrl URL to check if backend is up
718
     * @return string
719
     */
720
    protected function _vcl_get_probe($probeUrl) {
721
        $urlParts = parse_url($probeUrl);
722
        if (empty($urlParts)) {
723
            // Malformed URL
724
            return '';
725
        } else {
726
            $tpl = <<<EOS
727
            .probe = {
728
                .request =
729
                    "GET {{probe_path}} HTTP/1.1"
730
                    "Host: {{probe_host}}"
731
                    "Connection: close";
732
            }
733
EOS;
734
            $vars = array(
735
                'probe_host' => $urlParts['host'],
736
                'probe_path' => $urlParts['path']
737
            );
738
            return $this->_formatTemplate($tpl, $vars);
739
        }
740
    }
741
742
    /**
743
     * Format a VCL ACL declaration
744
     *
745
     * @param  string $name  ACL name
746
     * @param  array  $hosts list of hosts to add to the ACL
747
     * @return string
748
     */
749
    protected function _vcl_acl($name, array $hosts) {
750
        $tpl = <<<EOS
751
acl {{name}} {
752
    {{hosts}}
753
}
754
EOS;
755
        $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...
756
        $vars = array(
757
            'name'  => $name,
758
            'hosts' => implode("\n    ", array_map($fmtHost, $hosts)),
759
        );
760
        return $this->_formatTemplate($tpl, $vars);
761
    }
762
763
    /**
764
     * Get the User-Agent normalization sub routine
765
     *
766
     * @return string
767
     */
768
    protected function _vcl_sub_normalize_user_agent() {
769
        /**
770
         * Mobile regex from
771
         * @link http://magebase.com/magento-tutorials/magento-design-exceptions-explained/
772
         */
773
        $tpl = <<<EOS
774
if (req.http.User-Agent ~ "iP(?:hone|ad|od)|BlackBerry|Palm|Googlebot-Mobile|Mobile|mobile|mobi|Windows Mobile|Safari Mobile|Android|Opera (?:Mini|Mobi)") {
775
        set req.http.X-Normalized-User-Agent = "mobile";
776
    } else if (req.http.User-Agent ~ "MSIE") {
777
        set req.http.X-Normalized-User-Agent = "msie";
778
    } else if (req.http.User-Agent ~ "Firefox") {
779
        set req.http.X-Normalized-User-Agent = "firefox";
780
    } else if (req.http.User-Agent ~ "Chrome") {
781
        set req.http.X-Normalized-User-Agent = "chrome";
782
    } else if (req.http.User-Agent ~ "Safari") {
783
        set req.http.X-Normalized-User-Agent = "safari";
784
    } else if (req.http.User-Agent ~ "Opera") {
785
        set req.http.X-Normalized-User-Agent = "opera";
786
    } else {
787
        set req.http.X-Normalized-User-Agent = "other";
788
    }
789
790
EOS;
791
        return $tpl;
792
    }
793
794
    /**
795
     * Get the Accept-Encoding normalization sub routine
796
     *
797
     * @return string
798
     */
799
    protected function _vcl_sub_normalize_encoding() {
800
        $tpl = <<<EOS
801
if (req.http.Accept-Encoding) {
802
        if (req.http.Accept-Encoding ~ "gzip") {
803
            set req.http.Accept-Encoding = "gzip";
804
        } else if (req.http.Accept-Encoding ~ "deflate") {
805
            set req.http.Accept-Encoding = "deflate";
806
        } else {
807
            # unknown algorithm
808
            unset req.http.Accept-Encoding;
809
        }
810
    }
811
812
EOS;
813
        return $tpl;
814
    }
815
816
    /**
817
     * Get the Host normalization sub routine
818
     *
819
     * @return string
820
     */
821
    protected function _vcl_sub_normalize_host() {
822
        $tpl = <<<EOS
823
set req.http.Host = "{{normalize_host_target}}";
824
825
EOS;
826
        return $this->_formatTemplate($tpl, array(
827
            'normalize_host_target' => $this->_getNormalizeHostTarget() ));
828
    }
829
830
    /**
831
     * Get the hostname for cookie normalization
832
     *
833
     * @return string
834
     */
835
    protected function _getNormalizeCookieTarget() {
836
        return trim(Mage::getStoreConfig(
837
            'turpentine_vcl/normalization/cookie_target' ));
838
    }
839
840
    /**
841
     * Get the regex for cookie normalization
842
     *
843
     * @return string
844
     */
845
    protected function _getNormalizeCookieRegex() {
846
        return trim(Mage::getStoreConfig(
847
            'turpentine_vcl/normalization/cookie_regex' ));
848
    }
849
850
    /**
851
     * Get the allowed IPs when in maintenance mode
852
     *
853
     * @return string
854
     */
855 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...
856
        if (( ! $this->_getDebugIps()) || ! Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')) {
857
            return false;
858
        }
859
860
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
861
            case 4.0:
862
                $tpl = <<<EOS
863
if (req.http.X-Forwarded-For) {
864
    if (req.http.X-Forwarded-For !~ "{{debug_ips}}") {
865
        return (synth(999, "Maintenance mode"));
866
    }
867
}
868
else {
869
    if (client.ip !~ debug_acl) {
870
        return (synth(999, "Maintenance mode"));
871
    }
872
}
873
874
EOS;
875
                break;
876
            default:
877
                $tpl = <<<EOS
878
if (req.http.X-Forwarded-For) {
879
    if(req.http.X-Forwarded-For !~ "{{debug_ips}}") {
880
        error 503;
881
    }
882
} else {
883
    if (client.ip !~ debug_acl) {
884
        error 503;
885
    }
886
}
887
EOS;
888
        }
889
890
        return $this->_formatTemplate($tpl, array(
891
            'debug_ips' => Mage::getStoreConfig('dev/restrict/allow_ips') ));
892
    }
893
894
    /**
895
     * Get the allowed IPs when in maintenance mode
896
     *
897
     * @return string
898
     */
899 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...
900
    {
901
        if (( ! $this->_getDebugIps()) || ! Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')) {
902
            return false;
903
        }
904
905
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
906
            case 4.0:
907
                $tpl = <<<EOS
908
sub vcl_synth {
909
    if (resp.status == 999) {
910
        set resp.status = 404;
911
        set resp.http.Content-Type = "text/html; charset=utf-8";
912
        synthetic({"{{vcl_synth_content}}"});
913
        return (deliver);
914
    }
915
    return (deliver);
916
}
917
918
EOS;
919
                break;
920
            default:
921
                $tpl = <<<EOS
922
sub vcl_error {
923
    set obj.http.Content-Type = "text/html; charset=utf-8";
924
    synthetic {"{{vcl_synth_content}}"};
925
    return (deliver);
926
}
927
EOS;
928
        }
929
930
        return $this->_formatTemplate($tpl, array(
931
            'vcl_synth_content' => Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')));
932
    }
933
934
935
936
    /**
937
     * Build the list of template variables to apply to the VCL template
938
     *
939
     * @return array
940
     */
941
    protected function _getTemplateVars() {
942
        $vars = array(
943
            'default_backend'   => $this->_getDefaultBackend(),
944
            'admin_backend'     => $this->_getAdminBackend(),
945
            'admin_frontname'   => $this->_getAdminFrontname(),
946
            'normalize_host_target' => $this->_getNormalizeHostTarget(),
947
            'url_base_regex'    => $this->getBaseUrlPathRegex(),
948
            'allowed_hosts_regex'   => $this->getAllowedHostsRegex(),
949
            'url_excludes'  => $this->_getUrlExcludes(),
950
            'get_param_excludes'    => $this->_getGetParamExcludes(),
951
            'get_param_ignored' => $this->_getIgnoreGetParameters(),
952
            'default_ttl'   => $this->_getDefaultTtl(),
953
            'enable_get_excludes'   => ($this->_getGetParamExcludes() ? 'true' : 'false'),
954
            'enable_get_ignored' => ($this->_getIgnoreGetParameters() ? 'true' : 'false'),
955
            'send_unmodified_url' => ($this->_sendUnModifiedUrlToBackend() ? 'true' : 'false'),
956
            'debug_headers' => $this->_getEnableDebugHeaders(),
957
            'grace_period'  => $this->_getGracePeriod(),
958
            'force_cache_static'    => $this->_getForceCacheStatic(),
959
            'simple_hash_static'    => $this->_getSimpleHashStatic(),
960
            'generate_session_expires'    => $this->_getGenerateSessionExpires(),
961
            'generate_session'    => $this->_getGenerateSession(),
962
            'generate_session_start'    => $this->_getGenerateSessionStart(),
963
            'generate_session_end'    => $this->_getGenerateSessionEnd(),
964
            'static_extensions' => $this->_getStaticExtensions(),
965
            'static_ttl'    => $this->_getStaticTtl(),
966
            'url_ttls'      => $this->_getUrlTtls(),
967
            'enable_caching'    => $this->_getEnableCaching(),
968
            'crawler_acl'   => $this->_vcl_acl('crawler_acl',
969
                $this->_getCrawlerIps()),
970
            'esi_cache_type_param'  =>
971
                Mage::helper('turpentine/esi')->getEsiCacheTypeParam(),
972
            'esi_method_param'  =>
973
                Mage::helper('turpentine/esi')->getEsiMethodParam(),
974
            'esi_ttl_param' => Mage::helper('turpentine/esi')->getEsiTtlParam(),
975
            'secret_handshake'  => Mage::helper('turpentine/varnish')
976
                ->getSecretHandshake(),
977
            'crawler_user_agent_regex'  => $this->_getCrawlerUserAgents(),
978
            // '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...
979
            'debug_acl'     => $this->_vcl_acl('debug_acl',
980
                $this->_getDebugIps()),
981
            'custom_c_code' => file_get_contents(
982
                $this->_getVclTemplateFilename(self::VCL_CUSTOM_C_CODE_FILE) ),
983
            'esi_private_ttl'   => Mage::helper('turpentine/esi')
984
                ->getDefaultEsiTtl(),
985
        );
986
987
        if ((bool) Mage::getStoreConfig('turpentine_vcl/urls/bypass_cache_store_url')) {
988
            $vars['allowed_hosts'] = $this->_vcl_sub_allowed_hosts_regex();
989
        }
990
991
        if (Mage::getStoreConfig('turpentine_vcl/normalization/encoding')) {
992
            $vars['normalize_encoding'] = $this->_vcl_sub_normalize_encoding();
993
        }
994
        if (Mage::getStoreConfig('turpentine_vcl/normalization/user_agent')) {
995
            $vars['normalize_user_agent'] = $this->_vcl_sub_normalize_user_agent();
996
        }
997
        if (Mage::getStoreConfig('turpentine_vcl/normalization/host')) {
998
            $vars['normalize_host'] = $this->_vcl_sub_normalize_host();
999
        }
1000
        if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_regex')) {
1001
            $vars['normalize_cookie_regex'] = $this->_getNormalizeCookieRegex();
1002
        }
1003
        if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_target')) {
1004
            $vars['normalize_cookie_target'] = $this->_getNormalizeCookieTarget();
1005
        }
1006
1007
        if (Mage::getStoreConfig('turpentine_vcl/maintenance/enable')) {
1008
            // in vcl_recv set the allowed IPs otherwise load the vcl_error (v3)/vcl_synth (v4)
1009
            $vars['maintenance_allowed_ips'] = $this->_vcl_sub_maintenance_allowed_ips();
1010
            // set the vcl_error from Magento database
1011
            $vars['vcl_synth'] = $this->_vcl_sub_synth();
1012
        }
1013
1014
        $customIncludeFile = $this->_getCustomIncludeFilename();
1015
        if (is_readable($customIncludeFile)) {
1016
            $vars['custom_vcl_include'] = file_get_contents($customIncludeFile);
1017
        }
1018
1019
        return $vars;
1020
    }
1021
}
1022