PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.9
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.9
1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 0.8.6 All 33 releases
desktop-mode / includes / framework / app / class-state.php

class-state.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.9, at includes/framework/app/class-state.php

235 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation App Framework — window state.
4 *
5 * The one bag of values a window's view is a function of. The app
6 * declares its shape with `App::state( $defaults )`; the client
7 * echoes the bag back with every dispatch; the server rebuilds it
8 * here, **admitting only declared keys and only values of the
9 * declared type**. A client can therefore never smuggle in a key the
10 * app did not ask for, and an action can rely on `$state->get(
11 * 'range' )` being the string it was declared as.
12 *
13 * Everything else — parsed entries, query results, computed rows —
14 * is not state. It is derived inside the view on every render, which
15 * keeps the wire small and the server stateless.
16 *
17 * @package OpenStation
18 */
19
20 namespace OpenStation\App;
21
22 // Direct access, unless a standalone host is booting on bare PHP.
23 if ( ! defined( 'ABSPATH' ) ) {
24 defined( 'OPENSTATION_STANDALONE' ) || exit;
25 }
26
27 /**
28 * Typed, schema-bound window state.
29 */
30 final class State implements \ArrayAccess, \JsonSerializable {
31
32 /**
33 * Declared defaults — the schema.
34 *
35 * @var array<string,mixed>
36 */
37 private $defaults;
38
39 /**
40 * Current values, always a superset of the defaults' keys.
41 *
42 * @var array<string,mixed>
43 */
44 private $values;
45
46 /**
47 * @param array<string,mixed> $defaults Declared defaults.
48 * @param array<string,mixed> $incoming Values sent by the client; filtered against the defaults.
49 */
50 public function __construct( array $defaults, array $incoming = array() ) {
51 $this->defaults = $defaults;
52 $this->values = $defaults;
53 foreach ( $defaults as $key => $default ) {
54 if ( array_key_exists( $key, $incoming ) ) {
55 $this->values[ $key ] = self::accept( $default, $incoming[ $key ] );
56 }
57 }
58 }
59
60 /**
61 * Coerce an incoming value onto the declared default's type, or
62 * fall back to the default when the shapes disagree.
63 *
64 * @param mixed $default Declared default.
65 * @param mixed $value Incoming value.
66 * @return mixed
67 */
68 private static function accept( $default, $value ) {
69 if ( is_bool( $default ) ) {
70 if ( is_bool( $value ) ) {
71 return $value;
72 }
73 if ( is_string( $value ) || is_int( $value ) ) {
74 return in_array( $value, array( '1', 1, 'true', 'on' ), true );
75 }
76 return $default;
77 }
78 if ( is_int( $default ) ) {
79 return is_numeric( $value ) ? (int) $value : $default;
80 }
81 if ( is_float( $default ) ) {
82 return is_numeric( $value ) ? (float) $value : $default;
83 }
84 if ( is_string( $default ) ) {
85 return is_scalar( $value ) ? (string) $value : $default;
86 }
87 if ( is_array( $default ) ) {
88 return is_array( $value ) ? $value : $default;
89 }
90 // A `null` default declares an untyped slot: any JSON value.
91 return is_scalar( $value ) || is_array( $value ) || null === $value ? $value : $default;
92 }
93
94 /**
95 * Read a key.
96 *
97 * @param string $key State key.
98 * @param mixed $fallback Returned for an undeclared key.
99 * @return mixed
100 */
101 public function get( $key, $fallback = null ) {
102 return array_key_exists( $key, $this->values ) ? $this->values[ $key ] : $fallback;
103 }
104
105 /**
106 * Write a key. Undeclared keys are ignored — declare them in
107 * `App::state()` first.
108 *
109 * @param string $key State key.
110 * @param mixed $value New value.
111 * @return self
112 */
113 public function set( $key, $value ) {
114 if ( array_key_exists( $key, $this->defaults ) ) {
115 $this->values[ $key ] = self::accept( $this->defaults[ $key ], $value );
116 }
117 return $this;
118 }
119
120 /**
121 * Whether a key is declared.
122 *
123 * @param string $key State key.
124 * @return bool
125 */
126 public function has( $key ) {
127 return array_key_exists( $key, $this->defaults );
128 }
129
130 /**
131 * Flip a boolean key.
132 *
133 * @param string $key State key.
134 * @return self
135 */
136 public function toggle( $key ) {
137 return $this->set( $key, ! $this->get( $key ) );
138 }
139
140 /**
141 * Add an item to a list key when absent, remove it when present.
142 * The shape every "expanded rows" / "hidden series" set needs.
143 *
144 * @param string $key State key holding a list.
145 * @param mixed $item Scalar item.
146 * @return self
147 */
148 public function toggle_item( $key, $item ) {
149 $list = $this->get( $key );
150 if ( ! is_array( $list ) ) {
151 return $this;
152 }
153 $index = array_search( $item, $list, true );
154 if ( false === $index ) {
155 $list[] = $item;
156 } else {
157 unset( $list[ $index ] );
158 }
159 return $this->set( $key, array_values( $list ) );
160 }
161
162 /**
163 * Whether a list key contains an item.
164 *
165 * @param string $key State key holding a list.
166 * @param mixed $item Scalar item.
167 * @return bool
168 */
169 public function contains( $key, $item ) {
170 $list = $this->get( $key );
171 return is_array( $list ) && in_array( $item, $list, true );
172 }
173
174 /**
175 * Put a key back to its declared default.
176 *
177 * @param string $key State key.
178 * @return self
179 */
180 public function reset( $key ) {
181 if ( array_key_exists( $key, $this->defaults ) ) {
182 $this->values[ $key ] = $this->defaults[ $key ];
183 }
184 return $this;
185 }
186
187 /**
188 * Every value, declared order.
189 *
190 * @return array<string,mixed>
191 */
192 public function all() {
193 return $this->values;
194 }
195
196 /**
197 * The declared defaults.
198 *
199 * @return array<string,mixed>
200 */
201 public function defaults() {
202 return $this->defaults;
203 }
204
205 /** {@inheritDoc} */
206 #[\ReturnTypeWillChange]
207 public function jsonSerialize() {
208 return $this->values;
209 }
210
211 /** {@inheritDoc} */
212 #[\ReturnTypeWillChange]
213 public function offsetExists( $offset ) {
214 return $this->has( $offset );
215 }
216
217 /** {@inheritDoc} */
218 #[\ReturnTypeWillChange]
219 public function offsetGet( $offset ) {
220 return $this->get( $offset );
221 }
222
223 /** {@inheritDoc} */
224 #[\ReturnTypeWillChange]
225 public function offsetSet( $offset, $value ) {
226 $this->set( $offset, $value );
227 }
228
229 /** {@inheritDoc} */
230 #[\ReturnTypeWillChange]
231 public function offsetUnset( $offset ) {
232 $this->reset( $offset );
233 }
234 }
235