Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/Tools.tar

Htaccess.php000066600000004320151131606460007020 0ustar00<?php
namespace AIOSEO\Plugin\Common\Tools;

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

class Htaccess {
	/**
	 * The path to the .htaccess file.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	private $path = '';

	/**
	 * Class constructor.
	 *
	 * @since 4.0.0
	 */
	public function __construct() {
		$this->path = ABSPATH . '.htaccess';
	}

	/**
	 * Get the contents of the .htaccess file.
	 *
	 * @since 4.0.0
	 *
	 * @return string The contents of the file.
	 */
	public function getContents() {
		$fs = aioseo()->core->fs;
		if ( ! $fs->exists( $this->path ) ) {
			return false;
		}

		$contents = $fs->getContents( $this->path );

		return aioseo()->helpers->encodeOutputHtml( $contents );
	}

	/**
	 * Saves the contents of the .htaccess file.
	 *
	 * @since 4.0.0
	 *
	 * @param  string  $contents The contents to write.
	 * @return boolean           True if the file was updated.
	 */
	public function saveContents( $contents ) {
		$fs = aioseo()->core->fs;
		if ( ! $fs->isWritable( $this->path ) ) {
			return [
				'success' => false,
				'reason'  => 'file-not-writable',
				'message' => __( 'We were unable to save the .htaccess file because the file was not writable. Please check the file permissions and try again.', 'all-in-one-seo-pack' )
			];
		}

		$fileExists       = $fs->exists( $this->path );
		$originalContents = $fileExists ? $fs->getContents( $this->path ) : null;
		$fileSaved        = $fs->putContents( $this->path, $contents );
		if ( false === $fileSaved ) {
			return [
				'success' => false,
				'reason'  => 'file-not-saved'
			];
		}

		$response       = wp_remote_get( home_url( '?' . time() ) );
		$isValidRequest = wp_remote_retrieve_response_code( $response );

		if (
			// Add an exception for Windows devs since the request fails in Local.
			! defined( 'AIOSEO_DEV_WINDOWS' ) &&
			( is_wp_error( $response ) || 200 !== $isValidRequest )
		) {
			$fs->putContents( $this->path, $originalContents );

			return [
				'success' => false,
				'reason'  => 'syntax-errors',
				'message' => __( 'We were unable to save the .htaccess file due to syntax errors. Please check the code below and try again.', 'all-in-one-seo-pack' )
			];
		}

		return [
			'success' => true
		];
	}
}RobotsTxt.php000066600000045145151131606460007245 0ustar00<?php
namespace AIOSEO\Plugin\Common\Tools;

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

use AIOSEO\Plugin\Common\Models;

class RobotsTxt {
	/**
	 * Which directives are allowed to be extracted.
	 *
	 * @since 4.4.2
	 *
	 * @var array
	 */
	private $allowedDirectives = [ 'user-agent', 'allow', 'disallow', 'clean-param', 'crawl-delay' ];

	/**
	 * Class constructor.
	 *
	 * @since 4.0.0
	 */
	public function __construct() {
		add_filter( 'robots_txt', [ $this, 'buildRules' ], 10000 );

		if ( ! is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
			return;
		}

		add_action( 'init', [ $this, 'checkForPhysicalFiles' ] );
	}

	/**
	 * Build out the robots.txt rules.
	 *
	 * @since 4.0.0
	 *
	 * @param  string $original The original rules to parse.
	 * @return string           The parsed/appended/modified rules.
	 */
	public function buildRules( $original ) {
		// Other plugins might call this too early.
		if ( ! property_exists( aioseo(), 'sitemap' ) ) {
			return $original;
		}

		$searchAppearanceRules = $this->extractSearchAppearanceRules();
		$networkRules          = [];
		if ( is_multisite() ) {
			$searchAppearanceRules = array_merge(
				$searchAppearanceRules,
				$this->extractSearchAppearanceRules( aioseo()->networkOptions->tools->robots->rules )
			);
			$networkRules          = aioseo()->networkOptions->tools->robots->enable ? aioseo()->networkOptions->tools->robots->rules : [];
		}

		$originalRules = $this->extractRules( $original );
		$ruleset       = $this->mergeRules( $originalRules, $this->groupRulesByUserAgent( $searchAppearanceRules ) );
		if ( ! aioseo()->options->tools->robots->enable ) {
			$ruleset = $this->mergeRules( $ruleset, $this->groupRulesByUserAgent( $networkRules ) );
		} else {
			$ruleset = $this->mergeRules(
				$ruleset,
				$this->mergeRules( $this->groupRulesByUserAgent( $networkRules ), $this->groupRulesByUserAgent( aioseo()->options->tools->robots->rules ) ),
				true
			);
		}

		/**
		 * Any plugin can wrongly modify the robots.txt output by hoking into the `do_robots` action hook,
		 * instead of hooking into the `robots_txt` filter hook.
		 * For the first scenario, to make sure our output doesn't conflict with theirs, a new line is necessary.
		 */
		return $this->stringifyRuleset( $ruleset ) . "\n";
	}

	/**
	 * Merges two rulesets.
	 *
	 * @since   4.0.0
	 * @version 4.4.2
	 *
	 * @param  array   $rules1          An array of rules to merge with.
	 * @param  array   $rules2          An array of rules to merge.
	 * @param  boolean $allowOverride   Whether to allow overriding.
	 * @param  boolean $allowDuplicates Whether to allow duplicates.
	 * @return array                    The validated rules.
	 */
	private function mergeRules( $rules1, $rules2, $allowOverride = false, $allowDuplicates = false ) {
		foreach ( $rules2 as $userAgent => $rules ) {
			if ( empty( $userAgent ) ) {
				continue;
			}

			if ( empty( $rules1[ $userAgent ] ) ) {
				$rules1[ $userAgent ] = array_unique( $rules2[ $userAgent ] );

				continue;
			}

			list( $rules1, $rules2 ) = $this->mergeRulesHelper( 'allow', $userAgent, $rules, $rules1, $rules2, $allowDuplicates, $allowOverride );
			list( $rules1, $rules2 ) = $this->mergeRulesHelper( 'disallow', $userAgent, $rules, $rules1, $rules2, $allowDuplicates, $allowOverride );

			$rules1[ $userAgent ] = array_unique( array_merge(
				$rules1[ $userAgent ],
				$rules2[ $userAgent ]
			) );
		}

		return $rules1;
	}

	/**
	 * Helper function for {@see mergeRules()}.
	 *
	 * @since   4.1.2
	 * @version 4.4.2
	 *
	 * @param  string $directive       The directive (allow/disallow).
	 * @param  string $userAgent       The user agent.
	 * @param  array  $rules           The rules.
	 * @param  array  $rules1          The original rules.
	 * @param  array  $rules2          The extra rules.
	 * @param  bool   $allowDuplicates Whether duplicates should be allowed
	 * @param  bool   $allowOverride   Whether the extra rules can override the original ones.
	 * @return array                   The original and extra rules.
	 */
	private function mergeRulesHelper( $directive, $userAgent, $rules, $rules1, $rules2, $allowDuplicates, $allowOverride ) {
		$otherDirective = ( 'allow' === $directive ) ? 'disallow' : 'allow';
		foreach ( $rules as $index1 => $rule ) {
			list( , $ruleValue ) = $this->parseRule( $rule );
			$index2 = array_search( "$otherDirective: $ruleValue", $rules1[ $userAgent ], true );
			if ( false !== $index2 && ! $allowDuplicates ) {
				if ( $allowOverride ) {
					unset( $rules1[ $userAgent ][ $index2 ] );
				} else {
					unset( $rules2[ $userAgent ][ $index1 ] );
				}
			}

			$pattern = str_replace( [ '.', '*', '?', '$' ], [ '\.', '(.*)', '\?', '\$' ], $ruleValue );

			foreach ( $rules1[ $userAgent ] as $rule1 ) {
				$matches = [];
				preg_match( "#^$otherDirective: $pattern$#", (string) $rule1, $matches );
			}

			if ( ! empty( $matches ) && ! $allowDuplicates ) {
				unset( $rules2[ $userAgent ][ $index1 ] );
			}
		}

		return [ $rules1, $rules2 ];
	}

	/**
	 * Parses a rule and extracts the directive and value.
	 *
	 * @since 4.4.2
	 *
	 * @param  string $rule The rule to parse.
	 * @return array        An array containing the parsed directive and value.
	 */
	private function parseRule( $rule ) {
		list( $directive, $value ) = array_map( 'trim', array_pad( explode( ':', $rule, 2 ), 2, '' ) );

		return [ $directive, $value ];
	}

	/**
	 * Stringifies the parsed rules.
	 *
	 * @since   4.0.0
	 * @version 4.4.2
	 *
	 * @param  array  $allRules The rules array.
	 * @return string           The stringified rules.
	 */
	private function stringifyRuleset( $allRules ) {
		$robots = [];
		foreach ( $allRules as $userAgent => $rules ) {
			if ( empty( $userAgent ) ) {
				continue;
			}

			$robots[] = "\r\nUser-agent: $userAgent";
			foreach ( $rules as $rule ) {
				list( $directive, $value ) = $this->parseRule( $rule );
				if ( empty( $directive ) || empty( $value ) ) {
					continue;
				}

				$robots[] = sprintf( '%s: %s', ucfirst( $directive ), $value );
			}
		}

		$robots      = implode( "\r\n", $robots );
		$sitemapUrls = $this->getSitemapRules();
		if ( ! empty( $sitemapUrls ) ) {
			$sitemapUrls = implode( "\r\n", $sitemapUrls );
			$robots      .= "\r\n\r\n$sitemapUrls";
		}

		return trim( $robots );
	}

	/**
	 * Get Sitemap URLs excluding the default ones.
	 *
	 * @since 4.1.7
	 *
	 * @return array An array of the Sitemap URLs.
	 */
	private function getSitemapRules() {
		$defaultSitemaps = $this->extractSitemapUrls( aioseo()->robotsTxt->getDefaultRobotsTxtContent() );
		$sitemapRules    = aioseo()->sitemap->helpers->getSitemapUrlsPrefixed();

		return array_diff( $sitemapRules, $defaultSitemaps );
	}

	/**
	 * Extracts the Search Appearance related rules.
	 *
	 * @since 4.8.1
	 *
	 * @param  array $rules The rules to extract from.
	 * @return array        The Search Appearance related rules.
	 */
	public function extractSearchAppearanceRules( $rules = [] ) {
		$currentRules = $rules ?: aioseo()->options->tools->robots->rules;

		return array_filter( $currentRules, function ( $rule ) {
			$parseRule = json_decode( $rule, true );

			return ! empty( $parseRule['bot'] ) || ! empty( $parseRule['preventCrawling'] );
		} );
	}

	/**
	 * Parses the rules.
	 *
	 * @since   4.0.0
	 * @version 4.4.2
	 *
	 * @param  array $rules An array of rules.
	 * @return array        The rules grouped by user agent.
	 */
	private function groupRulesByUserAgent( $rules ) {
		$groups = [];
		foreach ( $rules as $rule ) {
			$r = is_string( $rule ) ? json_decode( $rule, true ) : $rule;
			if ( empty( $r['userAgent'] ) || empty( $r['fieldValue'] ) ) {
				continue;
			}

			if ( empty( $groups[ $r['userAgent'] ] ) ) {
				$groups[ $r['userAgent'] ] = [];
			}

			$groups[ $r['userAgent'] ][] = "{$r['directive']}: {$r['fieldValue']}";
		}

		return $groups;
	}

	/**
	 * Extract rules from a string.
	 *
	 * @since   4.0.0
	 * @version 4.4.2
	 *
	 * @param  string $lines The lines to extract from.
	 * @return array         An array of extracted rules.
	 */
	public function extractRules( $lines ) {
		$lines              = array_filter( array_map( 'trim', explode( "\n", (string) $lines ) ) );
		$rules              = [];
		$userAgent          = null;
		$prevDirective      = null;
		$prevValue          = null;
		$siblingsUserAgents = [];
		foreach ( $lines as $line ) {
			list( $directive, $value ) = $this->parseRule( $line );
			if ( empty( $directive ) || empty( $value ) ) {
				continue;
			}

			$directive = strtolower( $directive );
			if ( ! in_array( $directive, $this->allowedDirectives, true ) ) {
				continue;
			}

			$value = $this->sanitizeDirectiveValue( $directive, $value );
			if ( ! $value ) {
				continue;
			}

			if ( 'user-agent' === $directive ) {
				if (
					! empty( $prevDirective ) &&
					! empty( $prevValue ) &&
					'user-agent' === $prevDirective
				) {
					$siblingsUserAgents[] = $prevValue;
				}

				$userAgent           = $value;
				$rules[ $userAgent ] = ! empty( $rules[ $userAgent ] ) ? $rules[ $userAgent ] : [];
			} else {
				$rules[ $userAgent ][] = "$directive: $value";
				if ( $siblingsUserAgents ) {
					foreach ( $siblingsUserAgents as $siblingUserAgent ) {
						$rules[ $siblingUserAgent ] = $rules[ $userAgent ];
					}

					$siblingsUserAgents = [];
				}
			}

			$prevDirective = $directive;
			$prevValue     = $value;
		}

		return $rules;
	}

	/**
	 * Extract sitemap URLs from a string.
	 *
	 * @since 4.0.10
	 *
	 * @param  string $lines The lines to extract from.
	 * @return array         An array of sitemap URLs.
	 */
	public function extractSitemapUrls( $lines ) {
		$lines       = array_filter( array_map( 'trim', explode( "\n", (string) $lines ) ) );
		$sitemapUrls = [];
		foreach ( $lines as $line ) {
			$array = array_map( 'trim', explode( 'sitemap:', strtolower( $line ) ) );
			if ( ! empty( $array[1] ) ) {
				$sitemapUrls[] = trim( $line );
			}
		}

		return $sitemapUrls;
	}

	/**
	 * Sanitize the robots.txt rule directive value.
	 *
	 * @since   4.0.0
	 * @version 4.4.2
	 *
	 * @param  string $directive The directive.
	 * @param  string $value     The value.
	 * @return string            The directive value.
	 */
	private function sanitizeDirectiveValue( $directive, $value ) {
		// Percent-encoded characters are stripped from our option values, so we decode.
		$value = rawurldecode( trim( $value ) );
		if ( ! $value ) {
			return $value;
		}

		$value = preg_replace( '/[><]/', '', (string) $value );

		if ( 'user-agent' === $directive ) {
			$value = preg_replace( '/[^a-z0-9\-_*,.\s]/i', '', (string) $value );
		}

		if ( 'allow' === $directive || 'disallow' === $directive ) {
			$value = preg_replace( '/^\/+/', '/', (string) $value );
		}

		return $value;
	}

	/**
	 * Check if a physical robots.txt file exists, and if it does add a notice.
	 *
	 * @since 4.0.0
	 *
	 * @return void
	 */
	public function checkForPhysicalFiles() {
		if ( ! $this->hasPhysicalRobotsTxt() ) {
			return;
		}

		$notification = Models\Notification::getNotificationByName( 'robots-physical-file' );
		if ( $notification->exists() ) {
			return;
		}

		Models\Notification::addNotification( [
			'slug'              => uniqid(),
			'notification_name' => 'robots-physical-file',
			'title'             => __( 'Physical Robots.txt File Detected', 'all-in-one-seo-pack' ),
			'content'           => sprintf(
				// Translators: 1 - The plugin short name ("AIOSEO"), 2 - The plugin short name ("AIOSEO").
				__( '%1$s has detected a physical robots.txt file in the root folder of your WordPress installation. We recommend removing this file as it could cause conflicts with WordPress\' dynamically generated one. %2$s can import this file and delete it, or you can simply delete it.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
				AIOSEO_PLUGIN_SHORT_NAME,
				AIOSEO_PLUGIN_SHORT_NAME
			),
			'type'              => 'error',
			'level'             => [ 'all' ],
			'button1_label'     => __( 'Import and Delete', 'all-in-one-seo-pack' ),
			'button1_action'    => 'http://action#tools/import-robots-txt?redirect=aioseo-tools:robots-editor',
			'button2_label'     => __( 'Delete', 'all-in-one-seo-pack' ),
			'button2_action'    => 'http://action#tools/delete-robots-txt?redirect=aioseo-tools:robots-editor',
			'start'             => gmdate( 'Y-m-d H:i:s' )
		] );
	}

	/**
	 * Import physical robots.txt file.
	 *
	 * @since   4.0.0
	 * @version 4.4.2
	 *
	 * @param  int|string $blogId The blog ID or 'network'.
	 * @throws \Exception         If request fails or file is not readable.
	 * @return boolean            Whether the file imported correctly.
	 */
	public function importPhysicalRobotsTxt( $blogId ) {
		try {
			$fs = aioseo()->core->fs;
			if ( ! $fs->isWpfsValid() ) {
				$invalid = true;
			}

			$file = trailingslashit( $fs->fs->abspath() ) . 'robots.txt';
			if (
				isset( $invalid ) ||
				! $fs->isReadable( $file )
			) {
				throw new \Exception( esc_html__( 'There was an error importing the static robots.txt file.', 'all-in-one-seo-pack' ) );
			}

			$lines = trim( (string) $fs->getContents( $file ) );
			if ( $lines ) {
				$this->importRobotsTxtFromText( $lines, $blogId );
			}

			return true;
		} catch ( \Exception $e ) {
			throw new \Exception( esc_html( $e->getMessage() ) );
		}
	}

	/**
	 * Import robots.txt from a URL.
	 *
	 * @since 4.4.2
	 *
	 * @param  string     $text   The text to import from.
	 * @param  int|string $blogId The blog ID or 'network'.
	 * @throws \Exception         If no User-agent is found.
	 * @return boolean            Whether the file imported correctly or not.
	 */
	public function importRobotsTxtFromText( $text, $blogId ) {
		$newRules = $this->extractRules( $text );
		if ( ! key( $newRules ) ) {
			throw new \Exception( esc_html__( 'No User-agent found in the content beginning.', 'all-in-one-seo-pack' ) );
		}

		$options = aioseo()->options;
		if ( 'network' === $blogId ) {
			$options = aioseo()->networkOptions;
		}

		$options->tools->robots->rules = array_unique( array_merge( $options->tools->robots->rules, $this->prepareRobotsTxt( $newRules ) ) );

		return true;
	}

	/**
	 * Import robots.txt from a URL.
	 *
	 * @since 4.4.2
	 *
	 * @param  string     $url    The URL to import from.
	 * @param  int|string $blogId The blog ID or 'network'.
	 * @throws \Exception         If request fails.
	 * @return bool               Whether the import was successful or not.
	 */
	public function importRobotsTxtFromUrl( $url, $blogId ) {
		$request          = wp_remote_get( $url, [
			'timeout'   => 10,
			'sslverify' => false
		] );
		$robotsTxtContent = wp_remote_retrieve_body( $request );
		if ( ! $robotsTxtContent ) {
			throw new \Exception( esc_html__( 'There was an error importing the robots.txt content from the URL.', 'all-in-one-seo-pack' ) );
		}

		$options = aioseo()->options;
		if ( 'network' === $blogId ) {
			$options = aioseo()->networkOptions;
		}

		$newRules = $this->extractRules( $robotsTxtContent );

		$options->tools->robots->rules = array_unique( array_merge( $options->tools->robots->rules, $this->prepareRobotsTxt( $newRules ) ) );

		return true;
	}

	/**
	 * Deletes the physical robots.txt file.
	 *
	 * @since 4.4.5
	 *
	 * @throws \Exception If the file is not readable, or it can't be deleted.
	 * @return true       True if the file was successfully deleted.
	 */
	public function deletePhysicalRobotsTxt() {
		try {
			$fs = aioseo()->core->fs;
			if (
				! $fs->isWpfsValid() ||
				! $fs->fs->delete( trailingslashit( $fs->fs->abspath() ) . 'robots.txt' )
			) {
				throw new \Exception( __( 'There was an error deleting the physical robots.txt file.', 'all-in-one-seo-pack' ) );
			}

			Models\Notification::deleteNotificationByName( 'robots-physical-file' );

			return true;
		} catch ( \Exception $e ) {
			throw new \Exception( esc_html( $e->getMessage() ) );
		}
	}

	/**
	 * Prepare robots.txt rules to save.
	 *
	 * @since 4.1.4
	 *
	 * @param  array $allRules Array with the rules.
	 * @return array           The prepared rules array.
	 */
	public function prepareRobotsTxt( $allRules = [] ) {
		$robots = [];
		foreach ( $allRules as $userAgent => $rules ) {
			if ( empty( $userAgent ) ) {
				continue;
			}

			foreach ( $rules as $rule ) {
				list( $directive, $value ) = $this->parseRule( $rule );
				if ( empty( $directive ) || empty( $value ) ) {
					continue;
				}

				if (
					'*' === $userAgent &&
					(
						'allow' === $directive && '/wp-admin/admin-ajax.php' === $value ||
						'disallow' === $directive && '/wp-admin/' === $value
					)
				) {
					continue;
				}

				$robots[] = wp_json_encode( [
					'userAgent'  => $userAgent,
					'directive'  => $directive,
					'fieldValue' => $value
				] );
			}
		}

		return $robots;
	}

	/**
	 * Checks if a physical robots.txt file exists.
	 *
	 * @since 4.0.0
	 *
	 * @return boolean True if it does, false if not.
	 */
	public function hasPhysicalRobotsTxt() {
		$fs = aioseo()->core->fs;
		if ( ! $fs->isWpfsValid() ) {
			return false;
		}

		$accessType = get_filesystem_method();
		if ( 'direct' === $accessType ) {
			$file = trailingslashit( $fs->fs->abspath() ) . 'robots.txt';

			return $fs->exists( $file );
		}

		return false;
	}

	/**
	 * Get the default Robots.txt lines (excluding our own).
	 *
	 * @since   4.1.7
	 * @version 4.4.2
	 *
	 * @return string The robots.txt content rules (excluding our own).
	 */
	public function getDefaultRobotsTxtContent() {
		// First, we need to remove our filter, so that it doesn't run unintentionally.
		remove_filter( 'robots_txt', [ $this, 'buildRules' ], 10000 );

		ob_start();
		do_robots();
		if ( is_admin() ) {
			header( 'Content-Type: text/html; charset=utf-8' );
		}
		$rules = strval( ob_get_clean() );

		// Add the filter back.
		add_filter( 'robots_txt', [ $this, 'buildRules' ], 10000 );

		return $rules;
	}

	/**
	 * A check to see if the rewrite rules are set.
	 * This isn't perfect, but it will help us know in most cases.
	 *
	 * @since 4.0.0
	 *
	 * @return boolean Whether the rewrite rules are set or not.
	 */
	public function rewriteRulesExist() {
		// If we have a physical file, it's almost impossible to tell if the rewrite rules are set.
		// The only scenario is if we still get a 404.
		$response = wp_remote_get( aioseo()->helpers->getSiteUrl() . '/robots.txt' );
		if ( 299 < wp_remote_retrieve_response_code( $response ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Reset the Search Appearance related rules.
	 *
	 * @since 4.8.1
	 *
	 * @return void
	 */
	public function resetSearchAppearanceRules() {
		$currentRules = aioseo()->options->tools->robots->rules;
		$newRules     = [];
		foreach ( ( $currentRules ?? [] ) as $rule ) {
			$parseRule = json_decode( $rule, true );
			if ( empty( $parseRule['bot'] ) && empty( $parseRule['preventCrawling'] ) ) {
				$newRules[] = $rule;
			}
		}

		aioseo()->options->tools->robots->rules = $newRules;
	}
}SystemStatus.php000066600000032103151131606460007753 0ustar00<?php
namespace AIOSEO\Plugin\Common\Tools;

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

class SystemStatus {
	/**
	 * Get an aggregated list of all system info.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system information.
	 */
	public static function getSystemStatusInfo() {
		return [
			'wordPress'       => self::getWordPressInfo(),
			'constants'       => self::getConstants(),
			'serverInfo'      => self::getServerInfo(),
			'muPlugins'       => self::mustUsePlugins(),
			'activeTheme'     => self::activeTheme(),
			'activePlugins'   => self::activePlugins(),
			'inactivePlugins' => self::inactivePlugins(),
			'database'        => self::getDatabaseInfo()
		];
	}

	/**
	 * Get an array of system info from WordPress.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function getWordPressInfo() {
		$uploadsDir    = wp_upload_dir();
		$version       = get_bloginfo( 'version' );
		$updates       = get_site_transient( 'update_core' );
		$updateVersion = ! empty( $updates->updates[0]->version ) ? $updates->updates[0]->version : '';
		if ( version_compare( $version, $updateVersion, '<' ) ) {
			$version .= ' (' . __( 'Latest version:', 'all-in-one-seo-pack' ) . ' ' . $updateVersion . ')';
		}

		return [
			'label'   => 'WordPress',
			'results' => [
				[
					'header' => __( 'Version', 'all-in-one-seo-pack' ),
					'value'  => $version
				],
				[
					'header' => __( 'Site Title', 'all-in-one-seo-pack' ),
					'value'  => get_bloginfo( 'name' )
				],
				[
					'header' => __( 'Site Language', 'all-in-one-seo-pack' ),
					'value'  => get_locale() ?: 'en_US'
				],
				[
					'header' => __( 'User Language', 'all-in-one-seo-pack' ),
					'value'  => get_user_locale( get_current_user_id() )
				],
				[
					'header' => __( 'Timezone', 'all-in-one-seo-pack' ),
					'value'  => wp_timezone_string()
				],
				[
					'header' => __( 'Home URL', 'all-in-one-seo-pack' ),
					'value'  => home_url()
				],
				[
					'header' => __( 'Site URL', 'all-in-one-seo-pack' ),
					'value'  => site_url()
				],
				[
					'header' => __( 'Permalink Structure', 'all-in-one-seo-pack' ),
					'value'  => get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : __( 'Default', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'Multisite', 'all-in-one-seo-pack' ),
					'value'  => is_multisite() ? __( 'Yes', 'all-in-one-seo-pack' ) : __( 'No', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'HTTPS',
					'value'  => is_ssl() ? __( 'Yes', 'all-in-one-seo-pack' ) : __( 'No', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'User Count', 'all-in-one-seo-pack' ),
					'value'  => count_users()['total_users']
				],
				[
					'header' => __( 'Front Page Info', 'all-in-one-seo-pack' ),
					'value'  => 'page' === get_option( 'show_on_front' ) ? get_option( 'show_on_front' ) . ' [ID: ' . get_option( 'page_on_front' ) . ']' : get_option( 'show_on_front' )
				],
				[
					'header' => __( 'Search Engine Visibility', 'all-in-one-seo-pack' ),
					'value'  => get_option( 'blog_public' ) ? __( 'Visible', 'all-in-one-seo-pack' ) : __( 'Hidden', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'Upload Directory Info', 'all-in-one-seo-pack' ),
					'value'  =>
						__( 'Path:', 'all-in-one-seo-pack' ) . ' ' . $uploadsDir['path'] . ', ' .
						__( 'Url:', 'all-in-one-seo-pack' ) . ' ' . $uploadsDir['url'] . ', ' .
						__( 'Base Directory:', 'all-in-one-seo-pack' ) . ' ' . $uploadsDir['basedir'] . ', ' .
						__( 'Base URL:', 'all-in-one-seo-pack' ) . ' ' . $uploadsDir['baseurl']
				]
			]
		];
	}

	/**
	 * Get an array of database info from WordPress.
	 *
	 * @since 4.4.5
	 *
	 * @return array An array of database info.
	 */
	public static function getDatabaseInfo() {
		$dbInfo = aioseo()->core->db->getDatabaseInfo();
		if ( empty( $dbInfo['tables'] ) ) {
			return [];
		}

		if ( ! aioseo()->helpers->isDev() ) {
			return [];
		}

		$results = [];
		$tables  = array_merge( $dbInfo['tables']['aioseo'], $dbInfo['tables']['other'] );
		foreach ( $tables as $tableName => $tableData ) {
			$results[] = [
				'header' => $tableName,
				'value'  => sprintf(
					// Translators: %1$s is the data size, %2$s is the index size, %3$s is the engine type.
					__( 'Data: %1$.2f MB / Index: %2$.2f MB / Engine: %3$s / Collation: %4$s', 'all-in-one-seo-pack' ),
					$tableData['data'],
					$tableData['index'],
					$tableData['engine'],
					$tableData['collation']
				)
			];
		}

		return [
			'label'   => __( 'Database', 'all-in-one-seo-pack' ),
			'results' => array_merge( [
				[
					'header' => __( 'Database Size', 'all-in-one-seo-pack' ),
					'value'  => sprintf( '%.2f MB', $dbInfo['size']['data'] + $dbInfo['size']['index'] )
				]
			], $results )
		];
	}

	/**
	 * Get an array of system info from WordPress constants.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function getConstants() {
		return [
			'label'   => __( 'Constants', 'all-in-one-seo-pack' ),
			'results' => [
				[
					'header' => 'ABSPATH',
					'value'  => ABSPATH
				],
				[
					'header' => 'WP_CONTENT_DIR',
					'value'  => defined( 'WP_CONTENT_DIR' ) ? ( WP_CONTENT_DIR ? WP_CONTENT_DIR : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'WP_CONTENT_URL',
					'value'  => defined( 'WP_CONTENT_URL' ) ? ( WP_CONTENT_URL ? WP_CONTENT_URL : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'UPLOADS',
					'value'  => defined( 'UPLOADS' ) ? ( UPLOADS ? UPLOADS : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'WP_DEBUG',
					'value'  => defined( 'WP_DEBUG' ) ? ( WP_DEBUG ? WP_DEBUG : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'WP_DEBUG_LOG',
					'value'  => defined( 'WP_DEBUG_LOG' ) ? ( WP_DEBUG_LOG ? WP_DEBUG_LOG : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'WP_DEBUG_DISPLAY',
					'value'  => defined( 'WP_DEBUG_DISPLAY' ) ? ( WP_DEBUG_DISPLAY ? WP_DEBUG_DISPLAY : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'WP_POST_REVISIONS',
					'value'  => defined( 'WP_POST_REVISIONS' ) ? WP_POST_REVISIONS : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'DISABLE_WP_CRON',
					'value'  => defined( 'DISABLE_WP_CRON' ) ? DISABLE_WP_CRON : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'EMPTY_TRASH_DAYS',
					'value'  => defined( 'EMPTY_TRASH_DAYS' ) ? EMPTY_TRASH_DAYS : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'AUTOSAVE_INTERVAL',
					'value'  => defined( 'AUTOSAVE_INTERVAL' ) ? AUTOSAVE_INTERVAL : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'SCRIPT_DEBUG',
					'value'  => defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'DB_CHARSET',
					'value'  => defined( 'DB_CHARSET' ) ? ( DB_CHARSET ? DB_CHARSET : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				],
				[
					'header' => 'DB_COLLATE',
					'value'  => defined( 'DB_COLLATE' ) ? ( DB_COLLATE ? DB_COLLATE : __( 'Disabled', 'all-in-one-seo-pack' ) ) : __( 'Not set', 'all-in-one-seo-pack' )
				]
			]
		];
	}

	/**
	 * Get an array of system info from the server.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function getServerInfo() {
		$sqlMode   = null;
		$mysqlInfo = aioseo()->core->db->db->get_results( "SHOW VARIABLES LIKE 'sql_mode'" );
		if ( ! empty( $mysqlInfo ) && is_array( $mysqlInfo ) ) {
			$sqlMode = $mysqlInfo[0]->Value;
		}

		$dbServerInfo = method_exists( aioseo()->core->db->db, 'db_server_info' )
			? aioseo()->core->db->db->db_server_info()
			: ( function_exists( 'mysqli_get_server_info' )
				? mysqli_get_server_info( aioseo()->core->db->db->dbh ) // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_get_server_info
				: ''
		);

		return [
			'label'   => __( 'Server Info', 'all-in-one-seo-pack' ),
			'results' => [
				[
					'header' => __( 'Operating System', 'all-in-one-seo-pack' ),
					'value'  => PHP_OS
				],
				[
					'header' => __( 'Web Server', 'all-in-one-seo-pack' ),
					'value'  => ! empty( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : __( 'unknown', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'Memory Usage', 'all-in-one-seo-pack' ),
					'value'  => function_exists( 'memory_get_usage' ) ? round( memory_get_usage() / 1024 / 1024, 2 ) . 'M' : __( 'N/A', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'Database Powered By', 'all-in-one-seo-pack' ),
					'value'  => stripos( $dbServerInfo, 'mariadb' ) !== false ? 'MariaDB' : 'MySQL'
				],
				[
					'header' => __( 'Database Version', 'all-in-one-seo-pack' ),
					'value'  => aioseo()->core->db->db->db_version()
				],
				[
					'header' => __( 'SQL Mode', 'all-in-one-seo-pack' ),
					'value'  => $sqlMode ?? __( 'Not Set', 'all-in-one-seo-pack' ),
				],
				[
					'header' => __( 'PHP Version', 'all-in-one-seo-pack' ),
					'value'  => PHP_VERSION
				],
				[
					'header' => __( 'PHP Memory Limit', 'all-in-one-seo-pack' ),
					'value'  => ini_get( 'memory_limit' )
				],
				[
					'header' => __( 'PHP Max Upload Size', 'all-in-one-seo-pack' ),
					'value'  => ini_get( 'upload_max_filesize' )
				],
				[
					'header' => __( 'PHP Max Post Size', 'all-in-one-seo-pack' ),
					'value'  => ini_get( 'post_max_size' )
				],
				[
					'header' => __( 'PHP Max Script Execution Time', 'all-in-one-seo-pack' ),
					'value'  => ini_get( 'max_execution_time' )
				],
				[
					'header' => __( 'PHP Exif Support', 'all-in-one-seo-pack' ),
					'value'  => is_callable( 'exif_read_data' ) ? __( 'Yes', 'all-in-one-seo-pack' ) : __( 'No', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'PHP IPTC Support', 'all-in-one-seo-pack' ),
					'value'  => is_callable( 'iptcparse' ) ? __( 'Yes', 'all-in-one-seo-pack' ) : __( 'No', 'all-in-one-seo-pack' )
				],
				[
					'header' => __( 'PHP XML Support', 'all-in-one-seo-pack' ),
					'value'  => is_callable( 'xml_parser_create' ) ? __( 'Yes', 'all-in-one-seo-pack' ) : __( 'No', 'all-in-one-seo-pack' )
				]
			]
		];
	}

	/**
	 * Get an array of system info from the active theme.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function activeTheme() {
		$themeData = wp_get_theme();

		return [
			'label'   => __( 'Active Theme', 'all-in-one-seo-pack' ),
			'results' => [
				[
					'header' => $themeData->name,
					'value'  => $themeData->version
				]
			]
		];
	}

	/**
	 * Get an array of system info from must-use plugins.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function mustUsePlugins() {
		$plugins   = [];
		$muPlugins = get_mu_plugins();
		if ( ! empty( $muPlugins ) ) {
			foreach ( $muPlugins as $pluginData ) {
				$plugins[] = [
					'header' => $pluginData['Name'],
					'value'  => $pluginData['Version']
				];
			}
		}

		return [
			'label'   => __( 'Must-Use Plugins', 'all-in-one-seo-pack' ),
			'results' => $plugins
		];
	}

	/**
	 * Get an array of system info from active plugins.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function activePlugins() {
		$plugins       = [];
		$allPlugins    = get_plugins();
		$activePlugins = get_option( 'active_plugins', [] );
		$updates       = get_plugin_updates();
		if ( ! empty( $allPlugins ) ) {
			foreach ( $allPlugins as $pluginPath => $pluginData ) {
				if ( ! in_array( $pluginPath, $activePlugins, true ) ) {
					continue;
				}

				$update    = ( array_key_exists( $pluginPath, $updates ) ) ? ' (' . __( 'needs update', 'all-in-one-seo-pack' ) . ' - ' . $updates[ $pluginPath ]->update->new_version . ')' : '';
				$plugins[] = [
					'header' => $pluginData['Name'],
					'value'  => $pluginData['Version'] . $update
				];
			}
		}

		return [
			'label'   => __( 'Active Plugins', 'all-in-one-seo-pack' ),
			'results' => $plugins
		];
	}

	/**
	 * Get an array of system info from inactive plugins.
	 *
	 * @since 4.0.0
	 *
	 * @return array An array of system info.
	 */
	public static function inactivePlugins() {
		$plugins       = [];
		$allPlugins    = get_plugins();
		$activePlugins = get_option( 'active_plugins', [] );
		$updates       = get_plugin_updates();
		if ( ! empty( $allPlugins ) ) {
			foreach ( $allPlugins as $pluginPath => $pluginData ) {
				if ( in_array( $pluginPath, $activePlugins, true ) ) {
					continue;
				}

				$update    = ( array_key_exists( $pluginPath, $updates ) ) ? ' (' . __( 'needs update', 'all-in-one-seo-pack' ) . ' - ' . $updates[ $pluginPath ]->update->new_version . ')' : '';
				$plugins[] = [
					'header' => $pluginData['Name'],
					'value'  => $pluginData['Version'] . $update
				];
			}
		}

		return [
			'label'   => __( 'Inactive Plugins', 'all-in-one-seo-pack' ),
			'results' => $plugins
		];
	}
}