ExportSite::run()   F
last analyzed

Complexity

Conditions 18
Paths 127

Size

Total Lines 103

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
nc 127
nop 1
dl 0
loc 103
rs 3.7133
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
use EvolutionCMS\Models;
5
use Illuminate\Database\Eloquent;
6
use UrlProcessor;
7
8
class ExportSite implements ExportSiteInerface
9
{
10
    /**
11
     * @var string
12
     */
13
    public $targetDir;
14
    /**
15
     * @var string
16
     */
17
    public $generate_mode;
18
    /**
19
     * @var
20
     */
21
    public $total;
22
    /**
23
     * @var int
24
     */
25
    public $count;
26
    /**
27
     * @var array
28
     */
29
    public $ignore_ids;
30
    /**
31
     * @var array|mixed
32
     */
33
    public $exportstart;
34
    /**
35
     * @var
36
     */
37
    public $repl_before;
38
    /**
39
     * @var
40
     */
41
    public $repl_after;
42
    /**
43
     * @var array
44
     */
45
    public $output = array();
46
    /**
47
     * @var int
48
     */
49
    public $dirCheckCount = 0;
50
51
    /**
52
     * EXPORT_SITE constructor.
53
     */
54
    public function __construct()
55
    {
56
        $modx = evolutionCMS();
0 ignored issues
show
Unused Code introduced by
$modx is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
57
58
        if (!defined('MODX_BASE_PATH')) {
59
            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...
60
        }
61
        $this->exportstart = $this->get_mtime();
62
        $this->count = 0;
63
        $this->setUrlMode();
64
        $this->generate_mode = 'crawl';
65
        $this->targetDir = MODX_BASE_PATH . 'temp/export';
66
        if (!isset($this->total)) {
67
            $this->getTotal();
68
        }
69
    }
70
71
    /**
72
     * @param string $dir
73
     */
74
    public function setExportDir($dir)
75
    {
76
        $dir = str_replace('\\', '/', $dir);
77
        $dir = rtrim($dir, '/');
78
        $this->targetDir = $dir;
79
    }
80
81
    /**
82
     * @return int
83
     */
84
    public function get_mtime()
85
    {
86
        $mtime = microtime();
87
        $mtime = explode(' ', $mtime);
88
        $mtime = $mtime[1] + $mtime[0];
89
90
        return $mtime;
91
    }
92
93
    /**
94
     * @return void
95
     */
96
    public function setUrlMode()
97
    {
98
        $modx = evolutionCMS();
99
100
        if ($modx->getConfig('friendly_urls') === false) {
101
            $modx->setConfig('friendly_urls', true);
102
            $modx->setConfig('use_alias_path', true);
103
            $modx->clearCache('full');
104
        }
105
        $modx->setConfig('make_folders', true);
106
    }
107
108
    /**
109
     * @param string|array $ignoreIds
110
     * @param string|int|bool $noncache
111
     * @return int
112
     */
113
    public function getTotal($ignoreIds = '', $noncache = false)
114
    {
115
        $this->ignore_ids = $this->cleanIDs($ignoreIds);
116
        $this->total = $this->makeQuery($this->ignore_ids, $noncache)->count();
0 ignored issues
show
Documentation Bug introduced by
The method count does not exist on object<Illuminate\Database\Eloquent\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
117
        return $this->total;
118
    }
119
120
    /**
121
     * @param string|array $ids
122
     */
123
    protected function cleanIDs($ids)
124
    {
125
        return array_filter(
126
            array_map('intval', \is_array($ids) ? $ids : explode(',', $ids))
127
        );
128
    }
129
130
    /**
131
     * @param string|array $ignoreIds
132
     * @param bool|int|string $noncache
133
     * @return Eloquent\Builder
134
     */
135
    protected function makeQuery($ignoreIds = '', $noncache = false) : Eloquent\Builder
136
    {
137
        /** @var Eloquent\Builder $query */
138
        $query = Models\SiteContent::where('deleted', '=', 0)
139
            ->where(function (Eloquent\Builder $query) {
140
                $query->where(function (Eloquent\Builder $query) {
141
                    $query->where('published', '=', 1)
142
                        ->where('type', '=', 'document');
143
                })->orWhere('isfolder', '=', 1);
144
            });
145
        if ((bool)$noncache === false) {
146
            $query->where('cacheable', '=', 1);
147
        }
148
149
        $ignoreIds = $this->cleanIDs($ignoreIds);
150
        if (!empty($ignoreIds)) {
151
            $query->whereNotIn('id', $ignoreIds);
0 ignored issues
show
Documentation Bug introduced by
The method whereNotIn does not exist on object<Illuminate\Database\Eloquent\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
152
        }
153
154
        return $query;
155
    }
156
    /**
157
     * @param string $directory
158
     * @return bool
159
     */
160
    public function removeDirectoryAll($directory = '')
161
    {
162
        $rs = false;
163
        if (empty($directory)) {
164
            $directory = $this->targetDir;
165
        }
166
        $directory = rtrim($directory, '/');
167
        // if the path is not valid or is not a directory ...
168
        if (empty($directory)) {
169
            return false;
170
        }
171
        if (strpos($directory, MODX_BASE_PATH) === false) {
172
            return $rs;
173
        }
174
175
        if (!is_dir($directory)) {
176
            return $rs;
177
        } elseif (!is_readable($directory)) {
178
            return $rs;
179
        } else {
180
            $files = glob($directory . '/*');
181
            if (!empty($files)) {
182
                foreach ($files as $path) {
183
                    $rs = is_dir($path) ? $this->removeDirectoryAll($path) : unlink($path);
184
                }
185
            }
186
        }
187
        if ($directory !== $this->targetDir) {
188
            $rs = rmdir($directory);
189
        }
190
191
        return $rs;
192
    }
193
194
    /**
195
     * @param int $docid
196
     * @param string $filepath
197
     * @return string
198
     */
199
    public function makeFile($docid, $filepath)
200
    {
201
        $modx = evolutionCMS(); global $_lang;
202
        $file_permission = octdec($modx->getConfig('new_file_permissions'));
203
        if ($this->generate_mode === 'direct') {
204
            $back_lang = $_lang;
205
            $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...
206
207
            $_lang = $back_lang;
208
        } else {
209
            $src = $this->curl_get_contents(MODX_SITE_URL . "index.php?id={$docid}");
210
        }
211
212
213
        if ($src !== false) {
214
            if ($this->repl_before !== $this->repl_after) {
215
                $src = str_replace($this->repl_before, $this->repl_after, $src);
216
            }
217
            $result = file_put_contents($filepath, $src);
218
            if ($result !== false) {
219
                @chmod($filepath, $file_permission);
220
            }
221
222
            if ($result !== false) {
223
                return 'success';
224
            } else {
225
                return 'failed_no_write';
226
            }
227
        } else {
228
            return 'failed_no_retrieve';
229
        }
230
    }
231
232
    /**
233
     * @param int $docid
234
     * @param string $alias
235
     * @param string $prefix
236
     * @param string $suffix
237
     * @return string
238
     */
239
    public function getFileName($docid, $alias = '', $prefix, $suffix)
240
    {
241
        $modx = evolutionCMS();
242
243
        if ($alias === '') {
244
            $filename = $prefix . $docid . $suffix;
245
        } else {
246
            if ($modx->getConfig('suffix_mode') == '1' && strpos($alias, '.') !== false) {
247
                $suffix = '';
248
            }
249
            $filename = $prefix . $alias . $suffix;
250
        }
251
252
        return $filename;
253
    }
254
255
    /**
256
     * @param int $parent
257
     * @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...
258
     */
259
    public function run($parent = 0)
260
    {
261
        global $_lang;
262
        $modx = evolutionCMS();
263
264
        $ignore_ids = $this->ignore_ids;
265
        $dirpath = $this->targetDir . '/';
266
267
        $prefix = $modx->getConfig('friendly_url_prefix');
268
        $suffix = $modx->getConfig('friendly_url_suffix');
269
270
        $tpl = ' <span class="[+status+]">[+msg1+]</span> [+msg2+]</span>';
271
        $ph = array();
272
273
        $ph['status'] = 'fail';
274
        $ph['msg1'] = $_lang['export_site_failed'];
275
        $ph['msg2'] = $_lang["export_site_failed_no_write"] . ' - ' . $dirpath;
276
        $msg_failed_no_write = $this->parsePlaceholder($tpl, $ph);
277
278
        $ph['msg2'] = $_lang["export_site_failed_no_retrieve"];
279
        $msg_failed_no_retrieve = $this->parsePlaceholder($tpl, $ph);
280
281
        $ph['status'] = 'success';
282
        $ph['msg1'] = $_lang['export_site_success'];
283
        $ph['msg2'] = '';
284
        $msg_success = $this->parsePlaceholder($tpl, $ph);
285
286
        $ph['msg2'] = $_lang['export_site_success_skip_doc'];
287
        $msg_success_skip_doc = $this->parsePlaceholder($tpl, $ph);
288
289
        $ph['msg2'] = $_lang['export_site_success_skip_dir'];
290
        $msg_success_skip_dir = $this->parsePlaceholder($tpl, $ph);
291
292
        $data = $this->makeQuery($ignore_ids, (bool)get_by_key($_POST, 'includenoncache', false))
293
            ->where('parent', '=', $parent)
294
            ->get();
295
296
        $ph = array();
297
        $ph['total'] = $this->total;
298
        $folder_permission = octdec($modx->getConfig('new_folder_permissions'));
299
        /** @var Models\SiteContent $item */
300
        foreach ($data as $item) {
301
            $this->count++;
302
            $row = $item->toArray();
303
            $row['count'] = $this->count;
304
            $row['url'] = UrlProcessor::makeUrl($row['id']);
305
            $filename = '';
306
307
            if ($item->wasNull === false) { // needs writing a document
308
                $docname = $this->getFileName($row['id'], $row['alias'], $prefix, $suffix);
309
                $filename = $dirpath . $docname;
310
                if (!is_file($filename)) {
311
                    if ($row['published'] === 1) {
312
                        $status = $this->makeFile($row['id'], $filename);
313
                        switch ($status) {
314
                            case 'failed_no_write'   :
315
                                $row['status'] = $msg_failed_no_write;
316
                                break;
317
                            case 'failed_no_retrieve':
318
                                $row['status'] = $msg_failed_no_retrieve;
319
                                break;
320
                            default:
321
                                $row['status'] = $msg_success;
322
                        }
323
                    } else {
324
                        $row['status'] = $msg_failed_no_retrieve;
325
                    }
326
                } else {
327
                    $row['status'] = $msg_success_skip_doc;
328
                }
329
                $this->output[] = $this->parsePlaceholder($_lang['export_site_exporting_document'], $row);
330
            } else {
331
                $row['status'] = $msg_success_skip_dir;
332
                $this->output[] = $this->parsePlaceholder($_lang['export_site_exporting_document'], $row);
333
            }
334
            if ($row['isfolder'] === '1' && ($modx->getConfig('suffix_mode') != '1' || strpos($row['alias'],
335
                        '.') === false)) { // needs making a folder
336
                $end_dir = ($row['alias'] !== '') ? $row['alias'] : $row['id'];
337
                $dir_path = $dirpath . $end_dir;
338
                if (strpos($dir_path, MODX_BASE_PATH) === false) {
339
                    return false;
340
                }
341
                if (!is_dir($dir_path)) {
342
                    if (is_file($dir_path)) {
343
                        @unlink($dir_path);
344
                    }
345
                    mkdir($dir_path);
346
                    @chmod($dir_path, $folder_permission);
347
348
                }
349
350
                if ($modx->getConfig('make_folders') == '1' && $row['published'] === '1') {
351
                    if (!empty($filename) && is_file($filename)) {
352
                        rename($filename, $dir_path . '/index.html');
353
                    }
354
                }
355
                $this->targetDir = $dir_path;
356
                $this->run($row['id']);
357
            }
358
        }
359
360
        return implode("\n", $this->output);
361
    }
362
363
    /**
364
     * @param string $url
365
     * @param int $timeout
366
     * @return string
367
     */
368
    public function curl_get_contents($url, $timeout = 30)
369
    {
370
        if (!function_exists('curl_init')) {
371
            return @file_get_contents($url);
372
        }
373
374
        $ch = curl_init();
375
        curl_setopt($ch, CURLOPT_URL, $url);
376
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);    // 0 = DO NOT VERIFY AUTHENTICITY OF SSL-CERT
377
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);    // 2 = CERT MUST INDICATE BEING CONNECTED TO RIGHT SERVER
378
        curl_setopt($ch, CURLOPT_HEADER, false);
379
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
380
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
381
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
382
        $result = curl_exec($ch);
383
        curl_close($ch);
384
385
        return $result;
386
    }
387
388
    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...
389
    {
390
        foreach ($ph as $k => $v) {
391
            $k = "[+{$k}+]";
392
            $tpl = str_replace($k, $v, $tpl);
393
        }
394
395
        return $tpl;
396
    }
397
398
}
399