PluginProbe
Parse.ly / 3.24.2
Parse.ly v3.24.2
3.24.2 3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 All 106 releases
← All changes | src/Utils/class-utils.php +137 -7 3.20.1 → 3.24.2 View file →
@@ -279,9 +279,9 @@
279 279
280 280 return esc_html(
281 281 sprintf(
282 282 /* translators: 1: Number of seconds */
283 - __( '%1$d sec.', 'wp-parsely' ),
283 + __( '%1$ds', 'wp-parsely' ),
284 284 round( $seconds )
285 285 )
286 286 );
287 287 }
@@ -419,10 +419,14 @@
419 419
420 420 /**
421 421 * Returns the post ID for the passed URL.
422 422 *
423 + * Posts the current user can neither view publicly nor read resolve to 0.
424 + *
423 425 * @since 3.16.0
424 426 * @since 3.18.0 Moved from `Models/class-smart-link.php`.
427 + * @since 3.24.2 Resolves only to posts visible to the current user, and
428 + * matches slugs only of this site's URLs, unambiguously.
425 429 *
426 430 * @param string $url The URL to get the post ID for.
427 431 * @return int The post ID of the URL, 0 if not found.
428 432 */
@@ -430,9 +434,9 @@
430 434 $cache_key = sprintf( 'url-to-postid-%s', hash( 'sha256', $url ) );
431 435 $cache = wp_cache_get( $cache_key, PARSELY_CACHE_GROUP );
432 436
433 437 if ( false !== $cache && is_numeric( $cache ) ) {
434 - return (int) $cache;
438 + return self::get_visible_post_id( (int) $cache );
435 439 }
436 440
437 441 if ( function_exists( 'wpcom_vip_url_to_postid' ) ) {
438 442 $post_id = wpcom_vip_url_to_postid( $url );
@@ -443,11 +447,16 @@
443 447 }
444 448
445 449 // A post ID was found, return it.
446 450 if ( 0 !== $post_id ) {
447 - return $post_id;
451 + return self::get_visible_post_id( $post_id );
448 452 }
449 453
454 + // A slug can only identify a post in this site's URLs.
455 + if ( ! self::is_site_url( $url ) ) {
456 + return 0;
457 + }
458 +
450 459 // No post ID was found, try to find it from the slug.
451 460 $clean_url = preg_replace( '/\?.*$/', '', $url ); // Remove the query string from the URL.
452 461 if ( null === $clean_url ) {
453 462 $clean_url = $url;
@@ -453,8 +462,43 @@
453 462 $clean_url = $url;
454 463 }
455 464 $post_slug = basename( $clean_url );
456 465
466 + // Public posts take precedence over other posts with the same slug.
467 + $post_ids = array_values(
468 + array_filter(
469 + self::get_top_level_post_ids_by_slug( $post_slug, array( 'publish', 'inherit' ) ),
470 + 'is_post_publicly_viewable'
471 + )
472 + );
473 +
474 + // Visibility is checked on return, so the resolution can be cached for all users.
475 + if ( 0 === count( $post_ids ) ) {
476 + $post_ids = self::get_top_level_post_ids_by_slug( $post_slug, array( 'any' ) );
477 + }
478 +
479 + // A slug shared by several posts identifies none of them.
480 + if ( 1 === count( $post_ids ) ) {
481 + wp_cache_set( $cache_key, $post_ids[0], PARSELY_CACHE_GROUP, WEEK_IN_SECONDS );
482 + return self::get_visible_post_id( $post_ids[0] );
483 + }
484 +
485 + return 0;
486 + }
487 +
488 + /**
489 + * Returns up to 2 IDs of top-level posts of public post types with the
490 + * passed slug, enough to tell whether the slug is ambiguous.
491 + *
492 + * Like get_page_by_path() given a single slug, child posts don't match.
493 + *
494 + * @since 3.24.2
495 + *
496 + * @param string $slug The post slug.
497 + * @param array<string> $post_statuses The post statuses to match.
498 + * @return array<int> The post IDs.
499 + */
500 + private static function get_top_level_post_ids_by_slug( string $slug, array $post_statuses ): array {
457 501 $public_post_types = get_post_types(
458 502 array(
459 503 'public' => true,
460 504 'show_in_rest' => true,
@@ -459,16 +503,102 @@
459 503 'public' => true,
460 504 'show_in_rest' => true,
461 505 )
462 506 );
463 - $post = get_page_by_path( $post_slug, OBJECT, array_keys( $public_post_types ) );
464 507
465 - if ( null !== $post ) {
466 - wp_cache_set( $cache_key, $post->ID, PARSELY_CACHE_GROUP, WEEK_IN_SECONDS );
467 - return $post->ID;
508 + $query = new \WP_Query(
509 + array(
510 + 'name' => $slug,
511 + 'post_type' => array_keys( $public_post_types ),
512 + 'post_status' => $post_statuses,
513 + 'post_parent' => 0,
514 + 'fields' => 'ids',
515 + 'posts_per_page' => 2,
516 + 'no_found_rows' => true,
517 + )
518 + );
519 +
520 + $post_ids = array();
521 + foreach ( $query->posts as $post_id ) {
522 + if ( is_int( $post_id ) ) {
523 + $post_ids[] = $post_id;
524 + }
468 525 }
469 526
527 + return $post_ids;
528 + }
529 +
530 + /**
531 + * Returns the passed post ID, or 0 if the current user can neither view
532 + * the post publicly nor read it.
533 + *
534 + * @since 3.24.2
535 + *
536 + * @param int $post_id The post ID.
537 + * @return int The post ID, or 0.
538 + */
539 + private static function get_visible_post_id( int $post_id ): int {
540 + if ( is_post_publicly_viewable( $post_id ) || current_user_can( 'read_post', $post_id ) ) {
541 + return $post_id;
542 + }
543 +
470 544 return 0;
545 + }
546 +
547 + /**
548 + * Returns whether the passed URL is relative, or has the host of the home
549 + * URL, the site URL, the Site ID or the canonical URL domain.
550 + *
551 + * A leading `www.` is ignored, as `url_to_postid()` does.
552 + *
553 + * @since 3.24.2
554 + *
555 + * @param string $url The URL to check.
556 + * @return bool Whether the URL belongs to this site.
557 + */
558 + private static function is_site_url( string $url ): bool {
559 + $url_host = wp_parse_url( $url, PHP_URL_HOST );
560 +
561 + if ( ! is_string( $url_host ) ) {
562 + return true;
563 + }
564 +
565 + $site_addresses = array(
566 + home_url(),
567 + site_url(),
568 + \Parsely\get_parsely()->get_site_id(),
569 + apply_filters( 'wp_parsely_canonical_url_domain', null ),
570 + );
571 +
572 + foreach ( $site_addresses as $site_address ) {
573 + if ( ! is_string( $site_address ) || '' === trim( $site_address ) ) {
574 + continue;
575 + }
576 +
577 + // Domain-only addresses need a scheme to be parsed.
578 + $site_host = wp_parse_url(
579 + 'http://' . preg_replace( '#^https?://#i', '', trim( $site_address ) ),
580 + PHP_URL_HOST
581 + );
582 +
583 + if ( is_string( $site_host ) && self::strip_www( $site_host ) === self::strip_www( $url_host ) ) {
584 + return true;
585 + }
586 + }
587 +
588 + return false;
589 + }
590 +
591 + /**
592 + * Lowercases a host and removes its leading `www.`.
593 + *
594 + * @since 3.24.2
595 + *
596 + * @param string $host The host.
597 + * @return string The host, without its leading `www.`.
598 + */
599 + private static function strip_www( string $host ): string {
600 + return (string) preg_replace( '/^www\./', '', strtolower( $host ) );
471 601 }
472 602
473 603 /**
474 604 * Appends ITM parameters to a URL.