ravinderk /
Give
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * User Functions |
||
| 4 | * |
||
| 5 | * Functions related to users / donors |
||
| 6 | * |
||
| 7 | * @package Give |
||
| 8 | * @subpackage Functions |
||
| 9 | * @copyright Copyright (c) 2016, WordImpress |
||
| 10 | * @license https://opensource.org/licenses/gpl-license GNU Public License |
||
| 11 | * @since 1.0 |
||
| 12 | */ |
||
| 13 | |||
| 14 | // Exit if accessed directly. |
||
| 15 | if ( ! defined( 'ABSPATH' ) ) { |
||
| 16 | exit; |
||
| 17 | } |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Get Users Donations |
||
| 21 | * |
||
| 22 | * Retrieves a list of all donations by a specific user. |
||
| 23 | * |
||
| 24 | * @since 1.0 |
||
| 25 | * |
||
| 26 | * @param int $user User ID or email address |
||
| 27 | * @param int $number Number of donations to retrieve |
||
| 28 | * @param bool $pagination |
||
| 29 | * @param string $status |
||
| 30 | * |
||
| 31 | * @return bool|object List of all user donations |
||
|
0 ignored issues
–
show
|
|||
| 32 | */ |
||
| 33 | function give_get_users_purchases( $user = 0, $number = 20, $pagination = false, $status = 'complete' ) { |
||
| 34 | |||
| 35 | if ( empty( $user ) ) { |
||
| 36 | $user = get_current_user_id(); |
||
| 37 | } |
||
| 38 | |||
| 39 | if ( 0 === $user && ! Give()->email_access->token_exists ) { |
||
| 40 | return false; |
||
| 41 | } |
||
| 42 | |||
| 43 | $status = $status === 'complete' ? 'publish' : $status; |
||
| 44 | |||
| 45 | if ( $pagination ) { |
||
| 46 | if ( get_query_var( 'paged' ) ) { |
||
| 47 | $paged = get_query_var( 'paged' ); |
||
| 48 | } elseif ( get_query_var( 'page' ) ) { |
||
| 49 | $paged = get_query_var( 'page' ); |
||
| 50 | } else { |
||
| 51 | $paged = 1; |
||
| 52 | } |
||
| 53 | } |
||
| 54 | |||
| 55 | $args = apply_filters( 'give_get_users_donations_args', array( |
||
| 56 | 'user' => $user, |
||
| 57 | 'number' => $number, |
||
| 58 | 'status' => $status, |
||
| 59 | 'orderby' => 'date', |
||
| 60 | ) ); |
||
| 61 | |||
| 62 | if ( $pagination ) { |
||
| 63 | |||
| 64 | $args['page'] = $paged; |
||
| 65 | |||
| 66 | } else { |
||
| 67 | |||
| 68 | $args['nopaging'] = true; |
||
| 69 | |||
| 70 | } |
||
| 71 | |||
| 72 | $by_user_id = is_numeric( $user ) ? true : false; |
||
| 73 | $customer = new Give_Customer( $user, $by_user_id ); |
||
| 74 | |||
| 75 | if ( ! empty( $customer->payment_ids ) ) { |
||
| 76 | |||
| 77 | unset( $args['user'] ); |
||
| 78 | $args['post__in'] = array_map( 'absint', explode( ',', $customer->payment_ids ) ); |
||
| 79 | |||
| 80 | } |
||
| 81 | |||
| 82 | $purchases = give_get_payments( apply_filters( 'give_get_users_donations_args', $args ) ); |
||
| 83 | |||
| 84 | // No donations |
||
| 85 | if ( ! $purchases ) { |
||
|
0 ignored issues
–
show
The expression
$purchases of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 86 | return false; |
||
| 87 | } |
||
| 88 | |||
| 89 | return $purchases; |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Get Users Donations |
||
| 94 | * |
||
| 95 | * Returns a list of unique donation forms given to by a specific user |
||
| 96 | * |
||
| 97 | * @since 1.0 |
||
| 98 | * |
||
| 99 | * @param int $user User ID or email address |
||
| 100 | * @param string $status |
||
| 101 | * |
||
| 102 | * @return bool|object List of unique forms donated by user |
||
| 103 | */ |
||
| 104 | function give_get_users_completed_donations( $user = 0, $status = 'complete' ) { |
||
| 105 | if ( empty( $user ) ) { |
||
| 106 | $user = get_current_user_id(); |
||
| 107 | } |
||
| 108 | |||
| 109 | if ( empty( $user ) ) { |
||
| 110 | return false; |
||
| 111 | } |
||
| 112 | |||
| 113 | $by_user_id = is_numeric( $user ) ? true : false; |
||
| 114 | |||
| 115 | $customer = new Give_Customer( $user, $by_user_id ); |
||
| 116 | |||
| 117 | if ( empty( $customer->payment_ids ) ) { |
||
| 118 | return false; |
||
| 119 | } |
||
| 120 | |||
| 121 | // Get all the items donated |
||
| 122 | $payment_ids = array_reverse( explode( ',', $customer->payment_ids ) ); |
||
| 123 | $limit_payments = apply_filters( 'give_users_completed_donations_payments', 50 ); |
||
| 124 | if ( ! empty( $limit_payments ) ) { |
||
| 125 | $payment_ids = array_slice( $payment_ids, 0, $limit_payments ); |
||
| 126 | } |
||
| 127 | $donation_data = array(); |
||
| 128 | foreach ( $payment_ids as $payment_id ) { |
||
| 129 | $donation_data[] = give_get_payment_meta( $payment_id ); |
||
| 130 | } |
||
| 131 | |||
| 132 | if ( empty( $donation_data ) ) { |
||
| 133 | return false; |
||
| 134 | } |
||
| 135 | |||
| 136 | // Grab only the post ids "form_id" of the forms donated on this order |
||
| 137 | $completed_donations_ids = array(); |
||
| 138 | foreach ( $donation_data as $purchase_meta ) { |
||
| 139 | $completed_donations_ids[] = isset( $purchase_meta['form_id'] ) ? $purchase_meta['form_id'] : ''; |
||
| 140 | } |
||
| 141 | |||
| 142 | if ( empty( $completed_donations_ids ) ) { |
||
| 143 | return false; |
||
| 144 | } |
||
| 145 | |||
| 146 | // Only include each donation once |
||
| 147 | $form_ids = array_unique( $completed_donations_ids ); |
||
| 148 | |||
| 149 | // Make sure we still have some products and a first item |
||
| 150 | if ( empty( $form_ids ) || ! isset( $form_ids[0] ) ) { |
||
| 151 | return false; |
||
| 152 | } |
||
| 153 | |||
| 154 | $post_type = get_post_type( $form_ids[0] ); |
||
| 155 | |||
| 156 | $args = apply_filters( 'give_get_users_completed_donations_args', array( |
||
| 157 | 'include' => $form_ids, |
||
| 158 | 'post_type' => $post_type, |
||
| 159 | 'posts_per_page' => - 1, |
||
| 160 | ) ); |
||
| 161 | |||
| 162 | return apply_filters( 'give_users_completed_donations_list', get_posts( $args ) ); |
||
| 163 | } |
||
| 164 | |||
| 165 | |||
| 166 | /** |
||
| 167 | * Has donations |
||
| 168 | * |
||
| 169 | * Checks to see if a user has donated to at least one form. |
||
| 170 | * |
||
| 171 | * @access public |
||
| 172 | * @since 1.0 |
||
| 173 | * |
||
| 174 | * @param int $user_id The ID of the user to check. |
||
| 175 | * |
||
| 176 | * @return bool True if has donated, false other wise. |
||
| 177 | */ |
||
| 178 | function give_has_purchases( $user_id = null ) { |
||
| 179 | if ( empty( $user_id ) ) { |
||
| 180 | $user_id = get_current_user_id(); |
||
| 181 | } |
||
| 182 | |||
| 183 | if ( give_get_users_purchases( $user_id, 1 ) ) { |
||
| 184 | return true; // User has at least one donation |
||
| 185 | } |
||
| 186 | |||
| 187 | return false; // User has never donated anything |
||
| 188 | } |
||
| 189 | |||
| 190 | |||
| 191 | /** |
||
| 192 | * Get Donation Status for User |
||
| 193 | * |
||
| 194 | * Retrieves the donation count and the total amount spent for a specific user |
||
| 195 | * |
||
| 196 | * @access public |
||
| 197 | * @since 1.0 |
||
| 198 | * |
||
| 199 | * @param int|string $user The ID or email of the donor to retrieve stats for |
||
| 200 | * |
||
| 201 | * @return array |
||
| 202 | */ |
||
| 203 | function give_get_purchase_stats_by_user( $user = '' ) { |
||
| 204 | |||
| 205 | if ( is_email( $user ) ) { |
||
| 206 | |||
| 207 | $field = 'email'; |
||
| 208 | |||
| 209 | } elseif ( is_numeric( $user ) ) { |
||
| 210 | |||
| 211 | $field = 'user_id'; |
||
| 212 | |||
| 213 | } |
||
| 214 | |||
| 215 | $stats = array(); |
||
| 216 | $customer = Give()->customers->get_customer_by( $field, $user ); |
||
| 217 | |||
| 218 | if ( $customer ) { |
||
| 219 | |||
| 220 | $customer = new Give_Customer( $customer->id ); |
||
| 221 | |||
| 222 | $stats['purchases'] = absint( $customer->purchase_count ); |
||
| 223 | $stats['total_spent'] = give_sanitize_amount( $customer->purchase_value ); |
||
| 224 | |||
| 225 | } |
||
| 226 | |||
| 227 | /** |
||
| 228 | * Filter the donation stats |
||
| 229 | * |
||
| 230 | * @since 1.7 |
||
| 231 | */ |
||
| 232 | $stats = (array) apply_filters( 'give_donation_stats_by_user', $stats, $user ); |
||
| 233 | |||
| 234 | return $stats; |
||
| 235 | } |
||
| 236 | |||
| 237 | |||
| 238 | /** |
||
| 239 | * Count number of donations of a donor |
||
| 240 | * |
||
| 241 | * Returns total number of donations a donor has made |
||
| 242 | * |
||
| 243 | * @access public |
||
| 244 | * @since 1.0 |
||
| 245 | * |
||
| 246 | * @param int|string $user The ID or email of the donor. |
||
| 247 | * |
||
| 248 | * @return int The total number of donations |
||
| 249 | */ |
||
| 250 | function give_count_purchases_of_customer( $user = null ) { |
||
| 251 | |||
| 252 | // Logged in? |
||
| 253 | if ( empty( $user ) ) { |
||
| 254 | $user = get_current_user_id(); |
||
| 255 | } |
||
| 256 | |||
| 257 | // Email access? |
||
| 258 | if ( empty( $user ) && Give()->email_access->token_email ) { |
||
| 259 | $user = Give()->email_access->token_email; |
||
| 260 | } |
||
| 261 | |||
| 262 | $stats = ! empty( $user ) ? give_get_purchase_stats_by_user( $user ) : false; |
||
| 263 | |||
| 264 | return isset( $stats['purchases'] ) ? $stats['purchases'] : 0; |
||
| 265 | } |
||
| 266 | |||
| 267 | /** |
||
| 268 | * Calculates the total amount spent by a user |
||
| 269 | * |
||
| 270 | * @access public |
||
| 271 | * @since 1.0 |
||
| 272 | * |
||
| 273 | * @param int|string $user The ID or email of the donor. |
||
| 274 | * |
||
| 275 | * @return float The total amount the user has spent |
||
| 276 | */ |
||
| 277 | function give_purchase_total_of_user( $user = null ) { |
||
| 278 | |||
| 279 | $stats = give_get_purchase_stats_by_user( $user ); |
||
| 280 | |||
| 281 | return $stats['total_spent']; |
||
| 282 | } |
||
| 283 | |||
| 284 | |||
| 285 | /** |
||
| 286 | * Validate a potential username |
||
| 287 | * |
||
| 288 | * @since 1.0 |
||
| 289 | * |
||
| 290 | * @param string $username The username to validate. |
||
| 291 | * @param int $form_id |
||
| 292 | * |
||
| 293 | * @return bool |
||
| 294 | */ |
||
| 295 | function give_validate_username( $username, $form_id = 0 ) { |
||
| 296 | $valid = true; |
||
| 297 | |||
| 298 | // Validate username. |
||
| 299 | if ( ! empty( $username ) ) { |
||
| 300 | |||
| 301 | // Sanitize username. |
||
| 302 | $sanitized_user_name = sanitize_user( $username, false ); |
||
| 303 | |||
| 304 | // We have an user name, check if it already exists. |
||
| 305 | if ( username_exists( $username ) ) { |
||
| 306 | // Username already registered. |
||
| 307 | give_set_error( 'username_unavailable', esc_html__( 'Username already taken.', 'give' ) ); |
||
| 308 | $valid = false; |
||
| 309 | |||
| 310 | // Check if it's valid. |
||
| 311 | } elseif ( $sanitized_user_name !== $username ) { |
||
| 312 | // Invalid username. |
||
| 313 | if ( is_multisite() ) { |
||
| 314 | give_set_error( 'username_invalid', esc_html__( 'Invalid username. Only lowercase letters (a-z) and numbers are allowed.', 'give' ) ); |
||
| 315 | $valid = false; |
||
| 316 | } else { |
||
| 317 | give_set_error( 'username_invalid', esc_html__( 'Invalid username.', 'give' ) ); |
||
| 318 | $valid = false; |
||
| 319 | } |
||
| 320 | } |
||
| 321 | } else { |
||
| 322 | // Username is empty. |
||
| 323 | give_set_error( 'username_empty', esc_html__( 'Enter a username.', 'give' ) ); |
||
| 324 | $valid = false; |
||
| 325 | |||
| 326 | // Check if guest checkout is disable for form. |
||
| 327 | if ( $form_id && give_logged_in_only( $form_id ) ) { |
||
| 328 | give_set_error( 'registration_required', esc_html__( 'You must register or login to complete your donation.', 'give' ) ); |
||
| 329 | $valid = false; |
||
| 330 | } |
||
| 331 | } |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Filter the username validation result. |
||
| 335 | * |
||
| 336 | * @since 1.8 |
||
| 337 | * |
||
| 338 | * @param bool $valid |
||
| 339 | * @param string $username |
||
| 340 | * @param bool $form_id |
||
| 341 | */ |
||
| 342 | $valid = (bool) apply_filters( 'give_validate_username', $valid, $username, $form_id ); |
||
| 343 | |||
| 344 | return $valid; |
||
| 345 | } |
||
| 346 | |||
| 347 | |||
| 348 | /** |
||
| 349 | * Validate user email. |
||
| 350 | * |
||
| 351 | * @since 1.8 |
||
| 352 | * |
||
| 353 | * @param string $email User email. |
||
| 354 | * @param bool $registering_new_user Flag to check user register or not. |
||
| 355 | * |
||
| 356 | * @return bool |
||
| 357 | */ |
||
| 358 | function give_validate_user_email( $email, $registering_new_user = false ) { |
||
| 359 | $valid = true; |
||
| 360 | |||
| 361 | if ( empty( $email ) ) { |
||
| 362 | // No email. |
||
| 363 | give_set_error( 'email_empty', esc_html__( 'Enter an email.', 'give' ) ); |
||
| 364 | $valid = false; |
||
| 365 | |||
| 366 | } elseif ( ! is_email( $email ) ) { |
||
| 367 | // Validate email. |
||
| 368 | give_set_error( 'email_invalid', esc_html__( 'Invalid email.', 'give' ) ); |
||
| 369 | $valid = false; |
||
| 370 | |||
| 371 | } elseif ( $registering_new_user && email_exists( $email ) ) { |
||
| 372 | // Check if email exists. |
||
| 373 | give_set_error( 'email_used', esc_html__( 'The email already active for another user.', 'give' ) ); |
||
| 374 | $valid = false; |
||
| 375 | } |
||
| 376 | |||
| 377 | /** |
||
| 378 | * Filter the email validation result. |
||
| 379 | * |
||
| 380 | * @since 1.8 |
||
| 381 | * |
||
| 382 | * @param bool $valid |
||
| 383 | * @param string $email |
||
| 384 | * @param bool $registering_new_user |
||
| 385 | */ |
||
| 386 | $valid = (bool) apply_filters( 'give_validate_user_email', $valid, $email, $registering_new_user ); |
||
| 387 | |||
| 388 | return $valid; |
||
| 389 | } |
||
| 390 | |||
| 391 | /** |
||
| 392 | * Validate password. |
||
| 393 | * |
||
| 394 | * @since 1.8 |
||
| 395 | * |
||
| 396 | * @param string $password |
||
| 397 | * @param string $confirm_password |
||
| 398 | * @param bool $registering_new_user |
||
| 399 | * |
||
| 400 | * @return bool |
||
| 401 | */ |
||
| 402 | function give_validate_user_password( $password = '', $confirm_password = '', $registering_new_user = false ) { |
||
| 403 | $valid = true; |
||
| 404 | |||
| 405 | if ( $password && $confirm_password ) { |
||
| 406 | // Verify confirmation matches. |
||
| 407 | if ( $password != $confirm_password ) { |
||
| 408 | // Passwords do not match |
||
| 409 | give_set_error( 'password_mismatch', esc_html__( 'Passwords don\'t match.', 'give' ) ); |
||
| 410 | $valid = false; |
||
| 411 | } |
||
| 412 | } elseif ( $registering_new_user ) { |
||
| 413 | // Password or confirmation missing. |
||
| 414 | if ( ! $password ) { |
||
| 415 | // The password is invalid. |
||
| 416 | give_set_error( 'password_empty', esc_html__( 'Enter a password.', 'give' ) ); |
||
| 417 | $valid = false; |
||
| 418 | } elseif ( ! $confirm_password ) { |
||
| 419 | // Confirmation password is invalid. |
||
| 420 | give_set_error( 'confirmation_empty', esc_html__( 'Enter the password confirmation.', 'give' ) ); |
||
| 421 | $valid = false; |
||
| 422 | } |
||
| 423 | } |
||
| 424 | |||
| 425 | /** |
||
| 426 | * Filter the password validation result. |
||
| 427 | * |
||
| 428 | * @since 1.8 |
||
| 429 | * |
||
| 430 | * @param bool $valid |
||
| 431 | * @param string $password |
||
| 432 | * @param string $confirm_password |
||
| 433 | * @param bool $registering_new_user |
||
| 434 | */ |
||
| 435 | $valid = (bool) apply_filters( 'give_validate_user_email', $valid, $password, $confirm_password, $registering_new_user ); |
||
| 436 | |||
| 437 | return $valid; |
||
| 438 | } |
||
| 439 | |||
| 440 | |||
| 441 | /** |
||
| 442 | * Looks up donations by email that match the registering user |
||
| 443 | * |
||
| 444 | * This is for users that donated as a guest and then came |
||
| 445 | * back and created an account. |
||
| 446 | * |
||
| 447 | * @access public |
||
| 448 | * @since 1.0 |
||
| 449 | * |
||
| 450 | * @param int $user_id The new user's ID. |
||
| 451 | * |
||
| 452 | * @return void |
||
| 453 | */ |
||
| 454 | function give_add_past_purchases_to_new_user( $user_id ) { |
||
| 455 | |||
| 456 | $email = get_the_author_meta( 'user_email', $user_id ); |
||
| 457 | |||
| 458 | $payments = give_get_payments( array( 's' => $email ) ); |
||
| 459 | |||
| 460 | if ( $payments ) { |
||
|
0 ignored issues
–
show
The expression
$payments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 461 | foreach ( $payments as $payment ) { |
||
| 462 | if ( intval( give_get_payment_user_id( $payment->ID ) ) > 0 ) { |
||
| 463 | continue; |
||
| 464 | } // This payment already associated with an account |
||
| 465 | |||
| 466 | $meta = give_get_payment_meta( $payment->ID ); |
||
| 467 | $meta['user_info'] = maybe_unserialize( $meta['user_info'] ); |
||
| 468 | $meta['user_info']['id'] = $user_id; |
||
| 469 | $meta['user_info'] = $meta['user_info']; |
||
| 470 | |||
| 471 | // Store the updated user ID in the payment meta |
||
| 472 | give_update_payment_meta( $payment->ID, '_give_payment_meta', $meta ); |
||
| 473 | give_update_payment_meta( $payment->ID, '_give_payment_user_id', $user_id ); |
||
| 474 | } |
||
| 475 | } |
||
| 476 | |||
| 477 | } |
||
| 478 | |||
| 479 | add_action( 'user_register', 'give_add_past_purchases_to_new_user' ); |
||
| 480 | |||
| 481 | |||
| 482 | /** |
||
| 483 | * Counts the total number of donors. |
||
| 484 | * |
||
| 485 | * @access public |
||
| 486 | * @since 1.0 |
||
| 487 | * |
||
| 488 | * @return int The total number of donors. |
||
| 489 | */ |
||
| 490 | function give_count_total_customers() { |
||
| 491 | return Give()->customers->count(); |
||
| 492 | } |
||
| 493 | |||
| 494 | |||
| 495 | /** |
||
| 496 | * Returns the saved address for a donor |
||
| 497 | * |
||
| 498 | * @access public |
||
| 499 | * @since 1.0 |
||
| 500 | * |
||
| 501 | * @param int $user_id The donor ID. |
||
| 502 | * |
||
| 503 | * @return array The donor's address, if any |
||
| 504 | */ |
||
| 505 | function give_get_donor_address( $user_id = 0 ) { |
||
| 506 | if ( empty( $user_id ) ) { |
||
| 507 | $user_id = get_current_user_id(); |
||
| 508 | } |
||
| 509 | |||
| 510 | $address = get_user_meta( $user_id, '_give_user_address', true ); |
||
| 511 | |||
| 512 | if ( ! isset( $address['line1'] ) ) { |
||
| 513 | $address['line1'] = ''; |
||
| 514 | } |
||
| 515 | |||
| 516 | if ( ! isset( $address['line2'] ) ) { |
||
| 517 | $address['line2'] = ''; |
||
| 518 | } |
||
| 519 | |||
| 520 | if ( ! isset( $address['city'] ) ) { |
||
| 521 | $address['city'] = ''; |
||
| 522 | } |
||
| 523 | |||
| 524 | if ( ! isset( $address['zip'] ) ) { |
||
| 525 | $address['zip'] = ''; |
||
| 526 | } |
||
| 527 | |||
| 528 | if ( ! isset( $address['country'] ) ) { |
||
| 529 | $address['country'] = ''; |
||
| 530 | } |
||
| 531 | |||
| 532 | if ( ! isset( $address['state'] ) ) { |
||
| 533 | $address['state'] = ''; |
||
| 534 | } |
||
| 535 | |||
| 536 | return $address; |
||
| 537 | } |
||
| 538 | |||
| 539 | /** |
||
| 540 | * Give New User Notification |
||
| 541 | * |
||
| 542 | * Sends the new user notification email when a user registers within the donation form |
||
| 543 | * |
||
| 544 | * @access public |
||
| 545 | * @since 1.0 |
||
| 546 | * |
||
| 547 | * @param int $user_id |
||
| 548 | * @param array $user_data |
||
| 549 | * |
||
| 550 | * @return void |
||
| 551 | */ |
||
| 552 | function give_new_user_notification( $user_id = 0, $user_data = array() ) { |
||
| 553 | |||
| 554 | if ( empty( $user_id ) || empty( $user_data ) ) { |
||
| 555 | return; |
||
| 556 | } |
||
| 557 | |||
| 558 | do_action( 'give_new-donor-register_email_notification', $user_id, $user_data ); |
||
| 559 | do_action( 'give_donor-register_email_notification', $user_id, $user_data ); |
||
| 560 | } |
||
| 561 | |||
| 562 | add_action( 'give_insert_user', 'give_new_user_notification', 10, 2 ); |
||
| 563 |
This check compares the return type specified in the
@returnannotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.