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

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

229 lines 6.1 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 $fields;
20
21 /**
22 * List of default values provided for fields.
23 *
24 * @var array<string, mixed>
25 */
26 protected $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 $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 return $this->fields;
85 }
86
87 /**
88 * Retrieve a list of current data fields, excluding values that are unchanged from the default.
89 *
90 * @return array<string, mixed>
91 */
92 public function get_modified_fields(): array {
93 $modified_fields = [];
94
95 foreach ( $this->get_fields() as $field => $value ) {
96 if ( $value && $value !== $this->default_values[ $field ] ) {
97 $modified_fields[ $field ] = $value;
98 }
99 }
100
101 return $modified_fields;
102 }
103
104 /**
105 * Internal function for resolving the actual name of a field.
106 *
107 * @param string $field A field name, potentially a field alias.
108 *
109 * @return string The resolved field name.
110 */
111 protected function resolve_field_name( string $field ): string {
112 return $this->field_aliases[ $field ] ?? $field;
113 }
114
115 /**
116 * Check if a field is set.
117 *
118 * @param string $field The field name.
119 *
120 * @return bool Whether the field is set.
121 */
122 public function __isset( string $field ) {
123 $field = $this->resolve_field_name( $field );
124 return isset( $this->fields[ $field ] ) || method_exists( $this, 'get_' . $field );
125 }
126
127 /**
128 * Retrieve a field's value.
129 *
130 * @param string $field The field name.
131 *
132 * @return mixed The field value
133 */
134 public function __get( string $field ) {
135 $field = $this->resolve_field_name( $field );
136
137 if ( method_exists( $this, 'get_' . $field ) ) {
138 return call_user_func( array( $this, 'get_' . $field ) );
139 }
140
141 if ( ! $this->is_allowed_field( $field ) ) {
142 if ( WP_DEBUG ) {
143 $message = sprintf( 'Trying to access invalid property on "%s" class: %s', get_class( $this ), $field );
144 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
145 trigger_error( esc_html( $message ), E_USER_WARNING );
146 }
147
148 return null;
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( string $field, $value ) {
161 $field = $this->resolve_field_name( $field );
162
163 if ( ! $this->is_allowed_field( $field ) ) {
164 if ( WP_DEBUG ) {
165 $message = sprintf( 'Trying to set invalid property on "%s" class: %s', get_class( $this ), $field );
166 // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
167 trigger_error( esc_html( $message ), E_USER_ERROR );
168 }
169
170 return;
171 }
172
173 $value = method_exists( $this, 'prepare_' . $field ) ?
174 call_user_func( array( $this, 'prepare_' . $field ), $value ) :
175 $this->prepare_field( $value, $field );
176
177 $this->fields[ $field ] = $value;
178 }
179
180 /**
181 * Prepare a value before it is stored.
182 *
183 * @param mixed $value Value to prepare.
184 * @param string $field Field name.
185 *
186 * @return mixed Value in the correct format.
187 */
188 abstract protected function prepare_field( $value, string $field );
189
190 /**
191 * Retrieve the list of fields that can be written to.
192 *
193 * @return array<string> List of field names.
194 */
195 public function get_allowed_fields(): array {
196 return array_keys( $this->fields ) + array_keys( $this->field_aliases );
197 }
198
199 /**
200 * Determine whether a field is allowed to be written to
201 *
202 * @param string $field The field name.
203 *
204 * @return bool true if the is allowed, false if invalid.
205 */
206 public function is_allowed_field( string $field ): bool {
207 return array_key_exists( $field, $this->fields ) || array_key_exists( $field, $this->field_aliases );
208 }
209
210 /**
211 * Safely set the value for a field.
212 * If the field name is invalid, false will be returned instead of an error thrown.
213 *
214 * @param string $field The field name.
215 * @param mixed $value The field value.
216 *
217 * @return bool true if the field was set successfully, false if the field name is invalid.
218 */
219 public function set_field( string $field, $value ): bool {
220 if ( ! $this->is_allowed_field( $field ) ) {
221 return false;
222 }
223
224 $this->__set( $field, $value );
225
226 return true;
227 }
228 }
229