PluginProbe
Code Snippets / 3.6.6
Code Snippets v3.6.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-data-item.php

class-data-item.php in Code Snippets 3.6.6, at php/class-data-item.php

236 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Code_Snippets;
4
5 /**
6 * Base class for representing an item of data without needing to use direct access or individual getter and setter functions.
7 *
8 * @package Code_Snippets
9 *
10 * @since 3.4.0
11 */
12 abstract class Data_Item {
13
14 /**
15 * List of data fields keyed to their current values. Will be initialised with default values.
16 *
17 * @var array<string, mixed>
18 */
19 protected array $fields;
20
21 /**
22 * List of default values provided for fields.
23 *
24 * @var array<string, mixed>
25 */
26 protected array $default_values;
27
28 /**
29 * Optional list of field name aliases to map when resolving a field name.
30 *
31 * @var array<string, string> Field alias names keyed to actual field names.
32 */
33 protected array $field_aliases;
34
35 /**
36 * Class constructor.
37 *
38 * @param array<string, mixed> $default_values List of valid fields mapped to their default values.
39 * @param array<string, mixed>|Data_Item $initial_data Optional initial data to populate fields.
40 * @param array<string, string> $field_aliases Optional list of field name aliases to map when resolving a field name.
41 */
42 public function __construct( array $default_values, $initial_data = null, array $field_aliases = [] ) {
43 $this->fields = $default_values;
44 $this->default_values = $default_values;
45 $this->field_aliases = $field_aliases;
46
47 // If we've accidentally passed an existing object, then fetch its fields before constructing the new object.
48 if ( is_object( $initial_data ) && method_exists( $initial_data, 'get_fields' ) ) {
49 $initial_data = $initial_data->get_fields();
50 }
51
52 $this->set_fields( $initial_data );
53 }
54
55
56 /**
57 * Set all data fields from an array or object. Invalid fields will be ignored.
58 *
59 * @param array<string, mixed>|mixed $data List of data.
60 */
61 public function set_fields( $data ) {
62 // Only accept arrays or objects.
63 if ( ! $data || is_string( $data ) ) {
64 return;
65 }
66
67 // Convert objects into arrays.
68 if ( is_object( $data ) ) {
69 $data = get_object_vars( $data );
70 }
71
72 // Loop through the provided fields and set their values.
73 foreach ( $data as $field => $value ) {
74 $this->set_field( $field, $value );
75 }
76 }
77
78 /**
79 * Retrieve list of current data fields.
80 *
81 * @return array<string, mixed> Field names keyed to current values.
82 */
83 public function get_fields(): array {
84 $fields = [];
85
86 foreach ( $this->get_allowed_fields() as $field_name ) {
87 $fields[ $field_name ] = $this->$field_name;
88 }
89
90 return $fields;
91 }
92
93 /**
94 * Retrieve a list of current data fields, excluding values that are unchanged from the default.
95 *
96 * @return array<string, mixed>
97 */
98 public function get_modified_fields(): array {
99 $modified_fields = [];
100
101 foreach ( $this->get_fields() as $field => $value ) {
102 if ( $value && $value !== $this->default_values[ $field ] ) {
103 $modified_fields[ $field ] = $value;
104 }
105 }
106
107 return $modified_fields;
108 }
109
110 /**
111 * Internal function for resolving the actual name of a field.
112 *
113 * @param string $field A field name, potentially a field alias.
114 *
115 * @return string The resolved field name.
116 */
117 protected function resolve_field_name( string $field ): string {
118 return $this->field_aliases[ $field ] ?? $field;
119 }
120
121 /**
122 * Check if a field is set.
123 *
124 * @param string $field The field name.
125 *
126 * @return bool Whether the field is set.
127 */
128 public function __isset( string $field ) {
129 $field = $this->resolve_field_name( $field );
130 return isset( $this->fields[ $field ] ) || method_exists( $this, 'get_' . $field );
131 }
132
133 /**
134 * Retrieve a field's value.
135 *
136 * @param string $field The field name.
137 *
138 * @return mixed The field value
139 */
140 public function __get( string $field ) {
141 $field = $this->resolve_field_name( $field );
142
143 if ( method_exists( $this, 'get_' . $field ) ) {
144 return call_user_func( array( $this, 'get_' . $field ) );
145 }
146
147 if ( ! $this->is_allowed_field( $field ) ) {
148 if ( WP_DEBUG ) {
149 $message = sprintf( 'Trying to access invalid property on "%s" class: %s', get_class( $this ), $field );
150 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
151 trigger_error( esc_html( $message ), E_USER_WARNING );
152 }
153
154 return null;
155 }
156
157 return $this->fields[ $field ];
158 }
159
160 /**
161 * Set the value of a field.
162 *
163 * @param string $field The field name.
164 * @param mixed $value The field value.
165 */
166 public function __set( string $field, $value ) {
167 $field = $this->resolve_field_name( $field );
168
169 if ( ! $this->is_allowed_field( $field ) ) {
170 if ( WP_DEBUG ) {
171 $message = sprintf( 'Trying to set invalid property on "%s" class: %s', get_class( $this ), $field );
172 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
173 trigger_error( esc_html( $message ), E_USER_ERROR );
174 }
175
176 return;
177 }
178
179 $value = method_exists( $this, 'prepare_' . $field ) ?
180 call_user_func( array( $this, 'prepare_' . $field ), $value ) :
181 $this->prepare_field( $value, $field );
182
183 $this->fields[ $field ] = $value;
184 }
185
186 /**
187 * Prepare a value before it is stored.
188 *
189 * @param mixed $value Value to prepare.
190 * @param string $field Field name.
191 *
192 * @return mixed Value in the correct format.
193 */
194 abstract protected function prepare_field( $value, string $field );
195
196 /**
197 * Retrieve the list of fields that can be written to.
198 *
199 * @return array<string> List of field names.
200 */
201 public function get_allowed_fields(): array {
202 return array_keys( $this->fields ) + array_keys( $this->field_aliases );
203 }
204
205 /**
206 * Determine whether a field is allowed to be written to
207 *
208 * @param string $field The field name.
209 *
210 * @return bool true if the is allowed, false if invalid.
211 */
212 public function is_allowed_field( string $field ): bool {
213 return ( $this->fields && array_key_exists( $field, $this->fields ) ) ||
214 ( $this->field_aliases && array_key_exists( $field, $this->field_aliases ) );
215 }
216
217 /**
218 * Safely set the value for a field.
219 * If the field name is invalid, false will be returned instead of an error thrown.
220 *
221 * @param string $field The field name.
222 * @param mixed $value The field value.
223 *
224 * @return bool true if the field was set successfully, false if the field name is invalid.
225 */
226 public function set_field( string $field, $value ): bool {
227 if ( ! $this->is_allowed_field( $field ) ) {
228 return false;
229 }
230
231 $this->__set( $field, $value );
232
233 return true;
234 }
235 }
236