PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.3
WooCommerce v10.9.3
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Internal / PushNotifications / DataStores / PushTokensDataStore.php
woocommerce / src / Internal / PushNotifications / DataStores Last commit date
NotificationPreferencesDataStore.php 4 weeks ago PushTokensDataStore.php 2 months ago
PushTokensDataStore.php
466 lines
1 <?php
2 /**
3 * PushTokensDataStore class file.
4 */
5
6 declare( strict_types = 1 );
7
8 namespace Automattic\WooCommerce\Internal\PushNotifications\DataStores;
9
10 defined( 'ABSPATH' ) || exit;
11
12 use Automattic\WooCommerce\Internal\PushNotifications\Entities\PushToken;
13 use Automattic\WooCommerce\Internal\PushNotifications\Exceptions\PushTokenInvalidDataException;
14 use Automattic\WooCommerce\Internal\PushNotifications\Exceptions\PushTokenNotFoundException;
15 use Exception;
16 use WC_Data_Exception;
17 use WP_Http;
18 use WP_Query;
19
20 /**
21 * Data store class for push tokens.
22 *
23 * @since 10.5.0
24 */
25 class PushTokensDataStore {
26 /**
27 * In-memory cache for get_tokens_for_roles() results, keyed by the
28 * comma-joined role list (with optional pagination suffix). Avoids
29 * repeated DB queries within the same PHP request.
30 *
31 * @var array<string, PushToken[]|array{tokens: PushToken[], total: int, total_pages: int}>
32 */
33 private array $tokens_by_roles_cache = array();
34
35 const SUPPORTED_META = array(
36 'origin',
37 'device_uuid',
38 'token',
39 'platform',
40 'device_locale',
41 'metadata',
42 );
43
44 /**
45 * Creates a post representing the push token.
46 *
47 * @since 10.5.0
48 * @param array $data Token data with keys: user_id, token, platform, device_uuid (optional), origin.
49 * @throws PushTokenInvalidDataException If the token data is invalid.
50 * @throws WC_Data_Exception If the token creation fails.
51 * @return PushToken The created push token with ID set.
52 */
53 public function create( array $data ): PushToken {
54 $push_token = new PushToken( $data );
55
56 if ( ! $push_token->can_be_created() ) {
57 throw new PushTokenInvalidDataException(
58 'Can\'t create push token because the push token data provided is invalid.'
59 );
60 }
61
62 $id = wp_insert_post(
63 array(
64 'post_author' => (int) $push_token->get_user_id(),
65 'post_type' => PushToken::POST_TYPE,
66 'post_status' => 'private',
67 'meta_input' => $this->build_meta_array_from_token( $push_token ),
68 ),
69 true
70 );
71
72 if ( is_wp_error( $id ) ) {
73 // phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
74 throw new WC_Data_Exception(
75 (string) $id->get_error_code(),
76 $id->get_error_message(),
77 WP_Http::INTERNAL_SERVER_ERROR
78 );
79 // phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
80 }
81
82 $push_token->set_id( $id );
83
84 return $push_token;
85 }
86
87 /**
88 * Gets post representing a push token.
89 *
90 * @since 10.5.0
91 * @param int $id The push token ID.
92 * @throws PushTokenInvalidDataException If the ID is invalid.
93 * @throws PushTokenNotFoundException If the token can't be found.
94 * @return PushToken The populated push token.
95 */
96 public function read( int $id ): PushToken {
97 $push_token = new PushToken( array( 'id' => $id ) );
98 $post = get_post( $push_token->get_id() );
99
100 if ( ! $post || PushToken::POST_TYPE !== $post->post_type ) {
101 throw new PushTokenNotFoundException();
102 }
103
104 $meta = $this->build_meta_array_from_database( (int) $push_token->get_id() );
105
106 if (
107 empty( $meta['token'] )
108 || empty( $meta['platform'] )
109 || empty( $meta['origin'] )
110 || (
111 empty( $meta['device_uuid'] )
112 && PushToken::PLATFORM_BROWSER !== $meta['platform']
113 )
114 ) {
115 throw new PushTokenInvalidDataException(
116 'Can\'t read push token because the push token record is malformed.'
117 );
118 }
119
120 $push_token->set_user_id( (int) $post->post_author );
121 $push_token->set_token( $meta['token'] );
122 $push_token->set_device_uuid( $meta['device_uuid'] ?? null );
123 $push_token->set_platform( $meta['platform'] );
124 $push_token->set_origin( $meta['origin'] );
125
126 /**
127 * These meta items were added after the ability to store tokens, so may
128 * not be available for older tokens. Use sensible defaults.
129 */
130 $push_token->set_device_locale( $meta['device_locale'] ?? PushToken::DEFAULT_DEVICE_LOCALE );
131 $push_token->set_metadata( $meta['metadata'] ?? array() );
132
133 return $push_token;
134 }
135
136 /**
137 * Updates a post representing the push token.
138 *
139 * @since 10.5.0
140 * @param PushToken $push_token The push token to update.
141 * @throws PushTokenInvalidDataException If the token can't be updated.
142 * @throws WC_Data_Exception If the token update fails.
143 * @return bool True on success.
144 */
145 public function update( PushToken $push_token ): bool {
146 if ( ! $push_token->can_be_updated() ) {
147 throw new PushTokenInvalidDataException(
148 'Can\'t update push token because the push token data provided is invalid.'
149 );
150 }
151
152 $result = wp_update_post(
153 array(
154 'ID' => (int) $push_token->get_id(),
155 'post_author' => (int) $push_token->get_user_id(),
156 'post_type' => PushToken::POST_TYPE,
157 'post_status' => 'private',
158 'meta_input' => $this->build_meta_array_from_token( $push_token ),
159 ),
160 true
161 );
162
163 if ( is_wp_error( $result ) ) {
164 // phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
165 throw new WC_Data_Exception(
166 (string) $result->get_error_code(),
167 $result->get_error_message(),
168 WP_Http::INTERNAL_SERVER_ERROR
169 );
170 // phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
171 }
172
173 if ( null === $push_token->get_device_uuid() ) {
174 delete_post_meta( (int) $push_token->get_id(), 'device_uuid' );
175 }
176
177 return true;
178 }
179
180 /**
181 * Deletes a push token.
182 *
183 * @since 10.5.0
184 * @param int $id The push token ID.
185 * @throws PushTokenNotFoundException If the token can't be found.
186 * @return bool True on success.
187 */
188 public function delete( int $id ): bool {
189 $post = get_post( $id );
190
191 if ( ! $post || PushToken::POST_TYPE !== $post->post_type ) {
192 throw new PushTokenNotFoundException();
193 }
194
195 return (bool) wp_delete_post( (int) $id, true );
196 }
197
198 /**
199 * Find tokens for this user and platform that match either the token
200 * or device UUID. We check the token value to avoid creating a duplicate.
201 * We check the device UUID value because only one token should be issued
202 * per device, therefore if we already have one then we can update it to
203 * avoid creating a duplicate.
204 *
205 * @since 10.5.0
206 * @param array $data Token data with keys: user_id, platform, origin, token (optional), device_uuid (optional).
207 * @return null|PushToken
208 * @throws PushTokenInvalidDataException If push token is missing data.
209 */
210 public function get_by_token_or_device_id( array $data ): ?PushToken {
211 $user_id = $data['user_id'] ?? null;
212 $platform = $data['platform'] ?? null;
213 $origin = $data['origin'] ?? null;
214 $token = $data['token'] ?? null;
215 $device_uuid = $data['device_uuid'] ?? null;
216
217 if (
218 ! $user_id
219 || ! $platform
220 || ! $origin
221 || (
222 /**
223 * Platforms iOS and Android require token OR device UUID.
224 */
225 PushToken::PLATFORM_BROWSER !== $platform
226 && ! $token
227 && ! $device_uuid
228 )
229 || (
230 /**
231 * Browsers don't have device UUIDs, so require token.
232 */
233 PushToken::PLATFORM_BROWSER === $platform
234 && ! $token
235 )
236 ) {
237 throw new PushTokenInvalidDataException(
238 'Can\'t retrieve push token because the push token data provided is invalid.'
239 );
240 }
241
242 $query = new WP_Query(
243 array(
244 'post_type' => PushToken::POST_TYPE,
245 'post_status' => 'private',
246 'author' => $user_id,
247 'posts_per_page' => -1,
248 'orderby' => 'ID',
249 'order' => 'DESC',
250 'fields' => 'ids',
251 )
252 );
253
254 /**
255 * Typehint for PHPStan, specifies these are IDs and not instances of
256 * WP_Post.
257 *
258 * @var int[] $post_ids
259 */
260 $post_ids = $query->posts;
261
262 if ( empty( $post_ids ) ) {
263 return null;
264 }
265
266 update_meta_cache( 'post', $post_ids );
267
268 foreach ( $post_ids as $post_id ) {
269 try {
270 $meta = $this->build_meta_array_from_database( $post_id );
271 } catch ( Exception $e ) {
272 wc_get_logger()->warning(
273 'Failed to load meta for push token.',
274 array(
275 'token_id' => $post_id,
276 'error' => $e->getMessage(),
277 )
278 );
279
280 continue;
281 }
282
283 if (
284 $meta['platform'] === $platform
285 && $meta['origin'] === $origin
286 && (
287 ( $token && $token === $meta['token'] )
288 || ( $device_uuid && $device_uuid === $meta['device_uuid'] )
289 )
290 ) {
291 return new PushToken(
292 array(
293 'id' => $post_id,
294 'user_id' => $user_id,
295 'token' => $meta['token'],
296 'device_uuid' => $meta['device_uuid'] ?? null,
297 'platform' => $meta['platform'],
298 'origin' => $meta['origin'],
299 /**
300 * These meta items were added after the ability to store
301 * tokens, so may not be available for older tokens. Use
302 * sensible defaults.
303 */
304 'device_locale' => $meta['device_locale'] ?? PushToken::DEFAULT_DEVICE_LOCALE,
305 'metadata' => $meta['metadata'] ?? array(),
306 )
307 );
308 }
309 }
310
311 return null;
312 }
313
314 /**
315 * Returns push tokens belonging to users with the given roles.
316 *
317 * When called without pagination parameters, returns all tokens as a
318 * flat array (cached per-request). When $page and $per_page are
319 * provided, returns a paginated result with total counts.
320 *
321 * @param string[] $roles The roles to query tokens for.
322 * @param int|null $page Optional page number (1-based).
323 * @param int|null $per_page Optional number of tokens per page.
324 * @return PushToken[]|array{tokens: PushToken[], total: int, total_pages: int}
325 *
326 * @since 10.7.0
327 */
328 public function get_tokens_for_roles( array $roles, ?int $page = null, ?int $per_page = null ) {
329 $paginate = null !== $page && null !== $per_page;
330 $cache_key = $paginate ? implode( ',', $roles ) . ":$page:$per_page" : implode( ',', $roles );
331
332 $empty_result = $paginate
333 ? array(
334 'tokens' => array(),
335 'total' => 0,
336 'total_pages' => 0,
337 )
338 : array();
339
340 if ( empty( $roles ) ) {
341 return $empty_result;
342 }
343
344 if ( isset( $this->tokens_by_roles_cache[ $cache_key ] ) ) {
345 return $this->tokens_by_roles_cache[ $cache_key ];
346 }
347
348 $user_ids = get_users(
349 array(
350 'role__in' => $roles,
351 'fields' => 'ID',
352 )
353 );
354
355 if ( empty( $user_ids ) ) {
356 $this->tokens_by_roles_cache[ $cache_key ] = $empty_result;
357 return $this->tokens_by_roles_cache[ $cache_key ];
358 }
359
360 $query_args = array(
361 'post_type' => PushToken::POST_TYPE,
362 'post_status' => 'private',
363 'author__in' => $user_ids,
364 'posts_per_page' => $paginate ? $per_page : -1,
365 'fields' => 'ids',
366 );
367
368 if ( $paginate ) {
369 $query_args['paged'] = $page;
370 $query_args['orderby'] = 'ID';
371 $query_args['order'] = 'ASC';
372 }
373
374 $query = new WP_Query( $query_args );
375
376 /**
377 * Typehint for PHPStan, specifies these are IDs and not instances of
378 * WP_Post.
379 *
380 * @var int[] $post_ids
381 */
382 $post_ids = $query->posts;
383
384 if ( empty( $post_ids ) ) {
385 $this->tokens_by_roles_cache[ $cache_key ] = $empty_result;
386 return $this->tokens_by_roles_cache[ $cache_key ];
387 }
388
389 update_meta_cache( 'post', $post_ids );
390
391 $tokens = array();
392
393 foreach ( $post_ids as $post_id ) {
394 try {
395 $tokens[] = $this->read( (int) $post_id );
396 } catch ( WC_Data_Exception $e ) {
397 wc_get_logger()->warning(
398 'Skipping malformed push token during role-based query.',
399 array(
400 'token_id' => $post_id,
401 'error' => $e->getMessage(),
402 )
403 );
404 }
405 }
406
407 $result = $paginate
408 ? array(
409 'tokens' => $tokens,
410 'total' => (int) $query->found_posts,
411 'total_pages' => (int) $query->max_num_pages,
412 )
413 : $tokens;
414
415 $this->tokens_by_roles_cache[ $cache_key ] = $result;
416 return $result;
417 }
418
419 /**
420 * Returns an associative array of post meta as key => value pairs for the
421 * keys defined in SUPPORTED_META; missing keys return null. Use
422 * `update_meta_cache` with `get_post_meta` to allow reading the meta as
423 * single values which automatically unserialize when requires,
424 * rather than nested arrays that don't.
425 *
426 * @since 10.5.0
427 * @param int $id The push token ID.
428 * @return array
429 */
430 private function build_meta_array_from_database( int $id ): array {
431 $meta_by_key = array_fill_keys( static::SUPPORTED_META, null );
432
433 foreach ( static::SUPPORTED_META as $key ) {
434 $meta = get_post_meta( $id, $key, true );
435
436 if ( '' !== $meta ) {
437 $meta_by_key[ $key ] = $meta;
438 }
439 }
440
441 return $meta_by_key;
442 }
443
444 /**
445 * Returns an associative array of post meta as key => value pairs, built
446 * using push token properties.
447 *
448 * @since 10.5.0
449 * @param PushToken $push_token An instance of PushToken.
450 * @return array
451 */
452 private function build_meta_array_from_token( PushToken $push_token ) {
453 return array_filter(
454 array(
455 'platform' => $push_token->get_platform(),
456 'token' => $push_token->get_token(),
457 'device_uuid' => $push_token->get_device_uuid(),
458 'origin' => $push_token->get_origin(),
459 'device_locale' => $push_token->get_device_locale(),
460 'metadata' => $push_token->get_metadata(),
461 ),
462 fn ( $value ) => null !== $value && '' !== $value
463 );
464 }
465 }
466