Completed
Branch BETA-4.9-messages-queue-fixed (941081)
by
unknown
30:24 queued 05:56
created

Read::validate_context()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 0
loc 11
rs 9.4285
c 1
b 1
f 0
cc 3
eloc 8
nc 4
nop 1
1
<?php
2
namespace EventEspresso\core\libraries\rest_api\controllers\model;
3
use EventEspresso\core\libraries\rest_api\Capabilities;
4
if ( !defined( 'EVENT_ESPRESSO_VERSION' ) ) {
5
	exit( 'No direct script access allowed' );
6
}
7
8
/**
9
 *
10
 * Read controller for models
11
 *
12
 * Handles requests relating to GET-ting model information
13
 *
14
 * @package			Event Espresso
15
 * @subpackage
16
 * @author				Mike Nelson
17
 *
18
 */
19
class Read extends Base {
20
21
22
23
24
25
	public function __construct() {
26
		parent::__construct();
27
		\EE_Registry::instance()->load_helper( 'Inflector' );
28
	}
29
30
	/**
31
	 * Handles requests to get all (or a filtered subset) of entities for a particular model
32
	 * @param \WP_REST_Request $request
33
	 * @return \WP_REST_Response|\WP_Error
34
	 */
35 View Code Duplication
	public static function handle_request_get_all( \WP_REST_Request $request) {
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
		$controller = new Read();
37
		try{
38
			$matches = $controller->parse_route(
39
				$request->get_route(),
40
				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)~',
41
				array( 'version', 'model' )
42
			);
43
			$controller->set_requested_version( $matches[ 'version' ] );
44
			$model_name_singular = \EEH_Inflector::singularize_and_upper( $matches[ 'model' ] );
45
			if ( ! $controller->get_model_version_info()->is_model_name_in_this_version( $model_name_singular ) ) {
46
				return $controller->send_response(
47
					new \WP_Error(
48
						'endpoint_parsing_error',
49
						sprintf(
50
							__( 'There is no model for endpoint %s. Please contact event espresso support', 'event_espresso' ),
51
							$model_name_singular
52
						)
53
					)
54
				);
55
			}
56
			return $controller->send_response(
57
					$controller->get_entities_from_model(
58
							$controller->get_model_version_info()->load_model( $model_name_singular ),
0 ignored issues
show
Documentation introduced by
$controller->get_model_v...l($model_name_singular) is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
59
							$request
60
					)
61
			);
62
		} catch( \Exception $e ) {
63
			return $controller->send_response( $e );
64
		}
65
	}
66
67
	/**
68
	 * Gets a single entity related to the model indicated in the path and its id
69
	 *
70
	 * @param \WP_Rest_Request $request
71
	 * @return \WP_REST_Response|\WP_Error
72
	 */
73 View Code Duplication
	public static function handle_request_get_one( \WP_Rest_Request $request ) {
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...
74
		$controller = new Read();
75
		try{
76
			$matches = $controller->parse_route(
77
				$request->get_route(),
78
				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)~',
79
				array( 'version', 'model', 'id' ) );
80
			$controller->set_requested_version( $matches[ 'version' ] );
81
			$model_name_singular = \EEH_Inflector::singularize_and_upper( $matches[ 'model' ] );
82
			if ( ! $controller->get_model_version_info()->is_model_name_in_this_version( $model_name_singular ) ) {
83
				return $controller->send_response(
84
					new \WP_Error(
85
						'endpoint_parsing_error',
86
						sprintf(
87
							__( 'There is no model for endpoint %s. Please contact event espresso support', 'event_espresso' ),
88
							$model_name_singular
89
						)
90
					)
91
				);
92
			}
93
			return $controller->send_response(
94
					$controller->get_entity_from_model(
95
							$controller->get_model_version_info()->load_model( $model_name_singular ),
0 ignored issues
show
Documentation introduced by
$controller->get_model_v...l($model_name_singular) is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
96
							$request
97
						)
98
				);
99
		} catch( \Exception $e ) {
100
			return $controller->send_response( $e );
101
		}
102
	}
103
104
	/**
105
	 *
106
	 * Gets all the related entities (or if its a belongs-to relation just the one)
107
	 * to the item with the given id
108
	 *
109
	 * @param \WP_REST_Request $request
110
	 * @return \WP_REST_Response|\WP_Error
111
	 */
112
	public static function handle_request_get_related( \WP_REST_Request $request ) {
113
		$controller = new Read();
114
		try{
115
			$matches = $controller->parse_route(
116
				$request->get_route(),
117
				'~' . \EED_Core_Rest_Api::ee_api_namespace_for_regex . '(.*)/(.*)/(.*)~',
118
				array( 'version', 'model', 'id', 'related_model' )
119
			);
120
			$controller->set_requested_version( $matches[ 'version' ] );
121
			$main_model_name_singular = \EEH_Inflector::singularize_and_upper( $matches[ 'model' ] );
122
			if ( ! $controller->get_model_version_info()->is_model_name_in_this_version( $main_model_name_singular ) ) {
123
				return $controller->send_response(
124
					new \WP_Error(
125
						'endpoint_parsing_error',
126
						sprintf(
127
							__( 'There is no model for endpoint %s. Please contact event espresso support', 'event_espresso' ),
128
							$main_model_name_singular
129
						)
130
					)
131
				);
132
			}
133
			$main_model = $controller->get_model_version_info()->load_model( $main_model_name_singular );
134
			$related_model_name_singular = \EEH_Inflector::singularize_and_upper( $matches[ 'related_model' ] );
135
			if ( ! $controller->get_model_version_info()->is_model_name_in_this_version( $related_model_name_singular ) ) {
136
				return $controller->send_response(
137
					new \WP_Error(
138
						'endpoint_parsing_error',
139
						sprintf(
140
							__( 'There is no model for endpoint %s. Please contact event espresso support', 'event_espresso' ),
141
							$related_model_name_singular
142
						)
143
					)
144
				);
145
			}
146
147
			return $controller->send_response(
148
					$controller->get_entities_from_relation(
149
						$request->get_param( 'id' ),
150
						$main_model->related_settings_for( $related_model_name_singular ) ,
0 ignored issues
show
Bug introduced by
The method related_settings_for cannot be called on $main_model (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
151
						$request
152
					)
153
				);
154
		} catch( \Exception $e ) {
155
			return $controller->send_response( $e );
156
		}
157
	}
158
159
160
161
	/**
162
	 * Gets a collection for the given model and filters
163
	 *
164
	 * @param \EEM_Base $model
165
	 * @param \WP_REST_Request $request
166
	 * @return array
167
	 */
168
	public function get_entities_from_model( $model, $request) {
169
		$query_params = $this->create_model_query_params( $model, $request->get_params() );
170
		if( ! Capabilities::current_user_has_partial_access_to( $model, $query_params[ 'caps' ] ) ) {
171
			$model_name_plural = \EEH_Inflector::pluralize_and_lower( $model->get_this_model_name() );
172
			return new \WP_Error(
173
				sprintf( 'rest_%s_cannot_list', $model_name_plural ),
174
				sprintf(
175
					__( 'Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso' ),
176
					$model_name_plural,
177
					Capabilities::get_missing_permissions_string( $model, $query_params[ 'caps' ] )
178
				),
179
				array( 'status' => 403 )
180
			);
181
		}
182
183
		$this->_set_debug_info( 'model query params', $query_params );
184
		$results = $model->get_all_wpdb_results( $query_params );
185
		$nice_results = array( );
186
		foreach ( $results as $result ) {
187
			$nice_results[ ] = $this->create_entity_from_wpdb_result(
188
					$model,
189
					$result,
190
					$request->get_param( 'include' ),
191
					$query_params[ 'caps' ]
192
				);
193
		}
194
		return $nice_results;
195
	}
196
197
	/**
198
	 * Gets the collection for given relation object
199
	 *
200
	 * The same as Read::get_entities_from_model(), except if the relation
201
	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
202
	 * the join-model-object into the results
203
	 * @param string $id the ID of the thing we are fetching related stuff from
204
	 * @param \EE_Model_Relation_Base $relation
205
	 * @param \WP_REST_Request $request
206
	 * @return array
207
	 */
208
	public function get_entities_from_relation( $id,  $relation, $request ) {
209
		$context = $this->validate_context( $request->get_param( 'caps' ));
210
		$model = $relation->get_this_model();
211
		$related_model = $relation->get_other_model();
212
		//check if they can access the 1st model object
213
		$query_params = array( array( $model->primary_key_name() => $id ),'limit' => 1 );
0 ignored issues
show
Bug introduced by
The method primary_key_name cannot be called on $model (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
214
		if( $model instanceof \EEM_Soft_Delete_Base ){
215
			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
216
		}
217
		$restricted_query_params = $query_params;
218
		$restricted_query_params[ 'caps' ] = $context;
219
		$this->_set_debug_info( 'main model query params', $restricted_query_params );
220
		$this->_set_debug_info( 'missing caps', Capabilities::get_missing_permissions_string( $related_model, $context ) );
0 ignored issues
show
Documentation introduced by
$related_model is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
221
222
		if(
223
			! (
224
				Capabilities::current_user_has_partial_access_to( $related_model, $context )
0 ignored issues
show
Documentation introduced by
$related_model is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
225
				&& $model->exists( $restricted_query_params )
0 ignored issues
show
Bug introduced by
The method exists cannot be called on $model (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
226
			)
227
		){
228
			if( $relation instanceof \EE_Belongs_To_Relation ) {
229
				$related_model_name_maybe_plural = strtolower( $related_model->get_this_model_name() );
0 ignored issues
show
Bug introduced by
The method get_this_model_name cannot be called on $related_model (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
230
			}else{
231
				$related_model_name_maybe_plural = \EEH_Inflector::pluralize_and_lower( $related_model->get_this_model_name() );
0 ignored issues
show
Bug introduced by
The method get_this_model_name cannot be called on $related_model (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
232
			}
233
			return new \WP_Error(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \WP_Error(spr...rray('status' => 403)); (WP_Error) is incompatible with the return type documented by EventEspresso\core\libra..._entities_from_relation of type array.

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...
234
				sprintf( 'rest_%s_cannot_list', $related_model_name_maybe_plural ),
235
				sprintf(
236
					__(	'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s', 'event_espresso' ),
237
					$related_model_name_maybe_plural,
238
					$relation->get_this_model()->get_this_model_name(),
0 ignored issues
show
Bug introduced by
The method get_this_model_name cannot be called on $relation->get_this_model() (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
239
					implode(
240
						',',
241
						array_keys(
242
							Capabilities::get_missing_permissions( $related_model, $context )
0 ignored issues
show
Documentation introduced by
$related_model is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
243
						)
244
					)
245
				),
246
				array( 'status' => 403 )
247
			);
248
		}
249
		$query_params = $this->create_model_query_params( $relation->get_other_model(), $request->get_params() );
0 ignored issues
show
Documentation introduced by
$relation->get_other_model() is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
250
		$query_params[0][ $relation->get_this_model()->get_this_model_name() . '.' . $relation->get_this_model()->primary_key_name() ] = $id;
0 ignored issues
show
Bug introduced by
The method get_this_model_name cannot be called on $relation->get_this_model() (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method primary_key_name cannot be called on $relation->get_this_model() (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
251
		$query_params[ 'default_where_conditions' ] = 'none';
252
		$query_params[ 'caps' ] = $context;
253
		$this->_set_debug_info( 'model query params', $query_params );
254
		$results = $relation->get_other_model()->get_all_wpdb_results( $query_params );
0 ignored issues
show
Bug introduced by
The method get_all_wpdb_results cannot be called on $relation->get_other_model() (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
255
		$nice_results = array();
256
		foreach( $results as $result ) {
257
			$nice_result = $this->create_entity_from_wpdb_result(
258
				$relation->get_other_model(),
0 ignored issues
show
Documentation introduced by
$relation->get_other_model() is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
259
				$result,
260
				$request->get_param( 'include' ),
261
				$query_params[ 'caps' ]
262
			);
263
			if( $relation instanceof \EE_HABTM_Relation ) {
264
				//put the unusual stuff (properties from the HABTM relation) first, and make sure
265
				//if there are conflicts we prefer the properties from the main model
266
				$join_model_result = $this->create_entity_from_wpdb_result(
267
					$relation->get_join_model(),
0 ignored issues
show
Documentation introduced by
$relation->get_join_model() is of type boolean, but the function expects a object<EEM_Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
268
					$result,
269
					$request->get_param( 'include' ),
270
					$query_params[ 'caps' ]
271
				);
272
				$joined_result = array_merge( $nice_result, $join_model_result );
273
				//but keep the meta stuff from the main model
274
				if( isset( $nice_result['meta'] ) ){
275
					$joined_result['meta'] = $nice_result['meta'];
276
				}
277
				$nice_result = $joined_result;
278
			}
279
			$nice_results[] = $nice_result;
280
		}
281
		if( $relation instanceof \EE_Belongs_To_Relation ){
282
			return array_shift( $nice_results );
283
		}else{
284
			return $nice_results;
285
		}
286
	}
287
288
289
290
	/**
291
	 * Changes database results into REST API entities
292
	 * @param \EEM_Base $model
293
	 * @param array $db_row like results from $wpdb->get_results()
294
	 * @param string $include string indicating which fields to include in the response,
295
	 *                        including fields on related entities.
296
	 *                        Eg, when querying for events, an include string like:
297
	 *                        "...&include=EVT_name,EVT_desc,Datetime, Datetime.Ticket.TKT_ID, Datetime.Ticket.TKT_name, Datetime.Ticket.TKT_price"
298
	 *                        instructs us to only include the event's name and description,
299
	 *                        each related datetime, and each related datetime's ticket's name and price.
300
	 *                        Eg json would be:
301
	 *                          '{
302
	 *                              "EVT_ID":12,
303
	 * 								"EVT_name":"star wars party",
304
	 * 								"EVT_desc":"so cool...",
305
	 * 								"datetimes":[{
306
	 * 									"DTT_ID":123,...,
307
	 * 									"tickets":[{
308
	 * 										"TKT_ID":234,
309
	 * 										"TKT_name":"student rate",
310
	 * 										"TKT_price":32.0
311
	 * 									},...]
312
	 * 								}]
313
	 * 							}',
314
	 *                        ie, events with all their associated datetimes
315
	 *                        (including ones that are trashed) embedded in the json object,
316
	 *                        and each datetime also has each associated ticket embedded in its json object.
317
	 * @param string $context one of the return values from EEM_Base::valid_cap_contexts()
318
	 * @return array ready for being converted into json for sending to client
319
	 */
320
	public function create_entity_from_wpdb_result( $model, $db_row, $include, $context ) {
321
		if( $include == null ) {
322
			$include = '*';
323
		}
324
		if( $context == null ) {
325
			$context = \EEM_Base::caps_read;
326
		}
327
		$result = $model->deduce_fields_n_values_from_cols_n_values( $db_row );
328
		$result = array_intersect_key( $result, $this->get_model_version_info()->fields_on_model_in_this_version( $model ) );
329
		foreach( $result as $field_name => $raw_field_value ) {
330
			$field_obj = $model->field_settings_for($field_name);
331
			$field_value = $field_obj->prepare_for_set_from_db( $raw_field_value );
332
			if( $this->is_subclass_of_one(  $field_obj, $this->get_model_version_info()->fields_ignored() ) ){
333
				unset( $result[ $field_name ] );
334
			}elseif(
335
				$this->is_subclass_of_one( $field_obj, $this->get_model_version_info()->fields_that_have_rendered_format() )
336
			){
337
				$result[ $field_name ] = array(
338
					'raw' => $field_obj->prepare_for_get( $field_value ),
339
					'rendered' => $field_obj->prepare_for_pretty_echoing( $field_value )
340
				);
341
			}elseif(
342
				$this->is_subclass_of_one( $field_obj, $this->get_model_version_info()->fields_that_have_pretty_format() )
343
			){
344
				$result[ $field_name ] = array(
345
					'raw' => $field_obj->prepare_for_get( $field_value ),
346
					'pretty' => $field_obj->prepare_for_pretty_echoing( $field_value )
347
				);
348
			}elseif( $field_obj instanceof \EE_Datetime_Field ){
349
				if( $raw_field_value instanceof \DateTime ) {
350
					$raw_field_value = $raw_field_value->format( 'c' );
351
				}
352
				$result[ $field_name ] = mysql_to_rfc3339( $raw_field_value );
353
			}else{
354
				$value_prepared = $field_obj->prepare_for_get( $field_value );
355
356
				$result[ $field_name ] = $value_prepared === INF ? EE_INF_IN_DB : $value_prepared;
357
			}
358
		}
359
		if( $model instanceof \EEM_CPT_Base ) {
360
			$attachment = wp_get_attachment_image_src(
361
				get_post_thumbnail_id( $db_row[ $model->get_primary_key_field()->get_qualified_column() ] ),
362
				'full'
363
			);
364
			$result[ 'featured_image_url' ] = !empty( $attachment ) ? $attachment[ 0 ] : null;
365
			$result[ 'link' ] = get_permalink( $db_row[ $model->get_primary_key_field()->get_qualified_column() ] );
366
		}
367
		//add links to related data
368
		$result['_links'] = array(
369
			'self' => array(
370
				array(
371
					'href' => $this->get_versioned_link_to(
372
						\EEH_Inflector::pluralize_and_lower( $model->get_this_model_name() ) . '/' . $result[ $model->primary_key_name() ]
373
					)
374
				)
375
			),
376
			'collection' => array(
377
				array(
378
					'href' => $this->get_versioned_link_to(
379
						\EEH_Inflector::pluralize_and_lower( $model->get_this_model_name() )
380
					)
381
				)
382
			),
383
		);
384
		global $wp_rest_server;
385
		if( $model instanceof \EEM_CPT_Base &&
386
			$wp_rest_server instanceof \WP_REST_Server &&
0 ignored issues
show
Bug introduced by
The class WP_REST_Server does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
387
			$wp_rest_server->get_route_options( '/wp/v2/posts' ) ) {
388
			$result[ '_links' ][ 'https://api.eventespresso.com/self_wp_post' ] = array(
389
				array(
390
					'href' => rest_url( '/wp/v2/posts/' . $db_row[ $model->get_primary_key_field()->get_qualified_column() ] ),
391
					'single' => true
392
				)
393
			);
394
		}
395
396
		//filter fields if specified
397
		$includes_for_this_model = $this->extract_includes_for_this_model( $include );
398
		if( ! empty( $includes_for_this_model ) ) {
399
			if( $model->has_primary_key_field() ) {
400
				//always include the primary key
401
				$includes_for_this_model[] = $model->primary_key_name();
402
			}
403
			$result = array_intersect_key( $result, array_flip( $includes_for_this_model ) );
404
		}
405
		//add meta links and possibly include related models
406
		$relation_settings = apply_filters(
407
			'FHEE__Read__create_entity_from_wpdb_result__related_models_to_include',
408
			$model->relation_settings()
409
		);
410
		foreach( $relation_settings as $relation_name => $relation_obj ) {
411
			$related_model_part = $this->get_related_entity_name( $relation_name, $relation_obj );
412
			if( empty( $includes_for_this_model ) || isset( $includes_for_this_model['meta'] ) ) {
413
				$result['_links']['https://api.eventespresso.com/' . $related_model_part] = array(
414
					array(
415
						'href' => $this->get_versioned_link_to(
416
							\EEH_Inflector::pluralize_and_lower( $model->get_this_model_name() ) . '/' . $result[ $model->primary_key_name() ] . '/' . $related_model_part
417
						),
418
						'single' => $relation_obj instanceof \EE_Belongs_To_Relation ? true : false
419
					)
420
				);
421
			}
422
			$related_fields_to_include = $this->extract_includes_for_this_model( $include, $relation_name );
423
			if( $related_fields_to_include ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $related_fields_to_include 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...
424
				$pretend_related_request = new \WP_REST_Request();
425
				$pretend_related_request->set_query_params(
426
					array(
427
						'caps' => $context,
428
						'include' => $this->extract_includes_for_this_model(
429
								$include,
430
								$relation_name
431
							)
432
					)
433
				);
434
				$related_results = $this->get_entities_from_relation(
435
					$result[ $model->primary_key_name() ],
0 ignored issues
show
Documentation introduced by
$result[$model->primary_key_name()] is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
436
					$relation_obj,
437
					$pretend_related_request
438
				);
439
				$result[ $related_model_part ] = $related_results instanceof \WP_Error ? null : $related_results;
0 ignored issues
show
Bug introduced by
The class WP_Error does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
440
			}
441
		}
442
		$result = apply_filters(
443
			'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
444
			$result,
445
			$model,
446
			$context
447
		);
448
		$result_without_inaccessible_fields = Capabilities::filter_out_inaccessible_entity_fields(
449
			$result,
450
			$model,
451
			$context,
452
			$this->get_model_version_info()
453
		);
454
		$this->_set_debug_info(
455
			'inaccessible fields',
456
			array_keys( array_diff_key( $result, $result_without_inaccessible_fields ) )
457
		);
458
		return apply_filters(
459
			'FHEE__Read__create_entity_from_wpdb_results__entity_return',
460
			$result_without_inaccessible_fields,
461
			$model,
462
			$context
463
		);
464
	}
465
466
	/**
467
	 * Gets the full URL to the resource, taking the requested version into account
468
	 * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
469
	 * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
470
	 */
471
	public function get_versioned_link_to( $link_part_after_version_and_slash ) {
472
		return rest_url( \EED_Core_Rest_Api::ee_api_namespace . $this->get_model_version_info()->requested_version() . '/' . $link_part_after_version_and_slash );
473
	}
474
475
	/**
476
	 * Gets the correct lowercase name for the relation in the API according
477
	 * to the relation's type
478
	 * @param string $relation_name
479
	 * @param \EE_Model_Relation_Base $relation_obj
480
	 * @return string
481
	 */
482
	public static function get_related_entity_name( $relation_name, $relation_obj ){
483
		if( $relation_obj instanceof \EE_Belongs_To_Relation ) {
484
			return strtolower( $relation_name );
485
		}else{
486
			return \EEH_Inflector::pluralize_and_lower( $relation_name );
487
		}
488
	}
489
490
//	public function
491
492
493
	/**
494
	 * Gets the one model object with the specified id for the specified model
495
	 * @param \EEM_Base $model
496
	 * @param \WP_REST_Request $request
497
	 * @return array
498
	 */
499
	public function get_entity_from_model( $model, $request ) {
500
		$query_params = array( array( $model->primary_key_name() => $request->get_param( 'id' ) ),'limit' => 1);
501
		if( $model instanceof \EEM_Soft_Delete_Base ){
502
			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
503
		}
504
		$restricted_query_params = $query_params;
505
		$restricted_query_params[ 'caps' ] =  $this->validate_context(  $request->get_param( 'caps' ) );
506
		$this->_set_debug_info( 'model query params', $restricted_query_params );
507
		$model_rows = $model->get_all_wpdb_results( $restricted_query_params );
508
		if ( ! empty ( $model_rows ) ) {
509
			return $this->create_entity_from_wpdb_result(
510
				$model,
511
				array_shift( $model_rows ),
512
				$request->get_param( 'include' ),
513
				$this->validate_context( $request->get_param( 'caps' ) ) );
514
		} else {
515
			//ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
516
			$lowercase_model_name = strtolower( $model->get_this_model_name() );
517
			$model_rows_found_sans_restrictions = $model->get_all_wpdb_results( $query_params );
518
			if( ! empty( $model_rows_found_sans_restrictions ) ) {
519
				//you got shafted- it existed but we didn't want to tell you!
520
				return new \WP_Error(
521
					'rest_user_cannot_read',
522
					sprintf(
523
						__( 'Sorry, you cannot read this %1$s. Missing permissions are: %2$s', 'event_espresso' ),
524
						strtolower( $model->get_this_model_name() ),
525
						Capabilities::get_missing_permissions_string(
526
							$model,
527
							$this->validate_context( $request->get_param( 'caps' ) ) )
528
					),
529
					array( 'status' => 403 )
530
				);
531 View Code Duplication
			} else {
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...
532
				//it's not you. It just doesn't exist
533
				return new \WP_Error(
534
					sprintf( 'rest_%s_invalid_id', $lowercase_model_name ),
535
					sprintf( __( 'Invalid %s ID.', 'event_espresso' ), $lowercase_model_name ),
536
					array( 'status' => 404 )
537
				);
538
			}
539
		}
540
	}
541
542
	/**
543
	 * If a context is provided which isn't valid, maybe it was added in a future
544
	 * version so just treat it as a default read
545
	 *
546
	 * @param string $context
547
	 * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
548
	 */
549
	public function validate_context( $context ) {
550
		if( ! $context ) {
551
			$context = \EEM_Base::caps_read;
552
		}
553
		$valid_contexts = \EEM_Base::valid_cap_contexts();
554
		if( in_array( $context, $valid_contexts )  ){
555
			return $context;
556
		}else{
557
			return \EEM_Base::caps_read;
558
		}
559
	}
560
561
562
563
	/**
564
	 * Translates API filter get parameter into $query_params array used by EEM_Base::get_all()
565
	 *
566
	 * @param \EEM_Base $model
567
	 * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
568
	 * @return array like what EEM_Base::get_all() expects or FALSE to indicate
569
	 *                          that absolutely no results should be returned
570
	 * @throws \EE_Error
571
	 */
572
	public function create_model_query_params( $model, $query_parameters ) {
573
		$model_query_params = array( );
574
		if ( isset( $query_parameters[ 'where' ] ) ) {
575
			$model_query_params[ 0 ] = $this->prepare_rest_query_params_key_for_models( $model, $query_parameters[ 'where' ] );
576
		}
577 View Code Duplication
		if ( isset( $query_parameters[ 'order_by' ] ) ) {
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...
578
			$order_by = $query_parameters[ 'order_by' ];
579
		} elseif ( isset( $query_parameters[ 'orderby' ] ) ) {
580
			$order_by = $query_parameters[ 'orderby' ];
581
		}else{
582
			$order_by = null;
583
		}
584
		if( $order_by !== null ){
585
			$model_query_params[ 'order_by' ] = $this->prepare_rest_query_params_key_for_models( $model, $order_by );
586
		}
587 View Code Duplication
		if ( isset( $query_parameters[ 'group_by' ] ) ) {
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...
588
			$group_by = $query_parameters[ 'group_by' ];
589
		} elseif ( isset( $query_parameters[ 'groupby' ] ) ) {
590
			$group_by = $query_parameters[ 'groupby' ];
591
		}else{
592
			$group_by = null;
593
		}
594
		if( $group_by !== null ){
595
			if( is_array( $group_by ) ) {
596
				$group_by = $this->prepare_rest_query_params_values_for_models( $model, $group_by );
597
			}
598
			$model_query_params[ 'group_by' ] = $group_by;
599
		}
600
		if ( isset( $query_parameters[ 'having' ] ) ) {
601
			//@todo: no good for permissions
602
			$model_query_params[ 'having' ] = $this->prepare_rest_query_params_key_for_models( $model, $query_parameters[ 'having' ] );
603
		}
604
		if ( isset( $query_parameters[ 'order' ] ) ) {
605
			$model_query_params[ 'order' ] = $query_parameters[ 'order' ];
606
		}
607
		if ( isset( $query_parameters[ 'mine' ] ) ){
608
			$model_query_params = $model->alter_query_params_to_only_include_mine( $model_query_params );
609
		}
610
		if( isset( $query_parameters[ 'limit' ] ) ) {
611
			//limit should be either a string like '23' or '23,43', or an array with two items in it
612
			if( is_string( $query_parameters[ 'limit' ] ) ) {
613
				$limit_array = explode(',', $query_parameters['limit']);
614
			}else {
615
				$limit_array = $query_parameters[ 'limit' ];
616
			}
617
			$sanitized_limit = array();
618
			foreach( $limit_array as $key => $limit_part ) {
619
				if( $this->_debug_mode && ( ! is_numeric( $limit_part ) || count( $sanitized_limit ) > 2 ) ) {
620
					throw new \EE_Error(
621
						sprintf(
622
							__( 'An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.', 'event_espresso' ),
623
							json_encode( $query_parameters[ 'limit' ] )
624
						)
625
					);
626
				}
627
				$sanitized_limit[] = intval( $limit_part );
628
			}
629
			$model_query_params[ 'limit' ] = implode( ',', $sanitized_limit );
630
		}else{
631
			$model_query_params[ 'limit' ] = 50;
632
		}
633
		if( isset( $query_parameters[ 'caps' ] ) ) {
634
			$model_query_params[ 'caps' ] = $this->validate_context( $query_parameters[ 'caps' ] );
635
		}else{
636
			$model_query_params[ 'caps' ] = \EEM_Base::caps_read;
637
		}
638
		return apply_filters( 'FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model );
639
	}
640
641
642
643
	/**
644
	 * Changes the REST-style query params for use in the models
645
	 *
646
	 * @param \EEM_Base $model
647
	 * @param array     $query_params sub-array from @see EEM_Base::get_all()
648
	 * @return array
649
	 */
650 View Code Duplication
	public function prepare_rest_query_params_key_for_models( $model,  $query_params ) {
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...
651
		$model_ready_query_params = array();
652
		foreach( $query_params as $key => $value ) {
653
			if( is_array( $value ) ) {
654
				$model_ready_query_params[ $key ] = $this->prepare_rest_query_params_key_for_models( $model, $value );
655
			}else{
656
				$model_ready_query_params[ $key ] = $value;
657
			}
658
		}
659
		return $model_ready_query_params;
660
	}
661 View Code Duplication
	public function prepare_rest_query_params_values_for_models( $model,  $query_params ) {
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...
662
		$model_ready_query_params = array();
663
		foreach( $query_params as $key => $value ) {
664
			if( is_array( $value ) ) {
665
				$model_ready_query_params[ $key ] = $this->prepare_rest_query_params_values_for_models( $model, $value );
666
			} else {
667
				$model_ready_query_params[ $key ] = $value;
668
			}
669
		}
670
		return $model_ready_query_params;
671
	}
672
673
674
	/**
675
	 * Parses the $include_string so we fetch all the field names relating to THIS model
676
	 * (ie have NO period in them), or for the provided model (ie start with the model
677
	 * name and then a period).
678
	 * @param string $include_string @see Read:handle_request_get_all
679
	 * @param string $model_name
680
	 * @return array of fields for this model. If $model_name is provided, then
681
	 * the fields for that model, with the model's name removed from each.
682
	 */
683
	public function extract_includes_for_this_model( $include_string, $model_name = null ) {
684
		if( is_array( $include_string ) ) {
685
			$include_string = implode( ',', $include_string );
686
		}
687
		if( $include_string === '*' ) {
688
			return array();
689
		}
690
		$includes = explode( ',', $include_string );
691
		$extracted_fields_to_include = array();
692
		if( $model_name ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $model_name 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...
693
			foreach( $includes as $field_to_include ) {
694
				$field_to_include = trim( $field_to_include );
695
				if( strpos( $field_to_include, $model_name . '.' ) === 0 ) {
696
					//found the model name at the exact start
697
					$field_sans_model_name = str_replace( $model_name . '.', '', $field_to_include );
698
					$extracted_fields_to_include[] = $field_sans_model_name;
699
				}elseif( $field_to_include == $model_name ){
700
					$extracted_fields_to_include[] = '*';
701
				}
702
			}
703
		}else{
704
			//look for ones with no period
705
			foreach( $includes as $field_to_include ) {
706
				$field_to_include = trim( $field_to_include );
707
				if (
708
					strpos( $field_to_include, '.' ) === false
709
					&& ! $this->get_model_version_info()->is_model_name_in_this_version( $field_to_include )
710
				) {
711
					$extracted_fields_to_include[] = $field_to_include;
712
				}
713
			}
714
		}
715
		return $extracted_fields_to_include;
716
717
	}
718
}
719
720
721
// End of file Read.php