PluginProbe
Code Snippets / 3.8.1
Code Snippets v3.8.1
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / php / evaluation / class-evaluate-functions.php

class-evaluate-functions.php in Code Snippets 3.8.1, at php/evaluation/class-evaluate-functions.php

194 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Evaluation;
4
5 use Code_Snippets\DB;
6 use Code_Snippets\REST_API\Snippets_REST_Controller;
7 use Code_Snippets\Settings;
8 use Code_Snippets\Snippet_Files;
9 use function Code_Snippets\clean_active_snippets_cache;
10 use function Code_Snippets\clean_snippets_cache;
11 use function Code_Snippets\execute_snippet;
12 use function Code_Snippets\code_snippets;
13 use function Code_Snippets\execute_snippet_from_flat_file;
14
15 /**
16 * Class for evaluating functions snippets.
17 *
18 * @package Code_Snippets
19 */
20 class Evaluate_Functions {
21
22 /**
23 * Database class.
24 *
25 * @var DB
26 */
27 private DB $db;
28
29 /**
30 * Class constructor.
31 *
32 * @param DB $db Database class instance.
33 */
34 public function __construct( DB $db ) {
35 $this->db = $db;
36 add_action( 'plugins_loaded', [ $this, 'evaluate_early' ], 1 );
37 }
38
39 /**
40 * Retrieve details about the currently edited snippet, if any.
41 *
42 * @return ?array{id: int, table: string}
43 */
44 private function get_currently_editing_snippet(): ?array {
45 if ( wp_is_json_request() && ! empty( $_SERVER['REQUEST_URI'] ) ) {
46 $url = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
47
48 if ( isset( $url['path'] ) && false !== strpos( $url['path'], Snippets_REST_Controller::get_prefixed_base_route() ) ) {
49 $path_parts = explode( '/', $url['path'] );
50 $edit_id = intval( end( $path_parts ) );
51
52 if ( ! empty( $url['query'] ) ) {
53 wp_parse_str( $url['query'], $path_params );
54 $edit_table = isset( $path_params['network'] ) && rest_sanitize_boolean( $path_params['network'] )
55 ? $this->db->ms_table
56 : $this->db->table;
57 }
58
59 return [
60 'id' => $edit_id,
61 'table' => $edit_table ?? $this->db->table,
62 ];
63 }
64 }
65
66 return null;
67 }
68
69 /**
70 * Check if the plugin is running in safe mode.
71 *
72 * @return bool
73 *
74 * @noinspection PhpUndefinedConstantInspection
75 */
76 public function is_safe_mode_active(): bool {
77 return ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) ||
78 ! apply_filters( 'code_snippets/execute_snippets', true );
79 }
80
81 /**
82 * Quickly deactivate a snippet with minimal overhead.
83 *
84 * @param int $snippet_id ID of the snippet to deactivate.
85 * @param string $table_name Name of the table where the snippet is stored.
86 *
87 * @return void
88 */
89 private function quick_deactivate_snippet( int $snippet_id, string $table_name ) {
90 global $wpdb;
91
92 $active_shared_ids = get_option( 'active_shared_network_snippets', [] );
93 $active_shared_ids = is_array( $active_shared_ids )
94 ? array_map( 'intval', $active_shared_ids )
95 : [];
96
97 if ( $table_name === $this->db->ms_table && in_array( $snippet_id, $active_shared_ids, true ) ) {
98 unset( $active_shared_ids[ array_search( $snippet_id, $active_shared_ids, true ) ] );
99 $active_shared_ids = array_values( $active_shared_ids );
100 update_option( 'active_shared_network_snippets', $active_shared_ids );
101 clean_active_snippets_cache( $table_name );
102 } else {
103 $wpdb->update(
104 $table_name,
105 [ 'active' => '0' ],
106 [ 'id' => $snippet_id ],
107 [ '%d' ],
108 [ '%d' ]
109 );
110 clean_snippets_cache( $table_name );
111
112 $network = $table_name === $this->db->ms_table;
113 do_action( 'code_snippets/deactivate_snippet', $snippet_id, $network );
114 }
115 }
116
117 private function evaluate_snippet_flat_file( array $snippet, string $file_path, ?array $edit_snippet = null ) {
118 $snippet_id = $snippet['id'];
119 $code = $snippet['code'];
120 $table_name = $snippet['table'];
121
122 // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens.
123 if ( 'single-use' === $snippet['scope'] ) {
124 $this->quick_deactivate_snippet( $snippet_id, $table_name );
125 }
126
127 if ( ! is_null( $edit_snippet ) && $edit_snippet['id'] === $snippet_id && $edit_snippet['table'] === $table_name ) {
128 return;
129 }
130
131 if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) ) {
132 execute_snippet_from_flat_file( $code, $file_path, $snippet_id );
133 }
134 }
135
136 /**
137 * Evaluate applicable active snippets as early as possible.
138 *
139 * @return bool True if snippets were evaluated, false if safe mode is active.
140 */
141 public function evaluate_early(): bool {
142 if ( $this->is_safe_mode_active() ) {
143 return false;
144 }
145
146 if ( Snippet_Files::is_active() ) {
147 return $this->evaluate_file_snippets();
148 }
149
150 return $this->evaluate_db_snippets();
151 }
152
153 public function evaluate_db_snippets(): bool {
154 $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ];
155 $active_snippets = $this->db->fetch_active_snippets( $scopes );
156 $edit_snippet = $this->get_currently_editing_snippet();
157
158 foreach ( $active_snippets as $snippet ) {
159 $snippet_id = $snippet['id'];
160 $code = $snippet['code'];
161 $table_name = $snippet['table'];
162
163 // If the snippet is a single-use snippet, deactivate it before execution to ensure that the process always happens.
164 if ( 'single-use' === $snippet['scope'] ) {
165 $this->quick_deactivate_snippet( $snippet_id, $table_name );
166 }
167
168 if ( apply_filters( 'code_snippets/allow_execute_snippet', true, $snippet_id, $table_name ) &&
169 ( is_null( $edit_snippet ) || $edit_snippet['id'] !== $snippet_id || $edit_snippet['table'] !== $table_name ) ) {
170 execute_snippet( $code, $snippet_id );
171 }
172 }
173
174 return true;
175 }
176
177 private function evaluate_file_snippets(): bool {
178 $type = 'php';
179 $scopes = [ 'global', 'single-use', is_admin() ? 'admin' : 'front-end' ];
180 $snippets = Snippet_Files::get_active_snippets_from_flat_files( $scopes, $type );
181 $edit_snippet = $this->get_currently_editing_snippet();
182
183 foreach ( $snippets as $snippet ) {
184 $table_name = Snippet_Files::get_hashed_table_name( $snippet['table'] );
185 $base_path = Snippet_Files::get_base_dir( $table_name, $type );
186 $file = $base_path . '/' . $snippet['id'] . '.' . $type;
187
188 $this->evaluate_snippet_flat_file( $snippet, $file, $edit_snippet );
189 }
190
191 return true;
192 }
193 }
194