/var/www/html_us/wp-content/plugins/woocommerce/src/Internal/Admin/Settings/Utils.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
<?php
declare( strict_types=);

namespace 
Automattic\WooCommerce\Internal\Admin\Settings;

defined'ABSPATH' ) || exit;
/**
 * Payments settings utilities class.
 */
class Utils {
    
/**
     * Apply order mappings to a base order map.
     *
     * @param array $base_map     The base order map.
     * @param array $new_mappings The order mappings to apply.
     *                            This can be a full or partial list of the base one,
     *                            but it can also contain (only) new IDs and their orders.
     *
     * @return array The updated base order map, normalized.
     */
    
public static function order_map_apply_mappings( array $base_map, array $new_mappings ): array {
        
// Make sure the base map is sorted ascending by their order values.
        // We don't normalize first because the order values have meaning.
        
asort$base_map );

        
$updated_map $base_map;
        
// Apply the new mappings in the order they were given.
        
foreach ( $new_mappings as $id => $order ) {
            
// If the ID is not in the base map, we ADD it at the desired order. Otherwise, we MOVE it.
            
if ( ! isset( $base_map$id ] ) ) {
                
$updated_map self::order_map_add_at_order$updated_map$id$order );
                continue;
            }

            
$updated_map self::order_map_move_at_order$updated_map$id$order );
        }

        return 
self::order_map_normalize$updated_map );
    }

    
/**
     * Move an id at a specific order in an order map.
     *
     * This method is used to simulate the behavior of a drag&drop sorting UI:
     * - When moving an id down, all the ids with an order equal or lower than the desired order
     *   but equal or higher than the current order are decreased by 1.
     * - When moving an id up, all the ids with an order equal or higher than the desired order
     *   but equal or lower than the current order are increased by 1.
     *
     * @param array  $order_map The order map.
     * @param string $id        The id to place.
     * @param int    $order     The order at which to place the id.
     *
     * @return array The updated order map. This map is not normalized.
     */
    
public static function order_map_move_at_order( array $order_mapstring $idint $order ): array {
        
// If the id is not in the order map, return the order map as is.
        
if ( ! isset( $order_map$id ] ) ) {
            return 
$order_map;
        }

        
// If the id is already at the desired order, return the order map as is.
        
if ( $order_map$id ] === $order ) {
            return 
$order_map;
        }

        
// If there is no id at the desired order, just place the id there.
        
if ( ! in_array$order$order_maptrue ) ) {
            
$order_map$id ] = $order;

            return 
$order_map;
        }

        
// We apply the normal behavior of a drag&drop sorting UI.
        
$existing_order $order_map$id ];
        if ( 
$order $existing_order ) {
            
// Moving down.
            
foreach ( $order_map as $key => $value ) {
                if ( 
$value <= $order && $value >= $existing_order ) {
                    --
$order_map$key ];
                }
            }
        } else {
            
// Moving up.
            
foreach ( $order_map as $key => $value ) {
                if ( 
$value >= $order && $value <= $existing_order ) {
                    ++
$order_map$key ];
                }
            }
        }

        
// Place the id at the desired order.
        
$order_map$id ] = $order;

        return 
$order_map;
    }

    
/**
     * Place an id at a specific order in an order map.
     *
     * @param array  $order_map The order map.
     * @param string $id        The id to place.
     * @param int    $order     The order at which to place the id.
     *
     * @return array The updated order map.
     */
    
public static function order_map_place_at_order( array $order_mapstring $idint $order ): array {
        
// If the id is already at the desired order, return the order map as is.
        
if ( isset( $order_map$id ] ) && $order_map$id ] === $order ) {
            return 
$order_map;
        }

        
// If there is no id at the desired order, just place the id there.
        
if ( ! in_array$order$order_maptrue ) ) {
            
$order_map$id ] = $order;

            return 
$order_map;
        }

        
// Bump the order of everything with an order equal or higher than the desired order.
        
foreach ( $order_map as $key => $value ) {
            if ( 
$value >= $order ) {
                ++
$order_map$key ];
            }
        }

        
// Place the id at the desired order.
        
$order_map$id ] = $order;

        return 
$order_map;
    }

    
/**
     * Add an id to a specific order in an order map.
     *
     * @param array  $order_map The order map.
     * @param string $id        The id to move.
     * @param int    $order     The order to move the id to.
     *
     * @return array The updated order map. If the id is already in the order map, the order map is returned as is.
     */
    
public static function order_map_add_at_order( array $order_mapstring $idint $order ): array {
        
// If the id is in the order map, return the order map as is.
        
if ( isset( $order_map$id ] ) ) {
            return 
$order_map;
        }

        return 
self::order_map_place_at_order$order_map$id$order );
    }

    
/**
     * Normalize an order map.
     *
     * Sort the order map by the order and ensure the order values start from 0 and are consecutive.
     *
     * @param array $order_map The order map.
     *
     * @return array The normalized order map.
     */
    
public static function order_map_normalize( array $order_map ): array {
        
asort$order_map );

        return 
array_fliparray_keys$order_map ) );
    }

    
/**
     * Change the minimum order of an order map.
     *
     * @param array $order_map     The order map.
     * @param int   $new_min_order The new minimum order.
     *
     * @return array The updated order map.
     */
    
public static function order_map_change_min_order( array $order_mapint $new_min_order ): array {
        
// Sanity checks.
        
if ( empty( $order_map ) ) {
            return array();
        }

        
$updated_map = array();
        
$bump        $new_min_order min$order_map );
        foreach ( 
$order_map as $id => $order ) {
            
$updated_map$id ] = $order $bump;
        }

        
asort$updated_map );

        return 
$updated_map;
    }

    
/**
     * Get the list of plugin slug suffixes used for handling non-standard testing slugs.
     *
     * @return string[] The list of plugin slug suffixes used for handling non-standard testing slugs.
     */
    
public static function get_testing_plugin_slug_suffixes(): array {
        return array( 
'-dev''-rc''-test''-beta''-alpha' );
    }

    
/**
     * Generate a list of testing plugin slugs from a standard/official plugin slug.
     *
     * @param string $slug             The standard/official plugin slug. Most likely the WPORG slug.
     * @param bool   $include_original Optional. Whether to include the original slug in the list.
     *                                 If true, the original slug will be the first item in the list.
     *
     * @return string[] The list of testing plugin slugs generated from the standard/official plugin slug.
     */
    
public static function generate_testing_plugin_slugsstring $slugbool $include_original false ): array {
        
$slugs = array();
        if ( 
$include_original ) {
            
$slugs[] = $slug;
        }

        foreach ( 
self::get_testing_plugin_slug_suffixes() as $suffix ) {
            
$slugs[] = $slug $suffix;
        }

        return 
$slugs;
    }

    
/**
     * Normalize a plugin slug to a standard/official slug.
     *
     * This is a best-effort approach.
     * It will remove beta testing suffixes and lowercase the slug.
     * It will NOT convert plugin titles to slugs or sanitize the slug like sanitize_title() does.
     *
     * @param string $slug The plugin slug.
     *
     * @return string The normalized plugin slug.
     */
    
public static function normalize_plugin_slugstring $slug ): string {
        
// If the slug is empty or contains anything other than alphanumeric and dash characters, it will be left as is.
        
if ( empty( $slug ) || ! preg_match'/^[\w-]+$/'$slug$matches ) ) {
            return 
$slug;
        }

        
// Lowercase the slug.
        
$slug strtolower$slug );
        
// Remove testing suffixes.
        
foreach ( self::get_testing_plugin_slug_suffixes() as $suffix ) {
            
$slug str_ends_with$slug$suffix ) ? substr$slug0, -strlen$suffix ) ) : $slug;
        }

        return 
$slug;
    }

    
/**
     * Truncate a text to a target character length while preserving whole words.
     *
     * We take a greedy approach: if some characters of a word fit in the target length, the whole word is included.
     * This means we might exceed the target length by a few characters.
     * The append string length is not included in the character count.
     *
     * @param string      $text          The text to truncate.
     *                                   It will not be sanitized, stripped of HTML tags, or modified in any way before truncation.
     * @param int         $target_length The target character length of the truncated text.
     * @param string|null $append        Optional. The string to append to the truncated text, if there is any truncation.
     *
     * @return string The truncated text.
     */
    
public static function truncate_with_wordsstring $textint $target_lengthstring $append null ): string {
        
// First, deal with locale that doesn't have words separated by spaces, but instead deals with characters.
        // Borrowed from wp_trim_words().
        
if ( str_starts_withwp_get_word_count_type(), 'characters' ) && preg_match'/^utf\-?8$/i'get_option'blog_charset' ) ) ) {
            
$text trimpreg_replace"/[\n\r\t ]+/"' '$text ), ' ' );
            
preg_match_all'/./u'$text$words_array );

            
// Nothing to do if the text is already short enough.
            
if ( count$words_array[0] ) <= $target_length ) {
                return 
$text;
            }

            
$words_array array_slice$words_array[0], 0$target_length );
            
$truncated   implode''$words_array );
            if ( 
null !== $append ) {
                
$truncated .= $append;
            }

            return 
$truncated;
        }

        
// Deal with locale that has words separated by spaces.
        
if ( strlen$text ) <= $target_length ) {
            return 
$text;
        }

        
$words_array preg_split"/[\n\r\t ]+/"$text, - 1PREG_SPLIT_NO_EMPTY );
        
$sep         ' ';

        
// Include words until the target length is reached.
        
$truncated        '';
        
$remaining_length $target_length;
        while ( 
$remaining_length && ! empty( $words_array ) ) {
            
$word              array_shift$words_array );
            
$truncated        .= $word $sep;
            
$remaining_length -= strlen$word $sep );
        }

        
// Remove the last separator.
        
$truncated rtrim$truncated$sep );

        if ( 
null !== $append ) {
            
$truncated .= $append;
        }

        return 
$truncated;
    }
}