PluginProbe
WP-Stateless – Google Cloud Storage / trunk
WP-Stateless – Google Cloud Storage vtrunk
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / lib / includes / class-settings.php

class-settings.php in WP-Stateless – Google Cloud Storage trunk, at lib/includes/class-settings.php

699 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings Library
4 *
5 * @namespace UDX
6 */
7 namespace UDX {
8
9 if( !class_exists( 'UDX\Settings' ) ) {
10
11 /**
12 * Class Settings
13 *
14 * @package UDX
15 */
16 class Settings {
17
18 /**
19 * Settings Class version.
20 *
21 * @public
22 * @static
23 * @property $version
24 * @type {Object}
25 */
26 static public $version = '0.3.0';
27
28 /**
29 * Prefix for option keys to unique
30 *
31 * @var string
32 */
33 private $prefix = '';
34
35 /**
36 * Whether or not to hash option keys before saving
37 *
38 * @var bool
39 */
40 private $hash_keys = false;
41
42 /**
43 * Data storage
44 *
45 * @var array
46 */
47 private $_data = array();
48
49 /**
50 * Settings Schema.
51 *
52 * @static
53 * @property $_schema
54 * @type {Object}
55 */
56 private $_schema = false;
57
58 /**
59 * Settings Namespace.
60 *
61 * @static
62 * @property $_namespace
63 * @type {String}
64 */
65 private $_namespace = false;
66
67 /**
68 * Storage Key.
69 *
70 * @static
71 * @property $_key
72 * @type {String}
73 */
74 private $_key = false;
75
76 /**
77 * Settings Format.
78 *
79 * Options are: 'json', 'object', 'array', 'hash-map'.
80 *
81 * @static
82 * @property $_format
83 * @type {String}
84 */
85 private $_format = null;
86
87 /**
88 * Storage Location
89 *
90 * @static
91 * @property $_store
92 * @type {String}
93 */
94 private $_store = false;
95
96 /**
97 * For transient storage.
98 *
99 * @static
100 * @property $_expiration
101 * @type {String}
102 */
103 private $_expiration = 60;
104
105 /**
106 * Toggle Debugger.
107 *
108 * @static
109 * @property $_debug
110 * @type {Boolean}
111 */
112 private $_debug = false;
113
114 /**
115 * Automatically Save.
116 *
117 * @static
118 * @property $_debug
119 * @type {Boolean}
120 */
121 private $auto_commit = false;
122
123 /**
124 * Instance Valid.
125 *
126 * @static
127 * @property $is_valid
128 * @type {Boolean}
129 */
130 public $is_valid = true;
131
132 /**
133 * Constructor
134 *
135 * @param bool $args
136 *
137 * @example
138 *
139 * $_settings = new Settings(array(
140 * "store" => "options"
141 * ));
142 *
143 * $_settings->set( 'my.key', 'my.value' );
144 *
145 * @internal param \UDX\type $defaults
146 * @internal param bool|\UDX\type $force_save
147 * @internal param string|\UDX\type $prefix
148 * @internal param bool|\UDX\type $hash_keys
149 */
150 public function __construct( $args = false ) {
151
152 $args = Utility::parse_args( $args, array(
153 'namespace' => '',
154 'key' => '',
155 'auto_commit' => false,
156 'debug' => false,
157 'store' => false,
158 'schema' => false,
159 'format' => false,
160 'data' => null
161 ));
162
163 // Load Schema.
164 if( isset( $args->schema ) && $args->schema ) {
165 $this->set_schema( $args->schema );
166 }
167
168 // Set Storage Location(s).
169 if( isset( $args->store ) && $args->store ) {
170 $this->_store = $args->store;
171 }
172
173 // Set Storage Key.
174 if( isset( $args->key ) && $args->key ) {
175 $this->_key = $args->key;
176 }
177
178 // Set transient vxpiration value.
179 if( isset( $args->expiration ) && $args->expiration ) {
180 $this->_expiration = $args->expiration;
181 }
182
183 // Set Format to enforce.
184 if( $args->format ) {
185 $this->_format = $args->format;
186 }
187 // Toggle Debugger.
188 if( $args->debug ) {
189 $this->_debug = $args->debug;
190 }
191
192 // Set Settings Namespace.
193 if( $args->namespace ) {
194 $this->_namespace = $args->namespace;
195 }
196
197 // Set Auto Commit.
198 if( $args->auto_commit ) {
199 $this->auto_commit = $args->auto_commit;
200 }
201
202 // Load Initial.
203 $this->_load();
204
205 // Set Initial Data.
206 if( $args->data ) {
207 $this->set( $args->data );
208 }
209
210 }
211
212 /**
213 * Getter for options
214 *
215 * @param bool|\UDX\type $key
216 *
217 * @param bool $default
218 *
219 * @return type
220 */
221 public function get( $key = false, $default = false ) {
222
223 // Return all data.
224 if( !$key ) {
225 return $this->_output( $this->_data );
226 }
227
228 // Resolve dot-notated key.
229 if( strpos( $key, '.' ) ) {
230 return $this->_resolve( $this->_data, $key, $default );
231 }
232
233 // Return value or default.
234 return isset( $this->_data[ $key ] ) ? $this->_data[ $key ] : $default;
235
236 }
237
238 /**
239 * Setter for options
240 *
241 * @param string|\UDX\type $key
242 * @param bool|\UDX\type $value
243 * @param bool $bypass_validation
244 *
245 * @internal param bool|\UDX\type $force_save
246 *
247 * @return \UDX\Settings
248 */
249 public function set( $key = '', $value = false, $bypass_validation = false ) {
250
251 if( !$this->_data ) {
252 $this->_data = array();
253 }
254
255 // First argument is an object/array.
256 if( Utility::get_type( $key ) === 'object' || Utility::get_type( $key ) === 'array' || Utility::get_type( $key ) === 'stdClass' ) {
257
258 // Conver to array so merge can be done
259 $this->_data = Utility::extend( (array) $this->_data, (array) $key );
260
261 }
262
263 // Standard key & value pair
264 if( Utility::get_type( $key ) === 'string' && ( Utility::get_type( $value ) === 'string' || Utility::get_type( $value ) === 'number' || Utility::get_type( $value ) === 'boolean' ) ) {
265
266 if( strpos( $key, '.' ) ) {
267 self::set_val( $this->_data, $key, $value );
268 } else {
269 $this->_data[ $key ] = $value;
270 }
271
272 }
273
274 // Standard key with complex value.
275 if( Utility::get_type( $key ) === 'string' ) {
276
277
278 if( Utility::get_type( $value ) === 'object' || Utility::get_type( $value ) === 'stdClass' ) {
279
280 if( strpos( $key, '.' ) ) {
281 self::set_val( $this->_data, $key, $value );
282 } else {
283
284 if( isset( $this->_data[ $key ] ) && Utility::get_type( $this->_data[ $key ] ) === 'object' ) {
285 $this->_data[ $key ] = Utility::extend( $this->_data[ $key ], $value );
286 } else {
287 $this->_data[ $key ] = $value;
288 }
289
290 }
291
292 }
293
294 // Standard key with array value
295 if( Utility::get_type( $value ) === 'array' ) {
296
297 if( strpos( $key, '.' ) ) {
298 self::set_val( $this->_data, $key, $value );
299 } else {
300 $this->_data[ $key ] = array_unique( array_merge( isset( $this->_data[ $key ] ) ? (array) $this->_data[ $key ] : array(), $value ) );
301 }
302
303 }
304
305 }
306
307 // Validate if we have a schema.
308 if( $this->_schema ) {
309 // $this->_validate();
310 }
311
312 // Commit to Storage if validation passed.
313 if( $this->is_valid && $this->auto_commit ) {
314 $this->commit();
315 }
316
317 return $this;
318
319 }
320
321 /**
322 * Handle File Transfer for downloading
323 *
324 * @param array $args
325 *
326 * @return bool
327 */
328 public function file_transfer( $args = array() ) {
329
330 $args = Utility::parse_args( $args, array(
331 "name" => "settings",
332 "format" => "json",
333 "cache" => 'public',
334 "filename" => null,
335 "charset" => 'utf8'
336 ) );
337
338 $args->filename = $args->filename ? $args->filename : $args->name . '-' . date( 'Y-m-d' ) . '.' . $args->format;
339
340 // Ensure headers have not been sent.
341 if( headers_sent() ) {
342 return false;
343 }
344
345 // Send headers.
346 header( "Cache-Control: {$args->cache}" );
347 header( "Content-Disposition: attachment; filename={$args->filename}" );
348 header( "Content-Type: text/plain; charset={$args->charset}" );
349 header( "Content-Description: File Transfer" );
350 header( "Content-Transfer-Encoding: binary" );
351
352 // Prepare in needed format.
353 $_data = $this->_output( $this->get(), $args->format );
354
355 // Write data.
356 die( $_data );
357
358 }
359
360 /**
361 * Set Schema from a string or objct.
362 *
363 * @param bool $schema
364 *
365 * @return array|bool|mixed|object
366 */
367 public function set_schema( $schema = false ) {
368
369 try {
370
371 // Take schema as given.
372 if( gettype( $schema ) === 'array' ) {
373 $this->_schema = (object) $schema;
374 }
375
376 // Take schema as given.
377 if( gettype( $schema ) === 'object' ) {
378 $this->_schema = $schema;
379 }
380
381 // Load schema from a file.
382 if( gettype( $schema ) === 'string' && is_file( $schema ) ) {
383 $this->_schema = json_decode( file_get_contents( $schema ) );
384 }
385
386 } catch( Exception $error ) {
387 $this->console( 'Caught exception: ' . $error->getMessage() );
388 }
389
390 return $this->_schema ? $this->_schema : false;
391
392 }
393
394 /**
395 * Commit Settings to Storage.
396 *
397 * * site_options - For WordPress, falls back to option if not in multisite.
398 * * options - For WordPress, stores in blog options.
399 *
400 * @author potanin@UD
401 * @method commit
402 */
403 public function commit() {
404
405 $_data = $this->_data;
406
407 // Exclude protected keys.
408 foreach( (array) $_data as $key => $value ) {
409
410 if( substr( $key, 0, 2 ) === '__' ) {
411 unset( $_data[ $key ] );
412 }
413
414 }
415
416 switch( $this->_store ) {
417
418 case 'transient':
419 $_value = json_encode( $_data, JSON_FORCE_OBJECT );
420
421 if( function_exists( 'set_transient' ) ) {
422 $_value = set_transient( $this->_key, $_value, $this->_expiration );
423 }
424
425 break;
426
427 case 'site_transient':
428
429 $_value = json_encode( $_data, JSON_FORCE_OBJECT );
430
431 if( function_exists( 'set_site_transient' ) ) {
432 $_value = set_site_transient( $this->_key, $_value, $this->_expiration );
433 }
434
435 break;
436
437 case 'site_options':
438
439 $_value = json_encode( $_data, JSON_FORCE_OBJECT );
440
441 if( function_exists( 'update_site_option' ) ) {
442 $_value = update_site_option( $this->_key, $_value );
443 }
444
445 break;
446
447 case 'options':
448 $_value = json_encode( $_data, JSON_FORCE_OBJECT );
449
450 if( function_exists( 'update_option' ) ) {
451 $_value = update_option( $this->_key, $_value );
452 }
453
454 break;
455
456 }
457
458 return $this;
459
460 }
461
462 /**
463 * Remove Stored Settings
464 *
465 * @example
466 *
467 * $settings->flush();
468 *
469 * @return $this
470 */
471 public function flush() {
472
473 switch( $this->_store ) {
474 case 'options':
475 if( function_exists( 'delete_option' ) ) {
476 delete_option( $this->_key );
477 }
478 case 'site_options':
479 if( function_exists( 'delete_site_option' ) ) {
480 delete_option( $this->_key );
481 }
482 break;
483
484 }
485
486 return $this;
487
488 }
489 /**
490 * Validate Settings against Schema
491 *
492 */
493 public function _validate() {
494
495 if( !class_exists( 'JsonSchema\Validator' ) ) {
496 return;
497 }
498
499 $validator = new \JsonSchema\Validator();
500
501 // Process Validation.
502 $validator->check( $this->_data, $this->_schema );
503
504 if( $validator->isValid() ) {
505 $this->is_valid = true;
506 $this->_console( "The supplied JSON validates against the schema." );
507 } else {
508 $this->is_valid = false;
509
510 $this->_console( "JSON does not validate. Violations:" );
511
512 foreach( $validator->getErrors() as $error ) {
513 $this->_console( sprintf( "[%s] %s\n", $error[ 'property' ], $error[ 'message' ] ) );
514 }
515
516 }
517
518 }
519
520 /**
521 * Library Debugger
522 *
523 * @param $data
524 */
525 public function _console( $data ) {
526
527 if( $this->_debug ) {
528 echo sprintf( "lib-settings debug: [%s].", $data );
529 }
530
531 }
532
533 /**
534 * Load options from DB
535 *
536 * @return \UDX\Settings
537 */
538 public function _load() {
539
540 switch( $this->_store ) {
541
542 // WordPress Site Options.
543 case 'options':
544 case 'site_options':
545
546 // Load from options.
547 if( $this->_store == 'site_options' ) {
548 $_value = \get_site_option( $this->_key );
549 } else {
550 $_value = \get_option( $this->_key );
551 }
552
553 // If already an array it must have been serialized
554 if( gettype( $_value ) === 'array' ) {
555 return $this->_output( $this->_data = $_value );
556 }
557
558 try {
559
560 $_value = json_decode( $_value, true );
561
562 } catch( Exception $error ) {
563 $this->_console( 'Caught exception: ' . $error->getMessage() );
564 }
565
566 $this->_data = $_value;
567
568 break;
569
570 default:
571
572 break;
573
574 }
575
576 return $this->_data;
577
578 }
579
580 /**
581 * Prepare Data for Output
582 *
583 * @param $data
584 *
585 * @param bool $format
586 *
587 * @return array|mixed|string|void
588 */
589 public function _output( $data, $format = false ) {
590
591 $format = $format ? $format : $this->_format;
592
593 // Stringify.
594 if( $format === 'json' ) {
595 return json_encode( $data );
596 }
597
598 // Deeep Object.
599 if( $format === 'object' ) {
600 return json_decode( json_encode( $data ) );
601 }
602
603 return $data;
604
605 }
606
607 /**
608 * @param array $arr
609 * @param $path
610 * @param $val
611 *
612 * @return mixed
613 */
614 public function set_val( array &$arr, $path, $val ) {
615 $loc = & $arr;
616
617 foreach( explode( '.', $path ) as $step ) {
618 $loc = & $loc[ $step ];
619 }
620
621 return $loc = $val;
622
623 }
624
625 /**
626 * Expand Array
627 * @source http://stackoverflow.com/questions/17365059/how-to-unflatten-array-in-php-using-dot-notation
628 *
629 * @param $array
630 * @param int $level
631 *
632 * @return array
633 */
634 public function _expand( $array, $level = 0 ) {
635 $result = array();
636 $next = $level + 1;
637
638 foreach( $array as $key => $value ) {
639 $tree = explode( '.', $key );
640
641 if( isset( $tree[ $level ] ) ) {
642 if( !isset( $tree[ $next ] ) ) {
643 $result[ $tree[ $level ] ][ 'id' ] = $key;
644 $result[ $tree[ $level ] ][ 'title' ] = $value;
645 if( !isset( $result[ $tree[ $level ] ][ 'children' ] ) ) {
646 $result[ $tree[ $level ] ][ 'children' ] = array();
647 }
648 } else {
649 if( isset( $result[ $tree[ $level ] ][ 'children' ] ) ) {
650 $result[ $tree[ $level ] ][ 'children' ] = array_merge_recursive( $result[ $tree[ $level ] ][ 'children' ], self::_expand( array( $key => $value ), $next ) );
651 } else {
652 $result[ $tree[ $level ] ][ 'children' ] = self::_expand( array( $key => $value ), $next );
653 }
654 }
655
656 }
657 }
658
659 return $result;
660
661 }
662
663 /**
664 * Resolve dot-notated key.
665 *
666 * @source http://stackoverflow.com/questions/14704984/best-way-for-dot-notation-access-to-multidimensional-array-in-php
667 *
668 * @param $a
669 * @param $path
670 * @param null $default
671 *
672 * @internal param array $a
673 * @return array|null
674 */
675 public function _resolve( $a, $path, $default = null ) {
676
677 $current = $a;
678 $p = strtok( $path, '.' );
679
680 while( $p !== false ) {
681
682 if( !isset( $current[ $p ] ) ) {
683 return $default;
684 }
685
686 $current = $current[ $p ];
687 $p = strtok( '.' );
688
689 }
690
691 return $current;
692
693 }
694
695 }
696
697 }
698
699 }