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 / Model / Snippet.php

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

513 lines 15.3 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\Model;
4
5 use FluentSnippets\App\Helpers\Arr;
6 use FluentSnippets\App\Helpers\Helper;
7
8 class Snippet
9 {
10
11 private $args = [];
12
13 public function __construct($args = [])
14 {
15 $this->args = $args;
16 }
17
18 public function get($args = [])
19 {
20 if ($args) {
21 $this->args = $args;
22 }
23
24 $args = $this->args;
25
26 $snippetDir = Helper::getStorageDir();
27 // get the file paths and store them in an array
28 $files = glob($snippetDir . '/*.php');
29
30 if (isset($args['order']) && $args['order'] == 'new_first') {
31 $files = array_reverse($files);
32 }
33
34 $formattedFiles = [];
35 foreach ($files as $file) {
36 $fileContent = file_get_contents($file);
37 [$docBlockArray, $code] = $this->parseBlock($fileContent);
38
39 if (!$docBlockArray) {
40 continue;
41 }
42
43 if (!empty($args['status'])) {
44 if ($args['status'] !== $docBlockArray['status']) {
45 continue;
46 }
47 }
48
49 $formattedFiles[] = [
50 'meta' => $docBlockArray,
51 'code' => $code,
52 'file' => $file,
53 'status' => (!empty($docBlockArray['status'])) ? $docBlockArray['status'] : 'draft'
54 ];
55 }
56 return $formattedFiles;
57 }
58
59 public function paginate($perPage = null, $page = null)
60 {
61 if ($perPage === null) {
62 if (isset($_GET['per_page'])) {
63 $perPage = $_GET['per_page'];
64 } else {
65 $perPage = 10;
66 }
67 }
68
69 if ($page === null) {
70 if (isset($_GET['page'])) {
71 $page = $_GET['page'];
72 } else {
73 $page = 1;
74 }
75 }
76 $offset = ($page - 1) * $perPage;
77
78 $snippets = $this->get([
79 'order' => 'new_first'
80 ]);
81
82 $total = count($snippets);
83 $snippets = array_slice($snippets, $offset, $perPage);
84
85 return [
86 'data' => $snippets,
87 'total' => $total,
88 'per_page' => (int)$perPage,
89 'current_page' => (int)$page,
90 'last_page' => (int)ceil($total / $perPage)
91 ];
92 }
93
94 public function getIndexedSnippets($perPage = null, $page = null)
95 {
96 $config = Helper::getIndexedConfig();
97
98 if (!$config || empty($config['meta'])) {
99 return [];
100 }
101
102 if (empty($config['published']) && empty($config['draft'])) {
103 return [];
104 }
105
106 if (!empty($this->args['status'])) {
107 if ($this->args['status'] == 'published') {
108 $snippets = $config['published'];
109 } else if ($this->args['status'] == 'draft') {
110 $snippets = $config['draft'];
111 } else if ($this->args['status'] == 'paused') {
112 $snippets = array_merge($config['published'], $config['draft']);
113 $errorFiles = Arr::get($config, 'error_files', []);
114 $snippets = Arr::only($snippets, array_keys($errorFiles));
115 } else {
116 $snippets = array_merge($config['published'], $config['draft']);
117 }
118 } else {
119 $snippets = array_merge($config['published'], $config['draft']);
120 }
121
122 if (empty($snippets)) {
123 return [];
124 }
125
126 $errorFiles = Arr::get($config, 'error_files', []);
127 if ($errorFiles) {
128 foreach ($errorFiles as $fileName => $error) {
129 if (isset($snippets[$fileName])) {
130 $snippets[$fileName]['error'] = $error;
131 }
132 }
133 }
134
135 $snippets = array_values($snippets);
136
137 $type = Arr::get($this->args, 'type');
138
139 if ($type && $type != 'all') {
140 $snippets = array_filter($snippets, function ($snippet) use ($type) {
141 return $snippet['type'] == $type;
142 });
143 }
144
145 if ($search = Arr::get($this->args, 'search')) {
146 $snippets = array_filter($snippets, function ($snippet) use ($search) {
147 return (strpos($snippet['name'], $search) !== false) || (strpos($snippet['description'], $search) !== false) || (strpos($snippet['tags'], $search) !== false) || (strpos($snippet['group'], $search) !== false);
148 });
149 }
150
151 if ($tag = Arr::get($this->args, 'tag')) {
152 $snippets = array_filter($snippets, function ($snippet) use ($tag) {
153 if (!$snippet['tags']) {
154 return false;
155 }
156 $tags = array_map('trim', explode(',', $snippet['tags']));
157 return in_array($tag, $tags);
158 });
159 }
160
161 $snippets = $this->sortSnippets($snippets);
162
163 if ($perPage != null && $page != null) {
164 $snippets = array_slice($snippets, ($page - 1) * $perPage, $perPage);
165 return [
166 'data' => $snippets,
167 'page' => (int)$page,
168 'per_page' => (int)$perPage,
169 'total' => count($snippets),
170 'last_page' => (int)ceil(count($snippets) / $perPage)
171 ];
172 }
173
174 return $snippets;
175 }
176
177 private function sortSnippets($snippets)
178 {
179 $sortingMaps = [
180 'created_at' => 'strtotime',
181 'updated_at' => 'strtotime',
182 'priority' => 'intval',
183 'name' => 'strtolower',
184 ];
185
186 $sortBy = $this->args['sort_by'] ?? 'created_at';
187 $sortOrder = $this->args['sort_order'] ?? 'desc';
188
189 $callback = Arr::get($sortingMaps, $sortBy);
190
191 if (!$callback) {
192 return $snippets;
193 }
194
195 // Short the snippets by name
196 usort($snippets, function ($a, $b) use ($sortBy, $sortOrder, $callback) {
197 $value1 = call_user_func($callback, $a[$sortBy]);
198 $value2 = call_user_func($callback, $b[$sortBy]);
199 if ($sortBy == 'name') {
200 return $sortOrder == 'asc' ? strcasecmp(trim($a['name']), trim($b['name'])) : strcasecmp(trim($b['name']), trim($a['name']));
201 }
202 return $sortOrder == 'asc' ? $value1 <=> $value2 : $value2 <=> $value1;
203 });
204
205 return $snippets;
206 }
207
208 public function getAllSnippetTagsGroups()
209 {
210 $config = Helper::getIndexedConfig();
211
212 if (!$config || empty($config['meta'])) {
213 return [[], []];
214 }
215
216 if (empty($config['published']) && empty($config['draft'])) {
217 return [[], []];
218 }
219
220 $snippets = array_merge($config['published'], $config['draft']);
221 if (!$snippets) {
222 return [[], []];
223 }
224
225 $allTags = [];
226 $allGroups = [];
227
228 foreach ($snippets as $snippet) {
229 if (!empty($snippet['tags'])) {
230 $tags = array_map('trim', explode(',', $snippet['tags']));
231 $allTags = array_merge($allTags, $tags);
232 }
233
234 if (!empty($snippet['group'])) {
235 $allGroups[] = trim($snippet['group']);
236 }
237 }
238
239 $allTags = array_unique($allTags);
240 asort($allTags);
241 $allGroups = array_unique($allGroups);
242 asort($allGroups);
243 return [array_values($allTags), array_values($allGroups)];
244 }
245
246 public function findByFileName($fileName)
247 {
248 $snippetDir = Helper::getStorageDir();
249 $file = $snippetDir . '/' . $fileName;
250
251 if (!is_file($file) || $fileName === 'index.php') {
252 return new \WP_Error('file_not_found', 'File not found');
253 }
254
255 $fileContent = file_get_contents($snippetDir . '/' . $fileName);
256 [$docBlockArray, $code] = $this->parseBlock($fileContent);
257
258 return [
259 'meta' => $docBlockArray,
260 'code' => $code,
261 'file' => $file,
262 'status' => (!empty($docBlockArray['status'])) ? $docBlockArray['status'] : 'draft'
263 ];
264 }
265
266 public function updateSnippet($fileName, $code, $metaData)
267 {
268 $metaData['updated_at'] = date('Y-m-d H:i:s');
269
270 $file = Helper::getStorageDir() . '/' . $fileName;
271
272 if (!is_file($file)) {
273 return new \WP_Error('file_not_found', 'File not found');
274 }
275
276 $docBlockString = $this->parseInputMeta($metaData, true);
277 $fullCode = $docBlockString . $code;
278
279 file_put_contents($file, $fullCode);
280
281 $type = Arr::get($metaData, 'type');
282
283 if ($type == 'css' || $type == 'js') {
284 $this->maybeCacheCssJs($fileName, $metaData, $code);
285 }
286
287 return $fileName;
288 }
289
290 public function createSnippet($code, $metaData)
291 {
292 $storageDir = Helper::getStorageDir();
293 $fileCount = count(glob($storageDir . '/*.php'));
294
295 if (!$fileCount) {
296 Helper::cacheSnippetIndex();
297 $fileCount = 1;
298 }
299
300 // get the first 4 words of the snippet name
301 $fileTitle = $metaData['name'];
302 $nameArr = explode(' ', $fileTitle);
303 if (count($nameArr) > 4) {
304 $nameArr = array_slice($nameArr, 0, 4);
305 $fileTitle = implode(' ', $nameArr);
306 }
307
308 $fileTitle = sanitize_title($fileTitle, 'snippet');
309
310 $fileName = $fileCount . '-' . $fileTitle . '.php';
311
312 $fileName = sanitize_file_name($fileName);
313
314 $file = $storageDir . '/' . $fileName;
315
316 if (is_file($file)) {
317 return new \WP_Error('file_exists', 'Please try a different name');
318 }
319
320 $docBlockString = $this->parseInputMeta($metaData, true);
321
322 $fullCode = $docBlockString . $code;
323
324 file_put_contents($file, $fullCode);
325
326 $this->maybeCacheCssJs($fileName, $metaData, $code);
327
328 return $fileName;
329 }
330
331 public function deleteSnippet($fileName)
332 {
333 $snippetDir = Helper::getStorageDir();
334 $file = $snippetDir . '/' . $fileName;
335
336 if (!is_file($file) && ($fileName === 'index.php' || $fileName === 'cached')) {
337 return new \WP_Error('file_not_found', 'File not found');
338 }
339
340 unlink($file);
341
342 return true;
343 }
344
345 public function parseBlock($fileContent, $codeOnly = false)
346 {
347 // get content from // <Internal Doc Start> to // <Internal Doc End>
348 $fileContent = explode('// <Internal Doc Start>', $fileContent);
349
350 if (count($fileContent) < 2) {
351 if ($codeOnly) {
352 return '';
353 }
354 return [null, null];
355 }
356
357 // Try different possible formats of the end marker
358 $endMarkers = [
359 '// <Internal Doc End> ?>' . PHP_EOL,
360 '// <Internal Doc End> ?>',
361 '<?php if (!defined("ABSPATH")) { return;} // <Internal Doc End> ?>' . PHP_EOL,
362 '<?php if (!defined("ABSPATH")) { return;} // <Internal Doc End> ?>'
363 ];
364
365 $docBlock = null;
366 $code = null;
367
368 foreach ($endMarkers as $marker) {
369 $parts = explode($marker, $fileContent[1]);
370 if (count($parts) > 1) {
371 $docBlock = $parts[0];
372 $code = $parts[1];
373 break;
374 }
375 }
376
377 if (!$docBlock || !$code) {
378 if ($codeOnly) {
379 return '';
380 }
381 return [null, null];
382 }
383
384 if ($codeOnly) {
385 return $code;
386 }
387
388 $docBlock = explode('*', $docBlock);
389 // Explode by : and get the key and value
390 $docBlockArray = [
391 'name' => '',
392 'status' => '',
393 'tags' => '',
394 'description' => '',
395 'type' => '',
396 'run_at' => '',
397 'group' => '',
398 'condition' => '',
399 'load_as_file' => '',
400 'load_in_block_editor' => ''
401 ];
402
403 foreach ($docBlock as $key => $value) {
404 $value = trim($value);
405 $arr = explode(':', $value);
406 if (count($arr) < 2) {
407 continue;
408 }
409
410 // get the first item from the array and remove it from $arr
411 $key = array_shift($arr);
412 $key = trim(str_replace('@', '', $key));
413 if (!$key) {
414 continue;
415 }
416 $docBlockArray[$key] = trim(implode(':', $arr));
417 }
418
419 if (!empty($docBlockArray['condition'])) {
420 $data = json_decode($docBlockArray['condition'], true);
421 if ($data && is_array($data)) {
422 $docBlockArray['condition'] = $data;
423 }
424 } else {
425 $docBlockArray['condition'] = [
426 'status' => 'no',
427 'run_if' => 'assertive',
428 'items' => [[]]
429 ];
430 }
431
432 if (empty($docBlockArray['condition'])) {
433 $docBlockArray['condition'] = [
434 'status' => 'no',
435 'run_if' => 'assertive',
436 'items' => [[]]
437 ];
438 }
439
440 return [$docBlockArray, $code];
441 }
442
443 private function parseInputMeta($metaData, $convertString = false)
444 {
445 $metaDefaults = [
446 'description' => '',
447 'tags' => '',
448 'group' => '',
449 'name' => 'Snippet Created @ ' . current_time('mysql'),
450 'type' => 'PHP',
451 'status' => 'draft',
452 'created_by' => get_current_user_id(),
453 'created_at' => gmdate('Y-m-d H:i:s'),
454 'updated_at' => gmdate('Y-m-d H:i:s'),
455 'is_valid' => 1,
456 'updated_by' => get_current_user_id(),
457 'priority' => 10,
458 'run_at' => '',
459 'load_as_file' => '',
460 'load_in_block_editor' => '',
461 'condition' => [
462 'status' => 'no',
463 'run_if' => 'assertive',
464 'items' => [[]]
465 ]
466 ];
467
468 $metaData = Arr::only($metaData, array_keys($metaDefaults));
469 $metaData = wp_parse_args($metaData, $metaDefaults);
470
471 if (!is_numeric($metaData['priority']) || $metaData['priority'] < 1) {
472 $metaData['priority'] = 10;
473 }
474
475 if (!$convertString) {
476 return $metaData;
477 }
478
479 $metaData['condition'] = json_encode($metaData['condition']);
480
481 $docBlockString = '<?php' . PHP_EOL . '// <Internal Doc Start>' . PHP_EOL . '/*' . PHP_EOL . '*';
482
483 foreach ($metaData as $key => $value) {
484 $docBlockString .= PHP_EOL . '* @' . $key . ': ' . Helper::sanitizeMetaValue($value);
485 }
486
487 $docBlockString .= PHP_EOL . '*/' . PHP_EOL . '?>' . PHP_EOL . '<?php if (!defined("ABSPATH")) { return;} // <Internal Doc End> ?>' . PHP_EOL;
488
489 return $docBlockString;
490 }
491
492 private function maybeCacheCssJs($fileName, $metaData = [], $code = '')
493 {
494 // type
495 $type = Arr::get($metaData, 'type');
496 if ($type == 'css' || $type == 'js') {
497 // get file name without extension
498 $cacheFileName = str_replace('.php', '.' . $type, $fileName);
499 $fullFileName = Helper::getCachedDir() . '/' . $cacheFileName;
500 if (Arr::get($metaData, 'load_as_file') == 'yes') {
501 file_put_contents($fullFileName, $code);
502 return $cacheFileName;
503 }
504
505 if (file_exists($fullFileName)) {
506 @unlink($fullFileName);
507 }
508 }
509
510 return false;
511 }
512 }
513