PluginProbe
Polylang / 3.6.7
Polylang v3.6.7
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / include / filter-rest-routes.php

filter-rest-routes.php in Polylang 3.6.7, at include/filter-rest-routes.php

179 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * Class to manage REST routes filterable by language.
8 *
9 * @since 3.5
10 */
11 class PLL_Filter_REST_Routes {
12 /**
13 * REST routes filterable by language ordered by entity type.
14 *
15 * @var string[]
16 * @phpstan-var array<string, string>
17 */
18 private $filtered_entities = array();
19
20 /**
21 * Other REST routes filterable by language.
22 *
23 * @var string[]
24 * @phpstan-var array<string, string>
25 */
26 private $filtered_routes = array();
27
28 /**
29 * @var PLL_Model
30 */
31 private $model;
32
33 /**
34 * Constructor.
35 *
36 * @since 3.5
37 *
38 * @param PLL_Model $model Shared instance of the current PLL_Model.
39 */
40 public function __construct( PLL_Model $model ) {
41 $this->model = $model;
42
43 // Adds search REST endpoint.
44 $this->filtered_routes['search'] = 'wp/v2/search';
45 }
46
47 /**
48 * Adds query parameters to preload paths.
49 *
50 * @since 3.5
51 *
52 * @param (string|string[])[] $preload_paths Array of paths to preload.
53 * @param array $args Array of query strings to add paired by key/value.
54 * @return (string|string[])[]
55 */
56 public function add_query_parameters( array $preload_paths, array $args ): array {
57 foreach ( $preload_paths as $k => $path ) {
58 if ( empty( $path ) ) {
59 continue;
60 }
61
62 $query_params = array();
63 // If the method request is OPTIONS, $path is an array and the first element is the path
64 if ( is_array( $path ) ) {
65 $temp_path = $path[0];
66 } else {
67 $temp_path = $path;
68 }
69
70 $path_parts = wp_parse_url( $temp_path );
71
72 if ( ! isset( $path_parts['path'] ) || ! $this->is_filtered( $path_parts['path'] ) ) {
73 continue;
74 }
75
76 if ( ! empty( $path_parts['query'] ) ) {
77 parse_str( $path_parts['query'], $query_params );
78 }
79
80 // Add params in query params
81 foreach ( $args as $key => $value ) {
82 $query_params[ $key ] = $value;
83 }
84
85 // Sort query params to put it in the same order as the preloading middleware does
86 ksort( $query_params );
87
88 // Replace the key by the correct path with query params reordered
89 $sorted_path = add_query_arg( urlencode_deep( $query_params ), $path_parts['path'] );
90
91 if ( is_array( $path ) ) {
92 $preload_paths[ $k ][0] = $sorted_path;
93 } else {
94 $preload_paths[ $k ] = $sorted_path;
95 }
96 }
97
98 return $preload_paths;
99 }
100
101 /**
102 * Adds inline script to declare filtered REST route on client side.
103 *
104 * @since 3.5
105 *
106 * @param string $script_handle Name of the script to add the inline script to.
107 * @return void
108 */
109 public function add_inline_script( string $script_handle ) {
110 $script_var = 'let pllFilteredRoutes = ' . (string) wp_json_encode( $this->get() );
111
112 wp_add_inline_script( $script_handle, $script_var, 'before' );
113 }
114
115 /**
116 * Returns filtered REST routes by entity type (e.g. post type or taxonomy).
117 *
118 * @since 3.5
119 *
120 * @return string[] REST routes.
121 * @phpstan-return array<string, string>
122 */
123 private function get(): array {
124 if ( ! empty( $this->filtered_entities ) ) {
125 return array_merge( $this->filtered_entities, $this->filtered_routes );
126 }
127
128 $translatable_post_types = $this->model->get_translated_post_types();
129 $translatable_taxonomies = $this->model->get_translated_taxonomies();
130
131 $post_types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
132 $taxonomies = get_taxonomies( array( 'show_in_rest' => true ), 'objects' );
133
134 $this->extract_filtered_rest_entities(
135 array_merge( $post_types, $taxonomies ),
136 array_merge( $translatable_post_types, $translatable_taxonomies )
137 );
138
139 return array_merge( $this->filtered_entities, $this->filtered_routes );
140 }
141
142 /**
143 * Tells if a given route is fileterable by language.
144 *
145 * @since 3.5
146 *
147 * @param string $rest_route Route to test.
148 * @return bool Whether the route is filterable or not.
149 */
150 private function is_filtered( string $rest_route ): bool {
151 $rest_route = trim( $rest_route );
152
153 return ! preg_match( '/\d+$/', $rest_route ) && in_array( trim( $rest_route, '/' ), $this->get(), true );
154 }
155
156 /**
157 * Extracts filterable REST route from an array of entity objects
158 * from a list of translatable entities (e.g. post types or taxonomies).
159 *
160 * @since 3.5
161 *
162 * @param object[] $rest_entities Array of post type or taxonomy objects.
163 * @param string[] $translatable_entities Array of translatable entity names.
164 * @return void
165 * @phpstan-param array<WP_Post_Type|WP_Taxonomy> $rest_entities
166 */
167 private function extract_filtered_rest_entities( array $rest_entities, array $translatable_entities ) {
168 $this->filtered_entities = array();
169 foreach ( $rest_entities as $rest_entity ) {
170 if ( in_array( $rest_entity->name, $translatable_entities, true ) ) {
171 $rest_base = empty( $rest_entity->rest_base ) ? $rest_entity->name : $rest_entity->rest_base;
172 $rest_namespace = empty( $rest_entity->rest_namespace ) ? 'wp/v2' : $rest_entity->rest_namespace;
173
174 $this->filtered_entities[ $rest_entity->name ] = "{$rest_namespace}/{$rest_base}";
175 }
176 }
177 }
178 }
179