PluginProbe
Code Snippets / 2.14.6
Code Snippets v2.14.6
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / php / class-snippet.php

class-snippet.php in Code Snippets 2.14.6, at php/class-snippet.php

474 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * A snippet object
5 *
6 * @since 2.4.0
7 * @package Code_Snippets
8 *
9 * @property int $id The database ID
10 * @property string $name The display name
11 * @property string $desc The formatted description
12 * @property string $code The executable code
13 * @property array $tags An array of the tags
14 * @property string $scope The scope name
15 * @property int $priority Execution priority
16 * @property bool $active The active status
17 * @property bool $network true if is multisite-wide snippet, false if site-wide
18 * @property bool $shared_network Whether the snippet is a shared network snippet
19 * @property string $modified The date and time when the snippet data was most recently saved to the database.
20 *
21 * @property-read array $tags_list The tags in string list format
22 * @property-read string $scope_icon The dashicon used to represent the current scope
23 * @property-read int $modified_timestamp The last modification date in Unix timestamp format.
24 * @property-read DateTime $modified_local The last modification date in the local timezone.
25 */
26 class Code_Snippet {
27
28 /**
29 * MySQL datetime format (YYYY-MM-DD hh:mm:ss)
30 */
31 const DATE_FORMAT = 'Y-m-d H:i:s';
32
33 /**
34 * Default value used for a datetime variable.
35 */
36 const DEFAULT_DATE = '0000-00-00 00:00:00';
37
38 /**
39 * The snippet metadata fields.
40 * Initialized with default values.
41 * @var array
42 */
43 private $fields = array(
44 'id' => 0,
45 'name' => '',
46 'desc' => '',
47 'code' => '',
48 'tags' => array(),
49 'scope' => 'global',
50 'active' => false,
51 'priority' => 10,
52 'network' => null,
53 'shared_network' => null,
54 'created' => null,
55 'modified' => null,
56 );
57
58 /**
59 * List of field aliases
60 * @var array
61 */
62 private static $field_aliases = array(
63 'description' => 'desc',
64 );
65
66 /**
67 * Constructor function
68 *
69 * @param array|object $fields Initial snippet fields
70 */
71 public function __construct( $fields = null ) {
72 $this->set_fields( $fields );
73 }
74
75 /**
76 * Set all snippet fields from an array or object.
77 * Invalid fields will be ignored
78 *
79 * @param array|object $fields List of fields
80 */
81 public function set_fields( $fields ) {
82
83 /* Only accept arrays or objects */
84 if ( ! $fields || is_string( $fields ) ) {
85 return;
86 }
87
88 /* Convert objects into arrays */
89 if ( is_object( $fields ) ) {
90 $fields = get_object_vars( $fields );
91 }
92
93 /* Loop through the passed fields and set them */
94 foreach ( $fields as $field => $value ) {
95 $this->set_field( $field, $value );
96 }
97 }
98
99 /**
100 * Retrieve all snippet fields
101 * @return array
102 */
103 public function get_fields() {
104 return $this->fields;
105 }
106
107 /**
108 * Internal function for validating the name of a field
109 *
110 * @param string $field A field name
111 *
112 * @return string The validated field name
113 */
114 private function validate_field_name( $field ) {
115
116 /* If a field alias is set, remap it to the valid field name */
117 if ( isset( self::$field_aliases[ $field ] ) ) {
118 return self::$field_aliases[ $field ];
119 }
120
121 return $field;
122 }
123
124 /**
125 * Check if a field is set
126 *
127 * @param string $field The field name
128 *
129 * @return bool Whether the field is set
130 */
131 public function __isset( $field ) {
132 $field = $this->validate_field_name( $field );
133
134 return isset( $this->fields[ $field ] ) || method_exists( $this, 'get_' . $field );
135 }
136
137 /**
138 * Retrieve a field's value
139 *
140 * @param string $field The field name
141 *
142 * @return mixed The field value
143 */
144 public function __get( $field ) {
145 $field = $this->validate_field_name( $field );
146
147 if ( method_exists( $this, 'get_' . $field ) ) {
148 return call_user_func( array( $this, 'get_' . $field ) );
149 }
150
151 if ( ! $this->is_allowed_field( $field ) ) {
152 if ( WP_DEBUG ) {
153 /** @phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error */
154 trigger_error( 'Trying to access invalid property on Snippets class: ' . esc_html( $field ), E_WARNING );
155 }
156
157 return null;
158 }
159
160 return $this->fields[ $field ];
161 }
162
163 /**
164 * Set the value of a field
165 *
166 * @param string $field The field name
167 * @param mixed $value The field value
168 */
169 public function __set( $field, $value ) {
170 $field = $this->validate_field_name( $field );
171
172 if ( ! $this->is_allowed_field( $field ) ) {
173 if ( WP_DEBUG ) {
174 /** @phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error */
175 trigger_error( 'Trying to set invalid property on Snippets class: ' . esc_html( $field ), E_WARNING );
176 }
177
178 return;
179 }
180
181 /* Check if the field value should be filtered */
182 if ( method_exists( $this, 'prepare_' . $field ) ) {
183 $value = call_user_func( array( $this, 'prepare_' . $field ), $value );
184 }
185
186 $this->fields[ $field ] = $value;
187 }
188
189 /**
190 * Retrieve the list of fields allowed to be written to
191 *
192 * @return array
193 */
194 public function get_allowed_fields() {
195 return array_keys( $this->fields ) + array_keys( self::$field_aliases );
196 }
197
198 /**
199 * Determine whether a field is allowed to be written to
200 *
201 * @param string $field The field name
202 *
203 * @return bool true if the is allowed, false if invalid
204 */
205 public function is_allowed_field( $field ) {
206 return array_key_exists( $field, $this->fields ) || array_key_exists( $field, self::$field_aliases );
207 }
208
209 /**
210 * Safely set the value for a field.
211 * If the field name is invalid, false will be returned instead of an error thrown
212 *
213 * @param string $field The field name
214 * @param mixed $value The field value
215 *
216 * @return bool true if the field was set successfully, false if the field name is invalid
217 */
218 public function set_field( $field, $value ) {
219 if ( ! $this->is_allowed_field( $field ) ) {
220 return false;
221 }
222
223 $this->__set( $field, $value );
224
225 return true;
226 }
227
228 /**
229 * Prepare the ID by ensuring it is an absolute integer
230 *
231 * @param int $id
232 *
233 * @return int
234 */
235 private function prepare_id( $id ) {
236 return absint( $id );
237 }
238
239 /**
240 * Prepare the code by removing php tags from beginning and end
241 *
242 * @param string $code
243 *
244 * @return string
245 */
246 private function prepare_code( $code ) {
247
248 /* Remove <?php and <? from beginning of snippet */
249 $code = preg_replace( '|^[\s]*<\?(php)?|', '', $code );
250
251 /* Remove ?> from end of snippet */
252 $code = preg_replace( '|\?>[\s]*$|', '', $code );
253
254 return $code;
255 }
256
257 /**
258 * Prepare the scope by ensuring that it is a valid choice
259 *
260 * @param int|string $scope The field as provided
261 *
262 * @return string The field in the correct format
263 */
264 private function prepare_scope( $scope ) {
265 $scopes = self::get_all_scopes();
266
267 if ( in_array( $scope, $scopes, true ) ) {
268 return $scope;
269 }
270
271 if ( is_numeric( $scope ) && isset( $scopes[ $scope ] ) ) {
272 return $scopes[ $scope ];
273 }
274
275 return $this->fields['scope'];
276 }
277
278 /**
279 * Prepare the snippet tags by ensuring they are in the correct format
280 *
281 * @param string|array $tags The tags as provided
282 *
283 * @return array The tags as an array
284 */
285 private function prepare_tags( $tags ) {
286 return code_snippets_build_tags_array( $tags );
287 }
288
289 /**
290 * Prepare the active field by ensuring it is the correct type
291 *
292 * @param bool|int $active The field as provided
293 *
294 * @return bool The field in the correct format
295 */
296 private function prepare_active( $active ) {
297
298 if ( is_bool( $active ) ) {
299 return $active;
300 }
301
302 return $active ? true : false;
303 }
304
305 /**
306 * Prepare the priority field by ensuring it is an integer
307 *
308 * @param int $priority
309 *
310 * @return int
311 */
312 private function prepare_priority( $priority ) {
313 return intval( $priority );
314 }
315
316 /**
317 * If $network is anything other than true, set it to false
318 *
319 * @param bool $network The provided field
320 *
321 * @return bool The filtered field
322 */
323 private function prepare_network( $network ) {
324
325 if ( null === $network && function_exists( 'is_network_admin' ) ) {
326 return is_network_admin();
327 }
328
329 return true === $network;
330 }
331
332 /**
333 * Prepare the modification field by ensuring it is in the correct format.
334 *
335 * @param DateTime|string $modified
336 *
337 * @return string
338 */
339 private function prepare_modified( $modified ) {
340
341 /* if the supplied value is a DateTime object, convert it to string representation */
342 if ( $modified instanceof DateTime ) {
343 return $modified->format( self::DATE_FORMAT );
344 }
345
346 /* if the supplied value is probably a timestamp, attempt to convert it to a string */
347 if ( is_numeric( $modified ) ) {
348 return gmdate( self::DATE_FORMAT, $modified );
349 }
350
351 /* if the supplied value is a string, check it is not just the default value */
352 if ( is_string( $modified ) && self::DEFAULT_DATE !== $modified ) {
353 return $modified;
354 }
355
356 /* otherwise, discard the supplied value */
357 return null;
358 }
359
360 /**
361 * Update the last modification date to the current date and time.
362 */
363 public function update_modified() {
364 $this->modified = gmdate( self::DATE_FORMAT );
365 }
366
367 /**
368 * Retrieve the tags in list format
369 * @return string The tags separated by a comma and a space
370 */
371 private function get_tags_list() {
372 return implode( ', ', $this->fields['tags'] );
373 }
374
375 /**
376 * Retrieve a list of all available scopes
377 * @return array
378 */
379 public static function get_all_scopes() {
380 return array( 'global', 'admin', 'front-end', 'single-use' );
381 }
382
383 /**
384 * Retrieve a list of all scope icons
385 * @return array
386 */
387 public static function get_scope_icons() {
388 return array(
389 'global' => 'admin-site',
390 'admin' => 'admin-tools',
391 'front-end' => 'admin-appearance',
392 'single-use' => 'clock',
393 );
394 }
395
396 /**
397 * Retrieve the string representation of the scope
398 * @return string The name of the scope
399 */
400 private function get_scope_name() {
401 return $this->scope;
402 }
403
404 /**
405 * Retrieve the icon used for the current scope
406 * @return string a dashicon name
407 */
408 private function get_scope_icon() {
409 $icons = self::get_scope_icons();
410
411 return $icons[ $this->scope ];
412 }
413
414 /**
415 * Determine if the snippet is a shared network snippet
416 * @return bool
417 */
418 private function get_shared_network() {
419
420 if ( isset( $this->fields['shared_network'] ) ) {
421 return $this->fields['shared_network'];
422 }
423
424 if ( ! is_multisite() || ! $this->fields['network'] ) {
425 $this->fields['shared_network'] = false;
426 } else {
427 $shared_network_snippets = get_site_option( 'shared_network_snippets', array() );
428 $this->fields['shared_network'] = in_array( $this->fields['id'], $shared_network_snippets, true );
429 }
430
431 return $this->fields['shared_network'];
432 }
433
434 /**
435 * Retrieve the snippet modification date as a timestamp.
436 *
437 * @return int Timestamp value.
438 */
439 private function get_modified_timestamp() {
440 $datetime = DateTime::createFromFormat( self::DATE_FORMAT, $this->modified, new DateTimeZone( 'UTC' ) );
441 return $datetime ? $datetime->getTimestamp() : 0;
442 }
443
444 /**
445 * Retrieve the modification time in the local timezone.
446 *
447 * @return DateTime
448 */
449 private function get_modified_local() {
450
451 if ( function_exists( 'wp_timezone' ) ) {
452 $timezone = wp_timezone();
453 } else {
454 $timezone = get_option( 'timezone_string' );
455
456 /* calculate the timezone manually if it is not available */
457 if ( ! $timezone ) {
458 $offset = (float) get_option( 'gmt_offset' );
459 $hours = (int) $offset;
460 $minutes = ( $offset - $hours ) * 60;
461
462 $sign = ( $offset < 0 ) ? '-' : '+';
463 $timezone = sprintf( '%s%02d:%02d', $sign, abs( $hours ), abs( $minutes ) );
464 }
465
466 $timezone = new DateTimeZone( $timezone );
467 }
468
469 $datetime = DateTime::createFromFormat( self::DATE_FORMAT, $this->modified, new DateTimeZone( 'UTC' ) );
470 $datetime->setTimezone( $timezone );
471 return $datetime;
472 }
473 }
474