<?php
/**
 * This file is part of Avatar Privacy.
 *
 * Copyright 2018-2023 Peter Putzer.
 * Copyright 2012-2013 Johannes Freudendahl.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 *  ***
 *
 * @package mundschenk-at/avatar-privacy
 * @license http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Avatar_Privacy\Components;

use Avatar_Privacy\Component;

use Avatar_Privacy\Core\Comment_Author_Fields;
use Avatar_Privacy\Core\User_Fields;
use Avatar_Privacy\Core\Settings;

use Avatar_Privacy\Exceptions\Avatar_Comment_Type_Exception;

use Avatar_Privacy\Tools\Images\Image_File;
use Avatar_Privacy\Tools\Network\Gravatar_Service;
use Avatar_Privacy\Tools\Network\Remote_Image_Service;

/**
 * Handles the display of avatars in WordPress.
 *
 * @since 1.0.0
 *
 * @author Peter Putzer <github@mundschenk.at>
 *
 * @phpstan-type AvatarData array{
 *     size: int, height: int, width: int,
 *     default: string, force_default: bool,
 *     rating: string,
 *     scheme: ?string,
 *     processed_args: ?mixed[],
 *     extra_attr: string,
 *     url?: string|false,
 *     found_avatar: bool
 * }
 * @phpstan-type IdentityTuple array{ 0: int|false, 1: string, 2: int }
 */
class Avatar_Handling implements Component {

	/**
	 * The settings API.
	 *
	 * @var Settings
	 */
	private Settings $settings;

	/**
	 * The user data helper.
	 *
	 * @since 2.4.0
	 *
	 * @var User_Fields
	 */
	private User_Fields $registered_user;

	/**
	 * The comment author data helper.
	 *
	 * @since 2.4.0
	 *
	 * @var Comment_Author_Fields
	 */
	private Comment_Author_Fields $comment_author;

	/**
	 * The Gravatar network service.
	 *
	 * @var Gravatar_Service
	 */
	private Gravatar_Service $gravatar;

	/**
	 * The remote image network service.
	 *
	 * @var Remote_Image_Service
	 */
	private Remote_Image_Service $remote_images;

	/**
	 * Creates a new instance.
	 *
	 * @since 2.0.0 Parameter $gravatar added.
	 * @since 2.1.0 Parameter $plugin_file removed.
	 * @since 2.3.4 Parameter $remote_images added.
	 * @since 2.4.0 Parameters $settings, $user_fields and $comment_author_fields
	 *              added, unused parameters $core and $options removed.
	 *
	 * @param Settings              $settings              The settings API.
	 * @param User_Fields           $user_fields           User data API.
	 * @param Comment_Author_Fields $comment_author_fields Comment author data API.
	 * @param Gravatar_Service      $gravatar              The Gravatar network service.
	 * @param Remote_Image_Service  $remote_images         The remote images network service.
	 */
	public function __construct( Settings $settings, User_Fields $user_fields, Comment_Author_Fields $comment_author_fields, Gravatar_Service $gravatar, Remote_Image_Service $remote_images ) {
		$this->settings        = $settings;
		$this->registered_user = $user_fields;
		$this->comment_author  = $comment_author_fields;
		$this->gravatar        = $gravatar;
		$this->remote_images   = $remote_images;
	}

	/**
	 * Sets up the various hooks for the plugin component.
	 *
	 * @return void
	 */
	public function run() {
		// Allow remote URLs by default for legacy avatar images. Use priority 9
		// to allow filters with the default priority to override this consistently.
		\add_filter( 'avatar_privacy_allow_remote_avatar_url', '__return_true', 9, 0 );

		// Start handling avatars when all plugins have been loaded and initialized.
		\add_action( 'init', [ $this, 'setup_avatar_filters' ] );

		// Generate presets from saved settings.
		\add_action( 'init', [ $this, 'enable_presets' ] );
	}

	/**
	 * Sets up avatar handling filters.
	 *
	 * @since 2.4.0 Renamed from init().
	 *
	 * @return void
	 */
	public function setup_avatar_filters() {
		/**
		 * Filters the priority used for filtering the `pre_get_avatar_data` hook.
		 *
		 * @since 2.3.4
		 *
		 * @param int $priority Default 9999.
		 */
		$priority = \apply_filters( 'avatar_privacy_pre_get_avatar_data_filter_priority', 9999 );

		// New default image display: filter the gravatar image upon display.
		\add_filter( 'pre_get_avatar_data', [ $this, 'get_avatar_data' ], $priority, 2 );
	}

	/**
	 * Enables default filters from the user settings.
	 *
	 * @return void
	 */
	public function enable_presets() {
		if ( ! empty( $this->settings->get( Settings::GRAVATAR_USE_DEFAULT ) ) ) {
			// Use priority 9 to allow filters with the default priority to override this consistently.
			\add_filter( 'avatar_privacy_gravatar_use_default', '__return_true', 9, 0 );
		}
	}

	/**
	 * Before displaying an avatar image, checks that displaying the gravatar
	 * for this e-mail address has been enabled (opted-in). Also, if the option
	 * "Don't publish encrypted e-mail addresses for non-members of Gravatar." is
	 * enabled, the function checks if a gravatar is actually available for the
	 * e-mail address. If not, it displays the default image directly.
	 *
	 * @param  array             $args        Arguments passed to get_avatar_data(), after processing.
	 * @param  int|string|object $id_or_email The Gravatar to retrieve. Accepts a user_id, user email, WP_User object, WP_Post object, or WP_Comment object.
	 *
	 * @return array
	 *
	 * @phpstan-param  AvatarData $args
	 * @phpstan-return AvatarData
	 */
	public function get_avatar_data( $args, $id_or_email ) {
		// Process the user identifier.
		try {
			list( $user_id, $email, $age ) = $this->parse_id_or_email( $id_or_email );
		} catch ( Avatar_Comment_Type_Exception $e ) {
			// The $id_or_email is a comment of a type that should not display an avatar.
			$args['url']          = false;
			$args['found_avatar'] = false;
			return $args;
		}

		// Generate the hash.
		if ( ! empty( $user_id ) ) {
			// Since we are having a non-empty $user_id, we'll always get a hash.
			$hash = (string) $this->registered_user->get_hash( (int) $user_id );
		} else {
			// This might generate hashes for empty email addresses.
			// That's OK in case some plugins want to display avatars for
			// e.g. trackbacks and linkbacks.
			$hash = $this->comment_author->get_hash( $email );
		}

		// We only need to check these if we are not forcing a default icon to be shown.
		if ( empty( $args['force_default'] ) ) {
			if ( ! empty( $user_id ) ) {
				// Uploaded avatars take precedence.
				$url = $this->get_local_avatar_url( $user_id, $hash, $args['size'], ! empty( $args['upload_timestamp'] ) );
			}

			if ( empty( $url ) ) {
				// "Sniffed" Gravatar MIME type.
				$mimetype = '';

				// Maybe display a gravatar.
				if ( $this->should_show_gravatar( $user_id, $email, $id_or_email, $age, $mimetype ) ) {
					$url = $this->get_gravatar_url( $user_id, $email, $hash, $args['size'], $args['rating'], $mimetype );
				} elseif ( ! empty( $args['url'] ) && $this->remote_images->validate_image_url( $args['url'], 'avatar' ) ) {
					// Fall back to avatars set by other plugins.
					$url = $this->get_legacy_icon_url( $args['url'], $args['size'] );
				}
			}
		}

		if ( empty( $url ) ) {
			// Nothing so far, use the default icon.
			$url = $this->get_default_icon_url( $hash, $args['default'], $args['size'] );
		}

		// Return found image.
		$args['url']          = $url;
		$args['found_avatar'] = true;

		return $args;
	}

	/**
	 * Determines if we should go for a gravatar.
	 *
	 * @since 2.1.0 Visibility changed to protected.
	 *
	 * @param int|false         $user_id     A WordPress user ID (or false).
	 * @param string            $email       The email address.
	 * @param int|string|object $id_or_email The Gravatar to retrieve. Accepts a user_id, user email, WP_User object, WP_Post object, or WP_Comment object.
	 * @param int               $age         The seconds since the post or comment was first created, or 0 if $id_or_email was not one of these object types.
	 * @param string            $mimetype    The expected MIME type of the gravatar image (if any). Passed by reference.
	 *
	 * @return bool
	 */
	protected function should_show_gravatar( $user_id, $email, $id_or_email, $age, &$mimetype ) {
		// Find out if the user opted into displaying a gravatar.
		$show_gravatar = $this->determine_gravatar_policy( $user_id, $email, $id_or_email );

		// Check if a gravatar exists for the e-mail address.
		if ( $show_gravatar ) {
			/**
			 * Filters whether we check if opting-in users and commenters actually have a Gravatar.com account.
			 *
			 * @param bool      $enable_check Defaults to true.
			 * @param string    $email        The email address.
			 * @param int|false $user_id      A WordPress user ID (or false).
			 */
			if ( \apply_filters( 'avatar_privacy_enable_gravatar_check', true, $email, $user_id ) ) {
				$mimetype      = $this->gravatar->validate( $email, $age );
				$show_gravatar = ! empty( $mimetype );
			}
		}

		return $show_gravatar;
	}

	/**
	 * Parses e-mail address and/or user ID from $id_or_email.
	 *
	 * @since 2.1.0 Visibility changed to protected.
	 * @since 2.3.4 Throws an Avatar_Comment_Type_Exception for invalid comment types.
	 *
	 * @param  int|string|object $id_or_email The Gravatar to retrieve. Accepts a user_id, user email, WP_User object, WP_Post object, or WP_Comment object.
	 *
	 * @return array {
	 *     The tuple `[ $user_id, $email, $age ]`.
	 *
	 *     @type int|false $user_id The WordPress user ID, or `false`.
	 *     @type string    $email   The email address (or the empty string).
	 *     @type int       $age     The seconds since the post or comment was first created,
	 *                              or 0 if `$id_or_email was` not one of these object types.
	 * }
	 *
	 * @throws Avatar_Comment_Type_Exception The function throws an
	 *     `Avatar_Comment_Type_Exception` if `$id_or_email` is an instance of
	 *     `WP_Comment` but its comment type is not one of the allowed avatar
	 *     comment types.
	 *
	 * @phpstan-return IdentityTuple
	 */
	protected function parse_id_or_email( $id_or_email ) {
		list( $user_id, $email, $age ) = $this->parse_id_or_email_unfiltered( $id_or_email );

		if ( ! empty( $user_id ) && empty( $email ) ) {
			$user = \get_user_by( 'ID', $user_id );

			// Prevent warnings when a user ID is invalid (e.g. because a user was deleted directly from the database).
			if ( ! empty( $user ) ) {
				$email = $user->user_email;
			} else {
				$user_id = false; // The user ID was invalid.
			}
		} elseif ( empty( $user_id ) && ! empty( $email ) ) {
			// Check if anonymous comments "as user" are allowed.
			$user = $this->registered_user->get_user_by_email( $email );
			if ( ! empty( $user ) && $this->registered_user->allows_anonymous_commenting( $user->ID ) ) {
				$user_id = $user->ID;
			}
		}

		/**
		 * Filters the parsed user ID, email address and "object age".
		 *
		 * @param array $parsed_data {
		 *     The information parsed from $id_or_email.
		 *
		 *     @type int|false $user_id The WordPress user ID, or `false`.
		 *     @type string    $email   The email address (or the empty string).
		 *     @type int       $age     The seconds since the post or comment was first created,
		 *                              or 0 if `$id_or_email was` not one of these object types.
		 * }
		 * @param int|string|object $id_or_email The Gravatar to retrieve. Accepts a user_id, user email, WP_User object, WP_Post object, or WP_Comment object.
		 *
		 * @phpstan-var IdentityTuple
		 */
		return \apply_filters( 'avatar_privacy_parse_id_or_email', [ $user_id, $email, $age ], $id_or_email );
	}

	/**
	 * Parses e-mail address and/or user ID from $id_or_email without filtering
	 * the result in any way.
	 *
	 * @since 2.3.0
	 * @since 2.3.4 Throws an Avatar_Comment_Type_Exception for invalid comment types.
	 *
	 * @internal
	 *
	 * @param  int|string|object $id_or_email The identity to retrieven an avatar for.
	 *                                        Accepts a user_id, user email, WP_User object,
	 *                                        WP_Post object, or WP_Comment object.
	 *
	 * @return array {
	 *     The tuple `[ $user_id, $email, $age ]`.
	 *
	 *     @type int|false $user_id The WordPress user ID, or `false`.
	 *     @type string    $email   The email address (or the empty string).
	 *     @type int       $age     The seconds since the post or comment was first created,
	 *                              or 0 if $id_or_email was not one of these object types.
	 * }
	 *
	 * @throws Avatar_Comment_Type_Exception The function throws an
	 *     `Avatar_Comment_Type_Exception` if `$id_or_email` is an instance of
	 *     `WP_Comment` but its comment type is not one of the allowed avatar
	 *     comment types.
	 *
	 * @phpstan-return IdentityTuple
	 */
	protected function parse_id_or_email_unfiltered( $id_or_email ) {
		$user_id = false;
		$email   = '';
		$age     = 0;

		if ( \is_numeric( $id_or_email ) ) {
			$user_id = \absint( $id_or_email );
		} elseif ( \is_string( $id_or_email ) ) {
			// E-mail address.
			$email = $id_or_email;
		} elseif ( $id_or_email instanceof \WP_User ) {
			// User object.
			$user_id = $id_or_email->ID;
			$email   = $id_or_email->user_email;
		} elseif ( $id_or_email instanceof \WP_Post ) {
			// Post object.
			$user_id = (int) $id_or_email->post_author;
			$age     = $this->get_age( $id_or_email->post_date_gmt );
		} elseif ( $id_or_email instanceof \WP_Comment ) {
			return $this->parse_comment( $id_or_email );
		}

		return [ $user_id, $email, $age ];
	}

	/**
	 * Parse a WP_Comment object.
	 *
	 * @since 2.1.0 Visibility changed to protected.
	 * @since 2.3.4 Throws an Avatar_Comment_Type_Exception for invalid comment types.
	 *
	 * @param  \WP_Comment $comment A comment.
	 *
	 * @return array {
	 *     The information parsed from $id_or_email.
	 *
	 *     @type int|false $user_id The WordPress user ID, or false.
	 *     @type string    $email   The email address.
	 *     @type int       $age     The seconds since the post or comment was first created, or 0 if $id_or_email was not one of these object types.
	 * }
	 *
	 * @throws Avatar_Comment_Type_Exception The function throws an
	 *     `Avatar_Comment_Type_Exception` if the comment type of `$comment` is
	 *     not one of the allowed avatar comment types.
	 *
	 * @phpstan-return IdentityTuple
	 */
	protected function parse_comment( \WP_Comment $comment ) {
		/** This filter is documented in wp-includes/pluggable.php */
		$allowed_comment_types = \apply_filters( 'get_avatar_comment_types', [ 'comment' ] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- can be replaced with is_avatar_comment_type() once WordPress 5.1 is released.

		if ( ! \in_array( \get_comment_type( $comment ), (array) $allowed_comment_types, true ) ) {
			// Abort.
			throw new Avatar_Comment_Type_Exception();
		}

		$user_id = false;
		$email   = '';
		$age     = $this->get_age( $comment->comment_date_gmt );
		if ( ! empty( $comment->user_id ) ) {
			$user_id = (int) $comment->user_id;
		} elseif ( ! empty( $comment->comment_author_email ) ) {
			$email = $comment->comment_author_email;
		}

		return [ $user_id, $email, $age ];
	}

	/**
	 * Calculates the age (seconds before now) from a GMT-based date/time string.
	 *
	 * @since 2.1.0
	 *
	 * @param  string $date_gmt A date/time string in the GMT time zone.
	 *
	 * @return int              The age in seconds.
	 */
	protected function get_age( $date_gmt ) {
		return \time() - (int) \mysql2date( 'U', $date_gmt );
	}

	/**
	 * Retrieves a URL pointing to the local avatar image of the appropriate size.
	 *
	 * @since  2.1.0  Visibility changed to protected.
	 * @since  2.4.0  Parameter $timestamp added.
	 *
	 * @param  int    $user_id   The user ID.
	 * @param  string $hash      The hashed mail address.
	 * @param  int    $size      The requested avatar size in pixels.
	 * @param  bool   $timestamp Optional. Whether a timestampe (`ts`) parameter
	 *                           should be added to the URL (for cache busting).
	 *
	 * @return string          The URL, or '' if no local avatar has been set.
	 */
	protected function get_local_avatar_url( $user_id, $hash, $size, $timestamp = false ) {
		// Bail if we haven't got a valid user ID.
		if ( empty( $user_id ) ) {
			return '';
		}

		// Fetch local avatar from meta and make sure it's properly stzed.
		$url          = '';
		$local_avatar = $this->registered_user->get_local_avatar( $user_id );
		if ( ! empty( $local_avatar['file'] ) && ! empty( $local_avatar['type'] ) ) {
			// Prepare filter arguments.
			$args = [
				'user_id'   => $user_id,
				'avatar'    => $local_avatar['file'],
				'mimetype'  => $local_avatar['type'],
				'timestamp' => $timestamp,
			];

			/**
			 * Filters the uploaded avatar URL for the given user.
			 *
			 * @since  2.4.0  Optional 'timestamp' argument added.
			 *
			 * @param  string $url   The URL. Default empty.
			 * @param  string $hash  The hashed mail address.
			 * @param  int    $size  The size of the avatar image in pixels.
			 * @param  array  $args {
			 *     An array of arguments.
			 *
			 *     @type int    $user_id   A WordPress user ID.
			 *     @type string $avatar    The full-size avatar image path.
			 *     @type string $mimetype  The expected MIME type of the avatar image.
			 *     @type bool   $timestamp Whether to add a timestamp for cache busting.
			 * }
			 */
			$url = \apply_filters( 'avatar_privacy_user_avatar_icon_url', '', $hash, $size, $args );
		}

		return $url;
	}

	/**
	 * Retrieves the default icon URL for the given hash.
	 *
	 * @since 2.4.0
	 * @since 2.7.0 Parameter `$default` renamed to `$type` to prevent conflict with reserved keyword.
	 *
	 * @param  string $hash The hashed mail address.
	 * @param  string $type The default icon type.
	 * @param  int    $size The size of the avatar image in pixels.
	 *
	 * @return string
	 */
	protected function get_default_icon_url( $hash, $type, $size ) {
		// Prepare filter arguments.
		$args = [
			'default' => $type,
			'type'    => $type,
		];

		/**
		 * Filters the default icon URL for the given e-mail.
		 *
		 * @since  2.7.0 The argument key 'default' has been deprecated. Use 'type' instead.
		 *
		 * @param  string $url   The fallback icon URL (a blank GIF).
		 * @param  string $hash  The hashed mail address.
		 * @param  int    $size  The size of the avatar image in pixels.
		 * @param  array  $args {
		 *     An array of arguments.
		 *
		 *     @type string $default Deprecated.
		 *     @type string $type    The default icon type.
		 * }
		 */
		return \apply_filters( 'avatar_privacy_default_icon_url', \includes_url( 'images/blank.gif' ), $hash, $size, $args );
	}

	/**
	 * Determines the gravatar use policy.
	 *
	 * @since 2.1.0 Visibility changed to protected.
	 *
	 * @param  int|false         $user_id     A WordPress user ID (or false).
	 * @param  string            $email       The email address.
	 * @param  int|string|object $id_or_email The Gravatar to retrieve. Can be a user_id, user email, WP_User object, WP_Post object, or WP_Comment object.
	 *
	 * @return bool
	 */
	protected function determine_gravatar_policy( $user_id, $email, $id_or_email ) {
		$show_gravatar = false;
		$use_default   = false;

		if ( ! empty( $user_id ) ) {
			// For users get the value from the usermeta table.
			$show_gravatar = $this->registered_user->allows_gravatar_use( $user_id );
			$use_default   = ! $this->registered_user->has_gravatar_policy( $user_id );
		} else {
			// For comments get the value from the plugin's table.
			$show_gravatar = $this->comment_author->allows_gravatar_use( $email );

			// Don't use the default policy for spam comments.
			if ( ! $show_gravatar && ( ! $id_or_email instanceof \WP_Comment || ( 'spam' !== $id_or_email->comment_approved && 'trash' !== $id_or_email->comment_approved ) ) ) {
				$use_default = ! $this->comment_author->has_gravatar_policy( $email );
			}
		}

		if ( $use_default ) {
			/**
			 * Filters the default policy for showing gravatars.
			 *
			 * The result only applies if a user or comment author has not
			 * explicitely set a value for `use_gravatar` (i.e. for comments
			 * created  before the plugin was installed).
			 *
			 * @param  bool              $show        Default false.
			 * @param  int|string|object $id_or_email The Gravatar to retrieve. Can be a user_id, user email, WP_User object, WP_Post object, or WP_Comment object.
			 */
			$show_gravatar = \apply_filters( 'avatar_privacy_gravatar_use_default', false, $id_or_email );

			if ( $show_gravatar && empty( $user_id ) ) {
				// Create only the hash so that the gravatar can be regenerated.
				$this->comment_author->update_hash( $email );
			}
		}

		return $show_gravatar;
	}

	/**
	 * Retrieves the Gravatar.com URL for the given e-mail.
	 *
	 * @since 2.4.0
	 *
	 * @param  int|false $user_id  A WordPress user ID (or false).
	 * @param  string    $email    The mail address used to generate the identity hash.
	 * @param  string    $hash     The hashed e-mail address.
	 * @param  int       $size     The size of the avatar image in pixels.
	 * @param  string    $rating   The audience rating (e.g. 'g', 'pg', 'r', 'x').
	 * @param  string    $mimetype The expected MIME type of the Gravatar image.
	 *
	 * @return string
	 */
	protected function get_gravatar_url( $user_id, $email, $hash, $size, $rating, $mimetype = null ) {
		// Prepare filter arguments.
		$args = [
			'user_id'  => $user_id,
			'email'    => $email,
			'rating'   => $rating,
			'mimetype' => empty( $mimetype ) ? Image_File::PNG_IMAGE : $mimetype,
		];

		/**
		 * Filters the Gravatar.com URL for the given e-mail.
		 *
		 * @param  string $url   The fallback default icon URL (or '').
		 * @param  string $hash  The hashed e-mail address.
		 * @param  int    $size  The size of the avatar image in pixels.
		 * @param  array  $args {
		 *     An array of arguments.
		 *
		 *     @type int|false $user_id  A WordPress user ID (or false).
		 *     @type string    $email    The mail address used to generate the identity hash.
		 *     @type string    $rating   The audience rating (e.g. 'g', 'pg', 'r', 'x').
		 *     @type string    $mimetype The expected MIME type of the Gravatar image.
		 * }
		 */
		return \apply_filters( 'avatar_privacy_gravatar_icon_url', '', $hash, $size, $args );
	}

	/**
	 * Checks if an image URL is valid to use as a fallback avatar icon.
	 *
	 * @since 2.4.0
	 *
	 * @deprecated 2.7.0
	 *
	 * @param  string $url The image URL.
	 *
	 * @return bool
	 */
	protected function is_valid_image_url( $url ) {
		\_deprecated_function( __METHOD__, '2.7.0', 'Avatar_Privacy\Tools\Network\Remote_Image_Service::validate_image_url' );

		return ( ! \strpos( $url, 'gravatar.com' ) && $this->remote_images->validate_image_url( $url, 'avatar' ) );
	}

	/**
	 * Retrieves a URL pointing to the legacy icon scaled to the appropriate size.
	 *
	 * @since 2.4.0
	 *
	 * @param  string $url  A valid image URL.
	 * @param  int    $size The size of the avatar image in pixels.
	 *
	 * @return string
	 */
	protected function get_legacy_icon_url( $url, $size ) {
		// Prepare filter arguments.
		$hash = $this->remote_images->get_hash( $url );
		$args = [];

		/**
		 * Filters the legacy icon URL.
		 *
		 * @since 2.4.0
		 *
		 * @param  string $url   The legacy image URL.
		 * @param  string $hash  The hashed URL.
		 * @param  int    $size  The size of the avatar image in pixels.
		 * @param  array  $args {
		 *     An array of arguments. Currently unused.
		 * }
		 */
		return \apply_filters( 'avatar_privacy_legacy_icon_url', $url, $hash, $size, $args );
	}
}
