Add PSR HTTP Message Interfaces and Dependencies
- Implemented StreamInterface, UploadedFileInterface, and UriInterface as per PSR standards. - Added getallheaders function to retrieve HTTP headers in a compatible manner. - Included LICENSE files for ralouphie/getallheaders and symfony/deprecation-contracts. - Introduced function for triggering deprecation notices in Symfony.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* Class ATFPP\AI_Translate\Services\Options\Option_Encrypter
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @package ai-services
|
||||
*/
|
||||
|
||||
namespace ATFPP\AI_Translate\Services\Options;
|
||||
|
||||
use ATFPP\AI_Translate\Services\Util\Data_Encryption;
|
||||
|
||||
/**
|
||||
* Class that allows for options to be encrypted when stored in the database as well as decrypted when retrieved.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
final class Option_Encrypter {
|
||||
|
||||
const ENCRYPTION_PREFIX = 'enc::';
|
||||
|
||||
/**
|
||||
* The data encryption instance.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @var Data_Encryption
|
||||
*/
|
||||
private $data_encryption;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param Data_Encryption $data_encryption The data encryption instance.
|
||||
*/
|
||||
public function __construct( Data_Encryption $data_encryption ) {
|
||||
$this->data_encryption = $data_encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds relevant hooks to handle encryption and decryption of the given option.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param string $option_slug The option to use encryption with.
|
||||
*/
|
||||
public function add_encryption_hooks( string $option_slug ): void {
|
||||
add_filter( "sanitize_option_{$option_slug}", array( $this, 'encrypt_option' ), 9999, 2 ); // Encrypt late.
|
||||
add_filter( "option_{$option_slug}", array( $this, 'decrypt_option' ), -9999, 2 ); // Decrypt early.
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given option has encryption enabled.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param string $option_slug The identifier/name of the option.
|
||||
* @return bool True if the option has encryption enabled, false otherwise.
|
||||
*/
|
||||
public function has_encryption( string $option_slug ): bool {
|
||||
return (
|
||||
has_filter( "sanitize_option_{$option_slug}", array( $this, 'encrypt_option' ) ) &&
|
||||
has_filter( "option_{$option_slug}", array( $this, 'decrypt_option' ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given option value.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param mixed $value The option value to encrypt.
|
||||
* @param string $option_slug The identifier/name of the option.
|
||||
* @return string Encrypted option value.
|
||||
*/
|
||||
public function encrypt_option( $value, string $option_slug ): string {
|
||||
// Do not encrypt if the value is empty.
|
||||
if ( '' === $value ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Bail if the value is already encrypted.
|
||||
if ( is_string( $value ) && str_starts_with( $value, self::ENCRYPTION_PREFIX ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$encrypted = $this->data_encryption->encrypt( maybe_serialize( $value ) );
|
||||
|
||||
// If encryption fails, trigger a warning but continue with the unencrypted value. Better not to lose data.
|
||||
if ( ! $encrypted ) {
|
||||
$this->trigger_error(
|
||||
__METHOD__,
|
||||
sprintf(
|
||||
/* translators: %s: Option slug */
|
||||
__( 'Failed to encrypt the value for the option "%s".', 'ai-services' ),
|
||||
$option_slug
|
||||
)
|
||||
);
|
||||
return $value;
|
||||
}
|
||||
|
||||
return self::ENCRYPTION_PREFIX . $encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given option value.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param mixed $value The option value to decrypt.
|
||||
* @param string $option_slug The identifier/name of the option.
|
||||
* @return mixed Decrypted option value.
|
||||
*/
|
||||
public function decrypt_option( $value, string $option_slug ) {
|
||||
// Bail if the value is already decrypted.
|
||||
if ( ! is_string( $value ) || ! str_starts_with( $value, self::ENCRYPTION_PREFIX ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$decrypted = $this->data_encryption->decrypt( substr( $value, strlen( self::ENCRYPTION_PREFIX ) ) );
|
||||
|
||||
// If decryption fails, trigger a warning and return an empty string.
|
||||
if ( ! $decrypted ) {
|
||||
$this->trigger_error(
|
||||
__METHOD__,
|
||||
sprintf(
|
||||
/* translators: %s: Option slug */
|
||||
__( 'Failed to decrypt the value for the option "%s".', 'ai-services' ),
|
||||
$option_slug
|
||||
)
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
return maybe_unserialize( $decrypted );
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an error, if WP_DEBUG is enabled.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param string $function_name The name of the function that triggered the error.
|
||||
* @param string $message The message explaining the error.
|
||||
* @param int $error_level Optional. The designated error type for this error.
|
||||
* Only works with E_USER family of constants. Default E_USER_NOTICE.
|
||||
*/
|
||||
private function trigger_error( string $function_name, string $message, int $error_level = E_USER_NOTICE ): void {
|
||||
// The wp_trigger_error() function was only added in WordPress 6.4, so this is a minimal shim.
|
||||
if ( ! function_exists( 'wp_trigger_error' ) ) {
|
||||
if ( ! WP_DEBUG ) {
|
||||
return;
|
||||
}
|
||||
if ( ! empty( $function_name ) ) {
|
||||
$message = sprintf( '%s(): %s', $function_name, $message );
|
||||
}
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
||||
trigger_error( esc_html( $message ), $error_level );
|
||||
return;
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
wp_trigger_error( $function_name, $message, $error_level );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user