Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Resources
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 4
462
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 register_google_taxonomy_resource
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
90
 retrieve_file
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 filter_google_taxonomies
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2/**
3 * This class manage the Abilities managed like Resources
4 *
5 * @package BLU\Abilities
6 */
7
8namespace BLU\Abilities;
9
10/**
11 * This class create abilities like "resources"
12 */
13class Resources {
14    /**
15     * Constructor - registers resources
16     */
17    public function __construct() {
18
19        $this->register_google_taxonomy_resource();
20    }
21
22    /**
23     * Read the official Google Product Taxonomy and return the results.
24     *
25     * @return void
26     */
27    private function register_google_taxonomy_resource() {
28
29        blu_register_ability(
30            'blu/google-product-taxonomy',
31            array(
32                'label'               => 'Google Product Taxonomy',
33                'description'         => 'The official Google Product Taxonomy resource',
34                'category'            => 'blu-mcp',
35                'input_schema'        => array(
36                    'type'       => 'object',
37                    'properties' => array(
38                        'patterns' => array(
39                            'type'        => 'array',
40                            'description' => 'List of relevant categories or relevant regex keywords based on product name',
41                            'items'       => array( 'type' => 'string' ),
42                            'minItems'    => 1,
43                            'maxItems'    => 5,
44                        ),
45                        'required' => array( 'patterns' ),
46                    ),
47                ),
48                'execute_callback'    => function ( $input ) {
49                    $patterns = $input['patterns'] ?? array();
50
51                    $is_valid = blu_is_valid_input_array( $patterns, 'patterns', 1, 5 );
52                    if ( is_wp_error( $is_valid ) ) {
53                        return blu_standardize_rest_response( $is_valid );
54                    }
55                    $locale = str_replace( '_', '-', get_locale() );
56
57                    $taxonomy = get_transient( 'blu/google-product-taxonomy-' . $locale );
58                    if ( false === $taxonomy ) {
59
60                        $content = $this->retrieve_file( $locale );
61
62                        if ( is_wp_error( $content ) ) {
63                            return blu_standardize_rest_response( $content );
64                        } elseif ( 'not_found' === $content ) {
65                            $content = $this->retrieve_file();
66                            if ( is_wp_error( $content ) ) {
67                                return blu_standardize_rest_response( $content );
68                            }
69                        }
70
71                        // Split into lines
72                        $lines = explode( "\n", $content );
73
74                        $taxonomy = '';
75
76                        foreach ( $lines as $line ) {
77                            $line = trim( $line );
78                            if ( '' === $line || strpos( $line, '#' ) === 0 ) {
79                                continue;
80                            }
81
82                            $line = preg_replace( '/^\d+\s*-\s*/', '', $line );
83
84                            $taxonomy .= $line . "\n";
85                        }
86                        set_transient( 'blu/google-product-taxonomy-' . $locale, $taxonomy, MONTH_IN_SECONDS );
87                    }
88
89                    $filtered = $this->filter_google_taxonomies( $taxonomy, $patterns );
90
91                    return blu_prepare_ability_response( 200, $filtered );
92                },
93                'permission_callback' => function () {
94                    return current_user_can( 'manage_options' );
95                },
96                'meta'                => array(
97                    'annotations' => array(
98                        'readonly'   => true,
99                        'idempotent' => true,
100                    ),
101                ),
102            )
103        );
104    }
105
106
107    /**
108     * Read the google product taxonomy file and get the content
109     *
110     * @param string $locale The locale.
111     *
112     * @return array|string|\WP_Error
113     */
114    private function retrieve_file( $locale = 'en-US' ) {
115        $response = wp_remote_get( 'https://www.google.com/basepages/producttype/taxonomy-with-ids.' . $locale . '.txt' );
116        if ( is_wp_error( $response ) ) {
117            return $response;
118        } elseif ( 404 == wp_remote_retrieve_response_code( $response ) ) { //phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
119            return 'not_found';
120        } else {
121            return wp_remote_retrieve_body( $response );
122        }
123    }
124
125    /**
126     * Filter the Google product taxonomies
127     *
128     * @param string $taxonomy The taxonomy.
129     * @param array  $patterns The patterns
130     *
131     * @return array
132     */
133    private function filter_google_taxonomies( $taxonomy, $patterns ) {
134        $lines = explode( "\n", $taxonomy );
135
136        $filtered = array();
137
138        foreach ( $lines as $line ) {
139            $line = trim( $line );
140
141            if ( '' === $line ) {
142                continue;
143            }
144
145            foreach ( $patterns as $pattern ) {
146
147                if ( @preg_match( $pattern, '' ) !== false ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors
148                    $regex = $pattern;
149                    if ( substr( $regex, - 1 ) !== 'i' ) {
150                        // Ensure case-insensitive
151                        $regex = rtrim( $regex, '/' ) . '/i';
152                    }
153                    if ( preg_match( $regex, $line ) ) {
154                        $filtered[] = $line;
155                        break;
156                    }
157                } elseif ( false !== stripos( $line, $pattern ) ) {
158                    $filtered[] = $line;
159                    break;
160                }
161            }
162        }
163
164        return $filtered;
165    }
166}