PluginProbe
Code Snippets / 2.14.0
Code Snippets v2.14.0
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.0, at php/class-snippet.php

465 lines 11.3 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 of the 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 return $this->fields[ $field ];
152 }
153
154 /**
155 * Set the value of a field
156 *
157 * @param string $field The field name
158 * @param mixed $value The field value
159 */
160 public function __set( $field, $value ) {
161 $field = $this->validate_field_name( $field );
162
163 if ( ! $this->is_allowed_field( $field ) ) {
164
165 if ( WP_DEBUG ) {
166 trigger_error( 'Trying to set invalid property on Snippets class: ' . esc_html( $field ), E_WARNING );
167 }
168
169 return;
170 }
171
172 /* Check if the field value should be filtered */
173 if ( method_exists( $this, 'prepare_' . $field ) ) {
174 $value = call_user_func( array( $this, 'prepare_' . $field ), $value );
175 }
176
177 $this->fields[ $field ] = $value;
178 }
179
180 /**
181 * Retrieve the list of fields allowed to be written to
182 *
183 * @return array
184 */
185 public function get_allowed_fields() {
186 return array_keys( $this->fields ) + array_keys( self::$field_aliases );
187 }
188
189 /**
190 * Determine whether a field is allowed to be written to
191 *
192 * @param string $field The field name
193 *
194 * @return bool true if the is allowed, false if invalid
195 */
196 public function is_allowed_field( $field ) {
197 return array_key_exists( $field, $this->fields ) || array_key_exists( $field, self::$field_aliases );
198 }
199
200 /**
201 * Safely set the value for a field.
202 * If the field name is invalid, false will be returned instead of an error thrown
203 *
204 * @param string $field The field name
205 * @param mixed $value The field value
206 *
207 * @return bool true if the field was set successfully, false if the field name is invalid
208 */
209 public function set_field( $field, $value ) {
210 if ( ! $this->is_allowed_field( $field ) ) {
211 return false;
212 }
213
214 $this->__set( $field, $value );
215
216 return true;
217 }
218
219 /**
220 * Prepare the ID by ensuring it is an absolute integer
221 *
222 * @param int $id
223 *
224 * @return int
225 */
226 private function prepare_id( $id ) {
227 return absint( $id );
228 }
229
230 /**
231 * Prepare the code by removing php tags from beginning and end
232 *
233 * @param string $code
234 *
235 * @return string
236 */
237 private function prepare_code( $code ) {
238
239 /* Remove <?php and <? from beginning of snippet */
240 $code = preg_replace( '|^[\s]*<\?(php)?|', '', $code );
241
242 /* Remove ?> from end of snippet */
243 $code = preg_replace( '|\?>[\s]*$|', '', $code );
244
245 return $code;
246 }
247
248 /**
249 * Prepare the scope by ensuring that it is a valid choice
250 *
251 * @param int|string $scope The field as provided
252 *
253 * @return string The field in the correct format
254 */
255 private function prepare_scope( $scope ) {
256 $scopes = self::get_all_scopes();
257
258 if ( in_array( $scope, $scopes, true ) ) {
259 return $scope;
260 }
261
262 if ( is_numeric( $scope ) && isset( $scopes[ $scope ] ) ) {
263 return $scopes[ $scope ];
264 }
265
266 return $this->fields['scope'];
267 }
268
269 /**
270 * Prepare the snippet tags by ensuring they are in the correct format
271 *
272 * @param string|array $tags The tags as provided
273 *
274 * @return array The tags as an array
275 */
276 private function prepare_tags( $tags ) {
277 return code_snippets_build_tags_array( $tags );
278 }
279
280 /**
281 * Prepare the active field by ensuring it is the correct type
282 *
283 * @param bool|int $active The field as provided
284 *
285 * @return bool The field in the correct format
286 */
287 private function prepare_active( $active ) {
288
289 if ( is_bool( $active ) ) {
290 return $active;
291 }
292
293 return $active ? true : false;
294 }
295
296 /**
297 * Prepare the priority field by ensuring it is an integer
298 *
299 * @param int $priority
300 *
301 * @return int
302 */
303 private function prepare_priority( $priority ) {
304 return intval( $priority );
305 }
306
307 /**
308 * If $network is anything other than true, set it to false
309 *
310 * @param bool $network The provided field
311 *
312 * @return bool The filtered field
313 */
314 private function prepare_network( $network ) {
315
316 if ( null === $network && function_exists( 'is_network_admin' ) ) {
317 return is_network_admin();
318 }
319
320 return true === $network;
321 }
322
323 /**
324 * Prepare the modification field by ensuring it is in the correct format.
325 *
326 * @param DateTime|string $modified
327 *
328 * @return string
329 */
330 private function prepare_modified( $modified ) {
331
332 /* if the supplied value is a DateTime object, convert it to string representation */
333 if ( $modified instanceof DateTime ) {
334 return $modified->format( self::DATE_FORMAT );
335 }
336
337 /* if the supplied value is probably a timestamp, attempt to convert it to a string */
338 if ( is_numeric( $modified ) ) {
339 return gmdate( self::DATE_FORMAT, $modified );
340 }
341
342 /* if the supplied value is a string, check it is not just the default value */
343 if ( is_string( $modified ) && self::DEFAULT_DATE !== $modified ) {
344 return $modified;
345 }
346
347 /* otherwise, discard the supplied value */
348 return null;
349 }
350
351 /**
352 * Update the last modification date to the current date and time.
353 */
354 public function update_modified() {
355 $this->modified = gmdate( Code_Snippet::DATE_FORMAT );
356 }
357
358 /**
359 * Retrieve the tags in list format
360 * @return string The tags separated by a comma and a space
361 */
362 private function get_tags_list() {
363 return implode( ', ', $this->fields['tags'] );
364 }
365
366 /**
367 * Retrieve a list of all available scopes
368 * @return array
369 */
370 public static function get_all_scopes() {
371 return array( 'global', 'admin', 'front-end', 'single-use' );
372 }
373
374 /**
375 * Retrieve a list of all scope icons
376 * @return array
377 */
378 public static function get_scope_icons() {
379 return array(
380 'global' => 'admin-site',
381 'admin' => 'admin-tools',
382 'front-end' => 'admin-appearance',
383 'single-use' => 'clock',
384 );
385 }
386
387 /**
388 * Retrieve the string representation of the scope
389 * @return string The name of the scope
390 */
391 private function get_scope_name() {
392 return $this->scope;
393 }
394
395 /**
396 * Retrieve the icon used for the current scope
397 * @return string a dashicon name
398 */
399 private function get_scope_icon() {
400 $icons = self::get_scope_icons();
401
402 return $icons[ $this->scope ];
403 }
404
405 /**
406 * Determine if the snippet is a shared network snippet
407 * @return bool
408 */
409 private function get_shared_network() {
410
411 if ( isset( $this->fields['shared_network'] ) ) {
412 return $this->fields['shared_network'];
413 }
414
415 if ( ! is_multisite() || ! $this->fields['network'] ) {
416 $this->fields['shared_network'] = false;
417 } else {
418 $shared_network_snippets = get_site_option( 'shared_network_snippets', array() );
419 $this->fields['shared_network'] = in_array( $this->fields['id'], $shared_network_snippets, true );
420 }
421
422 return $this->fields['shared_network'];
423 }
424
425 /**
426 * Retrieve the snippet modification date as a timestamp.
427 *
428 * @return int Timestamp value.
429 */
430 private function get_modified_timestamp() {
431 $datetime = DateTime::createFromFormat( self::DATE_FORMAT, $this->modified, new DateTimeZone( 'UTC' ) );
432 return $datetime ? $datetime->getTimestamp() : 0;
433 }
434
435 /**
436 * Retrieve the modification time in the local timezone.
437 *
438 * @return DateTime
439 */
440 private function get_modified_local() {
441
442 if ( function_exists( 'wp_timezone' ) ) {
443 $timezone = wp_timezone();
444 } else {
445 $timezone = get_option( 'timezone_string' );
446
447 /* calculate the timezone manually if it is not available */
448 if ( ! $timezone ) {
449 $offset = (float) get_option( 'gmt_offset' );
450 $hours = (int) $offset;
451 $minutes = ( $offset - $hours ) * 60;
452
453 $sign = ( $offset < 0 ) ? '-' : '+';
454 $timezone = sprintf( '%s%02d:%02d', $sign, abs( $hours ), abs( $minutes ) );
455 }
456
457 $timezone = new DateTimeZone( $timezone );
458 }
459
460 $datetime = DateTime::createFromFormat( self::DATE_FORMAT, $this->modified, new DateTimeZone( 'UTC' ) );
461 $datetime->setTimezone( $timezone );
462 return $datetime;
463 }
464 }
465