Completed
Branch EDTR/master (2c3e96)
by
unknown
19:06 queued 09:20
created

EEH_File::loadWpFileSystem()   C

Complexity

Conditions 10
Paths 25

Size

Total Lines 73

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 25
nop 0
dl 0
loc 73
rs 6.7224
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
2
3
/**
4
 *
5
 * Class EEH_File
6
 *
7
 * Mostly methods are wrappers for WP_Filesystem. Primarily these methods should be used
8
 * when you intend to WRITE to the filesystem. Many of these methods throw an EE_Error
9
 * when the current filesystem user doesn't have permission to read/write the given file.
10
 * Note: that means if FS_METHOD is defined to be ssh or ftp, and the ftp password isn't
11
 * entered into wp-config.php, code like `EEH_File::is_readable()` will THROW AN EXCEPTION
12
 * when trying ot read anything, unless the user has just entered their ftp/ssh credentials
13
 * into the wp filesystem credentials form. See http://ottopress.com/2011/tutorial-using-the-wp_filesystem/
14
 * If you want to test your usage of EEH_File and WP_Filesystem, you can use our
15
 * filesystem debugger plugin: https://github.com/eventespresso/filesystem-debug-helper,
16
 * which simulates requiring ftp or ssh to access your site (even if your site is
17
 * actually local and you haven't set it up for ftp or ssh access)
18
 *
19
 * @package             Event Espresso
20
 * @subpackage          core
21
 * @author              Brent Christensen
22
 *
23
 *
24
 */
25
class EEH_File extends EEH_Base implements EEHI_File
26
{
27
28
    /**
29
     * @var string $_credentials_form
30
     */
31
    private static $_credentials_form;
32
33
    /**
34
     * @var WP_Filesystem_Base $_wp_filesystem
35
     */
36
    protected static $_wp_filesystem;
37
38
39
    /**
40
     * @param string|null $filepath the filepath we want to work in. If its in the
41
     *                              wp uploads directory, we'll want to just use the filesystem directly.
42
     *                              If not provided, we have to assume its not in the uploads directory
43
     * @return WP_Filesystem_Base
44
     */
45
    private static function _get_wp_filesystem($filepath = null)
46
    {
47
        if (apply_filters(
48
            'FHEE__EEH_File___get_wp_filesystem__allow_using_filesystem_direct',
49
            $filepath && EEH_File::is_in_uploads_folder($filepath),
0 ignored issues
show
Bug Best Practice introduced by
The expression $filepath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
50
            $filepath
51
        )) {
52
            return EEH_File::loadAlternateWpFileSystem();
53
        }
54
        return EEH_File::loadWpFileSystem();
55
    }
56
57
58
    /**
59
     * @return WP_Filesystem_Base
60
     */
61
    private static function loadAlternateWpFileSystem()
62
    {
63
        if (! EEH_File::$_wp_filesystem instanceof WP_Filesystem_Base) {
64
            require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
65
            $method             = 'direct';
66
            $wp_filesystem_file =
67
                apply_filters(
68
                    'filesystem_method_file',
69
                    ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php',
70
                    $method
71
                );
72
            // added the following validation logic
73
            // because we allow the filesystem filepath to be filtered,
74
            // and are loading whatever file the path pointed to,
75
            // but we were not validating things in any way :scream_emoji:
76
            $valid_wp_filesystem_types = [
77
                'direct'     => 'WP_Filesystem_Direct',
78
                'ftpext'     => 'WP_Filesystem_FTPext',
79
                'ftpsockets' => 'WP_Filesystem_ftpsockets',
80
                'ssh2'       => 'WP_Filesystem_SSH2',
81
            ];
82
            $valid                     = false;
83
            $wp_filesystem_class       = '';
84
            foreach ($valid_wp_filesystem_types as $method => $filesystem_class) {
85
                // if file path matches for one of valid types, then toggle $valid to true
86
                if (strpos($wp_filesystem_file, $method) > 0) {
87
                    $valid               = true;
88
                    $wp_filesystem_class = $filesystem_class;
89
                }
90
            }
91 View Code Duplication
            if (! $valid || ! file_exists($wp_filesystem_file)) {
92
                EE_Error::add_error(
93
                    sprintf(
94
                        __(
95
                            'The supplied WP Filesystem filepath "%1$s" is either missing or invalid.',
96
                            'event_espresso'
97
                        ),
98
                        $wp_filesystem_file
99
                    ),
100
                    __FILE__,
101
                    __FUNCTION__,
102
                    __LINE__
103
                );
104
            }
105
            // check constants defined, just like in the wp-admin/includes/file.php WP_Filesystem()
106
            if (! defined('FS_CHMOD_DIR')) {
107
                define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0775 | 0755));
108
            }
109
            if (! defined('FS_CHMOD_FILE')) {
110
                define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0775 | 0644));
111
            }
112
            require_once($wp_filesystem_file);
113
            EEH_File::$_wp_filesystem = new $wp_filesystem_class([]);
114
        }
115
        return EEH_File::$_wp_filesystem;
116
    }
117
118
119
    /**
120
     * @return WP_Filesystem_Base
121
     */
122
    private static function loadWpFileSystem()
123
    {
124
        global $wp_filesystem;
125
        // no filesystem setup ???
126
        if (! $wp_filesystem instanceof WP_Filesystem_Base) {
127
            // if some eager beaver's just trying to get in there too early...
128
            // let them do it, because we are one of those eager beavers! :P
129
            /**
130
             * more explanations are probably merited. http://codex.wordpress.org/Filesystem_API#Initializing_WP_Filesystem_Base
131
             * says WP_Filesystem should be used after 'wp_loaded', but currently EE's activation process
132
             * is setup to mostly happen on 'init', and refactoring to have it happen on
133
             * 'wp_loaded' is too much work on a BETA milestone.
134
             * So this fix is expected to work if the WP files are owned by the server user,
135
             * but probably not if the user needs to enter their FTP credentials to modify files
136
             * and there may be troubles if the WP files are owned by a different user
137
             * than the server user. But both of these issues should exist in 4.4 and earlier too
138
             */
139
            if (false && ! did_action('wp_loaded')) {
140
                $msg =
141
                    __(
142
                        'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.',
143
                        'event_espresso'
144
                    );
145
                if (WP_DEBUG) {
146
                    $msg .= '<br />' . __(
147
                        'The WP Filesystem can not be accessed until after the "wp_loaded" hook has run, so it\'s best not to attempt access until the "admin_init" hookpoint.',
148
                        'event_espresso'
149
                    );
150
                }
151
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
152
            }
153
            // should be loaded if we are past the wp_loaded hook...
154
            if (! function_exists('WP_Filesystem')) {
155
                require_once(ABSPATH . 'wp-admin/includes/file.php');
156
                require_once(ABSPATH . 'wp-admin/includes/template.php');
157
            }
158
            // turn on output buffering so that we can capture the credentials form
159
            ob_start();
160
            $credentials = request_filesystem_credentials(false);
161
            // store credentials form for the time being
162
            EEH_File::$_credentials_form = ob_get_clean();
163
            // if credentials do NOT exist
164
            if ($credentials === false) {
165
                add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999);
166
                EE_Error::add_error(
167
                    __(
168
                        'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.',
169
                        'event_espresso'
170
                    ),
171
                    __FILE__,
172
                    __FUNCTION__,
173
                    __LINE__
174
                );
175
            }
176
            // basically check for direct or previously configured access
177
            if (! WP_Filesystem($credentials)
178
                && is_wp_error($wp_filesystem->errors)
179
                && $wp_filesystem->errors->get_error_code()
180
            ) {
181
                add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999);
182
                EE_Error::add_error(
183
                    sprintf(
184
                        __('WP Filesystem Error: $1%s', 'event_espresso'),
185
                        $wp_filesystem->errors->get_error_message()
186
                    ),
187
                    __FILE__,
188
                    __FUNCTION__,
189
                    __LINE__
190
                );
191
            }
192
        }
193
        return $wp_filesystem;
194
    }
195
196
197
    /**
198
     * display_request_filesystem_credentials_form
199
     */
200
    public static function display_request_filesystem_credentials_form()
201
    {
202
        if (! empty(EEH_File::$_credentials_form)) {
203
            echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>';
204
        }
205
    }
206
207
208
    /**
209
     *    verify_filepath_and_permissions
210
     *    checks that a file is readable and has sufficient file permissions set to access
211
     *
212
     * @access public
213
     * @param string $full_file_path - full server path to the folder or file
214
     * @param string $file_name      - name of file if checking a file
215
     * @param string $file_ext       - file extension (ie: "php") if checking a file
216
     * @param string $type_of_file   - general type of file (ie: "module"), this is only used to improve error messages
217
     * @return bool
218
     */
219
    public static function verify_filepath_and_permissions(
220
        $full_file_path = '',
221
        $file_name = '',
222
        $file_ext = '',
223
        $type_of_file = ''
224
    ) {
225
        // load WP_Filesystem and set file permissions
226
        $wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
227
        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
228
        if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
229
            $file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name;
230
            $file_name .= ! empty($file_ext) ? ' file' : ' folder';
231
            $msg       = sprintf(
232
                __(
233
                    'The requested %1$s could not be found or is not readable, possibly due to an incorrect filepath, or incorrect file permissions.%2$s',
234
                    'event_espresso'
235
                ),
236
                $file_name,
237
                '<br />'
238
            );
239
            if (EEH_File::exists($full_file_path)) {
240
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, $type_of_file);
241
            } else {
242
                // no file permissions means the file was not found
243
                $msg .= sprintf(
244
                    __('Please ensure the following path is correct: "%s".', 'event_espresso'),
245
                    $full_file_path
246
                );
247
            }
248
            if (defined('WP_DEBUG') && WP_DEBUG) {
249
                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
250
            }
251
            return false;
252
        }
253
        return true;
254
    }
255
256
257
    /**
258
     * _permissions_error_for_unreadable_filepath - attempts to determine why permissions are set incorrectly for a
259
     * file or folder
260
     *
261
     * @access private
262
     * @param string $full_file_path - full server path to the folder or file
263
     * @param string $type_of_file   - general type of file (ie: "module"), this is only used to improve error messages
264
     * @return string
265
     */
266
    private static function _permissions_error_for_unreadable_filepath($full_file_path = '', $type_of_file = '')
267
    {
268
        // load WP_Filesystem and set file permissions
269
        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
270
        // check file permissions
271
        $perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
272
        if ($perms) {
273
            // file permissions exist, but way be set incorrectly
274
            $type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : '';
275
            $type_of_file .= ! empty($type_of_file) ? 'file' : 'folder';
276
            return ' ' . sprintf(
277
                __(
278
                    'File permissions for the requested %1$s are currently set at "%2$s". The recommended permissions are 644 for files and 755 for folders.',
279
                    'event_espresso'
280
                ),
281
                $type_of_file,
282
                $perms
283
            );
284
        } else {
285
            // file exists but file permissions could not be read ?!?!
286
            return ' ' . sprintf(
287
                __(
288
                    'Please ensure that the server and/or PHP configuration allows the current process to access the following file: "%s".',
289
                    'event_espresso'
290
                ),
291
                $full_file_path
292
            );
293
        }
294
    }
295
296
297
    /**
298
     * ensure_folder_exists_and_is_writable
299
     * ensures that a folder exists and is writable, will attempt to create folder if it does not exist
300
     * Also ensures all the parent folders exist, and if not tries to create them.
301
     * Also, if this function creates the folder, adds a .htaccess file and index.html file
302
     *
303
     * @param string $folder
304
     * @return bool false if folder isn't writable; true if it exists and is writeable,
305
     */
306
    public static function ensure_folder_exists_and_is_writable($folder = '')
307
    {
308
        if (empty($folder)) {
309
            return false;
310
        }
311
        // remove ending /
312
        $folder        = EEH_File::standardise_directory_separators(rtrim($folder, '/\\'));
313
        $parent_folder = EEH_File::get_parent_folder($folder);
314
        // add / to folder
315
        $folder        = EEH_File::end_with_directory_separator($folder);
316
        $wp_filesystem = EEH_File::_get_wp_filesystem($folder);
317
        $remote_dir    = EEH_File::convert_local_filepath_to_remote_filepath($folder);
318
        if (! $wp_filesystem->is_dir($remote_dir)) {
319
            // ok so it doesn't exist. Does its parent? Can we write to it?
320
            if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
321
                return false;
322
            }
323
            if (! EEH_File::verify_is_writable($parent_folder, 'folder')) {
324
                return false;
325
            }
326
            if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) {
327
                if (defined('WP_DEBUG') && WP_DEBUG) {
328
                    $msg = sprintf(__('"%s" could not be created.', 'event_espresso'), $folder);
329
                    $msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder);
330
                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
331
                }
332
                return false;
333
            }
334
            EEH_File::add_index_file($folder);
335
        } elseif (! EEH_File::verify_is_writable($folder, 'folder')) {
336
            return false;
337
        }
338
        return true;
339
    }
340
341
342
    /**
343
     * verify_is_writable - checks if a file or folder is writable
344
     *
345
     * @param string $full_path      - full server path to file or folder
346
     * @param string $file_or_folder - whether checking a file or folder
347
     * @return bool
348
     */
349
    public static function verify_is_writable($full_path = '', $file_or_folder = 'folder')
350
    {
351
        // load WP_Filesystem and set file permissions
352
        $wp_filesystem = EEH_File::_get_wp_filesystem($full_path);
353
        $full_path     = EEH_File::standardise_directory_separators($full_path);
354
        $remote_path   = EEH_File::convert_local_filepath_to_remote_filepath($full_path);
355
        $remote_path   = rtrim($remote_path, '/\\');
356
        if (! $wp_filesystem->is_writable($remote_path)) {
357
            if (defined('WP_DEBUG') && WP_DEBUG) {
358
                $msg = sprintf(__('The "%1$s" %2$s is not writable.', 'event_espresso'), $full_path, $file_or_folder);
359
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_path);
360
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
361
            }
362
            return false;
363
        }
364
        return true;
365
    }
366
367
368
    /**
369
     * ensure_file_exists_and_is_writable
370
     * ensures that a file exists and is writable, will attempt to create file if it does not exist.
371
     * Also ensures all the parent folders exist, and if not tries to create them.
372
     *
373
     * @param string $full_file_path
374
     * @return bool
375
     */
376
    public static function ensure_file_exists_and_is_writable($full_file_path = '')
377
    {
378
        // load WP_Filesystem and set file permissions
379
        $wp_filesystem  = EEH_File::_get_wp_filesystem($full_file_path);
380
        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
381
        $parent_folder  = EEH_File::get_parent_folder($full_file_path);
382
        if (! EEH_File::exists($full_file_path)) {
383
            if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) {
384
                return false;
385
            }
386
            if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) {
387
                if (defined('WP_DEBUG') && WP_DEBUG) {
388
                    $msg = sprintf(__('The "%s" file could not be created.', 'event_espresso'), $full_file_path);
389
                    $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
390
                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
391
                }
392
                return false;
393
            }
394
        }
395
        if (! EEH_File::verify_is_writable($full_file_path, 'file')) {
396
            return false;
397
        }
398
        return true;
399
    }
400
401
402
    /**
403
     * Gets the parent folder. If provided with file, gets the folder that contains it.
404
     * If provided a folder, gets its parent folder.
405
     *
406
     * @param string $file_or_folder_path
407
     * @return string parent folder, ENDING with a directory separator
408
     */
409
    public static function get_parent_folder($file_or_folder_path)
410
    {
411
        // find the last /, ignoring a / on the very end
412
        // eg if given "/var/something/somewhere/", we want to get "somewhere"'s
413
        // parent folder, "/var/something/"
414
        $ds = strlen($file_or_folder_path) > 1
415
            ? strrpos($file_or_folder_path, '/', -2)
416
            : strlen($file_or_folder_path);
417
        return substr($file_or_folder_path, 0, $ds + 1);
418
    }
419
420
421
    /**
422
     * get_file_contents
423
     *
424
     * @param string $full_file_path
425
     * @return string
426
     */
427
    public static function get_file_contents($full_file_path = '')
428
    {
429
        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
430
        if (EEH_File::verify_filepath_and_permissions(
431
            $full_file_path,
432
            EEH_File::get_filename_from_filepath($full_file_path),
433
            EEH_File::get_file_extension($full_file_path)
434
        )) {
435
            // load WP_Filesystem and set file permissions
436
            $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
437
            return $wp_filesystem->get_contents(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
438
        }
439
        return '';
440
    }
441
442
443
    /**
444
     * write_file
445
     *
446
     * @param string $full_file_path
447
     * @param string $file_contents - the content to be written to the file
448
     * @param string $file_type
449
     * @return bool
450
     */
451
    public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '')
452
    {
453
        $full_file_path = EEH_File::standardise_directory_separators($full_file_path);
454
        $file_type      = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : '';
455
        $folder         = EEH_File::remove_filename_from_filepath($full_file_path);
456 View Code Duplication
        if (! EEH_File::verify_is_writable($folder, 'folder')) {
457
            if (defined('WP_DEBUG') && WP_DEBUG) {
458
                $msg =
459
                    sprintf(
460
                        __('The %1$sfile located at "%2$s" is not writable.', 'event_espresso'),
461
                        $file_type,
462
                        $full_file_path
463
                    );
464
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path);
465
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
466
            }
467
            return false;
468
        }
469
        // load WP_Filesystem and set file permissions
470
        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
471
        // write the file
472
        if (! $wp_filesystem->put_contents(
473
            EEH_File::convert_local_filepath_to_remote_filepath($full_file_path),
474
            $file_contents
475
        )) {
476
            if (defined('WP_DEBUG') && WP_DEBUG) {
477
                $msg =
478
                    sprintf(
479
                        __('The %1$sfile located at "%2$s" could not be written to.', 'event_espresso'),
480
                        $file_type,
481
                        $full_file_path
482
                    );
483
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, 'f');
484
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
485
            }
486
            return false;
487
        }
488
        return true;
489
    }
490
491
492
    /**
493
     * Wrapper for WP_Filesystem_Base::delete
494
     *
495
     * @param string         $filepath
496
     * @param boolean        $recursive
497
     * @param boolean|string $type 'd' for directory, 'f' for file
498
     * @return boolean
499
     */
500
    public static function delete($filepath, $recursive = false, $type = false)
501
    {
502
        $wp_filesystem = EEH_File::_get_wp_filesystem();
503
        return $wp_filesystem->delete($filepath, $recursive, $type);
504
    }
505
506
507
    /**
508
     * exists
509
     * checks if a file exists using the WP filesystem
510
     *
511
     * @param string $full_file_path
512
     * @return bool
513
     */
514
    public static function exists($full_file_path = '')
515
    {
516
        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
517
        return $wp_filesystem->exists(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
518
    }
519
520
521
    /**
522
     * is_readable
523
     * checks if a file is_readable using the WP filesystem
524
     *
525
     * @param string $full_file_path
526
     * @return bool
527
     */
528
    public static function is_readable($full_file_path = '')
529
    {
530
        $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path);
531
        return $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path));
532
    }
533
534
535
    /**
536
     * remove_filename_from_filepath
537
     * given a full path to a file including the filename itself, this removes  the filename and returns the path, up
538
     * to, but NOT including the filename OR slash
539
     *
540
     * @param string $full_file_path
541
     * @return string
542
     */
543
    public static function remove_filename_from_filepath($full_file_path = '')
544
    {
545
        return pathinfo($full_file_path, PATHINFO_DIRNAME);
546
    }
547
548
549
    /**
550
     * get_filename_from_filepath. Arguably the same as basename()
551
     *
552
     * @param string $full_file_path
553
     * @return string
554
     */
555
    public static function get_filename_from_filepath($full_file_path = '')
556
    {
557
        return pathinfo($full_file_path, PATHINFO_BASENAME);
558
    }
559
560
561
    /**
562
     * get_file_extension
563
     *
564
     * @param string $full_file_path
565
     * @return string
566
     */
567
    public static function get_file_extension($full_file_path = '')
568
    {
569
        return pathinfo($full_file_path, PATHINFO_EXTENSION);
570
    }
571
572
573
    /**
574
     * add_htaccess_deny_from_all so the web server cannot access this folder
575
     *
576
     * @param string $folder
577
     * @return bool
578
     */
579 View Code Duplication
    public static function add_htaccess_deny_from_all($folder = '')
580
    {
581
        $folder = EEH_File::standardise_and_end_with_directory_separator($folder);
582
        if (! EEH_File::exists($folder . '.htaccess')) {
583
            if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) {
584
                return false;
585
            }
586
        }
587
588
        return true;
589
    }
590
591
592
    /**
593
     * Adds an index file to this folder, so folks can't list all the file's contents
594
     *
595
     * @param string $folder
596
     * @return boolean
597
     */
598 View Code Duplication
    public static function add_index_file($folder)
599
    {
600
        $folder = EEH_File::standardise_and_end_with_directory_separator($folder);
601
        if (! EEH_File::exists($folder . 'index.php')) {
602
            if (! EEH_File::write_to_file(
603
                $folder . 'index.php',
604
                'You are not permitted to read from this folder',
605
                '.php'
606
            )) {
607
                return false;
608
            }
609
        }
610
        return true;
611
    }
612
613
614
    /**
615
     * Given that the file in $file_path has the normal name, (ie, CLASSNAME.whatever.php),
616
     * extract that classname.
617
     *
618
     * @param string $file_path
619
     * @return string
620
     */
621
    public static function get_classname_from_filepath_with_standard_filename($file_path)
622
    {
623
        // extract file from path
624
        $filename = basename($file_path);
625
        // now remove the first period and everything after
626
        $pos_of_first_period = strpos($filename, '.');
627
        return substr($filename, 0, $pos_of_first_period);
628
    }
629
630
631
    /**
632
     * standardise_directory_separators
633
     *  convert all directory separators in a file path.
634
     *
635
     * @param string $file_path
636
     * @param bool   $rtrim will remove trailing backslash
637
     * @return string
638
     */
639
    public static function standardise_directory_separators($file_path, $rtrim = false)
640
    {
641
        $file_path = $rtrim ? rtrim($file_path, '/\\') : $file_path;
642
        return str_replace(['\\', '/'], '/', $file_path);
643
    }
644
645
646
    /**
647
     * end_with_directory_separator
648
     *  ensures that file path ends with '/'
649
     *
650
     * @param string $file_path
651
     * @return string
652
     */
653
    public static function end_with_directory_separator($file_path)
654
    {
655
        return rtrim($file_path, '/\\') . '/';
656
    }
657
658
659
    /**
660
     * shorthand for both EEH_FIle::end_with_directory_separator AND EEH_File::standardise_directory_separators
661
     *
662
     * @param $file_path
663
     * @return string
664
     */
665
    public static function standardise_and_end_with_directory_separator($file_path)
666
    {
667
        return self::end_with_directory_separator(self::standardise_directory_separators($file_path));
668
    }
669
670
671
    /**
672
     * takes the folder name (with or without trailing slash) and finds the files it in,
673
     * and what the class's name inside of each should be.
674
     *
675
     * @param array   $folder_paths
676
     * @param boolean $index_numerically if TRUE, the returned array will be indexed numerically;
677
     *                                   if FALSE (Default), returned array will be indexed by the filenames minus
678
     *                                   extensions. Set it TRUE if you know there are files in the directory with the
679
     *                                   same name but different extensions
680
     * @return array if $index_numerically == TRUE keys are numeric ,
681
     *                                   if $index_numerically == FALSE (Default) keys are what the class names SHOULD
682
     *                                   be; and values are their file paths
683
     */
684
    public static function get_contents_of_folders($folder_paths = [], $index_numerically = false)
685
    {
686
        $class_to_folder_path = [];
687
        foreach ($folder_paths as $folder_path) {
688
            $folder_path = self::standardise_and_end_with_directory_separator($folder_path);
689
            // load WP_Filesystem and set file permissions
690
            $files_in_folder      = glob($folder_path . '*');
691
            $class_to_folder_path = [];
692
            if ($files_in_folder) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $files_in_folder 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...
693
                foreach ($files_in_folder as $file_path) {
694
                    // only add files, not folders
695
                    if (! is_dir($file_path)) {
696
                        if ($index_numerically) {
697
                            $class_to_folder_path[] = $file_path;
698
                        } else {
699
                            $classname =
700
                                self::get_classname_from_filepath_with_standard_filename($file_path);
701
                            $class_to_folder_path[ $classname ] = $file_path;
702
                        }
703
                    }
704
                }
705
            }
706
        }
707
        return $class_to_folder_path;
708
    }
709
710
711
    /**
712
     * Copies a file. Mostly a wrapper of WP_Filesystem::copy
713
     *
714
     * @param string  $source_file
715
     * @param string  $destination_file
716
     * @param boolean $overwrite
717
     * @return boolean success
718
     */
719
    public static function copy($source_file, $destination_file, $overwrite = false)
720
    {
721
        $source_file      = EEH_File::validateFileForCopyOrMove($source_file);
722
        $destination_file = EEH_File::validateFolderForCopyOrMove($destination_file);
723
        if (! $source_file || ! $destination_file) {
724
            return false;
725
        }
726
        // load WP_Filesystem and set file permissions
727
        $wp_filesystem = EEH_File::_get_wp_filesystem($destination_file);
728
        // write the file
729
        $copied = $wp_filesystem->copy(
730
            EEH_File::convert_local_filepath_to_remote_filepath($source_file),
731
            EEH_File::convert_local_filepath_to_remote_filepath($destination_file),
732
            $overwrite
733
        );
734 View Code Duplication
        if (! $copied) {
735
            if (defined('WP_DEBUG') && WP_DEBUG) {
736
                $msg = sprintf(
737
                    __(
738
                        'Attempted writing to file %1$s, but could not, probably because of permissions issues',
739
                        'event_espresso'
740
                    ),
741
                    $source_file
742
                );
743
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($source_file, 'f');
744
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
745
            }
746
            return false;
747
        }
748
        return true;
749
    }
750
751
752
    /**
753
     * Reports whether or not the filepath is in the EE uploads folder or not
754
     *
755
     * @param string $filepath
756
     * @return boolean
757
     */
758
    public static function is_in_uploads_folder($filepath)
759
    {
760
        $uploads = wp_upload_dir();
761
        return strpos($filepath, $uploads['basedir']) === 0;
762
    }
763
764
765
    /**
766
     * Given a "local" filepath (what you probably thought was the only filepath),
767
     * converts it into a "remote" filepath (the filepath the currently-in-use
768
     * $wp_filesystem needs to use access the folder or file).
769
     * See http://wordpress.stackexchange.com/questions/124900/using-wp-filesystem-in-plugins
770
     *
771
     * @param string $local_filepath the filepath to the folder/file locally
772
     * @return string the remote filepath (eg the filepath the filesystem method, eg
773
     *                               ftp or ssh, will use to access the folder
774
     */
775
    public static function convert_local_filepath_to_remote_filepath($local_filepath)
776
    {
777
        $wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath);
778
        return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath);
779
    }
780
781
782
    /**
783
     * wrapper for WP_Filesystem::chmod()
784
     *
785
     * @param string    $file      Path to the file.
786
     * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
787
     *                             0755 for directories. Default false.
788
     * @param bool      $recursive Optional. If set to true, changes file permissions recursively.
789
     *                             Default false.
790
     * @return bool True on success, false on failure.
791
     */
792
    public static function chmod($file, $mode = false, $recursive = false)
793
    {
794
        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
795
        return $wp_filesystem->chmod($file, $mode, $recursive);
796
    }
797
798
799
    /**
800
     * wrapper for WP_Filesystem::getchmod()
801
     *
802
     * @param string $file Path to the file.
803
     * @return string Mode of the file (the last 3 digits).
804
     */
805
    public static function permissions($file)
806
    {
807
        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
808
        return $wp_filesystem->getchmod($file);
809
    }
810
811
812
    /**
813
     * wrapper for WP_Filesystem::owner()
814
     *
815
     * @param string $file Path to the file.
816
     * @return string|false Username of the owner on success, false on failure.
817
     */
818
    public static function owner($file)
819
    {
820
        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
821
        return $wp_filesystem->owner($file);
822
    }
823
824
825
    /**
826
     * wrapper for WP_Filesystem::group()
827
     *
828
     * @param string $file Path to the file.
829
     * @return string|false The group on success, false on failure.
830
     */
831
    public static function group($file)
832
    {
833
        $wp_filesystem = EEH_File::_get_wp_filesystem($file);
834
        return $wp_filesystem->group($file);
835
    }
836
837
838
    /**
839
     * wrapper for WP_Filesystem::move()
840
     *
841
     * @param string $source      Path to the source file.
842
     * @param string $destination Path to the destination file.
843
     * @param bool   $overwrite   Optional. Whether to overwrite the destination file if it exists.
844
     *                            Default false.
845
     * @return bool True on success, false on failure.
846
     */
847
    public static function move($source, $destination, $overwrite = false)
848
    {
849
        // throw new RuntimeException("source: {$source} && destination: {$destination}");
850
        $source      = EEH_File::validateFileForCopyOrMove($source);
851
        $destination = EEH_File::validateFolderForCopyOrMove($destination);
852
        if (! $source || ! $destination) {
853
            return false;
854
        }
855
        $wp_filesystem = EEH_File::_get_wp_filesystem($source);
856
        if ($wp_filesystem->move($source, $destination, $overwrite)) {
857
            return true;
858
        }
859
        if (defined('WP_DEBUG') && WP_DEBUG) {
860
            $file        = EEH_File::convert_local_filepath_to_remote_filepath($source);
861
            $owner       = EEH_File::owner($file);
862
            $group       = EEH_File::group($file);
863
            $permissions = EEH_File::permissions($file);
864
            EE_Error::add_error(
865
                sprintf(
866
                    esc_html__(
867
                        'Unable to move the file "%1$s" to new location (possible permissions errors). The existing "owner:group permissions" for the file are: "%2$s"',
868
                        'event_espresso'
869
                    ),
870
                    $destination,
871
                    "{$owner}:{$group} $permissions"
872
                ),
873
                __FILE__,
874
                __FUNCTION__,
875
                __LINE__
876
            );
877
        }
878
        return false;
879
    }
880
881
882
    /**
883
     * @param string $source_file
884
     * @return string
885
     */
886
    private static function validateFileForCopyOrMove($source_file)
887
    {
888
        $full_source_path = EEH_File::standardise_directory_separators($source_file);
889 View Code Duplication
        if (! EEH_File::exists($full_source_path)) {
890
            if (defined('WP_DEBUG') && WP_DEBUG) {
891
                $msg =
892
                    sprintf(
893
                        __('The file located at "%2$s" is not readable or doesn\'t exist.', 'event_espresso'),
894
                        '',
895
                        $full_source_path
896
                    );
897
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path);
898
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
899
            }
900
            return '';
901
        }
902
        return $full_source_path;
903
    }
904
905
906
    /**
907
     * @param string $destination_file
908
     * @return string
909
     */
910
    private static function validateFolderForCopyOrMove($destination_file)
911
    {
912
        $full_dest_path = EEH_File::standardise_directory_separators($destination_file);
913
        $folder         = EEH_File::remove_filename_from_filepath($full_dest_path);
914
        EEH_File::ensure_folder_exists_and_is_writable($folder);
915 View Code Duplication
        if (! EEH_File::verify_is_writable($folder, 'folder')) {
916
            if (defined('WP_DEBUG') && WP_DEBUG) {
917
                $msg = sprintf(
918
                    __('The file located at "%2$s" is not writable.', 'event_espresso'),
919
                    '',
920
                    $full_dest_path
921
                );
922
                $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_dest_path);
923
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
924
            }
925
            return '';
926
        }
927
        return $full_dest_path;
928
    }
929
}
930