| @@ -229,113 +229,24 @@ | ||
| 229 | 229 | return (bool) apply_filters( 'woocommerce_pos_is_pro_active', \defined( 'WCPOS\WooCommercePOSPro\VERSION' ) ); |
| 230 | 230 | } |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | -if ( ! \function_exists( 'wcpos_get_site_identity_home' ) ) { | |
| 234 | - /** | |
| 235 | - * Get the site address as an identity: no scheme, no `www.`, no trailing | |
| 236 | - * slash; the host lower-cased; the port and the case-sensitive path kept, | |
| 237 | - * so `example.com/staging` is another site and `www.example.com` is not. | |
| 238 | - * | |
| 239 | - * @return string Normalised home address. | |
| 240 | - */ | |
| 241 | - function wcpos_get_site_identity_home(): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix. | |
| 242 | - $parts = wp_parse_url( '//' . preg_replace( '#^(?:https?:)?//#i', '', home_url() ) ); | |
| 243 | - $host = preg_replace( '#^www\.#', '', strtolower( (string) ( $parts['host'] ?? '' ) ) ); | |
| 244 | - $port = isset( $parts['port'] ) ? ':' . $parts['port'] : ''; | |
| 245 | - | |
| 246 | - return untrailingslashit( $host . $port . ( $parts['path'] ?? '' ) ); | |
| 247 | - } | |
| 248 | -} | |
| 249 | - | |
| 250 | -if ( ! \function_exists( 'wcpos_site_identity_home_row' ) ) { | |
| 251 | - /** | |
| 252 | - * Read the persisted home marker straight from the options table. | |
| 253 | - * | |
| 254 | - * The option cache answers get_option() for the rest of the request, and a | |
| 255 | - * same-value update_option() leaves it untouched; this is the only read that can tell a | |
| 256 | - * marker another request already advanced from one whose write failed. | |
| 257 | - * | |
| 258 | - * @return string|null The stored marker, or null when absent. | |
| 259 | - */ | |
| 260 | - function wcpos_site_identity_home_row(): ?string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix. | |
| 261 | - global $wpdb; | |
| 262 | - $row = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- bypasses the option cache on purpose, see docblock. | |
| 263 | - $wpdb->prepare( "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", 'woocommerce_pos_uuid_home' ) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- core table name. | |
| 264 | - ); | |
| 265 | - | |
| 266 | - return \is_string( $row ) ? $row : null; | |
| 267 | - } | |
| 268 | -} | |
| 269 | - | |
| 270 | 233 | /* |
| 271 | - * Get the site UUID (Plugin State), tied to the site address. | |
| 234 | + * Get the site UUID (Plugin State), generating and persisting it on first use. | |
| 272 | 235 | * |
| 273 | 236 | * @return string Site UUID. |
| 274 | 237 | */ |
| 275 | 238 | if ( ! \function_exists( 'wcpos_get_site_uuid' ) ) { |
| 276 | 239 | /** |
| 277 | - * Get the site UUID, generating it on first use or when the address changes. | |
| 240 | + * Get the site UUID, generating and persisting it on first use. | |
| 278 | 241 | * |
| 279 | 242 | * Single owner for the woocommerce_pos_uuid option — the |
| 280 | 243 | * generate-if-missing logic previously lived in three places (REST index, |
| 281 | 244 | * POS frontend, analytics) and could race. |
| 282 | - * The saved home prevents database clones sharing a live store's identity. | |
| 283 | - * On upgrade, an absent home is recorded without changing the existing UUID. | |
| 284 | 245 | * |
| 285 | 246 | * @return string Site UUID. |
| 286 | 247 | */ |
| 287 | 248 | function wcpos_get_site_uuid(): string { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- uses wcpos_ prefix. |
| 288 | - $home = wcpos_get_site_identity_home(); | |
| 289 | - $stored_home = get_option( 'woocommerce_pos_uuid_home' ); | |
| 290 | - if ( false === $stored_home ) { | |
| 291 | - // Keep the first writer's home if another request wins the race. | |
| 292 | - $stored_home = add_option( 'woocommerce_pos_uuid_home', $home ) | |
| 293 | - ? $home | |
| 294 | - : get_option( 'woocommerce_pos_uuid_home' ); | |
| 295 | - } | |
| 296 | - | |
| 297 | - if ( $stored_home !== $home ) { | |
| 298 | - // A moved address is a new identity. Two requests can both land | |
| 299 | - // here; the last write wins and each returns what is stored, so the | |
| 300 | - // REST response never advertises a uuid the database does not hold. | |
| 301 | - // The home marker advances only behind a persisted uuid: a vetoed or | |
| 302 | - // failed write would otherwise hand out an identity that the next | |
| 303 | - // call, seeing a matching home, no longer returns. | |
| 304 | - $previous = get_option( 'woocommerce_pos_uuid', '' ); | |
| 305 | - $minted = \Ramsey\Uuid\Uuid::uuid4()->toString(); | |
| 306 | - if ( update_option( 'woocommerce_pos_uuid', $minted ) ) { | |
| 307 | - // update_option() also answers false when the row already holds | |
| 308 | - // $home: a concurrent request finished the move between this one's | |
| 309 | - // read and its write. That is a completed move, not a failed one, | |
| 310 | - // and rolling back would leave the old uuid behind the new marker | |
| 311 | - // for good, since every later call sees a matching home. This | |
| 312 | - // request's option cache still holds the old marker (the same-value | |
| 313 | - // UPDATE touched no row, so nothing refreshed it), so the check | |
| 314 | - // reads the row itself and, when the move is confirmed, drops the | |
| 315 | - // stale cache so a later call in this request does not mint again, | |
| 316 | - // and drops the uuid this request cached from its own write so the | |
| 317 | - // value returned below is the one the other request left stored. | |
| 318 | - $marker_moved = update_option( 'woocommerce_pos_uuid_home', $home ) || wcpos_site_identity_home_row() === $home; | |
| 319 | - if ( $marker_moved && get_option( 'woocommerce_pos_uuid_home' ) !== $home ) { | |
| 320 | - wp_cache_delete( 'woocommerce_pos_uuid', 'options' ); | |
| 321 | - wp_cache_delete( 'woocommerce_pos_uuid_home', 'options' ); | |
| 322 | - wp_cache_delete( 'alloptions', 'options' ); | |
| 323 | - } | |
| 324 | - if ( ! $marker_moved && \is_string( $previous ) && '' !== $previous ) { | |
| 325 | - // The marker did not move: put the previous uuid back so the next | |
| 326 | - // call retries the whole move, rather than minting again on top of | |
| 327 | - // a rotation the marker never recorded. | |
| 328 | - update_option( 'woocommerce_pos_uuid', $previous ); | |
| 329 | - } | |
| 330 | - } | |
| 331 | - | |
| 332 | - $stored = get_option( 'woocommerce_pos_uuid', '' ); | |
| 333 | - if ( \is_string( $stored ) && '' !== $stored ) { | |
| 334 | - return $stored; | |
| 335 | - } | |
| 336 | - } | |
| 337 | - | |
| 338 | 249 | $uuid = get_option( 'woocommerce_pos_uuid', '' ); |
| 339 | 250 | if ( \is_string( $uuid ) && '' !== $uuid ) { |
| 340 | 251 | return $uuid; |
| 341 | 252 | } |