Completed
Push — master ( d208c8...974e64 )
by Nicolaas
01:57
created

Requirements_Backend_For_Webpack   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 306
Duplicated Lines 2.61 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 61
lcom 2
cbo 6
dl 8
loc 306
rs 6.018
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A set_files_to_ignore() 0 4 1
A set_copy_css_to_folder() 0 4 1
A set_copy_js_to_folder() 0 4 1
A set_urls_to_exclude() 0 4 1
A get_urls_to_exclude() 0 4 1
A set_force_update() 0 4 1
A get_force_update() 0 4 1
D includeInHTML() 0 103 35
A include_in_response() 0 10 2
A canSaveRequirements() 0 11 4
A themedRequest() 0 4 3
D moveFileToRequirementsFolder() 8 43 10

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 Requirements_Backend_For_Webpack 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 Requirements_Backend_For_Webpack, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 *
5
 *
6
 *
7
 */
8
class Requirements_Backend_For_Webpack extends Requirements_Backend
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...
9
{
10
11
12
    /**
13
     * e.g. /mysite/javascript/test.js
14
     * @ var array
15
     */
16
    private static $files_to_ignore = array();
17
18
    /**
19
     * we need this method because Requirements_Backend does not extend Object!
20
     * @param array $array
21
     */
22
    public static function set_files_to_ignore($array)
23
    {
24
        self::$files_to_ignore = $array;
25
    }
26
27
    /**
28
     * @ var string
29
     */
30
    private static $copy_css_to_folder = "source/css/requirements";
31
32
    /**
33
     * we need this method because Requirements_Backend does not extend Object!
34
     * @param string $string
35
     */
36
    public static function set_copy_css_to_folder($string)
37
    {
38
        self::$copy_css_to_folder = $string;
39
    }
40
41
    /**
42
     * @ var string
43
     */
44
    private static $copy_js_to_folder = "source/js/requirements";
45
46
    /**
47
     * we need this method because Requirements_Backend does not extend Object!
48
     * @param string $string
49
     */
50
    public static function set_copy_js_to_folder($string)
51
    {
52
        self::$copy_js_to_folder = $string;
53
    }
54
55
    /**
56
     * @ var string
57
     */
58
    private static $urls_to_exclude = array();
59
60
    /**
61
     * we need this method because Requirements_Backend does not extend Object!
62
     * @param array $array
0 ignored issues
show
Bug introduced by
There is no parameter named $array. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
63
     */
64
    public static function set_urls_to_exclude($a)
65
    {
66
        self::$urls_to_exclude = $a;
67
    }
68
    public static function get_urls_to_exclude()
69
    {
70
        return self::$urls_to_exclude;
71
    }
72
73
    /**
74
     * @ var bool
75
     */
76
    private static $force_update = true;
77
    public static function set_force_update($bool)
78
    {
79
        self::$force_update = $bool;
80
    }
81
    public static function get_force_update($bool)
0 ignored issues
show
Unused Code introduced by
The parameter $bool is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
82
    {
83
        return self::$force_update;
84
    }
85
86
    /**
87
     * Whether to add caching query params to the requests for file-based requirements.
88
     * Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
89
     * filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
90
     * while automatically busting this cache every time the file is changed.
91
     *
92
     * @var bool
93
     */
94
    protected $suffix_requirements = false;
95
96
    /**
97
     * Whether to combine CSS and JavaScript files
98
     *
99
     * @var bool
100
     */
101
    protected $combined_files_enabled = false;
102
103
104
    /**
105
     * Force the JavaScript to the bottom of the page, even if there's a script tag in the body already
106
     *
107
     * @var boolean
108
     */
109
    protected $force_js_to_bottom = true;
110
111
    /**
112
     * Update the given HTML content with the appropriate include tags for the registered
113
     * requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
114
     * including a head and body tag.
115
     *
116
     * @param string $templateFile No longer used, only retained for compatibility
117
     * @param string $content      HTML content that has already been parsed from the $templateFile
118
     *                             through {@link SSViewer}
119
     * @return string HTML content augmented with the requirements tags
120
     */
121
    public function includeInHTML($templateFile, $content)
122
    {
123
        if ($this->themedRequest()) {
124
125
            //=====================================================================
126
            // start copy-ish from parent class
127
128
            $hasHead = (strpos($content, '</head>') !== false || strpos($content, '</head ') !== false) ? true : false;
129
            $hasRequirements = ($this->css || $this->javascript || $this->customCSS || $this->customScript || $this->customHeadTags) ? true: false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->css of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->javascript of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->customCSS of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->customScript of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $this->customHeadTags of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
130
            if ($hasHead && $hasRequirements) {
131
                $requirements = '';
132
                $jsRequirements = '';
133
                $requirementsCSSFiles = array();
134
                $requirementsJSFiles = array();
135
136
                // Combine files - updates $this->javascript and $this->css
137
                $this->process_combined_files();
138
                $isDev = Director::isDev();
139
                foreach (array_diff_key($this->javascript, $this->blocked) as $file => $dummy) {
140
                    $ignore = in_array($file, self::$files_to_ignore) ? true : false;
141
                    if($isDev || $ignore) {
142
                        $path = Convert::raw2xml($this->path_for_file($file));
0 ignored issues
show
Bug introduced by
It seems like $this->path_for_file($file) targeting Requirements_Backend::path_for_file() can also be of type boolean; however, Convert::raw2xml() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
143
                        if ($path) {
144
                            if ($isDev) {
145
                                $requirementsJSFiles[$path] = $path;
146
                            }
147
                            if(in_array($file, self::$files_to_ignore)) {
148
                                $jsRequirements .= "<script type=\"text/javascript\" src=\"$path\"></script>\n";
149
                            }
150
                        }
151
                    }
152
                }
153
154
                // Add all inline JavaScript *after* including external files they might rely on
155
                if ($this->customScript) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customScript of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
156
                    foreach (array_diff_key($this->customScript, $this->blocked) as $script) {
157
                        $jsRequirements .= "<script type=\"text/javascript\">\n//<![CDATA[\n";
158
                        $jsRequirements .= "$script\n";
159
                        $jsRequirements .= "\n//]]>\n</script>\n";
160
                    }
161
                }
162
163
                foreach (array_diff_key($this->css, $this->blocked) as $file => $params) {
164
                    $ignore = in_array($file, self::$files_to_ignore) ? true : false;
165
                    if($isDev || $ignore) {
166
                        $path = Convert::raw2xml($this->path_for_file($file));
0 ignored issues
show
Bug introduced by
It seems like $this->path_for_file($file) targeting Requirements_Backend::path_for_file() can also be of type boolean; however, Convert::raw2xml() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
167
                        if ($path) {
168
                            $media = (isset($params['media']) && !empty($params['media'])) ? $params['media'] : "";
169
                            if ($isDev) {
170
                                $requirementsCSSFiles[$path."_".$media] = $path;
171
                            }
172
                            if($ignore) {
173
                                if($media !== '') {
174
                                    $media = " media=\"{$media}\"";
175
                                }
176
                                $requirements .= "<link rel=\"stylesheet\" type=\"text/css\"{$media} href=\"$path\" />\n";
177
                            }
178
                        }
179
                    }
180
                }
181
182
                foreach (array_diff_key($this->customCSS, $this->blocked) as $css) {
183
                    $requirements .= "<style type=\"text/css\">\n$css\n</style>\n";
184
                }
185
186
                foreach (array_diff_key($this->customHeadTags, $this->blocked) as $customHeadTag) {
187
                    $requirements .= "$customHeadTag\n";
188
                }
189
190
                // Remove all newlines from code to preserve layout
191
                $jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
192
193
                // Forcefully put the scripts at the bottom of the body instead of before the first
194
                // script tag.
195
                $content = preg_replace("/(<\/body[^>]*>)/i", $jsRequirements . "\\1", $content);
196
197
                // Put CSS at the bottom of the head
198
                $content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
199
200
                //end copy-ish from parent class
201
                //=====================================================================
202
203
                //copy files ...
204
                if ($this->canSaveRequirements()) {
205
206
                    //css
207
                    $cssFolder = '/themes/'.SSViewer::current_theme().'/'.self::$copy_css_to_folder;
0 ignored issues
show
Deprecated Code introduced by
The method SSViewer::current_theme() has been deprecated with message: 4.0 Use the "SSViewer.theme" config setting instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
208
209
                    foreach ($requirementsCSSFiles as $cssFile) {
210
                        $this->moveFileToRequirementsFolder($cssFile, $cssFolder);
211
                    }
212
                    //js
213
                    $jsFolder = '/themes/'.SSViewer::current_theme().'/'.self::$copy_js_to_folder;
0 ignored issues
show
Deprecated Code introduced by
The method SSViewer::current_theme() has been deprecated with message: 4.0 Use the "SSViewer.theme" config setting instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
214
                    foreach ($requirementsJSFiles as $jsFile) {
215
                        $this->moveFileToRequirementsFolder($jsFile, $jsFolder);
216
                    }
217
                }
218
            }
219
            return $content;
220
        } else {
221
            return parent::includeInHTML($templateFile, $content);
222
        }
223
    }
224
225
    /**
226
     * Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
227
     * HTTP Response
228
     *
229
     * @param SS_HTTPResponse $response
230
     */
231
    public function include_in_response(SS_HTTPResponse $response)
232
    {
233
        if ($this->themedRequest()) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
234
            //do nothing
235
        } else {
236
            return parent::include_in_response($response);
237
        }
238
        //$this->process_combined_files();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% 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...
239
        //do nothing ...
240
    }
241
242
    /**
243
     *
244
     *
245
     *
246
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
247
     */
248
    protected function canSaveRequirements()
249
    {
250
        if (Director::isDev()) {
251
            if ($this->themedRequest()) {
252
                $socket = @fsockopen('localhost', 3000, $errno, $errstr, 1);
253
                if ($socket) {
254
                    return true;
255
                }
256
            }
257
        }
258
    }
259
260
    /**
261
     *
262
     *
263
     * @return bool
264
     */
265
    protected function themedRequest()
266
    {
267
        return Config::inst()->get('SSViewer', 'theme') && Config::inst()->get('SSViewer', 'theme_enabled') ? true : false;
268
    }
269
270
    protected function moveFileToRequirementsFolder($fileLocation, $folderLocation)
0 ignored issues
show
Coding Style introduced by
moveFileToRequirementsFolder uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
271
    {
272
        $base = Director::baseFolder();
273
        $folderLocationWithBase = $base . $folderLocation;
274
        Filesystem::makeFolder($folderLocationWithBase);
275
        if (!file_exists($folderLocationWithBase)) {
276
            user_error('Please update Requirements_Backend_For_Webpack for the right folder or create '.$folderLocationWithBase);
277
        }
278
        if (strpos($fileLocation, "//") !== false) {
279
            $logFile = $folderLocationWithBase."/EXTERNALS.log";
280
            $lines = array();
281
            $line = $_SERVER['REQUEST_URI']." | ".$fileLocation."\n";
282
            if (file_exists($logFile)) {
283
                $lines = file($logFile);
284
            }
285 View Code Duplication
            if (! in_array($fileLocation, $lines)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
286
                $handle = fopen($logFile, 'a');
287
                fwrite($handle, $line);
288
            }
289
        } else {
290
            $from = $fileLocation;
291
            $to = basename($fileLocation);
292
            $line = '"cp .'.$from.' .'.$folderLocation.$to.'",'."\n";
293
            $from = $base.$from;
294
            $to = $folderLocationWithBase . '/' . $to;
295
            $logFile = $folderLocationWithBase."/TO.INCLUDE.IN.COMPOSER.log";
296
            $lines = array();
297
            if (file_exists($logFile)) {
298
                $lines = file($logFile);
299
            }
300 View Code Duplication
            if (! in_array($line, $lines)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
301
                $handle = fopen($logFile, 'a');
302
                fwrite($handle, $line);
303
            }
304
            if (in_array($fileLocation, self::$files_to_ignore)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
305
                //to be completed ...
306
            } else {
307
                if (! file_exists($to) || self::$force_update) {
308
                    copy($from, $to);
309
                }
310
            }
311
        }
312
    }
313
}
314