Completed
Push — develop ( d9328c...0bc50d )
by
unknown
03:21
created

Analysis_Response_Ops::add_occurrences()   D

Complexity

Conditions 15
Paths 26

Size

Total Lines 96

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
nc 26
nop 1
dl 0
loc 96
rs 4.8206
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
 * This file provides a class to manipulate the analysis response.
4
 *
5
 * @author David Riccitelli <[email protected]>
6
 * @since 3.25.0
7
 * @package Wordlift\Analysis\Response
8
 */
9
10
namespace Wordlift\Analysis\Response;
11
12
use stdClass;
13
use Wordlift\Entity\Entity_Helper;
14
15
class Analysis_Response_Ops {
16
17
	/**
18
	 * The analysis response json.
19
	 *
20
	 * @since 3.21.5
21
	 * @access private
22
	 * @var mixed $json Holds the analysis response json.
23
	 */
24
	private $json;
25
26
	/**
27
	 * Holds the {@link Wordlift_Entity_Uri_Service}.
28
	 *
29
	 * @since 3.21.5
30
	 * @access private
31
	 * @var \Wordlift_Entity_Uri_Service $entity_uri_service The {@link Wordlift_Entity_Uri_Service} instance.
32
	 */
33
	private $entity_uri_service;
34
35
	private $entity_service;
36
37
	/**
38
	 * @var \Wordlift_Entity_Type_Service
39
	 */
40
	private $entity_type_service;
41
	/**
42
	 * @var \Wordlift_Post_Image_Storage
43
	 */
44
	private $post_image_storage;
45
46
	/**
47
	 * @var Entity_Helper
48
	 */
49
	private $entity_helper;
50
51
	/**
52
	 * Analysis_Response_Ops constructor.
53
	 *
54
	 * @param \Wordlift_Entity_Uri_Service $entity_uri_service The {@link Wordlift_Entity_Uri_Service}.
55
	 * @param \Wordlift_Entity_Service $entity_service The {@link Wordlift_Entity_Service}.
56
	 * @param \Wordlift_Entity_Type_Service $entity_type_service The {@link Wordlift_Entity_Type_Service}.
57
	 * @param \Wordlift_Post_Image_Storage $post_image_storage A {@link Wordlift_Post_Image_Storage} instance.
58
	 * @param Entity_Helper $entity_helper The {@link Entity_Helper}.
59
	 * @param mixed $json The analysis response json.
60
	 *
61
	 * @since 3.21.5
62
	 */
63 View Code Duplication
	public function __construct( $entity_uri_service, $entity_service, $entity_type_service, $post_image_storage, $entity_helper, $json ) {
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...
64
65
		$this->json                = $json;
66
		$this->entity_uri_service  = $entity_uri_service;
67
		$this->entity_service      = $entity_service;
68
		$this->entity_type_service = $entity_type_service;
69
		$this->post_image_storage  = $post_image_storage;
70
		$this->entity_helper       = $entity_helper;
71
72
	}
73
74
	/**
75
	 * Switches remote entities, i.e. entities with id outside the local dataset, to local entities.
76
	 *
77
	 * The function takes all the entities that have an id which is not local. For each remote entity, a list of URIs
78
	 * is built comprising the entity id and the sameAs. Then a query is issued in the local database to find potential
79
	 * matches from the local vocabulary.
80
	 *
81
	 * If found, the entity id is swapped with the local id and the remote id is added to the sameAs.
82
	 *
83
	 * @return Analysis_Response_Ops The current Analysis_Response_Ops instance.
84
	 */
85
	public function make_entities_local() {
86
87
		if ( ! isset( $this->json->entities ) ) {
88
			return $this;
89
		}
90
91
		// Get the URIs.
92
		$uris     = array_keys( get_object_vars( $this->json->entities ) );
93
		$mappings = $this->entity_helper->map_many_to_local( $uris );
94
95
		foreach ( $mappings as $external_uri => $internal_uri ) {
96
97
			// Move the data from the external URI to the internal URI.
98
			if ( ! isset( $this->json->entities->{$internal_uri} ) ) {
99
				$this->json->entities->{$internal_uri} = $this->json->entities->{$external_uri};
100
			}
101
102
			// Ensure sameAs is an array.
103
			if ( ! isset( $this->json->entities->{$internal_uri}->sameAs )
104
			     || ! is_array( $this->json->entities->{$internal_uri}->sameAs ) ) {
105
				$this->json->entities->{$internal_uri}->sameAs = array();
106
			}
107
108
			// Add the external URI as sameAs.
109
			$this->json->entities->{$internal_uri}->sameAs[] = $external_uri;
110
111
			// Finally remove the external URI.
112
			unset( $this->json->entities->{$external_uri} );
113
		}
114
115
		if ( isset( $this->json->annotations ) ) {
116
			foreach ( $this->json->annotations as $key => $annotation ) {
117
				if ( isset( $annotation->entityMatches ) ) {
118
					foreach ( $annotation->entityMatches as $match ) {
119
						if ( isset( $match->entityId ) && isset( $mappings[ $match->entityId ] ) ) {
120
							$match->entityId = $mappings[ $match->entityId ];
121
						}
122
					}
123
				}
124
			}
125
		}
126
127
		return $this;
128
	}
129
130
	/**
131
	 * Add occurrences by parsing the provided html content.
132
	 *
133
	 * @param string $content The html content with annotations.
134
	 *
135
	 * @return Analysis_Response_Ops The {@link Analysis_Response_Ops} instance.
136
	 *
137
	 * @since 3.23.7 refactor the regex pattern to take into account that there might be css classes between textannotation
138
	 *  and disambiguated.
139
	 *
140
	 * @link https://github.com/insideout10/wordlift-plugin/issues/1001
141
	 */
142
	public function add_occurrences( $content ) {
143
144
		// Try to get all the disambiguated annotations and bail out if an error occurs.
145
		if ( false === preg_match_all(
146
				'|<span\s+id="([^"]+)"\s+class="textannotation\s+(?:\S+\s+)?disambiguated(?=[\s"])[^"]*"\s+itemid="([^"]*)">(.*?)</span>|',
147
				$content,
148
				$matches,
149
				PREG_OFFSET_CAPTURE
150
			) ) {
151
			return $this;
152
		}
153
154
		if ( empty( $matches ) ) {
155
			return $this;
156
		}
157
158
		$parse_data = array_reduce( range( 0, count( $matches[1] ) - 1 ), function ( $carry, $i ) use ( $matches ) {
159
			if ( empty( $matches[0] ) ) {
160
				return $carry;
161
			}
162
163
			$start         = $matches[0][ $i ][1];
164
			$end           = $start + strlen( $matches[0][ $i ][0] );
165
			$annotation_id = $matches[1][ $i ][0];
166
			$item_id       = $matches[2][ $i ][0];
167
			$text          = $matches[3][ $i ][0];
168
169
			$annotation               = new StdClass;
170
			$annotation->annotationId = $annotation_id;
171
			$annotation->start        = $start;
172
			$annotation->end          = $end;
173
			$annotation->text         = $text;
174
175
			$entity_match                = new StdClass;
176
			$entity_match->confidence    = 100;
177
			$entity_match->entityId      = $item_id;
178
			$annotation->entityMatches[] = $entity_match;
179
180
			$carry['annotations'][ $annotation_id ] = $annotation;
181
			$carry['occurrences'][ $item_id ][]     = $annotation_id;
182
183
			return $carry;
184
		}, array( 'annotations' => array(), 'occurrences' => array(), ) );
185
186
		$annotations = $parse_data['annotations'];
187
		$occurrences = $parse_data['occurrences'];
188
189
		foreach ( array_keys( $occurrences ) as $item_id ) {
190
191
			// If the entity isn't there, add it.
192
			if ( ! is_bool( $this->json ) && ! isset( $this->json->entities->{$item_id} ) ) {
193
				$entity = $this->get_local_entity( $item_id );
194
195
				// Entity not found in the local vocabulary, continue to the next one.
196
				if ( false === $entity ) {
197
					continue;
198
				}
199
200
				$this->json->entities->{$item_id} = $entity;
201
			}
202
		}
203
204
		// Here we're adding back some data structures required by the client-side code.
205
		//
206
		// We're adding:
207
		//  1. the .entities[entity_id].occurrences array with the annotations' ids.
208
		//  2. the .entities[entity_id].annotations[annotation_id] = { id: annotation_id } map.
209
		//
210
		// Before 3.23.0 this was done by the client-side code located in src/coffee/editpost-widget/app.services.AnalysisService.coffee
211
		// function `preselect`, which was called by src/coffee/editpost-widget/app.services.EditorService.coffee in
212
		// `embedAnalysis`.
213
214
		if ( ! is_bool( $this->json ) ) {
215
			foreach ( $this->json->entities as $id => $entity ) {
216
				$this->json->entities->{$id}->occurrences = isset( $occurrences[ $id ] ) ? $occurrences[ $id ] : array();;
217
218
				foreach ( $this->json->entities->{$id}->occurrences as $annotation_id ) {
219
					$this->json->entities->{$id}->annotations[ $annotation_id ] = array(
220
						'id' => $annotation_id,
221
					);
222
				}
223
			}
224
		}
225
226
		// Add the missing annotations. This allows the analysis response to work also if we didn't receive results
227
		// from the analysis API.
228
		foreach ( $annotations as $annotation_id => $annotation ) {
229
230
			if ( ! is_bool( $this->json ) && ! isset( $this->json->annotations->{$annotation_id} ) ) {
231
				$this->json->annotations->{$annotation_id} = $annotation;
232
			}
233
234
		}
235
236
		return $this;
237
	}
238
239
	/**
240
	 * Add local entities
241
	 *
242
	 * @return Analysis_Response_Ops The {@link Analysis_Response_Ops} instance.
243
	 *
244
	 * @since 3.27.6
245
	 *
246
	 * @link https://github.com/insideout10/wordlift-plugin/issues/1178
247
	 */
248
	public function add_local_entities(){
249
250
		// Populating the local entities object
251
		$entities = array();
252
		foreach ( $this->json->annotations as $annotation ) {
253
			foreach ($annotation->entityMatches as $entity_matches){
254
255
				$entity_id = url_to_postid( htmlspecialchars_decode($entity_matches->entityId) );
256
				$serialized_entity = wl_serialize_entity( $entity_id );
257
258
				if($serialized_entity){
259
					$serialized_entity['entityId'] = $serialized_entity['id'];
260
					unset($serialized_entity['id']);
261
262
					$entities[$entity_matches->entityId] = $serialized_entity;
263
				}
264
265
			}
266
		}
267
268
		// Adding occurrences and annotations data structures required by the client-side code.
269
		foreach ($entities as $entity_id => $entity ){
270
			foreach ( $this->json->annotations as $annotation ) {
271
				if($annotation->entityMatches[0]->entityId === $entity_id){
272
					$entities[$entity_id]['occurrences'][] = $annotation->annotationId;
273
					$entities[$entity_id]['annotations'][$annotation->annotationId]['id'] = $annotation->annotationId;
274
				}
275
			}
276
		}
277
278
		$this->json->entities = $entities;
279
280
		return $this;
281
282
	}
283
284
	private function get_local_entity( $uri ) {
285
286
		$entity = $this->entity_uri_service->get_entity( $uri );
287
288
		if ( null === $entity ) {
289
			return false;
290
		}
291
292
		$type   = $this->entity_type_service->get( $entity->ID );
293
		$images = $this->post_image_storage->get( $entity->ID );
294
295
		return (object) array(
296
			'id'          => $uri,
297
			'label'       => $entity->post_title,
298
			/*
299
			 * As of 2020.06.29 we're comment out the `post_content` because Gutenberg posts will return here
300
			 * the whole Gutenberg source including potentially our own wordlift/classification block, which means
301
			 * that data may grow quickly to more than a 100 KBytes and could break web servers.
302
			 *
303
			 * We don't really need the description for local entities (because the description is indeed taken from
304
			 * the local WordPress database) and we're not using it anywhere in the UI.
305
			 *
306
			 * So take extra care in enabling this line: eventually consider using the post_excerpt.
307
			 *
308
			 * PS: We didn't test using the WordLift Post Excerpt Helper.
309
			 */
310
			// 'description' => $entity->post_content,
311
			'description' => '',
312
			'sameAs'      => wl_schema_get_value( $entity->ID, 'sameAs' ),
313
			'mainType'    => str_replace( 'wl-', '', $type['css_class'] ),
314
			'types'       => wl_get_entity_rdf_types( $entity->ID ),
315
			'images'      => $images,
316
		);
317
	}
318
319
	/**
320
	 * Return the JSON response.
321
	 *
322
	 * @return mixed The JSON response.
323
	 * @since 3.24.2
324
	 */
325
	public function get_json() {
326
327
		return $this->json;
328
	}
329
330
	/**
331
	 * Get the string representation of the JSON.
332
	 *
333
	 * @return false|string The string representation or false in case of error.
334
	 */
335
	public function to_string() {
336
337
		// Add the `JSON_UNESCAPED_UNICODE` only for PHP 5.4+.
338
		$options = ( version_compare( PHP_VERSION, '5.4', '>=' )
339
			? 256 : 0 );
340
341
		return wp_json_encode( $this->json, $options );
342
	}
343
344
}
345