ile array. * * The 4.9.6 sensitive-options migration unset these on the raw DB option, but the * in-memory InternalOptions model (already loaded at boot) re-saved them on shutdown * because `searchStatistics.profile` was a generic `array` leaf — its inner keys * weren't filtered against the schema. The schema is now structured, so a forced * save strips legacy subkeys automatically; backfill sensitive options first in case * the prior migration missed them. * * @since 4.9.7 * * @return void */ private function cleanupSearchStatisticsProfile() { $rawInternalOptions = json_decode( (string) get_option( 'aioseo_options_internal', '' ), true ); $profile = is_array( $rawInternalOptions ) ? aioseo()->helpers->getNestedValue( $rawInternalOptions, [ 'internal', 'searchStatistics', 'profile' ] ) : null; if ( is_array( $profile ) ) { if ( ! empty( $profile['key'] ) && is_string( $profile['key'] ) && ! aioseo()->sensitiveOptions->hasValue( 'searchStatisticsProfileKey' ) ) { aioseo()->sensitiveOptions->set( 'searchStatisticsProfileKey', $profile['key'] ); } if ( ! empty( $profile['token'] ) && is_string( $profile['token'] ) && ! aioseo()->sensitiveOptions->hasValue( 'searchStatisticsProfileToken' ) ) { aioseo()->sensitiveOptions->set( 'searchStatisticsProfileToken', $profile['token'] ); } aioseo()->sensitiveOptions->save( true ); } // Force-save internal options so the structured `profile` schema strips legacy subkeys. aioseo()->internalOptions->save( true ); } /** * Unsets a nested value in an array by path. * * @since 4.9.6 * * @param array $array The array to modify (passed by reference). * @param array $path The path to the value. * @return void */ private function unsetNestedValue( &$array, $path ) { $lastKey = array_pop( $path ); $current = &$array; foreach ( $path as $key ) { if ( ! isset( $current[ $key ] ) || ! is_array( $current[ $key ] ) ) { return; } $current = &$current[ $key ]; } unset( $current[ $lastKey ] ); } }