%PDF- %PDF-
Direktori : /var/www/knwn/wp-content/plugins/wordpress-seo/src/actions/wincher/ |
Current File : /var/www/knwn/wp-content/plugins/wordpress-seo/src/actions/wincher/wincher-keyphrases-action.php |
<?php namespace Yoast\WP\SEO\Actions\Wincher; use Exception; use WP_Post; use WPSEO_Meta; use Yoast\WP\SEO\Config\Wincher_Client; use Yoast\WP\SEO\Helpers\Options_Helper; use Yoast\WP\SEO\Repositories\Indexable_Repository; /** * Class Wincher_Keyphrases_Action */ class Wincher_Keyphrases_Action { /** * The Wincher keyphrase URL for bulk addition. * * @var string */ const KEYPHRASES_ADD_URL = 'https://api.wincher.com/beta/websites/%s/keywords/bulk'; /** * The Wincher tracked keyphrase retrieval URL. * * @var string */ const KEYPHRASES_URL = 'https://api.wincher.com/beta/yoast/%s'; /** * The Wincher delete tracked keyphrase URL. * * @var string */ const KEYPHRASE_DELETE_URL = 'https://api.wincher.com/beta/websites/%s/keywords/%s'; /** * The Wincher_Client instance. * * @var Wincher_Client */ protected $client; /** * The Options_Helper instance. * * @var Options_Helper */ protected $options_helper; /** * The Indexable_Repository instance. * * @var Indexable_Repository */ protected $indexable_repository; /** * Wincher_Keyphrases_Action constructor. * * @param Wincher_Client $client The API client. * @param Options_Helper $options_helper The options helper. * @param Indexable_Repository $indexable_repository The indexables repository. */ public function __construct( Wincher_Client $client, Options_Helper $options_helper, Indexable_Repository $indexable_repository ) { $this->client = $client; $this->options_helper = $options_helper; $this->indexable_repository = $indexable_repository; } /** * Sends the tracking API request for one or more keyphrases. * * @param string|array $keyphrases One or more keyphrases that should be tracked. * @param Object $limits The limits API call response data. * * @return Object The reponse object. */ public function track_keyphrases( $keyphrases, $limits ) { try { $endpoint = \sprintf( self::KEYPHRASES_ADD_URL, $this->options_helper->get( 'wincher_website_id' ) ); // Enforce arrrays to ensure a consistent way of preparing the request. if ( ! is_array( $keyphrases ) ) { $keyphrases = [ $keyphrases ]; } // Calculate if the user would exceed their limit. // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- To ensure JS code style, this can be ignored. if ( ! $limits->canTrack || $this->would_exceed_limits( $keyphrases, $limits ) ) { $response = [ 'limit' => $limits->limit, 'error' => 'Account limit exceeded', 'status' => 400, ]; return $this->to_result_object( $response ); } $formatted_keyphrases = \array_values( \array_map( function ( $keyphrase ) { return [ 'keyword' => $keyphrase, 'groups' => [], ]; }, $keyphrases ) ); $results = $this->client->post( $endpoint, \WPSEO_Utils::format_json_encode( $formatted_keyphrases ) ); if ( ! \array_key_exists( 'data', $results ) ) { return $this->to_result_object( $results ); } // The endpoint returns a lot of stuff that we don't want/need. $results['data'] = array_map( function( $keyphrase ) { return [ 'id' => $keyphrase['id'], 'keyword' => $keyphrase['keyword'], ]; }, $results['data'] ); $results['data'] = \array_combine( \array_column( $results['data'], 'keyword' ), \array_values( $results['data'] ) ); return $this->to_result_object( $results ); } catch ( Exception $e ) { return (object) [ 'error' => $e->getMessage(), 'status' => $e->getCode(), ]; } } /** * Sends an untrack request for the passed keyword ID. * * @param int $keyphrase_id The ID of the keyphrase to untrack. * * @return object The response object. */ public function untrack_keyphrase( $keyphrase_id ) { try { $endpoint = \sprintf( self::KEYPHRASE_DELETE_URL, $this->options_helper->get( 'wincher_website_id' ), $keyphrase_id ); $this->client->delete( $endpoint ); return (object) [ 'status' => 200, ]; } catch ( Exception $e ) { return (object) [ 'error' => $e->getMessage(), 'status' => $e->getCode(), ]; } } /** * Gets the keyphrase data for the passed keyphrases. * Retrieves all available data if no keyphrases are provided. * * @param array $used_keyphrases The currently used keyphrases. Optional. * @param string $permalink The current permalink. Optional. * * @return object The keyphrase chart data. */ public function get_tracked_keyphrases( $used_keyphrases = null, $permalink = null ) { try { if ( $used_keyphrases === null ) { $used_keyphrases = $this->collect_all_keyphrases(); } $endpoint = \sprintf( self::KEYPHRASES_URL, $this->options_helper->get( 'wincher_website_id' ) ); $results = $this->client->post( $endpoint, \WPSEO_Utils::format_json_encode( [ 'keywords' => $used_keyphrases, 'url' => $permalink, ] ), [ 'timeout' => 60, ] ); if ( ! \array_key_exists( 'data', $results ) ) { return $this->to_result_object( $results ); } if ( ! empty( $used_keyphrases ) ) { $results['data'] = $this->filter_results_by_used_keyphrases( $results['data'], $used_keyphrases ); } // Extract the positional data and assign it to the keyphrase. $results['data'] = \array_combine( \array_column( $results['data'], 'keyword' ), \array_values( $results['data'] ) ); return $this->to_result_object( $results ); } catch ( Exception $e ) { return (object) [ 'error' => $e->getMessage(), 'status' => $e->getCode(), ]; } } /** * Collects the keyphrases associated with the post. * * @param WP_Post $post The post object. * * @return array The keyphrases. */ public function collect_keyphrases_from_post( $post ) { $keyphrases = []; $primary_keyphrase = $this->indexable_repository ->query() ->select( 'primary_focus_keyword' ) ->where( 'object_id', $post->ID ) ->find_one(); if ( $primary_keyphrase ) { $keyphrases[] = $primary_keyphrase->primary_focus_keyword; } if ( YoastSEO()->helpers->product->is_premium() ) { $additional_keywords = \json_decode( WPSEO_Meta::get_value( 'focuskeywords', $post->ID ), true ); $keyphrases = \array_merge( $keyphrases, $additional_keywords ); } return $keyphrases; } /** * Collects all keyphrases known to Yoast. * * @return array */ protected function collect_all_keyphrases() { global $wpdb; // Collect primary keyphrases first. $keyphrases = \array_column( $this->indexable_repository ->query() ->select( 'primary_focus_keyword' ) ->where_not_null( 'primary_focus_keyword' ) ->where( 'object_type', 'post' ) ->where_not_equal( 'post_status', 'trash' ) ->distinct() ->find_array(), 'primary_focus_keyword' ); if ( YoastSEO()->helpers->product->is_premium() ) { // Collect all related keyphrases. $meta_key = WPSEO_Meta::$meta_prefix . 'focuskeywords'; $query = " SELECT meta_value FROM $wpdb->postmeta JOIN $wpdb->posts ON {$wpdb->posts}.id = {$wpdb->postmeta}.post_id WHERE meta_key = '$meta_key' AND post_status != 'trash' "; // phpcs:ignore -- ignoring since it's complaining about not using prepare when it's perfectly safe here. $results = $wpdb->get_results( $query ); if ( $results ) { foreach ( $results as $row ) { $additional_keywords = \json_decode( $row->meta_value, true ); if ( $additional_keywords !== null ) { $additional_keywords = \array_column( $additional_keywords, 'keyword' ); $keyphrases = \array_merge( $keyphrases, $additional_keywords ); } } } } // Filter out empty entries. return \array_filter( $keyphrases ); } /** * Filters the results based on the passed keyphrases. * * @param array $results The results to filter. * @param array $used_keyphrases The used keyphrases. * * @return array The filtered results. */ protected function filter_results_by_used_keyphrases( $results, $used_keyphrases ) { return \array_filter( $results, function( $result ) use ( $used_keyphrases ) { return \in_array( $result['keyword'], \array_map( 'strtolower', $used_keyphrases ), true ); } ); } /** * Determines whether the amount of keyphrases would mean the user exceeds their account limits. * * @param string|array $keyphrases The keyphrases to be added. * @param object $limits The current account limits. * * @return bool Whether the limit is exceeded. */ protected function would_exceed_limits( $keyphrases, $limits ) { if ( ! is_array( $keyphrases ) ) { $keyphrases = [ $keyphrases ]; } if ( \is_null( $limits->limit ) ) { return false; } return ( count( $keyphrases ) + $limits->usage ) > $limits->limit; } /** * Converts the passed dataset to an object. * * @param array $result The result dataset to convert to an object. * * @return object The result object. */ protected function to_result_object( $result ) { if ( \array_key_exists( 'data', $result ) ) { $result['results'] = $result['data']; unset( $result['data'] ); } if ( \array_key_exists( 'message', $result ) ) { $result['error'] = $result['message']; unset( $result['message'] ); } return (object) $result; } }