Passed
Push — ci ( 38d7bc...9f72b5 )
by litefeel
03:33
created

Writing_On_GitHub_Import::compare()   D

Complexity

Conditions 18
Paths 241

Size

Total Lines 73
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 0
Metric Value
dl 0
loc 73
ccs 0
cts 47
cp 0
rs 4.2411
c 0
b 0
f 0
cc 18
eloc 39
nc 241
nop 2
crap 342

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
 * GitHub Import Manager
4
 *
5
 * @package Writing_On_GitHub
6
 */
7
8
/**
9
 * Class Writing_On_GitHub_Import
10
 */
11
class Writing_On_GitHub_Import {
12
13
	/**
14
	 * Application container.
15
	 *
16
	 * @var Writing_On_GitHub
17
	 */
18
	protected $app;
19
20
	/**
21
	 * Initializes a new import manager.
22
	 *
23
	 * @param Writing_On_GitHub $app Application container.
24
	 */
25
	public function __construct( Writing_On_GitHub $app ) {
26
		$this->app = $app;
27
	}
28
29
    /**
30
     * Imports a payload.
31
     * @param  Writing_On_GitHub_Payload $payload
32
     *
33
     * @return string|WP_Error
34
     */
35 View Code Duplication
	public function payload( Writing_On_GitHub_Payload $payload ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
36
37
		$result = $this->app->api()->fetch()->compare( $payload->get_before_commit_id() );
38
39
		if ( is_wp_error( $result ) ) {
40
            /* @var WP_Error $result */
41
			return $result;
42
		}
43
44
		$result = $this->import_files( $result );
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->import_files($result) on line 44 can also be of type array or object<stdClass>; however, Writing_On_GitHub_Import::import_files() does only seem to accept array<integer,object<Wri...g_On_GitHub_File_Info>>, 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...
45
46
		if ( is_wp_error( $result ) ) {
47
			return $files;
48
		}
49
50
		return __( 'Payload processed', 'writing-on-github' );
51
	}
52
53
	/**
54
	 * import blob by files
55
	 * @param  Writing_On_GitHub_File_Info[] $files
56
     *
57
	 * @return string|WP_Error
0 ignored issues
show
Documentation introduced by
Should the return type not be WP_Error|string|array|false? Also, consider making the array more specific, something like array<String>, or 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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
58
	 */
59
	protected function import_files( $files ) {
60
61
		$error 		= false;
62
		$delete_ids = false;
63
64
		$result = $this->compare( $files, $delete_ids );
65
66
		if ( is_wp_error( $result ) ) {
67
			return $result;
68
		}
69
70
		if ( $delete_ids ) {
71 View Code Duplication
			foreach ($delete_ids as $id) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
introduced by
No space after opening parenthesis is prohibited
Loading history...
introduced by
No space before closing parenthesis is prohibited
Loading history...
72
				$result = $this->app->database()->delete_post( $id );
73
				if ( is_wp_error( $result ) ) {
74
					if ( $error ) {
75
						$error->add( $result->get_error_code(), $result->get_error_message() );
76
					} else {
77
						$error = $result;
78
					}
79
				}
80
			}
81
		}
82
83
		return $error;
84
	}
85
86
	/**
87
	 * Imports the latest commit on the master branch.
88
	 *
89
	 * @return string|WP_Error
90
	 */
91 View Code Duplication
	public function master() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
92
		$result = $this->app->api()->fetch()->tree_recursive();
93
94
		if ( is_wp_error( $result ) ) {
95
            /* @var WP_Error $result */
96
			return $result;
97
		}
98
99
		$result = $this->import_files( $result );
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->import_files($result) on line 99 can also be of type array or object<stdClass>; however, Writing_On_GitHub_Import::import_files() does only seem to accept array<integer,object<Wri...g_On_GitHub_File_Info>>, 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...
100
101
		if ( is_wp_error( $result ) ) {
102
			return $result;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $result; (WP_Error|string|array|false) is incompatible with the return type documented by Writing_On_GitHub_Import::master of type string|WP_Error.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
103
		}
104
105
		return __( 'Payload processed', 'writing-on-github' );
106
	}
107
108
    /**
109
     * Do compare
110
     * @param  Writing_On_GitHub_File_Info[]|WP_Error $files
111
     * @param  int[] &$delete_ids
112
     *
113
     * @return string|WP_Error
0 ignored issues
show
Documentation introduced by
Should the return type not be WP_Error|string|array? Also, consider making the array more specific, something like array<String>, or 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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
114
     */
115
	protected function compare( $files, &$delete_ids ) {
0 ignored issues
show
Complexity introduced by
This operation has 7776 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
116
		if ( is_wp_error( $files ) ) {
117
            /* @var WP_Error $files */
118
			return $files;
119
		}
120
121
		$posts = array();
122
		$new   = array();
123
124
		$idsmap = array();
125
126
		foreach ( $files as $file ) {
127
			if ( ! $this->importable_file( $file ) ) {
128
				continue;
129
			}
130
131
			$blob = $this->app->api()->fetch()->blob( $file );
132
			// network error ?
133
			if ( ! $blob instanceof Writing_On_GitHub_Blob ) {
134
				continue;
135
			}
136
137
			if ( $this->importable_raw_file( $blob ) ) {
138
				$this->import_raw_file( $blob, $file->status == 'removed' );
139
				continue;
140
			}
141
142
			if ( ! $this->importable_blob( $blob ) ) {
143
				continue;
144
			}
145
146
			$post = $this->blob_to_post( $blob );
147
148
			if ( $file->status == 'removed' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
149
				if ( $blob->id() ) {
150
					$idsmap[$blob->id()] = true;
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
151
				}
152
			} elseif ( $post != false ) {
0 ignored issues
show
introduced by
Found "!= false". Use Yoda Condition checks, you must
Loading history...
153
				$posts[] = $post;
154
				if ( $post->is_new() ) {
155
					$new[] = $post;
156
				}
157
			}
158
		}
159
160
		foreach ( $posts as $post ) {
161
			if ( $post->id() && isset( $idsmap[ $post->id() ] ) ) {
162
				unset( $idsmap[ $post->id() ] );
163
			}
164
		}
165
		$delete_ids = array();
166
		foreach ( $idsmap as $id => $value ) {
167
			$delete_ids[] = $id;
168
		}
169
170
		// $this->app->database()->save_posts( $posts, $commit->author_email() );
171
172
		$result = $this->app->database()->save_posts( $posts );
173
174
		if ( is_wp_error( $result ) ) {
175
			return $result;
176
		}
177
178
		if ( ! empty( $new ) ) {
179
			$result = $this->app->export()->new_posts( $new );
180
181
			if ( is_wp_error( $result ) ) {
182
				return $result;
183
			}
184
		}
185
186
		return $posts;
187
	}
188
189
	/**
190
	 * Checks whether the provided blob should be imported.
191
	 *
192
	 * @param Writing_On_GitHub_File_Info $file
193
	 *
194
	 * @return bool
195
	 */
196
	protected function importable_file( Writing_On_GitHub_File_Info $file ) {
197
198
		// only _pages and _posts
199
		if ( strncasecmp($file->path, '_pages/', strlen('_pages/') ) != 0 &&
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !(strncasecmp($fi...rlen('images/')) != 0);.
Loading history...
introduced by
Found "!= 0". Use Yoda Condition checks, you must
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
200
			 strncasecmp($file->path, '_posts/', strlen('_posts/') ) != 0 &&
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
201
			 strncasecmp($file->path, 'images/', strlen('images/') ) != 0 ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
202
			return false;
203
		}
204
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
205
206
		// if ( ! $file->has_frontmatter() ) {
207
		// 	return false;
208
		// }
209
210
		return true;
211
	}
212
213
	/**
214
	 * Checks whether the provided blob should be imported.
215
	 *
216
	 * @param Writing_On_GitHub_Blob $blob Blob to validate.
217
	 *
218
	 * @return bool
219
	 */
220
	protected function importable_blob( Writing_On_GitHub_Blob $blob ) {
221
		// global $wpdb;
222
223
		// // Skip the repo's readme.
224
		// if ( 'readme' === strtolower( substr( $blob->path(), 0, 6 ) ) ) {
225
		// 	return false;
226
		// }
227
228
		// // If the blob sha already matches a post, then move on.
229
		// if ( ! is_wp_error( $this->app->database()->fetch_by_sha( $blob->sha() ) ) ) {
230
		// 	return false;
231
		// }
232
233
		if ( ! $blob->has_frontmatter() ) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $blob->has_frontmatter();.
Loading history...
234
			return false;
235
		}
236
237
		return true;
238
	}
239
240
	protected function importable_raw_file( Writing_On_GitHub_Blob $blob ) {
241
		if ( $blob->has_frontmatter() ) {
242
			return false;
243
		}
244
245
		// only images
246
		if ( strncasecmp($blob->path(), 'images/', strlen('images/') ) != 0) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !(strncasecmp($bl...rlen('images/')) != 0);.
Loading history...
introduced by
Found "!= 0". Use Yoda Condition checks, you must
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
introduced by
No space before closing parenthesis is prohibited
Loading history...
247
			return false;
248
		}
249
250
		return true;
251
	}
252
253
	/**
254
	 * Imports a raw file content into file system.
255
	 * @param  Writing_On_GitHub_Blob $blob
256
	 * @param  bool                   $is_remove
257
	 */
258
	protected function import_raw_file( Writing_On_GitHub_Blob $blob, $is_remove ) {
259
		$arr = wp_upload_dir();
260
		$path = $arr['basedir'] . '/writing-on-github/' . $blob->path();
261
		if ( $is_remove ) {
262
			if ( file_exists($path) ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
263
				unlink($path);
0 ignored issues
show
introduced by
Filesystem writes are forbidden, you should not be using unlink()
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
264
			}
265
		} else {
266
			$dirname = dirname($path);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
267
			if ( ! file_exists($dirname) ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
268
				wp_mkdir_p($dirname);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
269
			}
270
271
			file_put_contents($path, $blob->content());
0 ignored issues
show
introduced by
Filesystem writes are forbidden, you should not be using file_put_contents()
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
272
		}
273
	}
274
275
	/**
276
	 * Imports a single blob content into matching post.
277
	 *
278
	 * @param Writing_On_GitHub_Blob $blob Blob to transform into a Post.
279
	 *
280
	 * @return Writing_On_GitHub_Post|false
281
	 */
282
	protected function blob_to_post( Writing_On_GitHub_Blob $blob ) {
0 ignored issues
show
Complexity introduced by
This operation has 582 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
283
		$args = array( 'post_content' => $blob->content_import() );
284
		$meta = $blob->meta();
285
286
		$id = false;
287
288
		if ( ! empty( $meta ) ) {
289
			if ( array_key_exists( 'layout', $meta ) ) {
290
				$args['post_type'] = $meta['layout'];
291
				unset( $meta['layout'] );
292
			}
293
294
			if ( array_key_exists( 'published', $meta ) ) {
295
				$args['post_status'] = true === $meta['published'] ? 'publish' : 'draft';
296
				unset( $meta['published'] );
297
			}
298
299
			if ( array_key_exists( 'post_title', $meta ) ) {
300
				$args['post_title'] = $meta['post_title'];
301
				unset( $meta['post_title'] );
302
			}
303
304
			if ( array_key_exists( 'post_name', $meta ) ) {
305
				$args['post_name'] = $meta['post_name'];
306
				unset( $meta['post_name'] );
307
			}
308
309
			if ( array_key_exists( 'ID', $meta ) ) {
310
				$id = $args['ID'] = $meta['ID'];
311
				$blob->set_id($id);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
312
				unset( $meta['ID'] );
313
			}
314
		}
315
316
		$meta['_wogh_sha'] = $blob->sha();
317
318
		if ( $id ) {
319
			$old_sha = get_post_meta( $id, '_wogh_sha', true );
320
			$old_github_path = get_post_meta( $id, '_wogh_github_path', true );
321
322
			// dont save post when has same sha
323
			if ( $old_sha  && $old_sha == $meta['_wogh_sha'] &&
324
				 $old_github_path && $old_github_path == $blob->path() ) {
325
				return false;
326
			}
327
		}
328
329
		$post = new Writing_On_GitHub_Post( $args, $this->app->api() );
330
		$post->set_old_github_path( $blob->path() );
331
		$post->set_meta( $meta );
332
		$blob->set_id( $post->id() );
333
334
		return $post;
335
	}
336
}
337