Файловый менеджер - Редактировать - /home/bean7936/perfect-community.com/442aa3/wordpress-seo.zip
Назад
PK ���\�D�7�D �D wp-seo-main.phpnu �[��� <?php /** * WPSEO plugin file. * * @package WPSEO\Main */ if ( ! function_exists( 'add_filter' ) ) { header( 'Status: 403 Forbidden' ); header( 'HTTP/1.1 403 Forbidden' ); exit(); } /** * {@internal Nobody should be able to overrule the real version number as this can cause * serious issues with the options, so no if ( ! defined() ).}} */ define( 'WPSEO_VERSION', '27.1.1' ); if ( ! defined( 'WPSEO_PATH' ) ) { define( 'WPSEO_PATH', plugin_dir_path( WPSEO_FILE ) ); } if ( ! defined( 'WPSEO_BASENAME' ) ) { define( 'WPSEO_BASENAME', plugin_basename( WPSEO_FILE ) ); } /* * {@internal The prefix constants are used to build prefixed versions of dependencies. * These should not be changed on run-time, thus missing the ! defined() check.}} */ define( 'YOAST_VENDOR_NS_PREFIX', 'YoastSEO_Vendor' ); define( 'YOAST_VENDOR_DEFINE_PREFIX', 'YOASTSEO_VENDOR__' ); define( 'YOAST_VENDOR_PREFIX_DIRECTORY', 'vendor_prefixed' ); define( 'YOAST_SEO_PHP_REQUIRED', '7.4' ); define( 'YOAST_SEO_WP_TESTED', '6.9.1' ); define( 'YOAST_SEO_WP_REQUIRED', '6.8' ); if ( ! defined( 'WPSEO_NAMESPACES' ) ) { define( 'WPSEO_NAMESPACES', true ); } /* ***************************** CLASS AUTOLOADING *************************** */ /** * Autoload our class files. * * @param string $class_name Class name. * * @return void */ function wpseo_auto_load( $class_name ) { static $classes = null; $classes ??= [ 'wp_list_table' => ABSPATH . 'wp-admin/includes/class-wp-list-table.php', 'walker_category' => ABSPATH . 'wp-includes/category-template.php', ]; $cn = strtolower( $class_name ); if ( ! class_exists( $class_name ) && isset( $classes[ $cn ] ) ) { require_once $classes[ $cn ]; } } $yoast_autoload_file = WPSEO_PATH . 'vendor/autoload.php'; if ( is_readable( $yoast_autoload_file ) ) { $yoast_autoloader = require $yoast_autoload_file; } elseif ( ! class_exists( 'WPSEO_Options' ) ) { // Still checking since might be site-level autoload R. add_action( 'admin_init', 'yoast_wpseo_missing_autoload', 1 ); return; } /** * Include the file from the `symfony/deprecation-contracts` dependency instead of autoloading it via composer. * * We need to do that because autoloading via composer prevents the vendor-prefixing of the dependency itself. * Note that we don't expect the function to be ever called since the OAuth2 library should not provide invalid input. */ $deprecation_contracts_file = WPSEO_PATH . 'vendor_prefixed/symfony/deprecation-contracts/functions.php'; if ( is_readable( $deprecation_contracts_file ) ) { include $deprecation_contracts_file; } if ( function_exists( 'spl_autoload_register' ) ) { spl_autoload_register( 'wpseo_auto_load' ); } require_once WPSEO_PATH . 'src/functions.php'; /* ********************* DEFINES DEPENDING ON AUTOLOADED CODE ********************* */ /** * Defaults to production, for safety. */ if ( ! defined( 'YOAST_ENVIRONMENT' ) ) { define( 'YOAST_ENVIRONMENT', 'production' ); } if ( YOAST_ENVIRONMENT === 'development' && isset( $yoast_autoloader ) ) { add_action( 'plugins_loaded', /** * Reregisters the autoloader so that Yoast SEO is at the front. * This prevents conflicts with the development versions of our addons. * An anonymous function is used so we can use the autoloader variable. * As this is only loaded in development removing this action is not a concern. * * @return void */ static function () use ( $yoast_autoloader ) { $yoast_autoloader->unregister(); $yoast_autoloader->register( true ); }, 1, ); } /** * Only use minified assets when we are in a production environment. */ if ( ! defined( 'WPSEO_CSSJS_SUFFIX' ) ) { define( 'WPSEO_CSSJS_SUFFIX', ( YOAST_ENVIRONMENT !== 'development' ) ? '.min' : '' ); } /* ***************************** PLUGIN (DE-)ACTIVATION *************************** */ /** * Run single site / network-wide activation of the plugin. * * @param bool $networkwide Whether the plugin is being activated network-wide. * * @return void */ function wpseo_activate( $networkwide = false ) { if ( ! is_multisite() || ! $networkwide ) { _wpseo_activate(); } else { /* Multi-site network activation - activate the plugin for all blogs. */ wpseo_network_activate_deactivate( true ); } // This is done so that the 'uninstall_{$file}' is triggered. register_uninstall_hook( WPSEO_FILE, '__return_false' ); } /** * Run single site / network-wide de-activation of the plugin. * * @param bool $networkwide Whether the plugin is being de-activated network-wide. * * @return void */ function wpseo_deactivate( $networkwide = false ) { if ( ! is_multisite() || ! $networkwide ) { _wpseo_deactivate(); } else { /* Multi-site network activation - de-activate the plugin for all blogs. */ wpseo_network_activate_deactivate( false ); } } /** * Run network-wide (de-)activation of the plugin. * * @param bool $activate True for plugin activation, false for de-activation. * * @return void */ function wpseo_network_activate_deactivate( $activate = true ) { global $wpdb; $network_blogs = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ) ); if ( is_array( $network_blogs ) && $network_blogs !== [] ) { foreach ( $network_blogs as $blog_id ) { switch_to_blog( $blog_id ); if ( $activate === true ) { _wpseo_activate(); } else { _wpseo_deactivate(); } restore_current_blog(); } } } /** * Runs on activation of the plugin. * * @return void */ function _wpseo_activate() { require_once WPSEO_PATH . 'inc/wpseo-functions.php'; require_once WPSEO_PATH . 'inc/class-wpseo-installation.php'; new WPSEO_Installation(); WPSEO_Options::get_instance(); if ( ! is_multisite() ) { WPSEO_Options::initialize(); } else { WPSEO_Options::maybe_set_multisite_defaults( true ); } WPSEO_Options::ensure_options_exist(); if ( ! is_multisite() || ! ms_is_switched() ) { // Constructor has side effects so this registers all hooks. $GLOBALS['wpseo_rewrite'] = new WPSEO_Rewrite(); } add_action( 'shutdown', [ 'WPSEO_Utils', 'clear_rewrites' ] ); WPSEO_Options::set( 'indexing_reason', 'first_install' ); WPSEO_Options::set( 'first_time_install', true ); if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) { WPSEO_Options::set( 'should_redirect_after_install_free', true ); } else { WPSEO_Options::set( 'activation_redirect_timestamp_free', time() ); } // Reset tracking to be disabled by default. if ( ! YoastSEO()->helpers->product->is_premium() && WPSEO_Options::get( 'toggled_tracking' ) !== true ) { WPSEO_Options::set( 'tracking', false ); } do_action( 'wpseo_register_roles' ); WPSEO_Role_Manager_Factory::get()->add(); do_action( 'wpseo_register_capabilities' ); WPSEO_Capability_Manager_Factory::get()->add(); // Clear cache so the changes are obvious. WPSEO_Utils::clear_cache(); do_action( 'wpseo_activate' ); } /** * On deactivation, flush the rewrite rules so XML sitemaps stop working. * * @return void */ function _wpseo_deactivate() { require_once WPSEO_PATH . 'inc/wpseo-functions.php'; add_action( 'shutdown', [ 'WPSEO_Utils', 'clear_rewrites' ] ); // Register capabilities, to make sure they are cleaned up. do_action( 'wpseo_register_roles' ); do_action( 'wpseo_register_capabilities' ); // Clean up capabilities. WPSEO_Role_Manager_Factory::get()->remove(); WPSEO_Capability_Manager_Factory::get()->remove(); // Clear cache so the changes are obvious. WPSEO_Utils::clear_cache(); do_action( 'wpseo_deactivate' ); } /** * Run wpseo activation routine on creation / activation of a multisite blog if WPSEO is activated * network-wide. * * Will only be called by multisite actions. * * {@internal Unfortunately will fail if the plugin is in the must-use directory. * {@link https://core.trac.wordpress.org/ticket/24205} }} * * @param int|WP_Site $blog_id Blog ID. * * @return void */ function wpseo_on_activate_blog( $blog_id ) { if ( ! function_exists( 'is_plugin_active_for_network' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } if ( $blog_id instanceof WP_Site ) { $blog_id = (int) $blog_id->blog_id; } if ( is_plugin_active_for_network( WPSEO_BASENAME ) ) { switch_to_blog( $blog_id ); wpseo_activate( false ); restore_current_blog(); } } /* ***************************** PLUGIN LOADING *************************** */ /** * Load translations. * * @deprecated 27.0 * @codeCoverageIgnore * * @return void */ function wpseo_load_textdomain() { _deprecated_function( __FUNCTION__, 'Yoast SEO 27.0' ); } /** * On plugins_loaded: load the minimum amount of essential files for this plugin. * * @return void */ function wpseo_init() { require_once WPSEO_PATH . 'inc/wpseo-functions.php'; require_once WPSEO_PATH . 'inc/wpseo-functions-deprecated.php'; // Make sure our option and meta value validation routines and default values are always registered and available. WPSEO_Options::get_instance(); WPSEO_Meta::init(); if ( version_compare( WPSEO_Options::get( 'version', 1, [ 'wpseo' ] ), WPSEO_VERSION, '<' ) ) { // Invalidate the opcache in 50% of the cases, randomly staggered based on the site URL. // @TODO: Move the staggering logic to its own class, but only after a few releases after the complete sunset of the opcache invalidation. Make sure to document that in the future, maybe `12` should be used as modulus, so that it's easier to tinker the percentage (divisible by 2, 3, 4, 6). (see the technical choices of https://github.com/Yoast/wordpress-seo/pull/22812). $random_seed = hexdec( substr( hash( 'sha256', site_url() ), 0, 8 ) ); $should_invalidate_opcache = ( $random_seed % 2 ) !== 0; /** * Filter: 'Yoast\WP\SEO\should_invalidate_opcache' - Allow developers to enable / disable * opcache invalidation upon upgrade of the Yoast SEO plugin. * * @since 26.1 * * @param bool $should_invalidate Whether opcache should be invalidated. */ $should_invalidate_opcache = (bool) apply_filters( 'Yoast\WP\SEO\should_invalidate_opcache', $should_invalidate_opcache ); if ( $should_invalidate_opcache && function_exists( 'opcache_reset' ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Prevent notices when opcache.restrict_api is set. @opcache_reset(); } new WPSEO_Upgrade(); // Get a cleaned up version of the $options. } $GLOBALS['wpseo_rewrite'] = new WPSEO_Rewrite(); if ( WPSEO_Options::get( 'enable_xml_sitemap', null, [ 'wpseo' ] ) === true ) { $GLOBALS['wpseo_sitemaps'] = new WPSEO_Sitemaps(); } if ( ! wp_doing_ajax() ) { require_once WPSEO_PATH . 'inc/wpseo-non-ajax-functions.php'; } $integrations = []; $integrations[] = new WPSEO_Slug_Change_Watcher(); foreach ( $integrations as $integration ) { $integration->register_hooks(); } } /** * Loads the rest api endpoints. * * @return void */ function wpseo_init_rest_api() { // We can't do anything when requirements are not met. if ( ! WPSEO_Utils::is_api_available() ) { return; } // Boot up REST API. $statistics_service = new WPSEO_Statistics_Service( new WPSEO_Statistics() ); $endpoints = []; $endpoints[] = new WPSEO_Endpoint_File_Size( new WPSEO_File_Size_Service() ); $endpoints[] = new WPSEO_Endpoint_Statistics( $statistics_service ); foreach ( $endpoints as $endpoint ) { $endpoint->register(); } } /** * Used to load the required files on the plugins_loaded hook, instead of immediately. * * @return void */ function wpseo_admin_init() { new WPSEO_Admin_Init(); } /* ***************************** BOOTSTRAP / HOOK INTO WP *************************** */ $spl_autoload_exists = function_exists( 'spl_autoload_register' ); if ( ! $spl_autoload_exists ) { add_action( 'admin_init', 'yoast_wpseo_missing_spl', 1 ); } if ( ! wp_installing() && ( $spl_autoload_exists ) ) { add_action( 'plugins_loaded', 'wpseo_init', 14 ); add_action( 'setup_theme', [ 'Yoast_Dynamic_Rewrites', 'instance' ], 1 ); add_action( 'rest_api_init', 'wpseo_init_rest_api' ); if ( is_admin() ) { new Yoast_Notifications(); $yoast_addon_manager = new WPSEO_Addon_Manager(); $yoast_addon_manager->register_hooks(); if ( wp_doing_ajax() ) { require_once WPSEO_PATH . 'admin/ajax.php'; // Plugin conflict ajax hooks. new Yoast_Plugin_Conflict_Ajax(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: We are not processing form information but only loading the admin init class. if ( isset( $_POST['action'] ) && is_string( $_POST['action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information but only loading the admin init class, We are strictly comparing only. if ( wp_unslash( $_POST['action'] ) === 'inline-save' ) { add_action( 'plugins_loaded', 'wpseo_admin_init', 15 ); } } } else { add_action( 'plugins_loaded', 'wpseo_admin_init', 15 ); } } add_action( 'plugins_loaded', 'load_yoast_notifications' ); add_action( 'init', [ 'WPSEO_Replace_Vars', 'setup_statics_once' ] ); // Initializes the Yoast indexables for the first time. YoastSEO(); /** * Action called when the Yoast SEO plugin file has loaded. */ do_action( 'wpseo_loaded' ); } // Activation and deactivation hook. register_activation_hook( WPSEO_FILE, 'wpseo_activate' ); register_deactivation_hook( WPSEO_FILE, 'wpseo_deactivate' ); add_action( 'wp_initialize_site', 'wpseo_on_activate_blog', 99 ); add_action( 'activate_blog', 'wpseo_on_activate_blog' ); // Registers SEO capabilities. $wpseo_register_capabilities = new WPSEO_Register_Capabilities(); $wpseo_register_capabilities->register_hooks(); // Registers SEO roles. $wpseo_register_capabilities = new WPSEO_Register_Roles(); $wpseo_register_capabilities->register_hooks(); /** * Wraps for notifications center class. * * @return void */ function load_yoast_notifications() { // Init Yoast_Notification_Center class. Yoast_Notification_Center::get(); } /** * Throw an error if the PHP SPL extension is disabled (prevent white screens) and self-deactivate plugin. * * @since 1.5.4 * * @return void */ function yoast_wpseo_missing_spl() { if ( is_admin() ) { add_action( 'admin_notices', 'yoast_wpseo_missing_spl_notice' ); yoast_wpseo_self_deactivate(); } } /** * Returns the notice in case of missing spl extension. * * @return void */ function yoast_wpseo_missing_spl_notice() { $message = esc_html__( 'The Standard PHP Library (SPL) extension seem to be unavailable. Please ask your web host to enable it.', 'wordpress-seo' ); yoast_wpseo_activation_failed_notice( $message ); } /** * Throw an error if the Composer autoload is missing and self-deactivate plugin. * * @return void */ function yoast_wpseo_missing_autoload() { if ( is_admin() ) { add_action( 'admin_notices', 'yoast_wpseo_missing_autoload_notice' ); yoast_wpseo_self_deactivate(); } } /** * Returns the notice in case of missing Composer autoload. * * @return void */ function yoast_wpseo_missing_autoload_notice() { /* translators: %1$s expands to Yoast SEO, %2$s / %3$s: links to the installation manual in the Readme for the Yoast SEO code repository on GitHub */ $message = esc_html__( 'The %1$s plugin installation is incomplete. Please refer to %2$sinstallation instructions%3$s.', 'wordpress-seo' ); $message = sprintf( $message, 'Yoast SEO', '<a href="https://github.com/Yoast/wordpress-seo#installation">', '</a>' ); yoast_wpseo_activation_failed_notice( $message ); } /** * Throw an error if the filter extension is disabled (prevent white screens) and self-deactivate plugin. * * @since 2.0 * @deprecated 23.3 * @codeCoverageIgnore * * @return void */ function yoast_wpseo_missing_filter() { _deprecated_function( __FUNCTION__, 'Yoast SEO 23.3' ); } /** * Returns the notice in case of missing filter extension. * * @deprecated 23.3 * @codeCoverageIgnore * * @return void */ function yoast_wpseo_missing_filter_notice() { _deprecated_function( __FUNCTION__, 'Yoast SEO 23.3' ); } /** * Echo's the Activation failed notice with any given message. * * @param string $message Message string. * * @return void */ function yoast_wpseo_activation_failed_notice( $message ) { $title = sprintf( /* translators: %s: Yoast SEO. */ esc_html__( '%s activation failed', 'wordpress-seo' ), 'Yoast SEO', ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This function is only called in 3 places that are safe. echo '<div class="error yoast-migrated-notice"><h4 class="yoast-notice-migrated-header">' . $title . '</h4><div class="notice-yoast-content"><p>' . strip_tags( $message, '<a>' ) . '</p></div></div>'; } /** * The method will deactivate the plugin, but only once, done by the static $is_deactivated. * * @return void */ function yoast_wpseo_self_deactivate() { static $is_deactivated; if ( $is_deactivated === null ) { $is_deactivated = true; deactivate_plugins( WPSEO_BASENAME ); if ( isset( $_GET['activate'] ) ) { unset( $_GET['activate'] ); } } } /** * Aliasses added in order to keep compatibility with Yoast SEO: Local. */ class_alias( '\Yoast\WP\SEO\Initializers\Initializer_Interface', '\Yoast\WP\SEO\WordPress\Initializer' ); class_alias( '\Yoast\WP\SEO\Loadable_Interface', '\Yoast\WP\SEO\WordPress\Loadable' ); class_alias( '\Yoast\WP\SEO\Integrations\Integration_Interface', '\Yoast\WP\SEO\WordPress\Integration' ); PK ���\~��/ / , blocks/dynamic-blocks/breadcrumbs/block.jsonnu �[��� { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "version": "22.8", "name": "yoast-seo/breadcrumbs", "title": "Yoast Breadcrumbs", "description": "Adds the Yoast SEO breadcrumbs to your template or content.", "category": "yoast-internal-linking-blocks", "icon": "admin-links", "keywords": [ "SEO", "breadcrumbs", "internal linking", "site structure" ], "textdomain": "wordpress-seo", "attributes": { "className": { "type": "string" } }, "example": { "attributes": {} } } PK ���\:�� � , blocks/structured-data-blocks/faq/block.jsonnu �[��� { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "version": "22.7", "name": "yoast/faq-block", "title": "Yoast FAQ", "description": "List your Frequently Asked Questions in an SEO-friendly way.", "category": "yoast-structured-data-blocks", "icon": "editor-ul", "keywords": [ "FAQ", "Frequently Asked Questions", "Schema", "SEO", "Structured Data" ], "textdomain": "wordpress-seo", "attributes": { "questions": { "type": "array" }, "additionalListCssClasses": { "type": "string" } }, "example": { "attributes": { "questions": [ { "id": "faq-question-1", "question": "", "answer": "", "images": [] }, { "id": "faq-question-2", "question": "", "answer": "", "images": [] } ] } }, "editorScript": "yoast-seo-faq-block", "editorStyle": "yoast-seo-structured-data-blocks" } PK ���\�{� ; ; / blocks/structured-data-blocks/how-to/block.jsonnu �[��� { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "version": "22.7", "name": "yoast/how-to-block", "title": "Yoast How-to", "description": "Create a How-to guide in an SEO-friendly way. You can only use one How-to block per post.", "category": "yoast-structured-data-blocks", "icon": "editor-ol", "keywords": [ "How-to", "How to", "Schema", "SEO", "Structured Data" ], "supports": { "multiple": false }, "textdomain": "wordpress-seo", "attributes": { "hasDuration": { "type": "boolean" }, "days": { "type": "string" }, "hours": { "type": "string" }, "minutes": { "type": "string" }, "description": { "type": "string", "source": "html", "selector": ".schema-how-to-description" }, "jsonDescription": { "type": "string" }, "steps": { "type": "array" }, "additionalListCssClasses": { "type": "string" }, "unorderedList": { "type": "boolean" }, "durationText": { "type": "string" }, "defaultDurationText": { "type": "string" } }, "example": { "attributes": { "steps": [ { "id": "how-to-step-example-1", "name": "", "text": "", "images": [] }, { "id": "how-to-step-example-2", "name": "", "text": "", "images": [] } ] } }, "editorScript": "yoast-seo-how-to-block", "editorStyle": "yoast-seo-structured-data-blocks" } PK ���\_o=�} } wp-seo.phpnu �[��� <?php /** * Yoast SEO Plugin. * * @package WPSEO\Main * @copyright Copyright (C) 2008-2024, Yoast BV - support@yoast.com * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 or higher * * @wordpress-plugin * Plugin Name: Yoast SEO * Version: 27.1.1 * Plugin URI: https://yoa.st/1uj * Description: The first true all-in-one SEO solution for WordPress, including on-page content analysis, XML sitemaps and much more. * Author: Team Yoast * Author URI: https://yoa.st/1uk * Text Domain: wordpress-seo * Domain Path: /languages/ * License: GPL v3 * Requires at least: 6.8 * Requires PHP: 7.4 * * WC requires at least: 7.1 * WC tested up to: 10.5 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if ( ! function_exists( 'add_filter' ) ) { header( 'Status: 403 Forbidden' ); header( 'HTTP/1.1 403 Forbidden' ); exit(); } if ( ! defined( 'WPSEO_FILE' ) ) { define( 'WPSEO_FILE', __FILE__ ); } // Load the Yoast SEO plugin. require_once dirname( WPSEO_FILE ) . '/wp-seo-main.php'; PK ���\��-��5 �5 / vendor_prefixed/guzzlehttp/guzzle/src/Utils.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp; use YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException; use YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler; use YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler; use YoastSEO_Vendor\GuzzleHttp\Handler\Proxy; use YoastSEO_Vendor\GuzzleHttp\Handler\StreamHandler; use YoastSEO_Vendor\Psr\Http\Message\UriInterface; final class Utils { /** * Debug function used to describe the provided value type and class. * * @param mixed $input * * @return string Returns a string containing the type of the variable and * if a class is provided, the class name. */ public static function describeType($input) : string { switch (\gettype($input)) { case 'object': return 'object(' . \get_class($input) . ')'; case 'array': return 'array(' . \count($input) . ')'; default: \ob_start(); \var_dump($input); // normalize float vs double /** @var string $varDumpContent */ $varDumpContent = \ob_get_clean(); return \str_replace('double(', 'float(', \rtrim($varDumpContent)); } } /** * Parses an array of header lines into an associative array of headers. * * @param iterable $lines Header lines array of strings in the following * format: "Name: Value" */ public static function headersFromLines(iterable $lines) : array { $headers = []; foreach ($lines as $line) { $parts = \explode(':', $line, 2); $headers[\trim($parts[0])][] = isset($parts[1]) ? \trim($parts[1]) : null; } return $headers; } /** * Returns a debug stream based on the provided variable. * * @param mixed $value Optional value * * @return resource */ public static function debugResource($value = null) { if (\is_resource($value)) { return $value; } if (\defined('STDOUT')) { return \STDOUT; } return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w'); } /** * Chooses and creates a default handler to use based on the environment. * * The returned handler is not wrapped by any default middlewares. * * @return callable(\Psr\Http\Message\RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system. * * @throws \RuntimeException if no viable Handler is available. */ public static function chooseHandler() : callable { $handler = null; if (\defined('CURLOPT_CUSTOMREQUEST') && \function_exists('curl_version') && \version_compare(\curl_version()['version'], '7.21.2') >= 0) { if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) { $handler = \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapSync(new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler(), new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler()); } elseif (\function_exists('curl_exec')) { $handler = new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlHandler(); } elseif (\function_exists('curl_multi_exec')) { $handler = new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlMultiHandler(); } } if (\ini_get('allow_url_fopen')) { $handler = $handler ? \YoastSEO_Vendor\GuzzleHttp\Handler\Proxy::wrapStreaming($handler, new \YoastSEO_Vendor\GuzzleHttp\Handler\StreamHandler()) : new \YoastSEO_Vendor\GuzzleHttp\Handler\StreamHandler(); } elseif (!$handler) { throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.'); } return $handler; } /** * Get the default User-Agent string to use with Guzzle. */ public static function defaultUserAgent() : string { return \sprintf('GuzzleHttp/%d', \YoastSEO_Vendor\GuzzleHttp\ClientInterface::MAJOR_VERSION); } /** * Returns the default cacert bundle for the current system. * * First, the openssl.cafile and curl.cainfo php.ini settings are checked. * If those settings are not configured, then the common locations for * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X * and Windows are checked. If any of these file locations are found on * disk, they will be utilized. * * Note: the result of this function is cached for subsequent calls. * * @throws \RuntimeException if no bundle can be found. * * @deprecated Utils::defaultCaBundle will be removed in guzzlehttp/guzzle:8.0. This method is not needed in PHP 5.6+. */ public static function defaultCaBundle() : string { static $cached = null; static $cafiles = [ // Red Hat, CentOS, Fedora (provided by the ca-certificates package) '/etc/pki/tls/certs/ca-bundle.crt', // Ubuntu, Debian (provided by the ca-certificates package) '/etc/ssl/certs/ca-certificates.crt', // FreeBSD (provided by the ca_root_nss package) '/usr/local/share/certs/ca-root-nss.crt', // SLES 12 (provided by the ca-certificates package) '/var/lib/ca-certificates/ca-bundle.pem', // OS X provided by homebrew (using the default path) '/usr/local/etc/openssl/cert.pem', // Google app engine '/etc/ca-certificates.crt', // Windows? 'C:\\windows\\system32\\curl-ca-bundle.crt', 'C:\\windows\\curl-ca-bundle.crt', ]; if ($cached) { return $cached; } if ($ca = \ini_get('openssl.cafile')) { return $cached = $ca; } if ($ca = \ini_get('curl.cainfo')) { return $cached = $ca; } foreach ($cafiles as $filename) { if (\file_exists($filename)) { return $cached = $filename; } } throw new \RuntimeException(<<<EOT No system CA bundle could be found in any of the the common system locations. PHP versions earlier than 5.6 are not properly configured to use the system's CA bundle by default. In order to verify peer certificates, you will need to supply the path on disk to a certificate bundle to the 'verify' request option: https://docs.guzzlephp.org/en/latest/request-options.html#verify. If you do not need a specific certificate bundle, then Mozilla provides a commonly used CA bundle which can be downloaded here (provided by the maintainer of cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to the file, allowing you to omit the 'verify' request option. See https://curl.haxx.se/docs/sslcerts.html for more information. EOT ); } /** * Creates an associative array of lowercase header names to the actual * header casing. */ public static function normalizeHeaderKeys(array $headers) : array { $result = []; foreach (\array_keys($headers) as $key) { $result[\strtolower($key)] = $key; } return $result; } /** * Returns true if the provided host matches any of the no proxy areas. * * This method will strip a port from the host if it is present. Each pattern * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" == * "baz.foo.com", but ".foo.com" != "foo.com"). * * Areas are matched in the following cases: * 1. "*" (without quotes) always matches any hosts. * 2. An exact match. * 3. The area starts with "." and the area is the last part of the host. e.g. * '.mit.edu' will match any host that ends with '.mit.edu'. * * @param string $host Host to check against the patterns. * @param string[] $noProxyArray An array of host patterns. * * @throws InvalidArgumentException */ public static function isHostInNoProxy(string $host, array $noProxyArray) : bool { if (\strlen($host) === 0) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Empty host provided'); } // Strip port if present. [$host] = \explode(':', $host, 2); foreach ($noProxyArray as $area) { // Always match on wildcards. if ($area === '*') { return \true; } if (empty($area)) { // Don't match on empty values. continue; } if ($area === $host) { // Exact matches. return \true; } // Special match if the area when prefixed with ".". Remove any // existing leading "." and add a new leading ".". $area = '.' . \ltrim($area, '.'); if (\substr($host, -\strlen($area)) === $area) { return \true; } } return \false; } /** * Wrapper for json_decode that throws when an error occurs. * * @param string $json JSON data to parse * @param bool $assoc When true, returned objects will be converted * into associative arrays. * @param int $depth User specified recursion depth. * @param int $options Bitmask of JSON decode options. * * @return object|array|string|int|float|bool|null * * @throws InvalidArgumentException if the JSON cannot be decoded. * * @see https://www.php.net/manual/en/function.json-decode.php */ public static function jsonDecode(string $json, bool $assoc = \false, int $depth = 512, int $options = 0) { $data = \json_decode($json, $assoc, $depth, $options); if (\JSON_ERROR_NONE !== \json_last_error()) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('json_decode error: ' . \json_last_error_msg()); } return $data; } /** * Wrapper for JSON encoding that throws when an error occurs. * * @param mixed $value The value being encoded * @param int $options JSON encode option bitmask * @param int $depth Set the maximum depth. Must be greater than zero. * * @throws InvalidArgumentException if the JSON cannot be encoded. * * @see https://www.php.net/manual/en/function.json-encode.php */ public static function jsonEncode($value, int $options = 0, int $depth = 512) : string { $json = \json_encode($value, $options, $depth); if (\JSON_ERROR_NONE !== \json_last_error()) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('json_encode error: ' . \json_last_error_msg()); } /** @var string */ return $json; } /** * Wrapper for the hrtime() or microtime() functions * (depending on the PHP version, one of the two is used) * * @return float UNIX timestamp * * @internal */ public static function currentTime() : float { return (float) \function_exists('hrtime') ? \hrtime(\true) / 1000000000.0 : \microtime(\true); } /** * @throws InvalidArgumentException * * @internal */ public static function idnUriConvert(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, int $options = 0) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { if ($uri->getHost()) { $asciiHost = self::idnToAsci($uri->getHost(), $options, $info); if ($asciiHost === \false) { $errorBitSet = $info['errors'] ?? 0; $errorConstants = \array_filter(\array_keys(\get_defined_constants()), static function (string $name) : bool { return \substr($name, 0, 11) === 'IDNA_ERROR_'; }); $errors = []; foreach ($errorConstants as $errorConstant) { if ($errorBitSet & \constant($errorConstant)) { $errors[] = $errorConstant; } } $errorMessage = 'IDN conversion failed'; if ($errors) { $errorMessage .= ' (errors: ' . \implode(', ', $errors) . ')'; } throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException($errorMessage); } if ($uri->getHost() !== $asciiHost) { // Replace URI only if the ASCII version is different $uri = $uri->withHost($asciiHost); } } return $uri; } /** * @internal */ public static function getenv(string $name) : ?string { if (isset($_SERVER[$name])) { return (string) $_SERVER[$name]; } if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== \false && $value !== null) { return (string) $value; } return null; } /** * @return string|false */ private static function idnToAsci(string $domain, int $options, ?array &$info = []) { if (\function_exists('idn_to_ascii') && \defined('INTL_IDNA_VARIANT_UTS46')) { return \idn_to_ascii($domain, $options, \INTL_IDNA_VARIANT_UTS46, $info); } throw new \Error('ext-idn or symfony/polyfill-intl-idn not loaded or too old'); } } PK ���\�tw�� � D vendor_prefixed/guzzlehttp/guzzle/src/Exception/RequestException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; use YoastSEO_Vendor\GuzzleHttp\BodySummarizer; use YoastSEO_Vendor\GuzzleHttp\BodySummarizerInterface; use YoastSEO_Vendor\Psr\Http\Client\RequestExceptionInterface; use YoastSEO_Vendor\Psr\Http\Message\RequestInterface; use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface; /** * HTTP Request exception */ class RequestException extends \YoastSEO_Vendor\GuzzleHttp\Exception\TransferException implements \YoastSEO_Vendor\Psr\Http\Client\RequestExceptionInterface { /** * @var RequestInterface */ private $request; /** * @var ResponseInterface|null */ private $response; /** * @var array */ private $handlerContext; public function __construct(string $message, \YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, ?\Throwable $previous = null, array $handlerContext = []) { // Set the code of the exception if the response is set and not future. $code = $response ? $response->getStatusCode() : 0; parent::__construct($message, $code, $previous); $this->request = $request; $this->response = $response; $this->handlerContext = $handlerContext; } /** * Wrap non-RequestExceptions with a RequestException */ public static function wrapException(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \Throwable $e) : \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException { return $e instanceof \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException ? $e : new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException($e->getMessage(), $request, null, $e); } /** * Factory method to create a new exception with a normalized error message * * @param RequestInterface $request Request sent * @param ResponseInterface $response Response received * @param \Throwable|null $previous Previous exception * @param array $handlerContext Optional handler context * @param BodySummarizerInterface|null $bodySummarizer Optional body summarizer */ public static function create(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, ?\Throwable $previous = null, array $handlerContext = [], ?\YoastSEO_Vendor\GuzzleHttp\BodySummarizerInterface $bodySummarizer = null) : self { if (!$response) { return new self('Error completing request', $request, null, $previous, $handlerContext); } $level = (int) \floor($response->getStatusCode() / 100); if ($level === 4) { $label = 'Client error'; $className = \YoastSEO_Vendor\GuzzleHttp\Exception\ClientException::class; } elseif ($level === 5) { $label = 'Server error'; $className = \YoastSEO_Vendor\GuzzleHttp\Exception\ServerException::class; } else { $label = 'Unsuccessful request'; $className = __CLASS__; } $uri = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::redactUserInfo($request->getUri()); // Client Error: `GET /` resulted in a `404 Not Found` response: // <html> ... (truncated) $message = \sprintf('%s: `%s %s` resulted in a `%s %s` response', $label, $request->getMethod(), $uri->__toString(), $response->getStatusCode(), $response->getReasonPhrase()); $summary = ($bodySummarizer ?? new \YoastSEO_Vendor\GuzzleHttp\BodySummarizer())->summarize($response); if ($summary !== null) { $message .= ":\n{$summary}\n"; } return new $className($message, $request, $response, $previous, $handlerContext); } /** * Get the request that caused the exception */ public function getRequest() : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return $this->request; } /** * Get the associated response */ public function getResponse() : ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->response; } /** * Check if a response was received */ public function hasResponse() : bool { return $this->response !== null; } /** * Get contextual information about the error from the underlying handler. * * The contents of this array will vary depending on which handler you are * using. It may also be just an empty array. Relying on this data will * couple you to a specific handler, but can give more debug information * when needed. */ public function getHandlerContext() : array { return $this->handlerContext; } } PK ���\JH� D vendor_prefixed/guzzlehttp/guzzle/src/Exception/ConnectException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; use YoastSEO_Vendor\Psr\Http\Client\NetworkExceptionInterface; use YoastSEO_Vendor\Psr\Http\Message\RequestInterface; /** * Exception thrown when a connection cannot be established. * * Note that no response is present for a ConnectException */ class ConnectException extends \YoastSEO_Vendor\GuzzleHttp\Exception\TransferException implements \YoastSEO_Vendor\Psr\Http\Client\NetworkExceptionInterface { /** * @var RequestInterface */ private $request; /** * @var array */ private $handlerContext; public function __construct(string $message, \YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, ?\Throwable $previous = null, array $handlerContext = []) { parent::__construct($message, 0, $previous); $this->request = $request; $this->handlerContext = $handlerContext; } /** * Get the request that caused the exception */ public function getRequest() : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return $this->request; } /** * Get contextual information about the error from the underlying handler. * * The contents of this array will vary depending on which handler you are * using. It may also be just an empty array. Relying on this data will * couple you to a specific handler, but can give more debug information * when needed. */ public function getHandlerContext() : array { return $this->handlerContext; } } PK ���\].gW� � C vendor_prefixed/guzzlehttp/guzzle/src/Exception/ClientException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; /** * Exception when a client error is encountered (4xx codes) */ class ClientException extends \YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException { } PK ���\ǥ�� � C vendor_prefixed/guzzlehttp/guzzle/src/Exception/GuzzleException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; use YoastSEO_Vendor\Psr\Http\Client\ClientExceptionInterface; interface GuzzleException extends \YoastSEO_Vendor\Psr\Http\Client\ClientExceptionInterface { } PK ���\��Ofh h H vendor_prefixed/guzzlehttp/guzzle/src/Exception/BadResponseException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; use YoastSEO_Vendor\Psr\Http\Message\RequestInterface; use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface; /** * Exception when an HTTP error occurs (4xx or 5xx error) */ class BadResponseException extends \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException { public function __construct(string $message, \YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response, ?\Throwable $previous = null, array $handlerContext = []) { parent::__construct($message, $request, $response, $previous, $handlerContext); } /** * Current exception and the ones that extend it will always have a response. */ public function hasResponse() : bool { return \true; } /** * This function narrows the return type from the parent class and does not allow it to be nullable. */ public function getResponse() : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { /** @var ResponseInterface */ return parent::getResponse(); } } PK ���\V�:e� � C vendor_prefixed/guzzlehttp/guzzle/src/Exception/ServerException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; /** * Exception when a server error is encountered (5xx codes) */ class ServerException extends \YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException { } PK ���\�ix�� � E vendor_prefixed/guzzlehttp/guzzle/src/Exception/TransferException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; class TransferException extends \RuntimeException implements \YoastSEO_Vendor\GuzzleHttp\Exception\GuzzleException { } PK ���\aO�� � M vendor_prefixed/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; class TooManyRedirectsException extends \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException { } PK ���\2̈@� � L vendor_prefixed/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp\Exception; final class InvalidArgumentException extends \InvalidArgumentException implements \YoastSEO_Vendor\GuzzleHttp\Exception\GuzzleException { } PK ���\�{"x x : vendor_prefixed/guzzlehttp/guzzle/src/MessageFormatter.phpnu �[��� <?php namespace YoastSEO_Vendor\GuzzleHttp; use YoastSEO_Vendor\Psr\Http\Message\MessageInterface; use YoastSEO_Vendor\Psr\Http\Message\RequestInterface; use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface; /** * Formats log messages using variable substitutions for requests, responses, * and other transactional data. * * The following variable substitutions are supported: * * - {request}: Full HTTP request message * - {response}: Full HTTP response message * - {ts}: ISO 8601 date in GMT * - {date_iso_8601} ISO 8601 date in GMT * - {date_common_log} Apache common log date using the configured timezone. * - {host}: Host of the request * - {method}: Method of the request * - {uri}: URI of the request * - {version}: Protocol version * - {target}: Request target of the request (path + query + fragment) * - {hostname}: Hostname of the machine that sent the request * - {code}: Status code of the response (if available) * - {phrase}: Reason phrase of the response (if available) * - {error}: Any error messages (if available) * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message * - {req_headers}: Request headers * - {res_headers}: Response headers * - {req_body}: Request body * - {res_body}: Response body * * @final */ class MessageFormatter implements \YoastSEO_Vendor\GuzzleHttp\MessageFormatterInterface { /** * Apache Common Log Format. * * @see https://httpd.apache.org/docs/2.4/logs.html#common * * @var string */ public const CLF = '{hostname} {req_header_User-Agent} - [{date_common_log}] "{method} {target} HTTP/{version}" {code} {res_header_Content-Length}'; public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; /** * @var string Template used to format log messages */ private $template; /** * @param string $template Log message template */ public function __construct(?string $template = self::CLF) { $this->template = $template ?: self::CLF; } /** * Returns a formatted message string. * * @param RequestInterface $request Request that was sent * @param ResponseInterface|null $response Response that was received * @param \Throwable|null $error Exception that was received */ public function format(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, ?\Throwable $error = null) : string { $cache = []; /** @var string */ return \preg_replace_callback('/{\\s*([A-Za-z_\\-\\.0-9]+)\\s*}/', function (array $matches) use($request, $response, $error, &$cache) { if (isset($cache[$matches[1]])) { return $cache[$matches[1]]; } $result = ''; switch ($matches[1]) { case 'request': $result = \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::toString($request); break; case 'response': $result = $response ? \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::toString($response) : ''; break; case 'req_headers': $result = \trim($request->getMethod() . ' ' . $request->getRequestTarget()) . ' HTTP/' . $request->getProtocolVersion() . "\r\n" . $this->headers($request); break; case 'res_headers': $result = $response ? \sprintf('HTTP/%s %d %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase()) . "\r\n" . $this->headers($response) : 'NULL'; break; case 'req_body': $result = $request->getBody()->__toString(); break; case 'res_body': if (!$response instanceof \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface) { $result = 'NULL'; break; } $body = $response->getBody(); if (!$body->isSeekable()) { $result = 'RESPONSE_NOT_LOGGEABLE'; break; } $result = $response->getBody()->__toString(); break; case 'ts': case 'date_iso_8601': $result = \gmdate('c'); break; case 'date_common_log': $result = \date('d/M/Y:H:i:s O'); break; case 'method': $result = $request->getMethod(); break; case 'version': $result = $request->getProtocolVersion(); break; case 'uri': case 'url': $result = $request->getUri()->__toString(); break; case 'target': $result = $request->getRequestTarget(); break; case 'req_version': $result = $request->getProtocolVersion(); break; case 'res_version': $result = $response ? $response->getProtocolVersion() : 'NULL'; break; case 'host': $result = $request->getHeaderLine('Host'); break; case 'hostname': $result = \gethostname(); break; case 'code': $result = $response ? $response->getStatusCode() : 'NULL'; break; case 'phrase': $result = $response ? $response->getReasonPhrase() : 'NULL'; break; case 'error': $result = $error ? $error->getMessage() : 'NULL'; break; default: // handle prefixed dynamic headers if (\strpos($matches[1], 'req_header_') === 0) { $result = $request->getHeaderLine(\substr($matches[1], 11)); } elseif (\strpos($matches[1], 'res_header_') === 0) { $result = $response ? $response->getHeaderLine(\substr($matches[1], 11)) : 'NULL'; } } $cache[$matches[1]] = $result; return $result; }, $this->template); } /** * Get headers from message as string */ private function headers(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message) : string { $result = ''; foreach ($message->getHeaders() as $name => $values) { $result .= $name . ': ' . \implode(', ', $values) . "\r\n"; } return \trim($result); } } PK ���\� ��� � ; vendor_prefixed/guzzlehttp/guzzle/src/functions_include.phpnu �[��� <?php namespace YoastSEO_Vendor; // Don't redefine the functions if included multiple times. if (!\function_exists('YoastSEO_Vendor\\GuzzleHttp\\describe_type')) { require __DIR__ . '/functions.php'; } PK ���\A�j! j! <