HEX
Server: LiteSpeed
System: Linux eko108.isimtescil.net 4.18.0-477.21.1.lve.1.el8.x86_64 #1 SMP Tue Sep 5 23:08:35 UTC 2023 x86_64
User: uyarreklamcomtr (11202)
PHP: 7.4.33
Disabled: opcache_get_status
Upload Files
File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/Options.tar
Cache.php000064400000003103151540527310006260 0ustar00<?php
namespace AIOSEO\Plugin\Common\Options;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class that holds all the cache for the AIOSEO options.
 *
 * @since 4.1.4
 */
class Cache {
	/**
	 * The DB options cache.
	 *
	 * @since 4.1.4
	 *
	 * @var array
	 */
	private static $db = [];

	/**
	 * The options cache.
	 *
	 * @since 4.1.4
	 *
	 * @var array
	 */
	private static $options = [];

	/**
	 * Sets the cache for the DB option.
	 *
	 * @since 4.1.4
	 *
	 * @param  string $name  The cache name.
	 * @param  array  $value The value.
	 * @return void
	 */
	public function setDb( $name, $value ) {
		self::$db[ $name ] = $value;
	}

	/**
	 * Gets the cache for the DB option.
	 *
	 * @since 4.1.4
	 *
	 * @param  string $name The cache name.
	 * @return array        The data from the cache.
	 */
	public function getDb( $name ) {
		return ! empty( self::$db[ $name ] ) ? self::$db[ $name ] : [];
	}

	/**
	 * Sets the cache for the options.
	 *
	 * @since 4.1.4
	 *
	 * @param  string $name  The cache name.
	 * @param  array  $value The value.
	 * @return void
	 */
	public function setOptions( $name, $value ) {
		self::$options[ $name ] = $value;
	}

	/**
	 * Gets the cache for the options.
	 *
	 * @since 4.1.4
	 *
	 * @param  string $name The cache name.
	 * @return array        The data from the cache.
	 */
	public function getOptions( $name ) {
		return ! empty( self::$options[ $name ] ) ? self::$options[ $name ] : [];
	}

	/**
	 * Resets the DB cache.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	public function resetDb() {
		self::$db = [];
	}
}InternalOptions.php000064400000002071151540527310010410 0ustar00<?php
namespace AIOSEO\Plugin\Lite\Options;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Options as CommonOptions;
use AIOSEO\Plugin\Lite\Traits;

/**
 * Class that holds all internal options for AIOSEO.
 *
 * @since 4.0.0
 */
class InternalOptions extends CommonOptions\InternalOptions {
	use Traits\Options;

	/**
	 * Defaults options for Lite.
	 *
	 * @since 4.0.0
	 *
	 * @var array
	 */
	private $liteDefaults = [
		// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
		'internal' => [
			'activated'      => [ 'type' => 'number', 'default' => 0 ],
			'firstActivated' => [ 'type' => 'number', 'default' => 0 ],
			'installed'      => [ 'type' => 'number', 'default' => 0 ],
			'connect'        => [
				'key'     => [ 'type' => 'string' ],
				'time'    => [ 'type' => 'number', 'default' => 0 ],
				'network' => [ 'type' => 'boolean', 'default' => false ],
				'token'   => [ 'type' => 'string' ]
			]
		]
		// phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
	];
}Options.php000064400000012474151540527310006723 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\Exception\InvalidOption;
use Automattic\WooCommerce\GoogleListingsAndAds\Exception\InvalidValue;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;
use Automattic\WooCommerce\GoogleListingsAndAds\Value\CastableValueInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Value\ValueInterface;

defined( 'ABSPATH' ) || exit;

/**
 * Class Options
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure
 */
final class Options implements OptionsInterface, Service {

	use PluginHelper;

	/**
	 * Array of options that we have loaded.
	 *
	 * @var array
	 */
	protected $options = [];

	/**
	 * Get an option.
	 *
	 * @param string $name          The option name.
	 * @param mixed  $default_value A default value for the option.
	 *
	 * @return mixed
	 */
	public function get( string $name, $default_value = null ) {
		$this->validate_option_key( $name );

		if ( ! array_key_exists( $name, $this->options ) ) {
			$value                  = get_option( $this->prefix_name( $name ), $default_value );
			$this->options[ $name ] = $this->maybe_cast_value( $name, $value );
		}

		return $this->raw_value( $this->options[ $name ] );
	}

	/**
	 * Add an option.
	 *
	 * @param string $name  The option name.
	 * @param mixed  $value The option value.
	 *
	 * @return bool
	 */
	public function add( string $name, $value ): bool {
		$this->validate_option_key( $name );
		$value                  = $this->maybe_convert_value( $name, $value );
		$this->options[ $name ] = $value;

		$result = add_option( $this->prefix_name( $name ), $this->raw_value( $value ) );

		do_action( "woocommerce_gla_options_updated_{$name}", $value );

		return $result;
	}

	/**
	 * Update an option.
	 *
	 * @param string $name  The option name.
	 * @param mixed  $value The option value.
	 *
	 * @return bool
	 */
	public function update( string $name, $value ): bool {
		$this->validate_option_key( $name );
		$value                  = $this->maybe_convert_value( $name, $value );
		$this->options[ $name ] = $value;

		$result = update_option( $this->prefix_name( $name ), $this->raw_value( $value ) );

		do_action( "woocommerce_gla_options_updated_{$name}", $value );

		return $result;
	}

	/**
	 * Delete an option.
	 *
	 * @param string $name The option name.
	 *
	 * @return bool
	 */
	public function delete( string $name ): bool {
		$this->validate_option_key( $name );
		unset( $this->options[ $name ] );

		$result = delete_option( $this->prefix_name( $name ) );

		do_action( "woocommerce_gla_options_deleted_{$name}" );

		return $result;
	}

	/**
	 * Helper function to retrieve the Ads Account ID.
	 *
	 * @return int
	 */
	public function get_ads_id(): int {
		// TODO: Remove overriding with default once ConnectionTest is removed.
		$default = intval( $_GET['customer_id'] ?? 0 ); // phpcs:ignore WordPress.Security
		return $default ?: $this->get( self::ADS_ID );
	}

	/**
	 * Helper function to retrieve the Merchant Account ID.
	 *
	 * @return int
	 */
	public function get_merchant_id(): int {
		// TODO: Remove overriding with default once ConnectionTest is removed.
		$default = intval( $_GET['merchant_id'] ?? 0 ); // phpcs:ignore WordPress.Security
		return $default ?: $this->get( self::MERCHANT_ID );
	}

	/**
	 * Returns all available option keys.
	 *
	 * @return array
	 */
	public static function get_all_option_keys(): array {
		return array_keys( self::VALID_OPTIONS );
	}

	/**
	 * Ensure that a given option key is valid.
	 *
	 * @param string $name The option name.
	 *
	 * @throws InvalidOption When the option key is not valid.
	 */
	protected function validate_option_key( string $name ) {
		if ( ! array_key_exists( $name, self::VALID_OPTIONS ) ) {
			throw InvalidOption::invalid_name( $name );
		}
	}

	/**
	 * Cast to a specific value type.
	 *
	 * @param string $name  The option name.
	 * @param mixed  $value The option value.
	 *
	 * @return mixed
	 */
	protected function maybe_cast_value( string $name, $value ) {
		if ( isset( self::OPTION_TYPES[ $name ] ) ) {
			/** @var CastableValueInterface $class */
			$class = self::OPTION_TYPES[ $name ];
			$value = $class::cast( $value );
		}

		return $value;
	}

	/**
	 * Convert to a specific value type.
	 *
	 * @param string $name  The option name.
	 * @param mixed  $value The option value.
	 *
	 * @return mixed
	 * @throws InvalidValue When the value is invalid.
	 */
	protected function maybe_convert_value( string $name, $value ) {
		if ( isset( self::OPTION_TYPES[ $name ] ) ) {
			$class = self::OPTION_TYPES[ $name ];
			$value = new $class( $value );
		}

		return $value;
	}

	/**
	 * Return raw value.
	 *
	 * @param mixed $value Possible object value.
	 *
	 * @return mixed
	 */
	protected function raw_value( $value ) {
		return $value instanceof ValueInterface ? $value->get() : $value;
	}

	/**
	 * Prefix an option name with the plugin prefix.
	 *
	 * @param string $name
	 *
	 * @return string
	 */
	protected function prefix_name( string $name ): string {
		return "{$this->get_slug()}_{$name}";
	}

	/**
	 * Checks if WPCOM API is Authorized.
	 *
	 * @return bool
	 */
	public function is_wpcom_api_authorized(): bool {
		return $this->get( self::WPCOM_REST_API_STATUS ) === 'approved';
	}
}
DynamicBackup.php000064400000021020151542512770007773 0ustar00<?php
namespace AIOSEO\Plugin\Common\Options;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Handles the dynamic backup.
 *
 * @since 4.1.3
 */
class DynamicBackup {
	/**
	 * A the name of the option to save dynamic backups to.
	 *
	 * @since 4.1.3
	 *
	 * @var string
	 */
	protected $optionsName = 'aioseo_dynamic_settings_backup';

	/**
	 * The dynamic backup.
	 *
	 * @since 4.1.3
	 *
	 * @var array
	 */
	protected $backup = [];

	/**
	 * Whether the backup should be updated.
	 *
	 * @since 4.1.3
	 *
	 * @var boolean
	 */
	protected $shouldBackup = false;

	/**
	 * The option defaults.
	 *
	 * @since 4.1.3
	 *
	 * @var array
	 */
	protected $defaultOptions = [];

	/**
	 * The public post types.
	 *
	 * @since 4.1.5
	 *
	 * @var array
	 */
	protected $postTypes = [];

	/**
	 * The public taxonomies.
	 *
	 * @since 4.1.5
	 *
	 * @var array
	 */
	protected $taxonomies = [];

	/**
	 * The public archives.
	 *
	 * @since 4.1.5
	 *
	 * @var array
	 */
	protected $archives = [];

	/**
	 * Class constructor.
	 *
	 * @since 4.1.3
	 */
	public function __construct() {
		add_action( 'wp_loaded', [ $this, 'init' ], 5000 );
		add_action( 'shutdown', [ $this, 'updateBackup' ] );
	}

	/**
	 * Updates the backup after restoring options.
	 *
	 * @since 4.1.3
	 *
	 * @return void
	 */
	public function updateBackup() {
		if ( $this->shouldBackup ) {
			$this->shouldBackup = false;
			$backup = aioseo()->dynamicOptions->convertOptionsToValues( $this->backup, 'value' );
			update_option( $this->optionsName, wp_json_encode( $backup ), 'no' );
		}
	}

	/**
	 * Checks whether data from the backup has to be restored.
	 *
	 * @since 4.1.3
	 *
	 * @return void
	 */
	public function init() {
		$this->postTypes  = wp_list_pluck( aioseo()->helpers->getPublicPostTypes( false, false, true ), 'name' );
		$this->taxonomies = wp_list_pluck( aioseo()->helpers->getPublicTaxonomies( false, true ), 'name' );
		$this->archives   = wp_list_pluck( aioseo()->helpers->getPublicPostTypes( false, true, true ), 'name' );

		$backup = json_decode( get_option( $this->optionsName ), true );
		if ( empty( $backup ) ) {
			update_option( $this->optionsName, '{}', 'no' );

			return;
		}

		$this->backup         = $backup;
		$this->defaultOptions = aioseo()->dynamicOptions->getDefaults();

		$this->restorePostTypes();
		$this->restoreTaxonomies();
		$this->restoreArchives();
	}

	/**
	 * Restores the dynamic Post Types options.
	 *
	 * @since 4.1.3
	 *
	 * @return void
	 */
	protected function restorePostTypes() {
		foreach ( $this->postTypes as $postType ) {
			// Restore the post types for Search Appearance.
			if ( ! empty( $this->backup['postTypes'][ $postType ]['searchAppearance'] ) ) {
				$this->restoreOptions( $this->backup['postTypes'][ $postType ]['searchAppearance'], [ 'searchAppearance', 'postTypes', $postType ] );
				unset( $this->backup['postTypes'][ $postType ]['searchAppearance'] );
				$this->shouldBackup = true;
			}

			// Restore the post types for Social Networks.
			if ( ! empty( $this->backup['postTypes'][ $postType ]['social']['facebook'] ) ) {
				$this->restoreOptions( $this->backup['postTypes'][ $postType ]['social']['facebook'], [ 'social', 'facebook', 'general', 'postTypes', $postType ] );
				unset( $this->backup['postTypes'][ $postType ]['social']['facebook'] );
				$this->shouldBackup = true;
			}
		}
	}

	/**
	 * Restores the dynamic Taxonomies options.
	 *
	 * @since 4.1.3
	 *
	 * @return void
	 */
	protected function restoreTaxonomies() {
		foreach ( $this->taxonomies as $taxonomy ) {
			// Restore the taxonomies for Search Appearance.
			if ( ! empty( $this->backup['taxonomies'][ $taxonomy ]['searchAppearance'] ) ) {
				$this->restoreOptions( $this->backup['taxonomies'][ $taxonomy ]['searchAppearance'], [ 'searchAppearance', 'taxonomies', $taxonomy ] );
				unset( $this->backup['taxonomies'][ $taxonomy ]['searchAppearance'] );
				$this->shouldBackup = true;
			}

			// Restore the taxonomies for Social Networks.
			if ( ! empty( $this->backup['taxonomies'][ $taxonomy ]['social']['facebook'] ) ) {
				$this->restoreOptions( $this->backup['taxonomies'][ $taxonomy ]['social']['facebook'], [ 'social', 'facebook', 'general', 'taxonomies', $taxonomy ] );
				unset( $this->backup['taxonomies'][ $taxonomy ]['social']['facebook'] );
				$this->shouldBackup = true;
			}
		}
	}

	/**
	 * Restores the dynamic Archives options.
	 *
	 * @since 4.1.3
	 *
	 * @return void
	 */
	protected function restoreArchives() {
		foreach ( $this->archives as $postType ) {
			// Restore the archives for Search Appearance.
			if ( ! empty( $this->backup['archives'][ $postType ]['searchAppearance'] ) ) {
				$this->restoreOptions( $this->backup['archives'][ $postType ]['searchAppearance'], [ 'searchAppearance', 'archives', $postType ] );
				unset( $this->backup['archives'][ $postType ]['searchAppearance'] );
				$this->shouldBackup = true;
			}
		}
	}

	/**
	 * Restores the backuped options.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $backupOptions The options to be restored.
	 * @param  array $groups        The group that the option should be restored to.
	 * @return void
	 */
	protected function restoreOptions( $backupOptions, $groups ) {
		$defaultOptions = $this->defaultOptions;
		foreach ( $groups as $group ) {
			if ( ! isset( $defaultOptions[ $group ] ) ) {
				return;
			}

			$defaultOptions = $defaultOptions[ $group ];
		}

		$dynamicOptions = aioseo()->dynamicOptions->noConflict();
		foreach ( $backupOptions as $setting => $value ) {
			// Check if the option exists before proceeding. If not, it might be a group.
			$type = $defaultOptions[ $setting ]['type'] ?? '';
			if (
				! $type &&
				is_array( $value ) &&
				aioseo()->helpers->isArrayAssociative( $value )
			) {
				$nextGroups = array_merge( $groups, [ $setting ] );

				$this->restoreOptions( $backupOptions[ $setting ], $nextGroups );

				continue;
			}

			// If we still can't find the option, it might be a group.
			if ( ! $type ) {
				continue;
			}

			foreach ( $groups as $group ) {
				$dynamicOptions = $dynamicOptions->$group;
			}

			$dynamicOptions->$setting = $value;
		}
	}

	/**
	 * Maybe backup the options if it has disappeared.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $newOptions An array of options to check.
	 * @return void
	 */
	public function maybeBackup( $newOptions ) {
		$this->maybeBackupPostType( $newOptions );
		$this->maybeBackupTaxonomy( $newOptions );
		$this->maybeBackupArchives( $newOptions );
	}

	/**
	 * Maybe backup the Post Types.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $newOptions An array of options to check.
	 * @return void
	 */
	protected function maybeBackupPostType( $newOptions ) {
		// Maybe backup the post types for Search Appearance.
		foreach ( $newOptions['searchAppearance']['postTypes'] as $dynamicPostTypeName => $dynamicPostTypeSettings ) {
			$found = in_array( $dynamicPostTypeName, $this->postTypes, true );
			if ( ! $found ) {
				$this->backup['postTypes'][ $dynamicPostTypeName ]['searchAppearance'] = $dynamicPostTypeSettings;
				$this->shouldBackup = true;
			}
		}

		// Maybe backup the post types for Social Networks.
		foreach ( $newOptions['social']['facebook']['general']['postTypes'] as $dynamicPostTypeName => $dynamicPostTypeSettings ) {
			$found = in_array( $dynamicPostTypeName, $this->postTypes, true );
			if ( ! $found ) {
				$this->backup['postTypes'][ $dynamicPostTypeName ]['social']['facebook'] = $dynamicPostTypeSettings;
				$this->shouldBackup = true;
			}
		}
	}

	/**
	 * Maybe backup the Taxonomies.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $newOptions An array of options to check.
	 * @return void
	 */
	protected function maybeBackupTaxonomy( $newOptions ) {
		// Maybe backup the taxonomies for Search Appearance.
		foreach ( $newOptions['searchAppearance']['taxonomies'] as $dynamicTaxonomyName => $dynamicTaxonomySettings ) {
			$found = in_array( $dynamicTaxonomyName, $this->taxonomies, true );
			if ( ! $found ) {
				$this->backup['taxonomies'][ $dynamicTaxonomyName ]['searchAppearance'] = $dynamicTaxonomySettings;
				$this->shouldBackup = true;
			}
		}
	}

	/**
	 * Maybe backup the Archives.
	 *
	 * @since 4.1.3
	 *
	 * @param  array $newOptions An array of options to check.
	 * @return void
	 */
	protected function maybeBackupArchives( $newOptions ) {
		// Maybe backup the archives for Search Appearance.
		foreach ( $newOptions['searchAppearance']['archives'] as $archiveName => $archiveSettings ) {
			$found = in_array( $archiveName, $this->archives, true );
			if ( ! $found ) {
				$this->backup['archives'][ $archiveName ]['searchAppearance'] = $archiveSettings;
				$this->shouldBackup = true;
			}
		}
	}
}DynamicOptions.php000064400000026113151542512770010231 0ustar00<?php
namespace AIOSEO\Plugin\Common\Options;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Traits;

/**
 * Handles the dynamic options.
 *
 * @since 4.1.4
 */
class DynamicOptions {
	use Traits\Options;

	/**
	 * The default options.
	 *
	 * @since 4.1.4
	 *
	 * @var array
	 */
	protected $defaults = [
		// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
		'sitemap'          => [
			'priority' => [
				'postTypes'  => [],
				'taxonomies' => []
			]
		],
		'social'           => [
			'facebook' => [
				'general' => [
					'postTypes' => []
				]
			]
		],
		'searchAppearance' => [
			'postTypes'  => [],
			'taxonomies' => [],
			'archives'   => []
		]
		// phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
	];

	/**
	 * Class constructor.
	 *
	 * @since 4.1.4
	 *
	 * @param string $optionsName The options name.
	 */
	public function __construct( $optionsName = 'aioseo_options_dynamic' ) {
		$this->optionsName = $optionsName;

		// Load defaults in case this is a complete fresh install.
		$this->init();

		add_action( 'shutdown', [ $this, 'save' ] );
	}

	/**
	 * Initializes the options.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	protected function init() {
		$this->addDynamicDefaults();
		$this->setDbOptions();
	}

	/**
	 * Sets the DB options.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	protected function setDbOptions() {
		$dbOptions = $this->getDbOptions( $this->optionsName );

		// Refactor options.
		$this->defaultsMerged = array_replace_recursive( $this->defaults, $this->defaultsMerged );

		$dbOptions = array_replace_recursive(
			$this->defaultsMerged,
			$this->addValueToValuesArray( $this->defaultsMerged, $dbOptions )
		);

		aioseo()->core->optionsCache->setOptions( $this->optionsName, $dbOptions );

		// Get the localized options.
		$dbOptionsLocalized = get_option( $this->optionsName . '_localized' );
		if ( empty( $dbOptionsLocalized ) ) {
			$dbOptionsLocalized = [];
		}
		$this->localized = $dbOptionsLocalized;
	}

	/**
	 * Sanitizes, then saves the options to the database.
	 *
	 * @since 4.1.4
	 *
	 * @param  array $options An array of options to sanitize, then save.
	 * @return void
	 */
	public function sanitizeAndSave( $options ) {
		if ( ! is_array( $options ) ) {
			return;
		}

		$cachedOptions = aioseo()->core->optionsCache->getOptions( $this->optionsName );

		aioseo()->dynamicBackup->maybeBackup( $cachedOptions );

		// First, recursively replace the new options into the cached state.
		// It's important we use the helper method since we want to replace populated arrays with empty ones if needed (when a setting was cleared out).
		$dbOptions = aioseo()->helpers->arrayReplaceRecursive(
			$cachedOptions,
			$this->addValueToValuesArray( $cachedOptions, $options, [], true )
		);

		// Now, we must also intersect both arrays to delete any individual keys that were unset.
		// We must do this because, while arrayReplaceRecursive will update the values for keys or empty them out,
		// it will keys that aren't present in the replacement array unaffected in the target array.
		$dbOptions = aioseo()->helpers->arrayIntersectRecursive(
			$dbOptions,
			$this->addValueToValuesArray( $cachedOptions, $options, [], true ),
			'value'
		);

		// Update the cache state.
		aioseo()->core->optionsCache->setOptions( $this->optionsName, $dbOptions );

		// Update localized options.
		update_option( $this->optionsName . '_localized', $this->localized );

		// Finally, save the new values to the DB.
		$this->save( true );
	}

	/**
	 * Adds some defaults that are dynamically generated.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	public function addDynamicDefaults() {
		$this->addDynamicPostTypeDefaults();
		$this->addDynamicTaxonomyDefaults();
		$this->addDynamicArchiveDefaults();
	}

	/**
	 * Adds the dynamic defaults for the public post types.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	protected function addDynamicPostTypeDefaults() {
		$postTypes = aioseo()->helpers->getPublicPostTypes( false, false, false, [ 'include' => [ 'buddypress' ] ] );
		foreach ( $postTypes as $postType ) {
			if ( 'type' === $postType['name'] ) {
				$postType['name'] = '_aioseo_type';
			}

			$defaultTitle = '#post_title #separator_sa #site_title';
			if ( ! empty( $postType['defaultTitle'] ) ) {
				$defaultTitle = $postType['defaultTitle'];
			}
			$defaultDescription = ! empty( $postType['supports']['excerpt'] ) ? '#post_excerpt' : '#post_content';
			if ( ! empty( $postType['defaultDescription'] ) ) {
				$defaultDescription = $postType['defaultDescription'];
			}
			$defaultSchemaType  = 'WebPage';
			$defaultWebPageType = 'WebPage';
			$defaultArticleType = 'BlogPosting';

			switch ( $postType['name'] ) {
				case 'post':
					$defaultSchemaType = 'Article';
					break;
				case 'attachment':
					$defaultDescription = '#attachment_caption';
					$defaultSchemaType  = 'ItemPage';
					$defaultWebPageType = 'ItemPage';
					break;
				case 'product':
					$defaultSchemaType  = 'WebPage';
					$defaultWebPageType = 'ItemPage';
					break;
				case 'news':
					$defaultArticleType = 'NewsArticle';
					break;
				case 'web-story':
					$defaultWebPageType = 'WebPage';
					$defaultSchemaType  = 'WebPage';
					break;
				default:
					break;
			}

			$defaultOptions = array_replace_recursive(
				$this->getDefaultSearchAppearanceOptions(),
				[
					'title'           => [
						'type'      => 'string',
						'localized' => true,
						'default'   => $defaultTitle
					],
					'metaDescription' => [
						'type'      => 'string',
						'localized' => true,
						'default'   => $defaultDescription
					],
					'schemaType'      => [
						'type'    => 'string',
						'default' => $defaultSchemaType
					],
					'webPageType'     => [
						'type'    => 'string',
						'default' => $defaultWebPageType
					],
					'articleType'     => [
						'type'    => 'string',
						'default' => $defaultArticleType
					],
					'customFields'    => [ 'type' => 'html' ],
					'advanced'        => [
						'bulkEditing' => [
							'type'    => 'string',
							'default' => 'enabled'
						]
					]
				]
			);

			if ( 'attachment' === $postType['name'] ) {
				$defaultOptions['redirectAttachmentUrls'] = [
					'type'    => 'string',
					'default' => 'attachment'
				];
			}

			$this->defaults['searchAppearance']['postTypes'][ $postType['name'] ] = $defaultOptions;
			$this->setDynamicSocialOptions( 'postTypes', $postType['name'] );
			$this->setDynamicSitemapOptions( 'postTypes', $postType['name'] );
		}
	}

	/**
	 * Adds the dynamic defaults for the public taxonomies.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	protected function addDynamicTaxonomyDefaults() {
		$taxonomies = aioseo()->helpers->getPublicTaxonomies();
		foreach ( $taxonomies as $taxonomy ) {
			if ( 'type' === $taxonomy['name'] ) {
				$taxonomy['name'] = '_aioseo_type';
			}

			$defaultOptions = array_replace_recursive(
				$this->getDefaultSearchAppearanceOptions(),
				[
					'title'           => [
						'type'      => 'string',
						'localized' => true,
						'default'   => '#taxonomy_title #separator_sa #site_title'
					],
					'metaDescription' => [
						'type'      => 'string',
						'localized' => true,
						'default'   => '#taxonomy_description'
					],
				]
			);

			$this->setDynamicSitemapOptions( 'taxonomies', $taxonomy['name'] );

			$this->defaults['searchAppearance']['taxonomies'][ $taxonomy['name'] ] = $defaultOptions;
		}
	}

	/**
	 * Adds the dynamic defaults for the archive pages.
	 *
	 * @since 4.1.4
	 *
	 * @return void
	 */
	protected function addDynamicArchiveDefaults() {
		$postTypes = aioseo()->helpers->getPublicPostTypes( false, true, false, [ 'include' => [ 'buddypress' ] ] );
		foreach ( $postTypes as $postType ) {
			if ( 'type' === $postType['name'] ) {
				$postType['name'] = '_aioseo_type';
			}

			$defaultOptions = array_replace_recursive(
				$this->getDefaultSearchAppearanceOptions(),
				[
					'title'           => [
						'type'      => 'string',
						'localized' => true,
						'default'   => '#archive_title #separator_sa #site_title'
					],
					'metaDescription' => [
						'type'      => 'string',
						'localized' => true,
						'default'   => ''
					],
					'customFields'    => [ 'type' => 'html' ],
					'advanced'        => [
						'keywords' => [
							'type'      => 'string',
							'localized' => true
						]
					]
				]
			);

			$this->defaults['searchAppearance']['archives'][ $postType['name'] ] = $defaultOptions;
		}
	}

	/**
	 * Returns the search appearance options for dynamic objects.
	 *
	 * @since 4.1.4
	 *
	 * @return array The default options.
	 */
	protected function getDefaultSearchAppearanceOptions() {
		return [ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
			'show'     => [ 'type' => 'boolean', 'default' => true ],
			'advanced' => [
				'robotsMeta'                => [
					'default'         => [ 'type' => 'boolean', 'default' => true ],
					'noindex'         => [ 'type' => 'boolean', 'default' => false ],
					'nofollow'        => [ 'type' => 'boolean', 'default' => false ],
					'noarchive'       => [ 'type' => 'boolean', 'default' => false ],
					'noimageindex'    => [ 'type' => 'boolean', 'default' => false ],
					'notranslate'     => [ 'type' => 'boolean', 'default' => false ],
					'nosnippet'       => [ 'type' => 'boolean', 'default' => false ],
					'noodp'           => [ 'type' => 'boolean', 'default' => false ],
					'maxSnippet'      => [ 'type' => 'number', 'default' => -1 ],
					'maxVideoPreview' => [ 'type' => 'number', 'default' => -1 ],
					'maxImagePreview' => [ 'type' => 'string', 'default' => 'large' ]
				],
				'showDateInGooglePreview'   => [ 'type' => 'boolean', 'default' => true ],
				'showPostThumbnailInSearch' => [ 'type' => 'boolean', 'default' => true ],
				'showMetaBox'               => [ 'type' => 'boolean', 'default' => true ]
			]
		]; // phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
	}

	/**
	 * Sets the dynamic social settings for a given post type or taxonomy.
	 *
	 * @since 4.1.4
	 *
	 * @param  string $objectType Whether the object belongs to the dynamic "postTypes" or "taxonomies".
	 * @param  string $objectName The object name.
	 * @return void
	 */
	protected function setDynamicSocialOptions( $objectType, $objectName ) {
		$defaultOptions = [
			'objectType' => [
				'type'    => 'string',
				'default' => 'article'
			]
		];

		$this->defaults['social']['facebook']['general'][ $objectType ][ $objectName ] = $defaultOptions;
	}

	/**
	 * Sets the dynamic sitemap settings for a given post type or taxonomy.
	 *
	 * @since 4.1.4
	 *
	 * @param  string $objectType Whether the object belongs to the dynamic "postTypes" or "taxonomies".
	 * @param  string $objectName The object name.
	 * @return void
	 */
	protected function setDynamicSitemapOptions( $objectType, $objectName ) {
		$this->defaults['sitemap']['priority'][ $objectType ][ $objectName ] = [
			'priority'  => [
				'type'    => 'string',
				'default' => '{"label":"default","value":"default"}'
			],
			'frequency' => [
				'type'    => 'string',
				'default' => '{"label":"default","value":"default"}'
			]
		];
	}
}InternalNetworkOptions.php000064400000001622151542512770011771 0ustar00<?php
namespace AIOSEO\Plugin\Common\Options;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Traits;
use AIOSEO\Plugin\Common\Utils;

/**
 * Class that holds all internal network options for AIOSEO.
 *
 * @since 4.2.5
 */
class InternalNetworkOptions {
	use Traits\Options;
	use Traits\NetworkOptions;

	/**
	 * Holds the helpers class.
	 *
	 * @since 4.2.5
	 *
	 * @var Utils\Helpers
	 */
	protected $helpers;

	/**
	 * All the default options.
	 *
	 * @since 4.2.5
	 *
	 * @var array
	 */
	protected $defaults = [];

	/**
	 * The Construct method.
	 *
	 * @since 4.2.5
	 *
	 * @param string $optionsName The options name.
	 */
	public function __construct( $optionsName = 'aioseo_options_network_internal' ) {
		$this->helpers     = new Utils\Helpers();
		$this->optionsName = $optionsName;

		$this->init();

		add_action( 'shutdown', [ $this, 'save' ] );
	}
}NetworkOptions.php000064400000003622151542512770010276 0ustar00<?php
namespace AIOSEO\Plugin\Common\Options;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AIOSEO\Plugin\Common\Traits;
use AIOSEO\Plugin\Common\Utils;

/**
 * Class that holds all network options for AIOSEO.
 *
 * @since 4.2.5
 */
class NetworkOptions {
	use Traits\Options;
	use Traits\NetworkOptions;

	/**
	 * Holds the helpers class.
	 *
	 * @since 4.2.5
	 *
	 * @var Utils\Helpers
	 */
	protected $helpers;

	/**
	 * All the default options.
	 *
	 * @since 4.2.5
	 *
	 * @var array
	 */
	protected $defaults = [
		// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
		'searchAppearance' => [
			'advanced' => [
				'unwantedBots'  => [
					'all'      => [ 'type' => 'boolean', 'default' => false ],
					'settings' => [
						'googleAdsBot'             => [ 'type' => 'boolean', 'default' => false ],
						'openAiGptBot'             => [ 'type' => 'boolean', 'default' => false ],
						'commonCrawlCcBot'         => [ 'type' => 'boolean', 'default' => false ],
						'googleGeminiVertexAiBots' => [ 'type' => 'boolean', 'default' => false ]
					]
				],
				'searchCleanup' => [
					'settings' => [
						'preventCrawling' => [ 'type' => 'boolean', 'default' => false ]
					]
				]
			]
		],
		'tools'            => [
			'robots' => [
				'enable'         => [ 'type' => 'boolean', 'default' => false ],
				'rules'          => [ 'type' => 'array', 'default' => [] ],
				'robotsDetected' => [ 'type' => 'boolean', 'default' => true ],
			]
		]
		// phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
	];

	/**
	 * The Construct method.
	 *
	 * @since 4.2.5
	 *
	 * @param string $optionsName The options name.
	 */
	public function __construct( $optionsName = 'aioseo_options_network' ) {
		$this->helpers     = new Utils\Helpers();
		$this->optionsName = $optionsName;

		$this->init();

		add_action( 'shutdown', [ $this, 'save' ] );
	}
}AccountState.php000064400000005134151545632130007662 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;

/**
 * Class AccountState
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
abstract class AccountState implements Service, OptionsAwareInterface {

	use OptionsAwareTrait;

	/** @var int Status value for a pending merchant account creation step */
	public const STEP_PENDING = 0;

	/** @var int Status value for a completed merchant account creation step */
	public const STEP_DONE = 1;

	/** @var int Status value for an unsuccessful merchant account creation step */
	public const STEP_ERROR = - 1;

	/**
	 * Return the option name.
	 *
	 * @return string
	 */
	abstract protected function option_name(): string;

	/**
	 * Return a list of account creation steps.
	 *
	 * @return string[]
	 */
	abstract protected function account_creation_steps(): array;

	/**
	 * Retrieve or initialize the account state option.
	 *
	 * @param bool $initialize_if_not_found True to initialize the array of steps.
	 *
	 * @return array The account creation steps and statuses.
	 */
	public function get( bool $initialize_if_not_found = true ): array {
		$state = $this->options->get( $this->option_name(), [] );
		if ( empty( $state ) && $initialize_if_not_found ) {
			$state = [];
			foreach ( $this->account_creation_steps() as $step ) {
				$state[ $step ] = [
					'status'  => self::STEP_PENDING,
					'message' => '',
					'data'    => [],
				];
			}
			$this->update( $state );
		}

		return $state;
	}

	/**
	 * Update the account state option.
	 *
	 * @param array $state
	 */
	public function update( array $state ) {
		$this->options->update( $this->option_name(), $state );
	}

	/**
	 * Mark a step as completed.
	 *
	 * @param string $step Name of the completed step.
	 */
	public function complete_step( string $step ) {
		$state = $this->get( false );

		if ( isset( $state[ $step ] ) ) {
			$state[ $step ]['status'] = self::STEP_DONE;
			$this->update( $state );
		}
	}

	/**
	 * Returns the name of the last incompleted step.
	 *
	 * @return string
	 */
	public function last_incomplete_step(): string {
		$incomplete = '';
		foreach ( $this->get( false ) as $name => $step ) {
			if ( ! isset( $step['status'] ) || self::STEP_DONE !== $step['status'] ) {
				$incomplete = $name;
				break;
			}
		}

		return $incomplete;
	}

	/**
	 * Returns any data from a specific step.
	 *
	 * @param string $step Step name.
	 * @return array
	 */
	public function get_step_data( string $step ): array {
		return $this->get( false )[ $step ]['data'] ?? [];
	}
}
AdsAccountState.php000064400000001164151545632130010311 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

/**
 * Class AdsAccountState
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
class AdsAccountState extends AccountState {

	/**
	 * Return the option name.
	 *
	 * @return string
	 */
	protected function option_name(): string {
		return OptionsInterface::ADS_ACCOUNT_STATE;
	}

	/**
	 * Return a list of account creation steps.
	 *
	 * @return string[]
	 */
	protected function account_creation_steps(): array {
		return [ 'set_id', 'account_access', 'conversion_action', 'link_merchant', 'billing' ];
	}
}
AdsSetupCompleted.php000064400000002067151545632130010654 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;

defined( 'ABSPATH' ) || exit;

/**
 * Class AdsSetupCompleted
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
class AdsSetupCompleted implements OptionsAwareInterface, Registerable, Service {

	use OptionsAwareTrait;

	protected const OPTION = OptionsInterface::ADS_SETUP_COMPLETED_AT;

	/**
	 * Register a service.
	 *
	 * TODO: call `do_action( 'woocommerce_gla_ads_settings_sync' );` when the initial Google Ads account,
	 *       paid campaign, and billing setup is completed.
	 */
	public function register(): void {
		add_action(
			'woocommerce_gla_ads_setup_completed',
			function () {
				$this->set_completed_timestamp();
			}
		);
	}

	/**
	 * Set the timestamp when setup was completed.
	 */
	protected function set_completed_timestamp() {
		$this->options->update( self::OPTION, time() );
	}
}
MerchantAccountState.php000064400000003245151545632130011345 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\SiteVerification;

/**
 * Class MerchantAccountState
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
class MerchantAccountState extends AccountState {

	/** @var int The number of seconds of delay to enforce between site verification and site claim. */
	public const MC_DELAY_AFTER_CREATE = 5;

	/**
	 * Return the option name.
	 *
	 * @return string
	 */
	protected function option_name(): string {
		return OptionsInterface::MERCHANT_ACCOUNT_STATE;
	}

	/**
	 * Return a list of account creation steps.
	 *
	 * @return string[]
	 */
	protected function account_creation_steps(): array {
		return [ 'set_id', 'verify', 'link', 'claim', 'link_ads' ];
	}

	/**
	 * Determine whether the site has already been verified.
	 *
	 * @return bool True if the site is marked as verified.
	 */
	public function is_site_verified(): bool {
		$current_options = $this->options->get( OptionsInterface::SITE_VERIFICATION );

		return ! empty( $current_options['verified'] ) && SiteVerification::VERIFICATION_STATUS_VERIFIED === $current_options['verified'];
	}

	/**
	 * Calculate the number of seconds to wait after creating a sub-account and
	 * before operating on the new sub-account (MCA link and website claim).
	 *
	 * @return int
	 */
	public function get_seconds_to_wait_after_created(): int {
		$state = $this->get( false );

		$created_timestamp = $state['set_id']['data']['created_timestamp'] ?? 0;
		$seconds_elapsed   = time() - $created_timestamp;

		return max( 0, self::MC_DELAY_AFTER_CREATE - $seconds_elapsed );
	}
}
MerchantSetupCompleted.php000064400000002220151545632130011675 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;

defined( 'ABSPATH' ) || exit;

/**
 * Class MerchantSetupCompleted
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
class MerchantSetupCompleted implements OptionsAwareInterface, Registerable, Service {

	use OptionsAwareTrait;

	protected const OPTION = OptionsInterface::MC_SETUP_COMPLETED_AT;

	/**
	 * Register a service.
	 */
	public function register(): void {
		add_action(
			'woocommerce_gla_mc_settings_sync',
			function () {
				$this->set_contact_information_setup();
				$this->set_completed_timestamp();
			}
		);
	}

	/**
	 * Mark the contact information as setup.
	 *
	 * @since 1.4.0
	 */
	protected function set_contact_information_setup() {
		$this->options->update( OptionsInterface::CONTACT_INFO_SETUP, true );
	}

	/**
	 * Set the timestamp when setup was completed.
	 */
	protected function set_completed_timestamp() {
		$this->options->update( self::OPTION, time() );
	}
}
OptionsAwareInterface.php000064400000000700151545632130011513 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

defined( 'ABSPATH' ) || exit;

/**
 * Interface OptionsAwareInterface
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
interface OptionsAwareInterface {

	/**
	 * Set the Options object.
	 *
	 * @param OptionsInterface $options
	 *
	 * @return void
	 */
	public function set_options_object( OptionsInterface $options ): void;
}
OptionsAwareTrait.php000064400000001022151545632130010674 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

defined( 'ABSPATH' ) || exit;

/**
 * Trait OptionsAwareTrait
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
trait OptionsAwareTrait {

	/**
	 * The Options object.
	 *
	 * @var OptionsInterface
	 */
	protected $options;

	/**
	 * Set the Options object.
	 *
	 * @param OptionsInterface $options
	 */
	public function set_options_object( OptionsInterface $options ): void {
		$this->options = $options;
	}
}
OptionsInterface.php000064400000014726151545632130010550 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\Value\PositiveInteger;

/**
 * Interface OptionsInterface
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
interface OptionsInterface {

	public const ADS_ACCOUNT_CURRENCY                      = 'ads_account_currency';
	public const ADS_ACCOUNT_OCID                          = 'ads_account_ocid';
	public const ADS_ACCOUNT_STATE                         = 'ads_account_state';
	public const ADS_BILLING_URL                           = 'ads_billing_url';
	public const ADS_ID                                    = 'ads_id';
	public const ADS_CONVERSION_ACTION                     = 'ads_conversion_action';
	public const ADS_SETUP_COMPLETED_AT                    = 'ads_setup_completed_at';
	public const CAMPAIGN_CONVERT_STATUS                   = 'campaign_convert_status';
	public const CLAIMED_URL_HASH                          = 'claimed_url_hash';
	public const CONTACT_INFO_SETUP                        = 'contact_info_setup';
	public const DELAYED_ACTIVATE                          = 'delayed_activate';
	public const DB_VERSION                                = 'db_version';
	public const FILE_VERSION                              = 'file_version';
	public const GOOGLE_CONNECTED                          = 'google_connected';
	public const GOOGLE_WPCOM_AUTH_NONCE                   = 'google_wpcom_auth_nonce';
	public const INSTALL_TIMESTAMP                         = 'install_timestamp';
	public const INSTALL_VERSION                           = 'install_version';
	public const JETPACK_CONNECTED                         = 'jetpack_connected';
	public const MC_SETUP_COMPLETED_AT                     = 'mc_setup_completed_at';
	public const MERCHANT_ACCOUNT_STATE                    = 'merchant_account_state';
	public const MERCHANT_CENTER                           = 'merchant_center';
	public const MERCHANT_ID                               = 'merchant_id';
	public const REDIRECT_TO_ONBOARDING                    = 'redirect_to_onboarding';
	public const SHIPPING_RATES                            = 'shipping_rates';
	public const SHIPPING_TIMES                            = 'shipping_times';
	public const SITE_VERIFICATION                         = 'site_verification';
	public const SYNCABLE_PRODUCTS_COUNT                   = 'syncable_products_count';
	public const SYNCABLE_PRODUCTS_COUNT_INTERMEDIATE_DATA = 'syncable_products_count_intermediate_data';
	public const PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA  = 'product_statuses_count_intermediate_data';
	public const TARGET_AUDIENCE                           = 'target_audience';
	public const TOURS                                     = 'tours';
	public const UPDATE_ALL_PRODUCTS_LAST_SYNC             = 'update_all_products_last_sync';
	public const WP_TOS_ACCEPTED                           = 'wp_tos_accepted';
	public const WPCOM_REST_API_STATUS                     = 'wpcom_rest_api_status';
	public const GTIN_MIGRATION_STATUS                     = 'gtin_migration_status';

	public const VALID_OPTIONS = [
		self::ADS_ACCOUNT_CURRENCY                      => true,
		self::ADS_ACCOUNT_OCID                          => true,
		self::ADS_ACCOUNT_STATE                         => true,
		self::ADS_BILLING_URL                           => true,
		self::ADS_ID                                    => true,
		self::ADS_CONVERSION_ACTION                     => true,
		self::ADS_SETUP_COMPLETED_AT                    => true,
		self::CAMPAIGN_CONVERT_STATUS                   => true,
		self::CLAIMED_URL_HASH                          => true,
		self::CONTACT_INFO_SETUP                        => true,
		self::DB_VERSION                                => true,
		self::FILE_VERSION                              => true,
		self::GOOGLE_CONNECTED                          => true,
		self::INSTALL_TIMESTAMP                         => true,
		self::INSTALL_VERSION                           => true,
		self::JETPACK_CONNECTED                         => true,
		self::MC_SETUP_COMPLETED_AT                     => true,
		self::MERCHANT_ACCOUNT_STATE                    => true,
		self::MERCHANT_CENTER                           => true,
		self::MERCHANT_ID                               => true,
		self::DELAYED_ACTIVATE                          => true,
		self::SHIPPING_RATES                            => true,
		self::SHIPPING_TIMES                            => true,
		self::REDIRECT_TO_ONBOARDING                    => true,
		self::SITE_VERIFICATION                         => true,
		self::SYNCABLE_PRODUCTS_COUNT                   => true,
		self::SYNCABLE_PRODUCTS_COUNT_INTERMEDIATE_DATA => true,
		self::PRODUCT_STATUSES_COUNT_INTERMEDIATE_DATA  => true,
		self::TARGET_AUDIENCE                           => true,
		self::TOURS                                     => true,
		self::UPDATE_ALL_PRODUCTS_LAST_SYNC             => true,
		self::WP_TOS_ACCEPTED                           => true,
		self::WPCOM_REST_API_STATUS                     => true,
		self::GOOGLE_WPCOM_AUTH_NONCE                   => true,
		self::GTIN_MIGRATION_STATUS                     => true,
	];

	public const OPTION_TYPES = [
		self::ADS_ID      => PositiveInteger::class,
		self::MERCHANT_ID => PositiveInteger::class,
	];

	/**
	 * Get an option.
	 *
	 * @param string $name          The option name.
	 * @param mixed  $default_value A default value for the option.
	 *
	 * @return mixed
	 */
	public function get( string $name, $default_value = null );

	/**
	 * Add an option.
	 *
	 * @param string $name  The option name.
	 * @param mixed  $value The option value.
	 *
	 * @return bool
	 */
	public function add( string $name, $value ): bool;

	/**
	 * Update an option.
	 *
	 * @param string $name  The option name.
	 * @param mixed  $value The option value.
	 *
	 * @return bool
	 */
	public function update( string $name, $value ): bool;

	/**
	 * Delete an option.
	 *
	 * @param string $name The option name.
	 *
	 * @return bool
	 */
	public function delete( string $name ): bool;

	/**
	 * Helper function to retrieve the Merchant Account ID.
	 *
	 * @return int
	 */
	public function get_merchant_id(): int;

	/**
	 * Returns all available option keys.
	 *
	 * @return array
	 */
	public static function get_all_option_keys(): array;

	/**
	 * Helper function to retrieve the Ads Account ID.
	 *
	 * @return int
	 */
	public function get_ads_id(): int;

	/**
	 * If the WPCOM API is authorized
	 *
	 * @return bool
	 */
	public function is_wpcom_api_authorized(): bool;
}
Transients.php000064400000005615151545632130007423 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

use Automattic\WooCommerce\GoogleListingsAndAds\Exception\InvalidOption;
use Automattic\WooCommerce\GoogleListingsAndAds\Exception\InvalidValue;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;
use Automattic\WooCommerce\GoogleListingsAndAds\PluginHelper;

defined( 'ABSPATH' ) || exit;

/**
 * Class Transients
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure
 */
final class Transients implements TransientsInterface, Service {

	use PluginHelper;

	/**
	 * Array of transients that we have loaded.
	 *
	 * @var array
	 */
	protected $transients = [];

	/**
	 * Get a transient.
	 *
	 * @param string $name          The transient name.
	 * @param mixed  $default_value A default value for the transient.
	 *
	 * @return mixed
	 */
	public function get( string $name, $default_value = null ) {
		$this->validate_transient_key( $name );

		if ( ! array_key_exists( $name, $this->transients ) ) {
			$value = get_transient( $this->prefix_name( $name ) );

			if ( false === $value ) {
				$value = $default_value;
			}

			$this->transients[ $name ] = $value;
		}

		return $this->transients[ $name ];
	}

	/**
	 * Add or update a transient.
	 *
	 * @param string $name  The transient name.
	 * @param mixed  $value The transient value.
	 * @param int    $expiration Time until expiration in seconds.
	 *
	 * @return bool
	 *
	 * @throws InvalidValue If a boolean $value is provided.
	 */
	public function set( string $name, $value, int $expiration = 0 ): bool {
		if ( is_bool( $value ) ) {
			throw new InvalidValue( 'Transients cannot have boolean values.' );
		}

		$this->validate_transient_key( $name );
		$this->transients[ $name ] = $value;
		return boolval( set_transient( $this->prefix_name( $name ), $value, $expiration ) );
	}

	/**
	 * Delete a transient.
	 *
	 * @param string $name The transient name.
	 *
	 * @return bool
	 */
	public function delete( string $name ): bool {
		$this->validate_transient_key( $name );
		unset( $this->transients[ $name ] );

		return boolval( delete_transient( $this->prefix_name( $name ) ) );
	}

	/**
	 * Returns all available transient keys.
	 *
	 * @return array
	 *
	 * @since 1.3.0
	 */
	public static function get_all_transient_keys(): array {
		return array_keys( self::VALID_OPTIONS );
	}

	/**
	 * Ensure that a given transient key is valid.
	 *
	 * @param string $name The transient name.
	 *
	 * @throws InvalidOption When the transient key is not valid.
	 */
	protected function validate_transient_key( string $name ) {
		if ( ! array_key_exists( $name, self::VALID_OPTIONS ) ) {
			throw InvalidOption::invalid_name( $name );
		}
	}

	/**
	 * Prefix a transient name with the plugin prefix.
	 *
	 * @param string $name
	 *
	 * @return string
	 */
	protected function prefix_name( string $name ): string {
		return "{$this->get_slug()}_{$name}";
	}
}
TransientsInterface.php000064400000003515151545632130011241 0ustar00<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Options;

/**
 * Interface TransientsInterface
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Options
 */
interface TransientsInterface {

	public const ADS_CAMPAIGN_COUNT   = 'ads_campaign_count';
	public const ADS_METRICS          = 'ads_metrics';
	public const FREE_LISTING_METRICS = 'free_listing_metrics';
	public const MC_ACCOUNT_REVIEW    = 'mc_account_review';
	public const MC_IS_SUBACCOUNT     = 'mc_is_subaccount';
	public const MC_STATUSES          = 'mc_statuses';
	public const URL_MATCHES          = 'url_matches';
	public const WPCOM_API_STATUS     = 'wpcom_api_status';

	public const VALID_OPTIONS = [
		self::ADS_CAMPAIGN_COUNT   => true,
		self::ADS_METRICS          => true,
		self::FREE_LISTING_METRICS => true,
		self::MC_ACCOUNT_REVIEW    => true,
		self::MC_IS_SUBACCOUNT     => true,
		self::MC_STATUSES          => true,
		self::URL_MATCHES          => true,
		self::WPCOM_API_STATUS     => true,
	];

	/**
	 * Get a transient.
	 *
	 * @param string $name          The transient name.
	 * @param mixed  $default_value A default value for the transient.
	 *
	 * @return mixed
	 */
	public function get( string $name, $default_value = null );

	/**
	 * Add or update a transient.
	 *
	 * @param string $name  The transient name.
	 * @param mixed  $value The transient value.
	 * @param int    $expiration Time until expiration in seconds.
	 *
	 * @return bool
	 */
	public function set( string $name, $value, int $expiration = 0 ): bool;

	/**
	 * Delete a transient.
	 *
	 * @param string $name The transient name.
	 *
	 * @return bool
	 */
	public function delete( string $name ): bool;

	/**
	 * Returns all available transient keys.
	 *
	 * @return array
	 *
	 * @since 1.3.0
	 */
	public static function get_all_transient_keys(): array;
}