Completed
Push — master ( 3ac4b1...21157a )
by Zack
05:18
created

GravityView_Field::__construct()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 24
rs 8.5125
cc 5
eloc 11
nc 4
nop 0
1
<?php
2
/**
3
 * @file class-gravityview-field.php
4
 * @package GravityView
5
 * @subpackage includes\fields
6
 */
7
8
/**
9
 * Modify field settings by extending this class.
10
 */
11
abstract class GravityView_Field {
12
13
	/**
14
	 * The name of the GravityView field type
15
	 * Example: `created_by`, `text`, `fileupload`, `address`, `entry_link`
16
	 * @var string
17
	 */
18
	public $name;
19
20
	/**
21
	 * @internal Not yet implemented
22
	 * @since 1.15.2
23
	 * @type string The description of the field in the field picker
24
	 */
25
	public $description;
26
27
	/**
28
	 * @since 1.15.2
29
	 * @type string The label of the field in the field picker
30
	 */
31
	public $label;
32
33
	/**
34
	 * `standard`, `advanced`, `post`, `pricing`, `meta`, `gravityview`
35
	 * @internal Not yet implemented
36
	 * @since 1.15.2
37
	 * @type string The group belongs to this field in the field picker
38
	 */
39
	public $group;
40
41
	/**
42
	 * @internal Not yet implemented
43
	 * @type boolean Can the field be searched?
44
	 * @since 1.15.2
45
	 */
46
	public $is_searchable;
47
48
	/**
49
	 * @internal Not yet implemented
50
	 * @type array $search_operators The type of search operators available for this field
51
	 * @since 1.15.2
52
	 */
53
	public $search_operators;
54
55
	/**
56
	 * @type boolean Can the field be sorted in search?
57
	 * @since 1.15.2
58
	 */
59
	public $is_sortable = true;
60
61
	/**
62
	 * @type boolean Is field content number-based?
63
	 * @since 1.15.2
64
	 */
65
	public $is_numeric;
66
67
	/**
68
	 * @internal Not yet implemented
69
	 * @todo implement supports_context() method
70
	 * The contexts in which a field is available. Some fields aren't editable, for example.
71
	 * - `singular` is an alias for both `single` and `edit`
72
	 * - `multiple` is an alias for `directory` (backward compatibility)
73
	 * @type array
74
	 * @since 1.15.2
75
	 */
76
	public $contexts = array( 'single', 'multiple', 'edit', 'export' );
77
78
	/**
79
	 * @internal Not yet implemented
80
	 * @since 1.15.2
81
	 * @type string The name of a corresponding Gravity Forms GF_Field class, if exists
82
	 */
83
	protected $_gf_field_class_name;
84
85
	/**
86
	 * @var string The field ID being requested
87
	 * @since 1.14
88
	 */
89
	protected $_field_id = '';
90
91
	/**
92
	 * @var string Field options to be rendered
93
	 * @since 1.14
94
	 */
95
	protected $_field_options = array();
96
97
	/**
98
	 * @var bool|string Name of merge tag (without curly brackets), if the field has custom GravityView merge tags to add. Otherwise, false.
99
	 * @since 1.16
100
	 */
101
	protected $_custom_merge_tag = false;
102
103
	function __construct() {
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...
104
105
		/**
106
		 * If this is a Gravity Forms field, use their labels. Spare our translation team!
107
		 */
108
		if( ! empty( $this->_gf_field_class_name ) && class_exists( $this->_gf_field_class_name ) && empty( $this->label ) ) {
109
			/** @var GF_Field $GF_Field */
110
			$GF_Field = new $this->_gf_field_class_name;
111
			$this->label = $GF_Field->get_form_editor_field_title();
112
			$this->label = ucwords( $this->label );
113
		}
114
115
		// Modify the field options based on the name of the field type
116
		add_filter( sprintf( 'gravityview_template_%s_options', $this->name ), array( &$this, 'field_options' ), 10, 5 );
117
118
		add_filter( 'gravityview/sortable/field_blacklist', array( $this, '_filter_sortable_fields' ), 1 );
119
120
		if( $this->_custom_merge_tag ) {
121
			add_filter( 'gform_custom_merge_tags', array( $this, '_filter_gform_custom_merge_tags' ), 10, 4 );
122
			add_filter( 'gform_replace_merge_tags', array( $this, '_filter_gform_replace_merge_tags' ), 10, 7 );
123
		}
124
125
		GravityView_Fields::register( $this );
126
	}
127
128
	/**
129
	 * Match the merge tag in replacement text for the field.  DO NOT OVERRIDE.
130
	 *
131
	 * @see replace_merge_tag Override replace_merge_tag() to handle any matches
132
	 *
133
	 * @since 1.16
134
	 *
135
	 * @param string $text Text to replace
136
	 * @param array $form Gravity Forms form array
137
	 * @param array $entry Entry array
138
	 * @param bool $url_encode Whether to URL-encode output
139
	 *
140
	 * @return string Original text if {_custom_merge_tag} isn't found. Otherwise, replaced text.
141
	 */
142
	public function _filter_gform_replace_merge_tags( $text, $form = array(), $entry = array(), $url_encode = false, $esc_html = false  ) {
143
144
		/**
145
		 * This prevents the gform_replace_merge_tags filter from being called twice, as defined in:
146
		 * @see GFCommon::replace_variables()
147
		 * @see GFCommon::replace_variables_prepopulate()
148
		 * @todo Remove eventually: Gravity Forms fixed this issue in 1.9.14
149
		 */
150
		if( false === $form ) {
151
			return $text;
152
		}
153
154
		// Is there is field merge tag? Strip whitespace off the ned, too.
155
		preg_match_all( '/{' . preg_quote( $this->_custom_merge_tag ) . ':?(.*?)(?:\s)?}/ism', $text, $matches, PREG_SET_ORDER );
156
157
		// If there are no matches, return original text
158
		if ( empty( $matches ) ) {
159
			return $text;
160
		}
161
162
		return $this->replace_merge_tag( $matches, $text, $form, $entry, $url_encode, $esc_html );
163
	}
164
165
	/**
166
	 * Run GravityView filters when using GFCommon::replace_variables()
167
	 *
168
	 * Instead of adding multiple hooks, add all hooks into this one method to improve speed
169
	 *
170
	 * @since 1.8.4
171
	 *
172
	 * @param array $matches Array of Merge Tag matches found in text by preg_match_all
173
	 * @param string $text Text to replace
174
	 * @param array|bool $form Gravity Forms form array. When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
175
	 * @param array|bool $entry Entry array.  When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
176
	 * @param bool $url_encode Whether to URL-encode output
177
	 * @param bool $esc_html Whether to apply `esc_html()` to output
178
	 *
179
	 * @return mixed
180
	 */
181
	public function replace_merge_tag( $matches = array(), $text = '', $form = array(), $entry = array(), $url_encode = false, $esc_html = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
182
183
		foreach( $matches as $match ) {
184
185
			$full_tag = $match[0];
186
187
			// Strip the Merge Tags
188
			$tag = str_replace( array( '{', '}'), '', $full_tag );
189
190
			// Replace the value from the entry, if exists
191
			if( isset( $entry[ $tag ] ) ) {
192
193
				$value = $entry[ $tag ];
194
195
				if( is_callable( array( $this, 'get_content') ) ) {
196
					$value = $this->get_content( $value );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class GravityView_Field as the method get_content() does only exist in the following sub-classes of GravityView_Field: GravityView_Field_Date_Created, GravityView_Field_Is_Fulfilled, GravityView_Field_Payment_Amount, GravityView_Field_Payment_Date, GravityView_Field_Transaction_Type. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
197
				}
198
199
				$text = str_replace( $full_tag, $value, $text );
200
			}
201
		}
202
203
		unset( $value, $tag, $full_tag );
204
205
		return $text;
206
	}
207
208
	/**
209
	 * Add custom merge tags to merge tag options. DO NOT OVERRIDE.
210
	 *
211
	 * @internal Not to be overridden by fields
212
	 *
213
	 * @since 1.8.4
214
	 *
215
	 * @param array $existing_merge_tags
0 ignored issues
show
Bug introduced by
There is no parameter named $existing_merge_tags. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
216
	 * @param int $form_id GF Form ID
217
	 * @param GF_Field[] $fields Array of fields in the form
218
	 * @param string $element_id The ID of the input that Merge Tags are being used on
219
	 *
220
	 * @return array Modified merge tags
221
	 */
222
	public function _filter_gform_custom_merge_tags( $custom_merge_tags = array(), $form_id, $fields = array(), $element_id = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $element_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
223
224
		$form = GVCommon::get_form( $form_id );
225
226
		$field_merge_tags = $this->custom_merge_tags( $form, $fields );
227
228
		return array_merge( $custom_merge_tags, $field_merge_tags );
229
	}
230
231
	/**
232
	 * Add custom Merge Tags to Merge Tag options, if custom Merge Tags exist
233
	 *
234
	 * Should be overridden if there's more than one Merge Tag to add or if the Merge Tag isn't {_custom_merge_tag}
235
	 *
236
	 * @since 1.16
237
	 *
238
	 * @param array $form GF Form array
239
	 * @param GF_Field[] $fields Array of fields in the form
240
	 *
241
	 * @return array Merge tag array with `label` and `tag` keys based on class `label` and `_custom_merge_tag` variables
242
	 */
243
	protected function custom_merge_tags( $form = array(), $fields = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fields is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
244
245
		// Use variables to make it unnecessary for other fields to override
246
		$merge_tags = array(
247
			array(
248
				'label' => $this->label,
249
				'tag' => '{' . $this->_custom_merge_tag . '}',
250
			),
251
		);
252
253
		return $merge_tags;
254
	}
255
256
	/**
257
	 * Use field settings to modify whether a field is sortable
258
	 *
259
	 * @see GravityView_frontend::is_field_sortable
260
	 * @since 1.15.3
261
	 *
262
	 * @param array $not_sortable Existing field types that aren't sortable
263
	 *
264
	 * @return array
265
	 */
266
	public function _filter_sortable_fields( $not_sortable ) {
267
268
		if( ! $this->is_sortable ) {
269
			$not_sortable[] = $this->name;
270
		}
271
272
		return $not_sortable;
273
	}
274
275
	private function field_support_options() {
276
		$options = array(
277
			'link_to_post' => array(
278
				'type' => 'checkbox',
279
				'label' => __( 'Link to the post', 'gravityview' ),
280
				'desc' => __( 'Link to the post created by the entry.', 'gravityview' ),
281
				'value' => false,
282
			),
283
			'link_to_term' => array(
284
				'type' => 'checkbox',
285
				'label' => __( 'Link to the category or tag', 'gravityview' ),
286
				'desc' => __( 'Link to the current category or tag. "Link to single entry" must be unchecked.', 'gravityview' ),
287
				'value' => false,
288
			),
289
			'dynamic_data' => array(
290
				'type' => 'checkbox',
291
				'label' => __( 'Use the live post data', 'gravityview' ),
292
				'desc' => __( 'Instead of using the entry data, instead use the current post data.', 'gravityview' ),
293
				'value' => true,
294
			),
295
			'date_display' => array(
296
				'type' => 'text',
297
				'label' => __( 'Override Date Format', 'gravityview' ),
298
				'desc' => sprintf( __( 'Define how the date is displayed (using %sthe PHP date format%s)', 'gravityview'), '<a href="https://codex.wordpress.org/Formatting_Date_and_Time">', '</a>' ),
299
				/**
300
				 * @filter `gravityview_date_format` Override the date format with a [PHP date format](https://codex.wordpress.org/Formatting_Date_and_Time)
301
				 * @param[in,out] null|string $date_format Date Format (default: null)
302
				 */
303
				'value' => apply_filters( 'gravityview_date_format', null )
304
			),
305
			'new_window' => array(
306
				'type' => 'checkbox',
307
				'label' => __( 'Open link in a new tab or window?', 'gravityview' ),
308
				'value' => false,
309
			),
310
		);
311
312
		/**
313
		 * @filter `gravityview_field_support_options` Modify the settings that a field supports
314
		 * @param array $options Options multidimensional array with each key being the input name, with each array setting having `type`, `label`, `desc` and `value` (default values) keys
315
		 */
316
		return apply_filters( 'gravityview_field_support_options', $options );
317
	}
318
319
	function add_field_support( $key = '', &$field_options ) {
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...
320
321
		$options = $this->field_support_options();
322
323
		if( isset( $options[ $key ] ) ) {
324
			$field_options[ $key ] = $options[ $key ];
325
		}
326
327
		return $field_options;
328
	}
329
330
	/**
331
	 * Tap in here to modify field options.
332
	 *
333
	 * Here's an example:
334
	 *
335
	 * <pre>
336
	 * $field_options['name_display'] = array(
337
	 * 	'type' => 'select',
338
	 * 	'label' => __( 'User Format', 'gravityview' ),
339
	 * 	'desc' => __( 'How should the User information be displayed?', 'gravityview'),
340
	 * 	'choices' => array(
341
	 * 		array(
342
	 *		 	'value' => 'display_name',
343
	 *		  	'label' => __('Display Name (Example: "Ellen Ripley")', 'gravityview'),
344
	 *		),
345
	 *  	array(
346
	 *			'value' => 'user_login',
347
	 *			'label' => __('Username (Example: "nostromo")', 'gravityview')
348
	 *		),
349
	 * 	 'value' => 'display_name'
350
	 * );
351
	 * </pre>
352
	 *
353
	 * @param  [type]      $field_options [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
354
	 * @param  [type]      $template_id   [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
355
	 * @param  [type]      $field_id      [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
356
	 * @param  [type]      $context       [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
357
	 * @param  [type]      $input_type    [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
358
	 * @return [type]                     [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
359
	 */
360
	function field_options( $field_options, $template_id, $field_id, $context, $input_type ) {
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...
361
362
		$this->_field_options = $field_options;
363
		$this->_field_id = $field_id;
364
365
		return $field_options;
366
	}
367
368
}
369