PluginProbe
ActivityPub / 9.2.1
ActivityPub v9.2.1
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / activity / class-generic-object.php

class-generic-object.php in ActivityPub 9.2.1, at includes/activity/class-generic-object.php

382 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Generic Object.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Activity;
9
10 use function Activitypub\camel_to_snake_case;
11 use function Activitypub\snake_to_camel_case;
12
13 /**
14 * Generic Object.
15 *
16 * This class is used to create Generic Objects.
17 * It is used to create objects that might be unknown by the plugin but
18 * conform to the ActivityStreams vocabulary.
19 *
20 * Provides generic magic methods for getting, setting, and adding properties
21 * through __call(). Specific property documentation is in the classes where
22 * the properties are actually defined.
23 *
24 * @since 5.3.0
25 */
26 #[\AllowDynamicProperties]
27 class Generic_Object {
28 /**
29 * The JSON-LD context for the object.
30 *
31 * @var array
32 */
33 const JSON_LD_CONTEXT = array(
34 'https://www.w3.org/ns/activitystreams',
35 );
36
37 /**
38 * The object's unique global identifier
39 *
40 * @see https://www.w3.org/TR/activitypub/#obj-id
41 *
42 * @var string
43 */
44 protected $id;
45
46 /**
47 * Magic function, to transform the object to string.
48 *
49 * @return string The object id.
50 */
51 public function __toString() {
52 return $this->to_string();
53 }
54
55 /**
56 * Function to transform the object to string.
57 *
58 * @return string The object id.
59 */
60 public function to_string() {
61 return $this->get_id();
62 }
63
64 /**
65 * Magic function to implement getter and setter.
66 *
67 * @param string $method The method name.
68 * @param string $params The method params.
69 *
70 * @return mixed
71 */
72 public function __call( $method, $params ) {
73 $var = \strtolower( \substr( $method, 4 ) );
74
75 if ( \strncasecmp( $method, 'get', 3 ) === 0 ) {
76 if ( ! $this->has( $var ) ) {
77 return null;
78 }
79
80 return $this->$var;
81 }
82
83 if ( \strncasecmp( $method, 'set', 3 ) === 0 ) {
84 return $this->set( $var, $params[0] );
85 }
86
87 if ( \strncasecmp( $method, 'add', 3 ) === 0 ) {
88 return $this->add( $var, $params[0] );
89 }
90
91 return null;
92 }
93
94 /**
95 * Generic getter.
96 *
97 * @param string $key The key to get.
98 *
99 * @return mixed The value.
100 */
101 public function get( $key ) {
102 return \call_user_func( array( $this, 'get_' . $key ) );
103 }
104
105 /**
106 * Generic setter.
107 *
108 * @param string $key The key to set.
109 * @param string $value The value to set.
110 *
111 * @return mixed The value.
112 */
113 public function set( $key, $value ) {
114 $this->$key = $value;
115
116 return $this;
117 }
118
119 /**
120 * Generic adder.
121 *
122 * @param string $key The key to set.
123 * @param mixed $value The value to add.
124 *
125 * @return mixed|void The value.
126 */
127 public function add( $key, $value ) {
128 if ( empty( $value ) ) {
129 return;
130 }
131
132 if ( ! isset( $this->$key ) ) {
133 $this->$key = array();
134 }
135
136 if ( \is_string( $this->$key ) ) {
137 $this->$key = array( $this->$key );
138 }
139
140 $attributes = $this->$key;
141
142 if ( \is_array( $value ) ) {
143 $attributes = \array_merge( $attributes, $value );
144 } else {
145 $attributes[] = $value;
146 }
147
148 $this->$key = \array_unique( $attributes );
149
150 return $this->$key;
151 }
152
153 /**
154 * Check if the object has a key
155 *
156 * @param string $key The key to check.
157 *
158 * @return boolean True if the object has the key.
159 */
160 public function has( $key ) {
161 return \property_exists( $this, $key );
162 }
163
164 /**
165 * Convert JSON input to an array.
166 *
167 * @param string $json The JSON string.
168 *
169 * @return static|\WP_Error An Object built from the JSON string or WP_Error when it's not a JSON string.
170 */
171 public static function init_from_json( $json ) {
172 $array = \json_decode( $json, true );
173
174 if ( ! \is_array( $array ) ) {
175 return new \WP_Error( 'invalid_json', \__( 'Invalid JSON', 'activitypub' ), array( 'status' => 400 ) );
176 }
177
178 return self::init_from_array( $array );
179 }
180
181 /**
182 * Convert input array to a Base_Object.
183 *
184 * @param array $data The object array.
185 *
186 * @return static|\WP_Error An Object built from the input array or WP_Error when it's not an array.
187 */
188 public static function init_from_array( $data ) {
189 if ( ! \is_array( $data ) ) {
190 return new \WP_Error( 'invalid_array', \__( 'Invalid array', 'activitypub' ), array( 'status' => 400 ) );
191 }
192
193 $object = new static();
194 $object->from_array( $data );
195
196 return $object;
197 }
198
199 /**
200 * Convert JSON input to an array and pre-fill the object.
201 *
202 * @param array $data The array.
203 */
204 public function from_array( $data ) {
205 foreach ( $data as $key => $value ) {
206 if ( null !== $value ) {
207 // Convert camelCase to snake_case if not prefixed with '_'.
208 if ( ! \str_starts_with( $key, '_' ) ) {
209 $key = camel_to_snake_case( $key );
210 }
211
212 \call_user_func( array( $this, 'set_' . $key ), $value );
213 }
214 }
215 }
216
217 /**
218 * Convert JSON input to an array and pre-fill the object.
219 *
220 * @param string $json The JSON string.
221 */
222 public function from_json( $json ) {
223 $array = \json_decode( $json, true );
224
225 $this->from_array( $array );
226 }
227
228 /**
229 * Convert Object to an array.
230 *
231 * It tries to get the object attributes if they exist
232 * and falls back to the getters. Empty values are ignored.
233 *
234 * By default, `bto` and `bcc` (the blind audience fields) are stripped
235 * from the output per ActivityPub Section 6, so every serialization path
236 * is safe for emission. Internal callers that need to persist the blind
237 * audience (e.g., outbox/inbox storage) can opt in by passing
238 * `$include_blind_audience = true`.
239 *
240 * @param bool $include_json_ld_context Whether to include the JSON-LD context. Default true.
241 * @param bool $include_blind_audience Whether to keep `bto` and `bcc` in the output. Default false.
242 *
243 * @return array An array built from the Object.
244 */
245 public function to_array( $include_json_ld_context = true, $include_blind_audience = false ) {
246 $array = array();
247 $vars = \get_object_vars( $this );
248
249 foreach ( $vars as $key => $value ) {
250 if ( \is_wp_error( $value ) ) {
251 continue;
252 }
253
254 // Ignore all _prefixed keys.
255 if ( '_' === \substr( $key, 0, 1 ) ) {
256 continue;
257 }
258
259 // If value is empty, try to get it from a getter.
260 if ( ! $value ) {
261 $value = \call_user_func( array( $this, 'get_' . $key ) );
262 }
263
264 if ( \is_object( $value ) ) {
265 $value = $value->to_array( false, $include_blind_audience );
266 }
267
268 if ( \is_array( $value ) && $this->is_namespaced( $key ) ) {
269 foreach ( $value as $sub_key => $sub_value ) {
270 $array[ snake_to_camel_case( $key ) . ':' . snake_to_camel_case( $sub_key ) ] = $sub_value;
271 }
272 } elseif ( isset( $value ) ) {
273 $array[ snake_to_camel_case( $key ) ] = $value;
274 }
275 }
276
277 if ( $include_json_ld_context ) {
278 // Get JsonLD context and move it to '@context' at the top.
279 $array = \array_merge( array( '@context' => $this->get_json_ld_context() ), $array );
280 }
281
282 $class = new \ReflectionClass( $this );
283 $class = \strtolower( $class->getShortName() );
284
285 /**
286 * Filter the array of the ActivityPub object.
287 *
288 * @param array $array The array of the ActivityPub object.
289 * @param string $class The class of the ActivityPub object.
290 * @param string $id The ID of the ActivityPub object.
291 * @param Generic_Object $object The ActivityPub object.
292 *
293 * @return array The filtered array of the ActivityPub object.
294 */
295 $array = \apply_filters( 'activitypub_activity_object_array', $array, $class, $this->id, $this );
296
297 /**
298 * Filter the array of the ActivityPub object by class.
299 *
300 * @param array $array The array of the ActivityPub object.
301 * @param string $id The ID of the ActivityPub object.
302 * @param Generic_Object $object The ActivityPub object.
303 *
304 * @return array The filtered array of the ActivityPub object.
305 */
306 $array = \apply_filters( "activitypub_activity_{$class}_object_array", $array, $this->id, $this );
307
308 if ( ! $include_blind_audience ) {
309 /*
310 * Strip `bto` and `bcc` from the serialized array per ActivityPub Section 6.
311 * Callers that need the blind audience either read it from the object via
312 * `get_bto()` / `get_bcc()` or opt in with `$include_blind_audience = true`.
313 */
314 unset( $array['bto'], $array['bcc'] );
315 if ( isset( $array['object'] ) && \is_array( $array['object'] ) ) {
316 unset( $array['object']['bto'], $array['object']['bcc'] );
317 }
318 }
319
320 return $array;
321 }
322
323 /**
324 * Convert Object to JSON.
325 *
326 * @param bool $include_json_ld_context Whether to include the JSON-LD context. Default true.
327 * @param bool $include_blind_audience Whether to keep `bto` and `bcc` in the output. Default false.
328 *
329 * @return string The JSON string.
330 */
331 public function to_json( $include_json_ld_context = true, $include_blind_audience = false ) {
332 $array = $this->to_array( $include_json_ld_context, $include_blind_audience );
333 $options = \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT | \JSON_UNESCAPED_SLASHES;
334
335 /**
336 * Options to be passed to json_encode().
337 *
338 * @param int $options The current options flags.
339 */
340 $options = \apply_filters( 'activitypub_json_encode_options', $options );
341
342 return \wp_json_encode( $array, $options );
343 }
344
345 /**
346 * Returns the keys of the object vars.
347 *
348 * @return array The keys of the object vars.
349 */
350 public function get_object_var_keys() {
351 return \array_keys( \get_object_vars( $this ) );
352 }
353
354 /**
355 * Returns the JSON-LD context of this object.
356 *
357 * @return array $context A compacted JSON-LD context for the ActivityPub object.
358 */
359 public function get_json_ld_context() {
360 return static::JSON_LD_CONTEXT;
361 }
362
363 /**
364 * Checks if an attribute is in a namespace.
365 *
366 * @param string $attribute The attribute to check.
367 *
368 * @return bool Whether the attribute is namespaced.
369 */
370 private function is_namespaced( $attribute ) {
371 $namespaces = array();
372
373 foreach ( static::JSON_LD_CONTEXT as $context ) {
374 if ( \is_array( $context ) ) {
375 $namespaces = \array_merge( $namespaces, $context );
376 }
377 }
378
379 return isset( $namespaces[ $attribute ] ) && \wp_http_validate_url( $namespaces[ $attribute ] );
380 }
381 }
382