get_api_key(); if ( '' === $api_key ) { throw new \RuntimeException( 'TinEye API key is not configured.' ); } $url = add_query_arg( array( 'api_key' => $api_key, 'image_url' => $file_url, ), self::ENDPOINT ); $response = wp_remote_get( $url, array( 'timeout' => 30 ) ); if ( is_wp_error( $response ) ) { throw new \RuntimeException( $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped } $http_code = (int) wp_remote_retrieve_response_code( $response ); if ( 200 !== $http_code ) { throw new \RuntimeException( sprintf( 'TinEye API returned HTTP %d.', $http_code ) // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped ); } $raw_body = wp_remote_retrieve_body( $response ); $decoded = json_decode( $raw_body, true ); if ( ! is_array( $decoded ) ) { throw new \RuntimeException( 'Failed to parse TinEye API response.' ); } return $this->parse_response( $decoded ); } // ------------------------------------------------------------------------- // Private helpers // ------------------------------------------------------------------------- /** * Reads the TinEye API key from plugin settings. * * @return string Empty string if not configured. */ private function get_api_key(): string { $raw = get_option( Settings::OPTION_NAME, array() ); $opts = is_array( $raw ) ? $raw : array(); $val = $opts['tineye_api_key'] ?? null; return is_string( $val ) ? trim( $val ) : ''; } /** * Parses a decoded TinEye API response into a ScanResult. * * Navigates results.matches, extracts backlink page URLs to build the * domain-frequency map, and counts distinct image matches. * * @param array $decoded json_decode()'d API response. * * @return ScanResult */ private function parse_response( array $decoded ): ScanResult { $results_raw = $decoded['results'] ?? null; $results = is_array( $results_raw ) ? $results_raw : array(); $matches_raw = $results['matches'] ?? null; $matches = is_array( $matches_raw ) ? $matches_raw : array(); $backlink_items = array(); foreach ( $matches as $match ) { if ( ! is_array( $match ) ) { continue; } $bls_raw = $match['backlinks'] ?? null; if ( ! is_array( $bls_raw ) ) { continue; } foreach ( $bls_raw as $bl ) { if ( ! is_array( $bl ) ) { continue; } $url_val = $bl['url'] ?? null; if ( is_string( $url_val ) && '' !== $url_val ) { $backlink_items[] = array( 'url' => $url_val ); } } } $match_count = count( $matches ); $top_domains = $this->extract_top_domains( $backlink_items ); return new ScanResult( $match_count, $top_domains, $decoded ); } }