1
|
|
|
<?php |
2
|
|
|
class WP_Filesystem_MockFS extends WP_Filesystem_Base { |
3
|
|
|
private $cwd; |
4
|
|
|
|
5
|
|
|
// Holds a array of objects which contain an array of objects, etc. |
6
|
|
|
private $fs = null; |
7
|
|
|
|
8
|
|
|
// Holds a array of /path/to/file.php and /path/to/dir/ map to an object in $fs above |
9
|
|
|
// a fast more efficient way of determining if a path exists, and access to that node |
10
|
|
|
private $fs_map = array(); |
11
|
|
|
|
12
|
|
|
public $verbose = false; // Enable to debug WP_Filesystem_Base::find_folder() / etc. |
13
|
|
|
public $errors = array(); |
14
|
|
|
public $method = 'MockFS'; |
15
|
|
|
|
16
|
|
|
function __construct() {} |
17
|
|
|
|
18
|
|
|
function connect() { |
19
|
|
|
return true; |
20
|
|
|
} |
21
|
|
|
|
22
|
|
|
// Copy of core's function, but accepts a path. |
23
|
|
|
function abspath( $path = false ) { |
24
|
|
|
if ( ! $path ) |
25
|
|
|
$path = ABSPATH; |
26
|
|
|
$folder = $this->find_folder( $path ); |
|
|
|
|
27
|
|
|
|
28
|
|
|
// Perhaps the FTP folder is rooted at the WordPress install, Check for wp-includes folder in root, Could have some false positives, but rare. |
29
|
|
|
if ( ! $folder && $this->is_dir('/wp-includes') ) |
|
|
|
|
30
|
|
|
$folder = '/'; |
31
|
|
|
return $folder; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
// Mock FS specific functions: |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Sets initial filesystem environment and/or clears the current environment. |
38
|
|
|
* Can also be passed the initial filesystem to be setup which is passed to self::setfs() |
39
|
|
|
*/ |
40
|
|
|
function init( $paths = '', $home_dir = '/' ) { |
41
|
|
|
$this->fs = new MockFS_Directory_Node( '/' ); |
42
|
|
|
$this->fs_map = array( |
43
|
|
|
'/' => $this->fs, |
44
|
|
|
); |
45
|
|
|
$this->cache = array(); // Used by find_folder() and friends |
46
|
|
|
$this->cwd = isset( $this->fs_map[ $home_dir ] ) ? $this->fs_map[ $home_dir ] : '/'; |
47
|
|
|
$this->setfs( $paths ); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* "Bulk Loads" a filesystem into the internal virtual filesystem |
52
|
|
|
*/ |
53
|
|
|
function setfs( $paths ) { |
54
|
|
|
if ( ! is_array($paths) ) |
55
|
|
|
$paths = explode( "\n", $paths ); |
56
|
|
|
|
57
|
|
|
$paths = array_filter( array_map( 'trim', $paths ) ); |
58
|
|
|
|
59
|
|
|
foreach ( $paths as $path ) { |
60
|
|
|
// Allow for comments |
61
|
|
|
if ( '#' == $path[0] ) |
62
|
|
|
continue; |
63
|
|
|
|
64
|
|
|
// Directories |
65
|
|
|
if ( '/' == $path[ strlen($path) -1 ] ) |
66
|
|
|
$this->mkdir( $path ); |
67
|
|
|
else // Files (with dummy content for now) |
68
|
|
|
$this->put_contents( $path, 'This is a test file' ); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Locates a filesystem "node" |
75
|
|
|
*/ |
76
|
|
|
private function locate_node( $path ) { |
77
|
|
|
return isset( $this->fs_map[ $path ] ) ? $this->fs_map[ $path ] : false; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Locates a filesystem node for the parent of the given item |
82
|
|
|
*/ |
83
|
|
|
private function locate_parent_node( $path ) { |
84
|
|
|
$dirname = str_replace( '\\', '/', dirname( $path ) ); |
85
|
|
|
return $this->locate_node( trailingslashit( $dirname ) ); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
// Here starteth the WP_Filesystem functions. |
89
|
|
|
|
90
|
|
|
function mkdir( $path, /* Optional args are ignored */ $chmod = false, $chown = false, $chgrp = false ) { |
91
|
|
|
$path = trailingslashit( $path ); |
92
|
|
|
|
93
|
|
|
$parent_node = $this->locate_parent_node( $path ); |
94
|
|
|
if ( ! $parent_node ) { |
95
|
|
|
$dirname = str_replace( '\\', '/', dirname( $path ) ); |
96
|
|
|
$this->mkdir( $dirname ); |
97
|
|
|
$parent_node = $this->locate_parent_node( $path ); |
98
|
|
|
if ( ! $parent_node ) |
99
|
|
|
return false; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$node = new MockFS_Directory_Node( $path ); |
103
|
|
|
|
104
|
|
|
$parent_node->children[ $node->name ] = $node; |
105
|
|
|
$this->fs_map[ $path ] = $node; |
106
|
|
|
|
107
|
|
|
return true; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
function put_contents( $path, $contents = '', $mode = null ) { |
111
|
|
|
if ( ! $this->is_dir( dirname( $path ) ) ) |
112
|
|
|
$this->mkdir( dirname( $path ) ); |
113
|
|
|
|
114
|
|
|
$parent = $this->locate_parent_node( $path ); |
115
|
|
|
$new_file = new MockFS_File_Node( $path, $contents ); |
116
|
|
|
|
117
|
|
|
$parent->children[ $new_file->name ] = $new_file; |
118
|
|
|
$this->fs_map[ $path ] = $new_file; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
function get_contents( $file ) { |
122
|
|
|
if ( ! $this->is_file( $file ) ) |
123
|
|
|
return false; |
124
|
|
|
return $this->fs_map[ $file ]->contents; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
function cwd() { |
128
|
|
|
return $this->cwd->path; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
function chdir( $path ) { |
132
|
|
|
if ( ! isset( $this->fs_map[ $path ] ) ) |
133
|
|
|
return false; |
134
|
|
|
|
135
|
|
|
$this->cwd = $this->fs_map[ $path ]; |
136
|
|
|
return true; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
function exists( $path ) { |
140
|
|
|
return isset( $this->fs_map[ $path ] ) || isset( $this->fs_map[ trailingslashit( $path ) ] ); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
function is_file( $file ) { |
144
|
|
|
return isset( $this->fs_map[ $file ] ) && $this->fs_map[ $file ]->is_file(); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
function is_dir( $path ) { |
148
|
|
|
$path = trailingslashit( $path ); |
149
|
|
|
|
150
|
|
|
return isset( $this->fs_map[ $path ] ) && $this->fs_map[ $path ]->is_dir(); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
function dirlist( $path = '.', $include_hidden = true, $recursive = false ) { |
154
|
|
|
|
155
|
|
|
if ( empty( $path ) || '.' == $path ) |
156
|
|
|
$path = $this->cwd(); |
157
|
|
|
|
158
|
|
|
if ( ! $this->exists( $path ) ) |
159
|
|
|
return false; |
160
|
|
|
|
161
|
|
|
$limit_file = false; |
162
|
|
|
if ( $this->is_file( $path ) ) { |
163
|
|
|
$limit_file = $this->locate_node( $path )->name; |
164
|
|
|
$path = dirname( $path ) . '/'; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$ret = array(); |
168
|
|
|
foreach ( $this->fs_map[ $path ]->children as $entry ) { |
169
|
|
|
if ( '.' == $entry->name || '..' == $entry->name ) |
170
|
|
|
continue; |
171
|
|
|
|
172
|
|
|
if ( ! $include_hidden && '.' == $entry->name ) |
173
|
|
|
continue; |
174
|
|
|
|
175
|
|
|
if ( $limit_file && $entry->name != $limit_file ) |
176
|
|
|
continue; |
177
|
|
|
|
178
|
|
|
$struc = array(); |
179
|
|
|
$struc['name'] = $entry->name; |
180
|
|
|
$struc['type'] = $entry->type; |
181
|
|
|
|
182
|
|
|
if ( 'd' == $struc['type'] ) { |
183
|
|
|
if ( $recursive ) |
184
|
|
|
$struc['files'] = $this->dirlist( trailingslashit( $path ) . trailingslashit( $struc['name'] ), $include_hidden, $recursive ); |
185
|
|
|
else |
186
|
|
|
$struc['files'] = array(); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
$ret[ $entry->name ] = $struc; |
190
|
|
|
} |
191
|
|
|
return $ret; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
class MockFS_Node { |
197
|
|
|
public $name; // The "name" of the entry, does not include a slash (exception, root) |
198
|
|
|
public $type; // The type of the entry 'f' for file, 'd' for Directory |
199
|
|
|
public $path; // The full path to the entry. |
200
|
|
|
|
201
|
|
|
function __construct( $path ) { |
202
|
|
|
$this->path = $path; |
203
|
|
|
$this->name = basename( $path ); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
function is_file() { |
207
|
|
|
return $this->type == 'f'; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
function is_dir() { |
211
|
|
|
return $this->type == 'd'; |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
class MockFS_Directory_Node extends MockFS_Node { |
216
|
|
|
public $type = 'd'; |
217
|
|
|
public $children = array(); // The child nodes of this directory |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
class MockFS_File_Node extends MockFS_Node { |
221
|
|
|
public $type = 'f'; |
222
|
|
|
public $contents = ''; // The contents of the file |
223
|
|
|
|
224
|
|
|
function __construct( $path, $contents = '' ) { |
225
|
|
|
parent::__construct( $path ); |
226
|
|
|
$this->contents = $contents; |
227
|
|
|
} |
228
|
|
|
} |
This check looks at variables that have been passed in as parameters and 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.