: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( ! class_exists( 'PPW_Password_Services' ) ) {
class PPW_Password_Services implements PPW_Service_Interfaces {
* @var PPW_Repository_Passwords
private $passwords_repository;
* PPW_Password_Services constructor.
* @param PPW_Repository_Passwords $repo The password repository class help to interact with DB.
public function __construct( $repo = null ) {
if ( is_null( $repo ) ) {
$this->passwords_repository = new PPW_Repository_Passwords();
$this->passwords_repository = $repo;
* Check content is protected
public function is_protected_content( $post_id ) {
$result = $this->get_passwords( $post_id );
if ( ! $result['has_global_passwords'] && ! $result['has_role_passwords'] ) {
* Check password is valid
public function is_valid_password( $password, $post_id, $current_roles ) {
if ( $this->check_password_type_is_global( $post_id, $password ) ) {
$this->set_cookie_bypass_cache( $password . $post_id, PPW_Constants::COOKIE_NAME . $post_id );
if ( ! is_user_logged_in() ) {
$role_meta = get_post_meta( $post_id, PPW_Constants::POST_PROTECTION_ROLES, true );
$protected_roles = ppw_free_fix_serialize_data( $role_meta );
if ( empty( $protected_roles ) ) {
return $this->check_password_type_is_roles( $current_roles, $protected_roles, $password, $post_id );
public function set_password_to_cookie( $password, $cookie_name ) {
$password_hashed = wp_hash_password( $password );
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + 7 * DAY_IN_SECONDS );
$password_cookie_expired = ppw_core_get_setting_type_string( PPW_Constants::COOKIE_EXPIRED );
if ( ! empty( $password_cookie_expired ) ) {
$time = explode( ' ', $password_cookie_expired )[0];
$unit = ppw_core_get_unit_time( $password_cookie_expired );
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + (int) $time * $unit );
$referer = wp_get_referer();
$secure = ( 'https' === parse_url( $referer, PHP_URL_SCHEME ) );
$expire = apply_filters( 'ppw_cookie_expire', $expire );
$expire = apply_filters( 'ppwp_cookie_expiry', $expire );
return setcookie( $cookie_name . COOKIEHASH, $password_hashed, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure );
public function generate_cookie_data( $cookie_name, $password ) {
$password_hashed = wp_hash_password( $password );
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + 7 * DAY_IN_SECONDS );
$password_cookie_expired = ppw_core_get_setting_type_string( PPW_Constants::COOKIE_EXPIRED );
if ( ! empty( $password_cookie_expired ) ) {
$time = explode( ' ', $password_cookie_expired )[0];
$unit = ppw_core_get_unit_time( $password_cookie_expired );
$expire = apply_filters( PPW_Constants::HOOK_COOKIE_EXPIRED, time() + (int) $time * $unit );
$referer = wp_get_referer();
$secure = ( 'https' === parse_url( $referer, PHP_URL_SCHEME ) );
$expire = apply_filters( 'ppw_cookie_expire', $expire );
$expire = apply_filters( 'ppwp_cookie_expiry', $expire );
'name' => $cookie_name . COOKIEHASH,
'value' => $password_hashed,
* Set password to cookie with case cookie name the same WP to bypass cache
* @param string $cookie_value The value of the cookie.
* @param string $cookie_name The name of the cookie.
public function set_cookie_bypass_cache( $cookie_value, $cookie_name ) {
// Bypass Caching plugin with WordPress cookie.
$this->set_password_to_cookie( $cookie_value, PPW_Constants::WP_POST_PASS );
$this->set_password_to_cookie( $cookie_value, $cookie_name );
* Check whether the current cookie is valid.
public function is_valid_cookie( $post_id, $passwords, $cookie_name ) {
$_cookie = wp_unslash( $_COOKIE );
if ( ! isset( $_cookie[ $cookie_name . $post_id . COOKIEHASH ] ) ) {
$cookie = sanitize_text_field( $_cookie[ $cookie_name . $post_id . COOKIEHASH ] );
$hash = wp_unslash( $cookie );
$checked = apply_filters( 'ppw_check_md5_format', true );
if ( $checked && 0 !== strpos( $hash, '$P$B' ) ) {
$roles = ppw_core_get_current_role();
foreach ( $passwords as $password ) {
if ( wp_check_password( $password . $post_id, $hash ) ) {
foreach ( $roles as $role ) {
if ( wp_check_password( $password . $role . $post_id, $hash ) ) {
* Redirect after enter password
* @param bool $is_valid Is entered password valid.
public function handle_redirect_after_enter_password( $is_valid ) {
// 2. Easier to write UT.
$redirect_url = $this->get_redirect_url( $is_valid );
wp_safe_redirect( $redirect_url );
* Get redirect URL after user entered password.
* @param bool $is_valid Is password valid.
public function get_redirect_url( $is_valid ) {
$referrer_url = $this->get_referer_url();
$params_in_referer = ppw_core_get_param_in_url( $referrer_url );
$referrer_url = apply_filters(
'parameters' => $params_in_referer,
$url_redirect = preg_replace( '/[&?]' . PPW_Constants::WRONG_PASSWORD_PARAM . '=true/', '', $referrer_url );
PPW_Constants::HOOK_PARAM_PASSWORD_SUCCESS,
'name' => PPW_Constants::PASSWORD_PARAM_NAME,
'value' => PPW_Constants::PASSWORD_PARAM_VALUE,
if ( array_key_exists( $params['name'], $params_in_referer ) && '1' === $params_in_referer[ $params['name'] ] ) {
$params_in_redirect = ppw_core_get_param_in_url( $url_redirect );
$new_param = empty( $params_in_redirect ) ? '?' . $params['name'] . '=' . $params['value'] : '&' . $params['name'] . '=' . $params['value'];
$new_param = apply_filters(
'ppwp_ppf_redirect_url_param_before_return_content',
return apply_filters( 'ppwp_ppf_redirect_url_before_return_content', $url_redirect . $new_param );
if ( array_key_exists( PPW_Constants::WRONG_PASSWORD_PARAM, $params_in_referer ) && 'true' === $params_in_referer[ PPW_Constants::WRONG_PASSWORD_PARAM ] ) {
'parameters' => $params_in_referer,
$new_param = empty( $params_in_referer ) ? '?' . PPW_Constants::WRONG_PASSWORD_PARAM . '=true' : '&' . PPW_Constants::WRONG_PASSWORD_PARAM . '=true';
$referrer_url . $new_param,
'parameters' => $params_in_referer,
* Get referer URL from HTTP Referrer or callback URL in post form action URL.
* @return mixed False if cannot find the referer URL.
public function get_referer_url() {
$referrer_url = wp_get_referer();
$using_cb = false === $referrer_url;
$using_cb = apply_filters( 'ppw_use_callback_url', $using_cb );
// We need to get the callback URL in the password form action URL.
// in case Referrer-Policy is set no-referrer.
$cb_param = PPW_Constants::CALL_BACK_URL_PARAM;
if ( isset( $_GET[ $cb_param ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verfication for this one because already verify in parent function.
$referrer_url = rawurldecode( esc_url_raw( wp_unslash( $_GET[ $cb_param ] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verfication for this one because already verify in parent function.
// If doesn't have callback URL and no-referer then return to home page.
if ( false === $referrer_url ) {
$referrer_url = home_url( $wp->request );
* Handle and check condition before create new password
* @param int|string $id The post ID.
* @param string $role_selected The role user select on client.
* @param array $new_global_passwords List global passwords user enter on client.
* @param string $new_role_password Role password user enter on client.
public function create_new_password( $id, $role_selected, $new_global_passwords, $new_role_password ) {
$post_meta = get_post_meta( $id, PPW_Constants::POST_PROTECTION_ROLES, true );
$current_roles_password = ppw_free_fix_serialize_data( $post_meta );
$current_global_passwords = get_post_meta( $id, PPW_Constants::GLOBAL_PASSWORDS, true );
if ( 'global' === $role_selected ) {
return $this->create_password_type_global( $id, $new_global_passwords, $current_global_passwords, $current_roles_password, $role_selected );
return $this->create_password_type_role( $id, $role_selected, $new_role_password, $current_global_passwords, $current_roles_password );
* Check condition before create new password type global
* @param int|string $id The post ID.
* @param array $new_global_passwords List global passwords user enter on client.
* @param array $current_global_passwords List all current global passwords.
* @param array $current_roles_password List all current role passwords.
* @param string $role_selected The role user select on client.
public function create_password_type_global( $id, $new_global_passwords, $current_global_passwords, $current_roles_password, $role_selected ) {
// Validate global password(check bad request).
if ( $this->global_passwords_is_bad_request( $new_global_passwords ) ) {
'message' => PPW_Constants::BAD_REQUEST_MESSAGE,
// Validate global password(empty and duplicate).
ppw_free_validate_password_type_global( $new_global_passwords, $current_global_passwords, $current_roles_password );
update_post_meta( $id, PPW_Constants::GLOBAL_PASSWORDS, $new_global_passwords );
// Clear cache for Cache plugin.
ppw_core_clear_cache_by_id( $id );
// Handle cache for page/post have password type is global with Super Cache plugin.
$free_cache = new PPW_Cache_Services();
$free_cache->handle_cache_for_password_type_global_with_super_cache( $new_global_passwords, $id, $current_roles_password );
$current_roles_password[ $role_selected ] = implode( "\n", $new_global_passwords );
return $current_roles_password;
* Check bad request with data type is global passwords
* @param array $passwords Global passwords.
private function global_passwords_is_bad_request( $passwords ) {
foreach ( $passwords as $password ) {
if ( strpos( $password, ' ' ) !== false ) {
// Check element unique in array.
return count( $passwords ) !== count( array_unique( $passwords ) );
* Check condition before create new password type role
* @param int|string $id The post ID.
* @param string $role_selected The role user select on client.
* @param string $new_role_password Role password user enter on client.
* @param array $current_global_passwords List all current global passwords.
* @param array $current_roles_password List all current role passwords.
public function create_password_type_role( $id, $role_selected, $new_role_password, $current_global_passwords, $current_roles_password ) {
// Validate role password(check bad request).
if ( $this->role_password_is_bad_request( $new_role_password ) ) {
'message' => PPW_Constants::BAD_REQUEST_MESSAGE,
// Validate role password(empty and duplicate).
ppw_free_validate_password_type_role( $role_selected, $new_role_password, $current_global_passwords, $current_roles_password );
$current_roles_password[ $role_selected ] = $new_role_password;
delete_post_meta( $id, PPW_Constants::POST_PROTECTION_ROLES );
add_post_meta( $id, PPW_Constants::POST_PROTECTION_ROLES, $current_roles_password );
// Clear cache for Cache plugin.
ppw_core_clear_cache_by_id( $id );
// Handle cache for page/post have password type is role with Super Cache plugin.
$free_cache = new PPW_Cache_Services();
$free_cache->handle_cache_for_password_type_role_with_super_cache( $new_role_password, $id, $current_roles_password, $current_global_passwords );
if ( ! empty( $current_global_passwords ) ) {
$current_roles_password['global'] = implode( "\n", $current_global_passwords );
return $current_roles_password;
* Check bad request with data type is role password
* @param string $password Role password.
private function role_password_is_bad_request( $password ) {
return strpos( $password, ' ' ) !== false;
* Password is empty with not 0.
* @param string $pwd Password.
public function has_no_empty_password( $pwd ) {
return ! empty( $pwd ) || '0' === $pwd;
* @param int|string $post_id The post ID.
public function get_passwords( $post_id ) {
$global_passwords = get_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS, true );
$global_passwords = ! empty( $global_passwords ) ? $global_passwords : array();
$has_global_passwords = ! empty( $global_passwords ) && is_array( $global_passwords );
$raw_data = get_post_meta( $post_id, PPW_Constants::POST_PROTECTION_ROLES, true );
$protected_roles = ppw_free_fix_serialize_data( $raw_data );
$filtered_protected_roles = array_filter(
return $this->has_no_empty_password( $pass );
$has_role_passwords = ! empty( $filtered_protected_roles );
$has_current_role_password = false;
if ( $has_role_passwords ) {
$roles = ppw_core_get_current_role();
foreach ( $roles as $role ) {
if ( array_key_exists( $role, $filtered_protected_roles ) ) {
$has_current_role_password = true;
array_push( $global_passwords, $protected_roles[ $role ] );
'passwords' => $global_passwords,
'has_role_passwords' => $has_role_passwords,
'has_current_role_password' => $has_current_role_password,
'has_global_passwords' => $has_global_passwords,
* Check password type is global
public function check_password_type_is_global( $post_id, $password ) {
$global_passwords = get_post_meta( $post_id, PPW_Constants::GLOBAL_PASSWORDS, true );
if ( empty( $global_passwords ) || ! is_array( $global_passwords ) ) {
foreach ( $global_passwords as $pass ) {
if ( $password === $pass ) {