PluginProbe
FluentSnippets – High-Performance Code Snippets, Header & Footer Code, Custom CSS & PHP Code Manager / 10.53
FluentSnippets – High-Performance Code Snippets, Header & Footer Code, Custom CSS & PHP Code Manager v10.53
10.56 1.2.1 10 10.1 10.2 10.3 10.31 10.32 10.33 10.34 10.50 10.51 10.52 10.53 10.55 9.0 9.0.1 9.4 trunk 1.0.0 1.1 1.2
easy-code-manager / app / Services / CodeRunner.php

CodeRunner.php in FluentSnippets – High-Performance Code Snippets, Header & Footer Code, Custom CSS & PHP Code Manager 10.53, at app/Services/CodeRunner.php

331 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSnippets\App\Services;
4
5 class CodeRunner
6 {
7
8 private $storageDir = '';
9
10 public function __construct()
11 {
12 $this->storageDir = WP_CONTENT_DIR . '/fluent-snippet-storage';
13 }
14
15 public function runSnippets()
16 {
17 if (!is_file($this->storageDir . '/index.php')) {
18 return;
19 }
20
21 $config = include $this->storageDir . '/index.php';
22
23 if (empty($config) || empty($config['published']) || !is_array($config['published'])) {
24 return; // No config or published scripts exist exists
25 }
26
27 if (isset($config['meta']['force_disabled']) && $config['meta']['force_disabled'] == 'yes') {
28 return; // this forcefully disabled via URL
29 }
30
31 $errorFiles = $this->get($config, 'error_files', []);
32
33 $snippets = $config['published'];
34
35 if (!$snippets) {
36 return;
37 }
38
39 $storageDir = $this->storageDir;
40
41 $hasInvalidFiles = false;
42
43 $conditionalClass = new FluentSnippetCondition();
44
45 $filterMaps = [
46 'before_content' => [
47 'hook' => 'the_content',
48 'insert' => 'before',
49 'is_single' => true
50 ],
51 'after_content' => [
52 'hook' => 'the_content',
53 'insert' => 'after',
54 'is_single' => true
55 ],
56 ];
57
58 foreach ($snippets as $fileName => $snippet) {
59 if (isset($_REQUEST['fluent_saving_snippet_name'])) {
60 if ($_REQUEST['fluent_saving_snippet_name'] === $fileName && current_user_can('manage_options')) {
61 continue;
62 }
63 }
64
65 if ($errorFiles && isset($errorFiles[$fileName])) {
66 // There has an error. Skip this
67 continue;
68 }
69
70 $file = $storageDir . '/' . sanitize_file_name($fileName);
71 if (!is_file($file)) {
72 $hasInvalidFiles = true;
73 continue;
74 }
75
76 $type = $this->get($snippet, 'type');
77
78 switch ($type) {
79 case 'PHP':
80 $conditionSettings = $snippet['condition'];
81 $hookName = 'wp';
82
83 if (empty($conditionSettings) || empty($conditionSettings['status']) || $conditionSettings['status'] != 'yes' || empty($conditionSettings['items'])) {
84 $hookName = 'setup_theme';
85 }
86
87 add_action($hookName, function () use ($file, $snippet, $conditionalClass) {
88 if (!$conditionalClass->evaluate($snippet['condition'])) {
89 return;
90 }
91
92 $runAt = $this->get($snippet, 'run_at', 'all');
93 if ($runAt == 'backend') {
94 if (is_admin()) {
95 require_once $file;
96 }
97 return;
98 }
99
100 require_once $file;
101 }, $this->get($snippet, 'priority', 10));
102
103 break;
104 case 'js':
105 $runAt = $this->get($snippet, 'run_at', 'wp_footer');
106 if (in_array($runAt, ['wp_head', 'wp_footer', 'admin_head', 'admin_footer'])) {
107
108 $loadUrl = '';
109 $isFooter = false;
110
111 if ($this->get($snippet, 'load_as_file') == 'yes') {
112 $cachedFile = str_replace('.php', '.js', $fileName);
113 $loadUrl = $this->getCachedFileUrl($cachedFile);
114 if ($loadUrl) {
115 $isFooter = ($runAt == 'wp_footer' || $runAt == 'admin_footer');
116 $runAt = ($runAt == 'admin_head' || $runAt == 'admin_footer') ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts';
117 }
118 }
119
120 add_action($runAt, function () use ($file, $snippet, $conditionalClass, $loadUrl, $isFooter) {
121 if (!$conditionalClass->evaluate($snippet['condition'])) {
122 return;
123 }
124
125 if ($loadUrl) {
126 $snippetScriptName = str_replace('.php', '', $snippet['file_name']);
127 wp_enqueue_script('fluent_snippet_' . $snippetScriptName, $loadUrl, [], strtotime($snippet['updated_at']), $isFooter);
128 } else {
129 $code = $this->parseBlock(file_get_contents($file), true);
130 ?>
131 <script><?php echo $this->escCssJs($code); // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped ?></script>
132 <?php
133 }
134 }, $this->get($snippet, 'priority', 10));
135 }
136 break;
137 case 'css':
138 $runAt = $this->get($snippet, 'run_at', 'wp_head');
139
140 $isBlockStyle = $this->get($snippet, 'load_in_block_editor', '') === 'yes';
141 if ($isBlockStyle) {
142 add_filter('block_editor_settings_all', function ($settings) use ($snippet, $file) {
143 $code = $this->parseBlock(file_get_contents($file), true);
144 if ($code) {
145 $settings['styles'][] = array(
146 'css' => $code,
147 '__unstableType' => 'plugin',
148 'source' => 'easy_code_manager'
149 );
150 }
151 return $settings;
152 }, $this->get($snippet, 'priority', 10));
153 }
154
155 if (($runAt == 'everywehere' && is_admin()) || $runAt == 'admin_head') {
156 $runAt = 'admin_head';
157 } else {
158 $runAt = 'wp_head';
159 }
160
161 $isAdminCss = ($runAt == 'admin_head');
162
163 $loadUrl = '';
164 if ($this->get($snippet, 'load_as_file') == 'yes') {
165 $cachedFile = str_replace('.php', '.css', $fileName);
166 $loadUrl = $this->getCachedFileUrl($cachedFile);
167 if ($loadUrl) {
168 $runAt = ($runAt == 'admin_head') ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts';
169 }
170 }
171
172 if ($isAdminCss) {
173 add_action('enqueue_block_editor_assets', function () use ($file, $snippet, $conditionalClass, $loadUrl) {
174 if (!$conditionalClass->evaluate($snippet['condition'])) {
175 return;
176 }
177
178 $code = $this->parseBlock(file_get_contents($file), true);
179 wp_add_inline_style('wp-edit-blocks', $code);
180 });
181 }
182
183 add_action($runAt, function () use ($file, $snippet, $conditionalClass, $loadUrl) {
184 if (!$conditionalClass->evaluate($snippet['condition'])) {
185 return;
186 }
187
188 if ($loadUrl) {
189 $snippetScriptName = str_replace('.php', '', $snippet['file_name']);
190 wp_enqueue_style('fluent_snippet_' . $snippetScriptName, $loadUrl, [], strtotime($snippet['updated_at']));
191 } else {
192 $code = $this->parseBlock(file_get_contents($file), true);
193 ?>
194 <style><?php echo $this->escCssJs($code); // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped ?></style>
195 <?php
196 }
197
198 }, $this->get($snippet, 'priority', 10));
199
200 break;
201 case 'php_content':
202 $runAt = $snippet['run_at'];
203 if (in_array($runAt, ['wp_footer', 'wp_head', 'wp_body_open'])) {
204 add_action($runAt, function () use ($file, $snippet, $conditionalClass) {
205 if (!$conditionalClass->evaluate($snippet['condition'])) {
206 return;
207 }
208 require_once $file;
209 }, $snippet['priority']);
210 }
211 if (isset($filterMaps[$runAt])) {
212 $filter = $filterMaps[$runAt];
213 add_filter($filter['hook'], function ($content) use ($file, $snippet, $conditionalClass, $filter) {
214 if (!empty($filter['is_single'])) {
215 if (!is_singular() || !in_the_loop() || !is_main_query()) {
216 return $content;
217 }
218 }
219
220 if (!$conditionalClass->evaluate($snippet['condition'])) {
221 return $content;
222 }
223
224 ob_start();
225 require_once $file;
226 $result = ob_get_clean();
227 if ($result) {
228 if ($filter['insert'] == 'before') {
229 return $result . $content;
230 }
231
232 return $content . $result;
233 }
234 return $content;
235 }, $this->get($snippet, 'priority', 10));
236 }
237 default:
238 break;
239 }
240 }
241
242 if ($hasInvalidFiles) {
243 do_action('fluent_snippets/rebuild_index', false, true);
244 }
245
246 do_action('fluent_snippets/after_run_snippets');
247 }
248
249
250 private function get($array, $key, $default = null)
251 {
252 if (isset($array[$key])) {
253 return $array[$key];
254 }
255
256 return $default;
257 }
258
259 private function parseBlock($fileContent, $codeOnly = false)
260 {
261 // get content from // <Internal Doc Start> to // <Internal Doc End>
262 $fileContent = explode('// <Internal Doc Start>', $fileContent);
263
264 if (count($fileContent) < 2) {
265 if ($codeOnly) {
266 return '';
267 }
268 return [null, null];
269 }
270
271 $fileContent = explode('// <Internal Doc End> ?>' . PHP_EOL, $fileContent[1]);
272 $docBlock = $fileContent[0];
273 $code = $fileContent[1];
274
275 if ($codeOnly) {
276 return $code;
277 }
278
279 $docBlock = explode('*', $docBlock);
280 // Explode by : and get the key and value
281 $docBlockArray = [
282 'name' => '',
283 'status' => '',
284 'tags' => '',
285 'description' => '',
286 'type' => '',
287 'run_at' => '',
288 'group' => ''
289 ];
290
291 foreach ($docBlock as $key => $value) {
292 $value = trim($value);
293 $arr = explode(':', $value);
294 if (count($arr) < 2) {
295 continue;
296 }
297
298 // get the first item from the array and remove it from $arr
299 $key = array_shift($arr);
300 $key = trim(str_replace('@', '', $key));
301 if (!$key) {
302 continue;
303 }
304 $docBlockArray[$key] = trim(implode(':', $arr));
305 }
306
307 return [$docBlockArray, $code];
308 }
309
310
311 private function escCssJs($code)
312 {
313 $code = preg_replace('/<script[^>]*>/', '', $code);
314 $code = preg_replace('/<\/script>/', '', $code);
315 // remove opening js tag and closing js tag maybe <script type="text/javascript"> too
316 $code = preg_replace('/<style[^>]*>/', '', $code);
317 return preg_replace('/<\/style>/', '', $code);
318 }
319
320 private function getCachedFileUrl($fileName)
321 {
322 $file = $this->storageDir . '/cached/' . $fileName;
323
324 if (!file_exists($file)) {
325 return false;
326 }
327
328 return content_url('/fluent-snippet-storage/cached/' . $fileName);
329 }
330 }
331