Completed
Push — develop ( cb7ecf...5e631f )
by Dmytro
17s
created

ExportSite::run()   F

Complexity

Conditions 19
Paths 254

Size

Total Lines 105
Code Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 76
nc 254
nop 1
dl 0
loc 105
rs 3.6524
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php namespace EvolutionCMS\Legacy;
2
3
use EvolutionCMS\Interfaces\ExportSiteInerface;
4
5
class ExportSite implements ExportSiteInerface
0 ignored issues
show
Coding Style introduced by
The property $generate_mode is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $ignore_ids is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $repl_before is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $repl_after is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
6
{
7
    /**
8
     * @var string
9
     */
10
    public $targetDir;
11
    /**
12
     * @var string
13
     */
14
    public $generate_mode;
15
    /**
16
     * @var
17
     */
18
    public $total;
19
    /**
20
     * @var int
21
     */
22
    public $count;
23
    /**
24
     * @var
25
     */
26
    public $ignore_ids;
27
    /**
28
     * @var array|mixed
29
     */
30
    public $exportstart;
31
    /**
32
     * @var
33
     */
34
    public $repl_before;
35
    /**
36
     * @var
37
     */
38
    public $repl_after;
39
    /**
40
     * @var array
41
     */
42
    public $output = array();
43
    /**
44
     * @var int
45
     */
46
    public $dirCheckCount = 0;
47
48
    /**
49
     * EXPORT_SITE constructor.
50
     */
51
    public function __construct()
52
    {
53
        $modx = evolutionCMS();
54
55
        if (!defined('MODX_BASE_PATH')) {
56
            return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
57
        }
58
        $this->exportstart = $this->get_mtime();
59
        $this->count = 0;
60
        $this->setUrlMode();
61
        $this->generate_mode = 'crawl';
62
        $this->targetDir = $modx->config['base_path'] . 'temp/export';
63
        if (!isset($this->total)) {
64
            $this->getTotal();
65
        }
66
    }
67
68
    /**
69
     * @param string $dir
70
     */
71
    public function setExportDir($dir)
72
    {
73
        $dir = str_replace('\\', '/', $dir);
74
        $dir = rtrim($dir, '/');
75
        $this->targetDir = $dir;
76
    }
77
78
    /**
79
     * @return int
80
     */
81
    public function get_mtime()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
82
    {
83
        $mtime = microtime();
84
        $mtime = explode(' ', $mtime);
85
        $mtime = $mtime[1] + $mtime[0];
86
87
        return $mtime;
88
    }
89
90
    /**
91
     * @return void
92
     */
93
    public function setUrlMode()
94
    {
95
        $modx = evolutionCMS();
96
97
        if ($modx->config['friendly_urls'] == 0) {
98
            $modx->config['friendly_urls'] = 1;
99
            $modx->config['use_alias_path'] = 1;
100
            $modx->clearCache('full');
101
        }
102
        $modx->config['make_folders'] = '1';
103
    }
104
105
    /**
106
     * @param string|array $ignore_ids
107
     * @param string|int|bool $noncache
108
     * @return int
109
     */
110
    public function getTotal($ignore_ids = '', $noncache = '0')
0 ignored issues
show
Coding Style Naming introduced by
The parameter $ignore_ids is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
111
    {
112
        $modx = evolutionCMS();
113
        $tbl_site_content = $modx->getFullTableName('site_content');
114
115
        $ignore_ids = array_filter(array_map('intval', explode(',', $ignore_ids)));
116
        if (count($ignore_ids) > 0) {
117
            $ignore_ids = "AND NOT id IN ('" . implode("','", $ignore_ids) . "')";
118
        } else {
119
            $ignore_ids = '';
120
        }
121
122
        $this->ignore_ids = $ignore_ids;
123
124
        $noncache = ($noncache == 1) ? '' : 'AND cacheable=1';
125
        $where = "deleted=0 AND ((published=1 AND type='document') OR (isfolder=1)) {$noncache} {$ignore_ids}";
126
        $rs = $modx->getDatabase()->select('count(id)', $tbl_site_content, $where);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
127
        $this->total = (int)$modx->getDatabase()->getValue($rs);
0 ignored issues
show
Bug introduced by
It seems like $rs defined by $modx->getDatabase()->se...l_site_content, $where) on line 126 can also be of type boolean; however, EvolutionCMS\Database::getValue() does only seem to accept object<mysqli_result>|string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
128
129
        return $this->total;
130
    }
131
132
    /**
133
     * @param string $directory
134
     * @return bool
135
     */
136
    public function removeDirectoryAll($directory = '')
137
    {
138
        $rs = false;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
139
        if (empty($directory)) {
140
            $directory = $this->targetDir;
141
        }
142
        $directory = rtrim($directory, '/');
143
        // if the path is not valid or is not a directory ...
144
        if (empty($directory)) {
145
            return false;
146
        }
147
        if (strpos($directory, MODX_BASE_PATH) === false) {
148
            return $rs;
149
        }
150
151
        if (!is_dir($directory)) {
152
            return $rs;
153
        } elseif (!is_readable($directory)) {
154
            return $rs;
155
        } else {
156
            $files = glob($directory . '/*');
157
            if (!empty($files)) {
158
                foreach ($files as $path) {
159
                    $rs = is_dir($path) ? $this->removeDirectoryAll($path) : unlink($path);
160
                }
161
            }
162
        }
163
        if ($directory !== $this->targetDir) {
164
            $rs = rmdir($directory);
165
        }
166
167
        return $rs;
168
    }
169
170
    /**
171
     * @param int $docid
172
     * @param string $filepath
173
     * @return string
174
     */
175
    public function makeFile($docid, $filepath)
176
    {
177
        $modx = evolutionCMS(); global $_lang;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
178
        $file_permission = octdec($modx->config['new_file_permissions']);
179
        if ($this->generate_mode === 'direct') {
180
            $back_lang = $_lang;
181
            $src = $modx->executeParser($docid);
0 ignored issues
show
Unused Code introduced by
The call to DocumentParser::executeParser() has too many arguments starting with $docid.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
Bug introduced by
Are you sure the assignment to $src is correct as $modx->executeParser($docid) (which targets EvolutionCMS\Core::executeParser()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
182
183
            $_lang = $back_lang;
184
        } else {
185
            $src = $this->curl_get_contents(MODX_SITE_URL . "index.php?id={$docid}");
186
        }
187
188
189
        if ($src !== false) {
190
            if ($this->repl_before !== $this->repl_after) {
191
                $src = str_replace($this->repl_before, $this->repl_after, $src);
192
            }
193
            $result = file_put_contents($filepath, $src);
194
            if ($result !== false) {
195
                @chmod($filepath, $file_permission);
196
            }
197
198
            if ($result !== false) {
199
                return 'success';
200
            } else {
201
                return 'failed_no_write';
202
            }
203
        } else {
204
            return 'failed_no_retrieve';
205
        }
206
    }
207
208
    /**
209
     * @param int $docid
210
     * @param string $alias
211
     * @param string $prefix
212
     * @param string $suffix
213
     * @return string
214
     */
215
    public function getFileName($docid, $alias = '', $prefix, $suffix)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
216
    {
217
        $modx = evolutionCMS();
218
219
        if ($alias === '') {
220
            $filename = $prefix . $docid . $suffix;
221
        } else {
222
            if ($modx->config['suffix_mode'] === '1' && strpos($alias, '.') !== false) {
223
                $suffix = '';
224
            }
225
            $filename = $prefix . $alias . $suffix;
226
        }
227
228
        return $filename;
229
    }
230
231
    /**
232
     * @param int $parent
233
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

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...
234
     */
235
    public function run($parent = 0)
0 ignored issues
show
Coding Style introduced by
run uses the super-global variable $_POST 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...
236
    {
237
        global $_lang;
238
        $modx = evolutionCMS();
239
240
        $tbl_site_content = $modx->getFullTableName('site_content');
241
242
        $ignore_ids = $this->ignore_ids;
243
        $dirpath = $this->targetDir . '/';
244
245
        $prefix = $modx->config['friendly_url_prefix'];
246
        $suffix = $modx->config['friendly_url_suffix'];
247
248
        $tpl = ' <span class="[+status+]">[+msg1+]</span> [+msg2+]</span>';
249
        $ph = array();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
250
251
        $ph['status'] = 'fail';
252
        $ph['msg1'] = $_lang['export_site_failed'];
253
        $ph['msg2'] = $_lang["export_site_failed_no_write"] . ' - ' . $dirpath;
254
        $msg_failed_no_write = $this->parsePlaceholder($tpl, $ph);
255
256
        $ph['msg2'] = $_lang["export_site_failed_no_retrieve"];
257
        $msg_failed_no_retrieve = $this->parsePlaceholder($tpl, $ph);
258
259
        $ph['status'] = 'success';
260
        $ph['msg1'] = $_lang['export_site_success'];
261
        $ph['msg2'] = '';
262
        $msg_success = $this->parsePlaceholder($tpl, $ph);
263
264
        $ph['msg2'] = $_lang['export_site_success_skip_doc'];
265
        $msg_success_skip_doc = $this->parsePlaceholder($tpl, $ph);
266
267
        $ph['msg2'] = $_lang['export_site_success_skip_dir'];
268
        $msg_success_skip_dir = $this->parsePlaceholder($tpl, $ph);
269
270
        $fields = "id, alias, pagetitle, isfolder, (content = '' AND template = 0) AS wasNull, published";
271
        $noncache = $_POST['includenoncache'] == 1 ? '' : 'AND cacheable=1';
272
        $where = "parent = '{$parent}' AND deleted=0 AND ((published=1 AND type='document') OR (isfolder=1)) {$noncache} {$ignore_ids}";
273
        $rs = $modx->getDatabase()->select($fields, $tbl_site_content, $where);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $rs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
274
275
        $ph = array();
276
        $ph['total'] = $this->total;
277
        $folder_permission = octdec($modx->config['new_folder_permissions']);
278
        while ($row = $modx->getDatabase()->getRow($rs)) {
0 ignored issues
show
Bug introduced by
It seems like $rs defined by $modx->getDatabase()->se...l_site_content, $where) on line 273 can also be of type boolean; however, EvolutionCMS\Database::getRow() does only seem to accept object<mysqli_result>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
279
            $this->count++;
280
            $filename = '';
281
            $row['count'] = $this->count;
282
            $row['url'] = $modx->makeUrl($row['id']);
283
284
            if (!$row['wasNull']) { // needs writing a document
285
                $docname = $this->getFileName($row['id'], $row['alias'], $prefix, $suffix);
286
                $filename = $dirpath . $docname;
287
                if (!is_file($filename)) {
288
                    if ($row['published'] === '1') {
289
                        $status = $this->makeFile($row['id'], $filename);
290
                        switch ($status) {
291
                            case 'failed_no_write'   :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
292
                                $row['status'] = $msg_failed_no_write;
293
                                break;
294
                            case 'failed_no_retrieve':
295
                                $row['status'] = $msg_failed_no_retrieve;
296
                                break;
297
                            default:
298
                                $row['status'] = $msg_success;
299
                        }
300
                    } else {
301
                        $row['status'] = $msg_failed_no_retrieve;
302
                    }
303
                } else {
304
                    $row['status'] = $msg_success_skip_doc;
305
                }
306
                $this->output[] = $this->parsePlaceholder($_lang['export_site_exporting_document'], $row);
307
            } else {
308
                $row['status'] = $msg_success_skip_dir;
309
                $this->output[] = $this->parsePlaceholder($_lang['export_site_exporting_document'], $row);
310
            }
311
            if ($row['isfolder'] === '1' && ($modx->config['suffix_mode'] !== '1' || strpos($row['alias'],
312
                        '.') === false)) { // needs making a folder
313
                $end_dir = ($row['alias'] !== '') ? $row['alias'] : $row['id'];
314
                $dir_path = $dirpath . $end_dir;
315
                if (strpos($dir_path, MODX_BASE_PATH) === false) {
316
                    return false;
317
                }
318
                if (!is_dir($dir_path)) {
319
                    if (is_file($dir_path)) {
320
                        @unlink($dir_path);
321
                    }
322
                    mkdir($dir_path);
323
                    @chmod($dir_path, $folder_permission);
324
325
                }
326
327
328
                if ($modx->config['make_folders'] === '1' && $row['published'] === '1') {
329
                    if (!empty($filename) && is_file($filename)) {
330
                        rename($filename, $dir_path . '/index.html');
331
                    }
332
                }
333
                $this->targetDir = $dir_path;
334
                $this->run($row['id']);
335
            }
336
        }
337
338
        return implode("\n", $this->output);
339
    }
340
341
    /**
342
     * @param string $url
343
     * @param int $timeout
344
     * @return string
345
     */
346
    public function curl_get_contents($url, $timeout = 30)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
347
    {
348
        if (!function_exists('curl_init')) {
349
            return @file_get_contents($url);
350
        }
351
352
        $ch = curl_init();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ch. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
353
        curl_setopt($ch, CURLOPT_URL, $url);
354
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);    // 0 = DO NOT VERIFY AUTHENTICITY OF SSL-CERT
355
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);    // 2 = CERT MUST INDICATE BEING CONNECTED TO RIGHT SERVER
356
        curl_setopt($ch, CURLOPT_HEADER, false);
357
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
358
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
359
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
360
        $result = curl_exec($ch);
361
        curl_close($ch);
362
363
        return $result;
364
    }
365
366
    public function parsePlaceholder($tpl, $ph = array())
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $ph. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
367
    {
368
        foreach ($ph as $k => $v) {
369
            $k = "[+{$k}+]";
370
            $tpl = str_replace($k, $v, $tpl);
371
        }
372
373
        return $tpl;
374
    }
375
376
}
377