Completed
Push — develop ( 507546...65eb42 )
by Zack
05:37
created

GVLogic_Shortcode::shortcode()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 46
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 23
nc 6
nop 3
dl 0
loc 46
rs 6.7272
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 6 and the first side effect is on line 317.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/**
4
 * Shortcode to handle showing/hiding content in merge tags. Works great with GravityView Custom Content fields
5
 */
6
class GVLogic_Shortcode {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
7
8
	private static $SUPPORTED_SCALAR_OPERATORS = array( 'is', 'isnot', 'contains', 'starts_with', 'ends_with' );
9
10
	private static $SUPPORTED_NUMERIC_OPERATORS = array( 'greater_than', 'less_than' );
11
12
	private static $SUPPORTED_ARRAY_OPERATORS = array( 'in', 'not_in', 'isnot', 'contains' );
13
14
	private static $SUPPORTED_CUSTOM_OPERATORS = array( 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals', 'not_contains' );
15
16
	/**
17
	 * Attributes passed to the shortcode
18
	 * @var array
19
	 */
20
	var $passed_atts;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $passed_atts.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
21
22
	/**
23
	 * Content inside the shortcode, displayed if matched
24
	 * @var string
25
	 */
26
	var $passed_content;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $passed_content.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
27
28
	/**
29
	 * Parsed attributes
30
	 * @var array
31
	 */
32
	var $atts = array();
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $atts.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
33
34
	/**
35
	 * Parsed content, shown if matched
36
	 * @var string
37
	 */
38
	var $content = '';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $content.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
39
40
	/**
41
	 * Content shown if not matched
42
	 * This is set by having `[else]` inside the $content block
43
	 * @var string
44
	 */
45
	var $else_content = '';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $else_content.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
46
47
	/**
48
	 * The current shortcode name being processed
49
	 * @var string
50
	 */
51
	var $shortcode = 'gvlogic';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $shortcode.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
52
53
	/**
54
	 * The left side of the comparison
55
	 * @var string
56
	 */
57
	var $if = '';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $if.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
58
59
	/**
60
	 * The right side of the comparison
61
	 * @var string
62
	 */
63
	var $comparison = '';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $comparison.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
64
65
	/**
66
	 * The comparison operator
67
	 * @var string
68
	 */
69
	var $operation = 'is';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $operation.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
70
71
	/**
72
	 * Does the comparison pass?
73
	 * @var bool
74
	 */
75
	var $is_match = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $is_match.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
76
77
	/**
78
	 * @var GVLogic_Shortcode
79
	 */
80
	private static $instance;
81
82
	/**
83
	 * Instantiate!
84
	 * @return GVLogic_Shortcode
85
	 */
86
	public static function get_instance() {
87
88
		if( empty( self::$instance ) ) {
89
			self::$instance = new self;
90
		}
91
92
		return self::$instance;
93
	}
94
95
	/**
96
	 * Add the WordPress hooks
97
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
98
	 */
99
	private function __construct() {
100
		$this->add_hooks();
101
	}
102
103
	/**
104
	 * Register the shortcode
105
	 * @return void
106
	 */
107
	function add_hooks() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
108
		add_shortcode( 'gvlogic', array( $this, 'shortcode' ) );
109
	}
110
111
	/**
112
	 * Get array of supported operators
113
	 * @param bool $with_values
114
	 *
115
	 * @return array
116
	 */
117
	function get_operators( $with_values = false ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
118
119
		$operators = array_merge( self::$SUPPORTED_ARRAY_OPERATORS, self::$SUPPORTED_NUMERIC_OPERATORS, self::$SUPPORTED_SCALAR_OPERATORS, self::$SUPPORTED_CUSTOM_OPERATORS );
120
121
		if( $with_values ) {
122
			$operators_with_values = array();
123
			foreach( $operators as $key ) {
124
				$operators_with_values[ $key ] = '';
125
			}
126
			return $operators_with_values;
127
		} else {
128
			return $operators;
129
		}
130
	}
131
132
	/**
133
	 * Set the operation for the shortcode.
134
	 * @param string $operation
135
	 *
136
	 * @return bool True: it's an allowed operation type and was added. False: invalid operation type
137
	 */
138
	function set_operation( $operation = '' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
139
140
		if( empty( $operation ) ) {
141
			return false;
142
		}
143
144
		$operators = $this->get_operators( false );
145
146
		if( !in_array( $operation, $operators ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
147
			do_action( 'gravityview_log_debug', __METHOD__ .' Attempted to add invalid operation type.', $operation );
148
			return false;
149
		}
150
151
		$this->operation = $operation;
152
		return true;
153
	}
154
155
	/**
156
	 * Set the operation and comparison for the shortcode
157
	 *
158
	 * Loop through each attribute passed to the shortcode and see if it's a valid operator. If so, set it.
159
	 * Example: [gvlogic if="{example}" greater_than="5"]
160
	 * `greater_than` will be set as the operator
161
	 * `5` will be set as the comparison value
162
	 *
163
	 * @return bool True: we've got an operation and comparison value; False: no, we don't
164
	 */
165
	private function setup_operation_and_comparison() {
166
167
		foreach( $this->atts as $key => $value ) {
168
169
			$valid = $this->set_operation( $key );
170
171
			if( $valid ) {
172
				$this->comparison = $value;
173
				return true;
174
			}
175
		}
176
177
		return false;
178
	}
179
180
	/**
181
	 * @param array $atts User defined attributes in shortcode tag.
182
	 * @param null $content
183
	 * @param string $shortcode_tag
184
	 *
185
	 * @return string|null
186
	 */
187
	public function shortcode( $atts = array(), $content = NULL, $shortcode_tag = '' ) {
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
188
189
		// Don't process except on frontend
190
		if ( function_exists( 'gravityview' ) && gravityview()->request->is_admin() ) {
191
			return null;
192
			/** Deprecated in favor of gravityview()->request->is_admin(). */
193
		} else if ( GravityView_Plugin::is_admin() ) {
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_Plugin::is_admin() has been deprecated.

This method has been deprecated.

Loading history...
194
			return null;
195
		}
196
197
		if( empty( $atts ) ) {
198
			do_action( 'gravityview_log_error', __METHOD__.' $atts are empty.', $atts );
199
			return null;
200
		}
201
202
		$this->passed_atts = $atts;
203
		$this->passed_content = $content;
204
		$this->shortcode = $shortcode_tag;
205
206
		$this->parse_atts();
207
208
		// We need an "if"
209
		if( false === $this->if ) {
210
			do_action( 'gravityview_log_error', __METHOD__.' $atts->if is empty.', $this->passed_atts );
211
			return null;
212
		}
213
214
		$setup = $this->setup_operation_and_comparison();
215
216
		// We need an operation and comparison value
217
		if( ! $setup ) {
218
			do_action( 'gravityview_log_error', __METHOD__.' No valid operators were passed.', $this->atts );
219
			return null;
220
		}
221
222
		// Set the content and else_content
223
		$this->set_content_and_else_content();
224
225
		// Check if it's a match
226
		$this->set_is_match();
227
228
		// Return the value!
229
		$output = $this->get_output();
230
231
		return $output;
232
	}
233
234
	/**
235
	 * Does the if and the comparison match?
236
	 * @uses GVCommon::matches_operation
237
	 *
238
	 * @return boolean True: yep; false: nope
239
	 */
240
	private function set_is_match() {
241
		$this->is_match = GVCommon::matches_operation( $this->if, $this->comparison, $this->operation );
242
	}
243
244
	/**
245
	 * Get the output for the shortcode, based on whether there's a matched value
246
	 *
247
	 * @return string HTML/Text output of the shortcode
248
	 */
249
	private function get_output() {
250
251
		if( $this->is_match ) {
252
			$output = $this->content;
253
		} else {
254
			$output = $this->else_content;
255
		}
256
257
		// Get recursive!
258
		$output = do_shortcode( $output );
259
260
		/**
261
		 * @filter `gravityview/gvlogic/output` Modify the [gvlogic] output
262
		 * @param string $output HTML/text output
263
		 * @param GVLogic_Shortcode $this This class
264
		 */
265
		$output = apply_filters('gravityview/gvlogic/output', $output, $this );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
266
267
		do_action( 'gravityview_log_debug', __METHOD__ .' Output: ', $output );
268
269
		return $output;
270
	}
271
272
	/**
273
	 * Check for `[else]` tag inside the shortcode content. If exists, set the else_content variable.
274
	 * If not, use the `else` attribute passed by the shortcode, if exists.
275
	 *
276
	 * @todo allow for chains of [else if="{another field:123}" is="example"] - requires registering [else] shortcode...
277
	 * @return void
278
	 */
279
	private function set_content_and_else_content() {
280
281
		$content = explode( '[else]', $this->passed_content );
282
283
		$this->content = $content[0];
284
285
		$else_attr = isset( $this->atts['else'] ) ? $this->atts['else'] : NULL;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
286
287
		$this->else_content = isset( $content[1] ) ? $content[1] : $else_attr;
288
	}
289
290
	/**
291
	 * Process the attributes passed to the shortcode. Make sure they're valid
292
	 * @return void
293
	 */
294
	private function parse_atts() {
295
296
		$supported = array(
297
			'if' => false,
298
			'else' => false,
299
		);
300
301
		$supported_args = $supported + $this->get_operators( true );
302
303
		// Whittle down the attributes to only valid pairs
304
		$this->atts = shortcode_atts( $supported_args, $this->passed_atts, $this->shortcode );
305
306
		// Only keep the passed attributes after making sure that they're valid pairs
307
		$this->atts = function_exists( 'array_intersect_key' ) ? array_intersect_key( $this->passed_atts, $this->atts ) : $this->atts;
308
309
		// Strip whitespace if it's not default false
310
		$this->if = ( isset( $this->atts['if'] ) && is_string( $this->atts['if'] ) ) ? trim( $this->atts['if'] ) : false;
311
312
		// Make sure the "if" isn't processed in self::setup_operation_and_comparison()
313
		unset( $this->atts['if'] );
314
	}
315
}
316
317
GVLogic_Shortcode::get_instance();
318