PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / 0.4.9
Code Engine – PHP Snippets, AI Functions & Automation for WordPress v0.4.9
0.5.6 0.5.5 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.9 0.4.8 0.4.7 0.4.6 trunk 0.0.1 0.0.2 0.2.8 0.2.9 0.3.0 0.3.1 0.3.2 0.3.3 0.3.4 0.3.5 0.3.6 0.3.7 0.3.8 All 32 releases
code-engine / classes / api.php

api.php in Code Engine – PHP Snippets, AI Functions & Automation for WordPress 0.4.9, at classes/api.php

871 lines 28.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Meow_MWCODE_API {
4 public $core;
5 private $snippet_module;
6 private $debug = false;
7
8 public function __construct( $core, $snippet_module ) {
9 $this->core = $core;
10 $this->snippet_module = $snippet_module;
11 $this->debug = $this->core->get_option( 'server_debug_mode' );
12 }
13
14 #region Simple API
15
16 /**
17 * Get a snippet by its ID.
18 *
19 * The options are used to filter the snippets:
20 * - 'php_ready_args' (bool): If false, the arguments will not be formatted for PHP. (no $ before the names).
21 *
22 * @param int $id The snippet ID.
23 * @param array $options Options for filtering.
24 *
25 * @return array|null The snippet data or null if not found.
26 * @throws Exception If the snippet cannot be retrieved.
27 */
28 public function getSnippet( $id, $options = [] ) {
29 try {
30 if ( empty( $id ) ) {
31 throw new Exception( 'The snippet ID is required.' );
32 }
33
34 if ( $this->debug ) {
35 $this->core->log( sprintf( 'API [GetSnippet]: ID=%d, Options=%s', $id, json_encode( $options ) ) );
36 }
37
38 // Try to get as function snippet first (for backward compatibility)
39 $snippet = $this->snippet_module->get_function( $id, $options );
40
41 // If not found as function, get from general snippets
42 if ( empty( $snippet ) ) {
43 $snippet = $this->snippet_module->select_one( $id, $options );
44 if ( empty( $snippet ) ) {
45 throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
46 }
47 }
48
49 return $snippet;
50 }
51 catch ( Exception $e ) {
52 if ( $this->debug ) {
53 $this->core->log( '⚠️ API Error [GetSnippet]: ' . $e->getMessage() );
54 }
55 throw $e;
56 }
57 }
58
59 /**
60 * Get a snippet by its name.
61 *
62 * The options are used to filter the snippets:
63 * - 'php_ready_args' (bool): If false, the arguments will not be formatted for PHP. (no $ before the names).
64 *
65 * @param string $name The name of the snippet to be retrieved.
66 * @param array $options Options for filtering.
67 *
68 * @return array|null The snippet data or null if not found.
69 * @throws Exception If the snippet cannot be retrieved.
70 */
71 public function getSnippetByName( $name, $options = [] ) {
72 try {
73 if ( empty( $name ) ) {
74 throw new Exception( 'The snippet name is required.' );
75 }
76
77 if ( $this->debug ) {
78 $this->core->log( sprintf( 'API [GetSnippetByName]: Name=%s, Options=%s', $name, json_encode( $options ) ) );
79 }
80
81 $snippet = $this->snippet_module->get_function_by_name( $name, $options );
82 if ( empty( $snippet ) ) {
83 throw new Exception( sprintf( 'Snippet with name "%s" not found.', $name ) );
84 }
85
86 return $snippet;
87 }
88 catch ( Exception $e ) {
89 if ( $this->debug ) {
90 $this->core->log( '⚠️ API Error [GetSnippetByName]: ' . $e->getMessage() );
91 }
92 throw $e;
93 }
94 }
95
96 /**
97 * Get all snippets.
98 *
99 * @param bool $safe Whether to filter out snippets with invalid names.
100 * @param string|null $scope Optional scope filter: 'function', 'backend', 'frontend', 'scheduled', 'persistent'.
101 * @return array The list of snippets.
102 */
103 public function getSnippets( $safe = true, $scope = null ) {
104 try {
105
106 $allSnippets = $this->snippet_module->select( 0, 9999,
107 [ ['accessor' => 'scope', 'value' => $scope] ]
108 , [] );
109
110 $snippets = $allSnippets['data'] ?? [];
111
112
113 if ( $safe ) {
114
115 $snippets = array_filter( $snippets, function( $snippet ) {
116
117 if( $snippet['scope'] === 'function' ) {
118
119 if( array_key_exists( 'functionName', $snippet ) )
120 {
121 $name = $snippet['functionName'];
122 if ( !preg_match( '/^[a-zA-Z0-9_-]{1,64}$/', $name ) ) {
123 if ( $this->debug ) {
124 $this->core->log( sprintf( 'API [GetSnippets]: Filtered out snippet with invalid name: %s', $name ) );
125 }
126 return false;
127 }
128 } else {
129 if ( $this->debug ) {
130 $this->core->log( 'API [GetSnippets]: Filtered out snippet with missing functionName.' );
131 }
132 return false;
133 }
134
135 }
136
137 return true;
138 } );
139 }
140
141 return array_values( $snippets ); // Reset array keys
142 }
143 catch ( Exception $e ) {
144 if ( $this->debug ) {
145 $this->core->log( '⚠️ API Error [GetSnippets]: ' . $e->getMessage() );
146 }
147 throw $e;
148 }
149 }
150
151 /**
152 * Backward compatibility wrapper for getSnippets().
153 * @deprecated Use getSnippets() instead.
154 *
155 * @param bool $safe Whether to filter out snippets with invalid names.
156 * @return array The list of function snippets only.
157 */
158 public function get_functions( $safe = true ) {
159 $this->core->log( '⚠️ API Warning: get_functions() is deprecated. Please use getSnippets() instead.' );
160 // Return only function snippets for backward compatibility
161 return $this->getSnippets( $safe, 'function' );
162 }
163
164 /**
165 * Execute a snippet by its ID.
166 * The arguments should be an associative array with the argument names as keys.
167 * Example: [ "$city" => "'Tokyo'", "$date" => "1999" ]
168 *
169 * @param int $id The snippet ID.
170 * @param array $args The arguments to pass to the snippet.
171 *
172 * @return mixed The result of the snippet execution.
173 * @throws Exception If the snippet cannot be executed.
174 */
175 public function executeSnippet( $id, $args = [], $reply = null ) {
176 try {
177 if ( empty( $id ) ) {
178 throw new Exception( 'The snippet ID is required.' );
179 }
180
181 if ( $this->debug ) {
182 $this->core->log( sprintf( 'API [ExecuteSnippet]: ID=%d, Args=%s', $id, json_encode( $args ) ) );
183 }
184
185 // Verify the snippet exists
186 $snippet = $this->snippet_module->get_function( $id );
187 if ( empty( $snippet ) ) {
188 throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
189 }
190
191 $query = $reply ? $reply->query : null;
192 $array_query = [];
193 if( $query ) {
194 $array_query['envId'] = $query->envId ?? 'No EnvId in Query';
195 $array_query['botId'] = $query->botId ?? 'No BotId in Query';
196 $array_query['customId'] = $query->customId ?? 'No CustomId in Query';
197 $array_query['embeddingsEnvId'] = $query->embeddingsEnvId ?? 'No EmbeddingsEnvId in Query';
198
199
200 $array_query['chatId'] = $query->chatId ?? 'No ChatId in Query';
201 $array_query['session'] = $query->session ?? 'No Session in Query';
202 $array_query['scope'] = $query->scope ?? 'No Scope in Query';
203
204 $array_query['instructions'] = $query->instructions ?? 'No Instructions in Query';
205 $array_query['context'] = $query->context ?? 'No Context in Query';
206
207 $array_query['messages'] = $query->messages ? json_encode( $query->messages ) : 'No Messages in Query';
208 $array_query['message'] = $query->message ?? 'No Message in Query';
209
210 $array_query['model'] = $query->model ?? 'No Model in Query';
211 $array_query['feature'] = $query->feature ?? 'No Feature in Query';
212 }
213
214 $args['__mwai_query'] = $array_query;
215
216 $output = $this->core->run_snippet( $id, $args );
217
218 if ( $this->debug ) {
219 $shortOutput = is_string( $output ) ? substr( $output, 0, 100 ) : json_encode( $output );
220 $this->core->log( sprintf( 'API [ExecuteSnippet]: Success, Output=%s%s',
221 $shortOutput,
222 strlen( $shortOutput ) > 100 ? '...' : ''
223 ) );
224 }
225
226 return $output;
227 }
228 catch ( Exception $e ) {
229 if ( $this->debug ) {
230 $this->core->log( '⚠️ API Error [ExecuteSnippet]: ' . $e->getMessage() );
231 }
232 throw $e;
233 }
234 }
235
236 /**
237 * Execute a snippet by its name.
238 *
239 * @param string $name The snippet name.
240 * @param array $args The arguments to pass to the snippet.
241 *
242 * @return mixed The result of the snippet execution.
243 * @throws Exception If the snippet cannot be executed.
244 */
245 /**
246 * Declare every active PHP function snippet in the current request so they can call
247 * one another, without executing any of them. Idempotent and cheap (runs once per
248 * request). executeSnippet()/executeSnippetByName() already call this internally, so
249 * you only need it to make function snippets callable from your own custom PHP.
250 *
251 * @return void
252 */
253 public function loadFunctions() {
254 $this->core->define_all_functions();
255 }
256
257 public function executeSnippetByName( $name, $args = [] ) {
258 try {
259 if ( empty( $name ) ) {
260 throw new Exception( 'The snippet name is required.' );
261 }
262
263 if ( $this->debug ) {
264 $this->core->log( sprintf( 'API [ExecuteSnippetByName]: Name=%s, Args=%s', $name, json_encode( $args ) ) );
265 }
266
267 // Get the snippet by name to find its ID
268 $snippet = $this->snippet_module->get_function_by_name( $name );
269 if ( empty( $snippet ) || empty( $snippet['snippetId'] ) ) {
270 throw new Exception( sprintf( 'Snippet with name "%s" not found.', $name ) );
271 }
272
273 return $this->executeSnippet( $snippet['snippetId'], $args );
274 }
275 catch ( Exception $e ) {
276 if ( $this->debug ) {
277 $this->core->log( '⚠️ API Error [ExecuteSnippetByName]: ' . $e->getMessage() );
278 }
279 throw $e;
280 }
281 }
282 #endregion
283
284 #region Standard API
285
286 /**
287 * Create a new snippet.
288 *
289 * @param string $name Name of the snippet.
290 * @param string $code Code of the snippet.
291 * @param string $scope Scope of the snippet: 'function', 'backend', 'frontend', 'scheduled', 'persistent'.
292 * @param array $options Additional options:
293 * - target: 'php' or 'js' (for function snippets)
294 * - description: Description of the snippet
295 * - args: Arguments for function snippets
296 * - argsData: Argument data for function snippets (use 'description' for each argument's description)
297 * - behavior: 'dynamic' or 'static' (for function snippets)
298 * - tags: Array of tags
299 * - priority: Execution priority
300 * - active: Active status (true/false)
301 * - endpoint: REST endpoint
302 * - method: HTTP method (GET/POST)
303 * - intervalHours: Hours for scheduled snippets
304 * - intervalMinutes: Minutes for scheduled snippets
305 *
306 * @return array The created snippet data.
307 * @throws Exception If the snippet cannot be created.
308 */
309 public function createSnippet( $name, $code, $scope = 'function', $options = [] ) {
310
311 try {
312 if ( empty( $name ) ) {
313 throw new Exception( 'The snippet name is required.' );
314 }
315
316 if ( empty( $code ) ) {
317 throw new Exception( 'The snippet code is required.' );
318 }
319
320 $validScopes = ['function', 'backend', 'frontend', 'scheduled', 'persistent'];
321 if ( !in_array( $scope, $validScopes ) ) {
322 throw new Exception( sprintf( 'Invalid scope. Must be one of: %s', implode( ', ', $validScopes ) ) );
323 }
324
325 // Extract options with defaults
326 $target = $options['target'] ?? 'php';
327 $description = $options['description'] ?? '';
328 $args = $options['args'] ?? [];
329 $argsData = $options['argsData'] ?? [];
330 $behavior = $options['behavior'] ?? 'dynamic';
331 $tags = $options['tags'] ?? ['api'];
332 $priority = $options['priority'] ?? 10;
333 $active = $options['active'] ?? true;
334 $endpoint = $options['endpoint'] ?? '';
335 $method = $options['method'] ?? 'POST';
336 $intervalHours = $options['intervalHours'] ?? 0;
337 $intervalMinutes = $options['intervalMinutes'] ?? 0;
338
339 // Validate function-specific options
340 if ( $scope === 'function' ) {
341 if ( !in_array( $target, ['php', 'js'] ) ) {
342 throw new Exception( 'The target must be either "php" or "js" for function snippets.' );
343 }
344 if ( !in_array( $behavior, ['dynamic', 'static'] ) ) {
345 throw new Exception( 'The behavior must be either "dynamic" or "static" for function snippets.' );
346 }
347 }
348
349 if ( $this->debug ) {
350 $shortCode = substr( $code, 0, 100 );
351 $this->core->log( sprintf( 'API [CreateSnippet]: Name=%s, Scope=%s, Options=%s',
352 $name, $scope, json_encode( $options ) ) );
353 }
354
355 $params = [
356 // Core values
357 'id' => null,
358 'name' => $name,
359 'code' => $code,
360 'scope' => $scope,
361 'active' => $active ? 1 : 0,
362 'priority' => $priority,
363 'tags' => $tags,
364 'description' => $description,
365 'endpoint' => $endpoint,
366 'method' => $method,
367 ];
368
369 // Add function-specific params
370 if ( $scope === 'function' ) {
371 $params['functionName'] = $name;
372 $params['functionTarget'] = $target;
373 $params['functionArgs'] = $args;
374 $params['functionArgsDict'] = $argsData;
375 $params['functionBehavior'] = $behavior;
376 }
377
378 // Add scheduled-specific params
379 if ( $scope === 'scheduled' ) {
380 $params['intervalHours'] = $intervalHours;
381 $params['intervalMinutes'] = $intervalMinutes;
382 }
383
384 $result = $this->core->add_snippet( $params );
385
386 if ( $this->debug ) {
387 $this->core->log( sprintf( 'API [CreateSnippet]: Success, ID=%d', $result['id'] ?? 0 ) );
388 }
389
390 return $result;
391 }
392 catch ( Exception $e ) {
393 if ( $this->debug ) {
394 $this->core->log( '⚠️ API Error [CreateSnippet]: ' . $e->getMessage() );
395 }
396 throw $e;
397 }
398 }
399
400 /**
401 * Update a snippet by its ID.
402 * All parameters except ID are optional. If not specified, the current values will be used.
403 *
404 * @param int $id ID of the snippet to update.
405 * @param array $params Parameters to update. Can include:
406 * - name: Snippet name
407 * - code: Snippet code
408 * - scope: Snippet scope
409 * - description: Description
410 * - active: Active status
411 * - priority: Execution priority
412 * - tags: Array of tags
413 * - endpoint: REST endpoint
414 * - method: HTTP method
415 * - target: 'php' or 'js' (for function snippets)
416 * - args: Arguments (for function snippets)
417 * - argsData: Argument data (for function snippets)
418 * - behavior: 'dynamic' or 'static' (for function snippets)
419 * - intervalHours: Hours (for scheduled snippets)
420 * - intervalMinutes: Minutes (for scheduled snippets)
421 *
422 * @return array The updated snippet data.
423 * @throws Exception If the snippet cannot be updated.
424 */
425 public function updateSnippet( $id, $params = [] ) {
426 try {
427 if ( empty( $id ) ) {
428 throw new Exception( 'The snippet ID is required.' );
429 }
430
431 if ( $this->debug ) {
432 $this->core->log( sprintf( 'API [UpdateSnippet]: ID=%d, Params=%s', $id, json_encode( $params ) ) );
433 }
434
435 // Get existing snippet
436 $snippet = $this->snippet_module->select_one( $id );
437 if ( empty( $snippet ) ) {
438 throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
439 }
440
441 // Validate scope if provided
442 if ( isset( $params['scope'] ) ) {
443 $validScopes = ['function', 'backend', 'frontend', 'scheduled', 'persistent'];
444 if ( !in_array( $params['scope'], $validScopes ) ) {
445 throw new Exception( sprintf( 'Invalid scope. Must be one of: %s', implode( ', ', $validScopes ) ) );
446 }
447 }
448
449 // Validate function-specific parameters if provided
450 $scope = $params['scope'] ?? $snippet['scope'];
451 if ( $scope === 'function' ) {
452 if ( isset( $params['target'] ) && !in_array( $params['target'], ['php', 'js'] ) ) {
453 throw new Exception( 'The target must be either "php" or "js" for function snippets.' );
454 }
455 if ( isset( $params['behavior'] ) && !in_array( $params['behavior'], ['dynamic', 'static'] ) ) {
456 throw new Exception( 'The behavior must be either "dynamic" or "static" for function snippets.' );
457 }
458 }
459
460 // Build update params - merge with existing values
461 $updateParams = [
462 'id' => $id,
463 'name' => $params['name'] ?? $snippet['name'],
464 'code' => $params['code'] ?? $snippet['code'],
465 'scope' => $scope,
466 'active' => isset( $params['active'] ) ? ( $params['active'] ? 1 : 0 ) : $snippet['active'],
467 'priority' => $params['priority'] ?? $snippet['priority'],
468 'tags' => $params['tags'] ?? $snippet['tags'],
469 'description' => $params['description'] ?? $snippet['description'] ?? '',
470 'endpoint' => $params['endpoint'] ?? $snippet['endpoint'] ?? '',
471 'method' => $params['method'] ?? $snippet['method'] ?? 'POST',
472 ];
473
474 // Add function-specific params if it's a function snippet
475 if ( $scope === 'function' ) {
476 $updateParams['functionName'] = $params['name'] ?? $snippet['name'];
477 $updateParams['functionTarget'] = $params['target'] ?? $snippet['functionTarget'] ?? 'php';
478 $updateParams['functionArgs'] = $params['args'] ?? $snippet['functionArgs'] ?? [];
479 $updateParams['functionArgsDict'] = $params['argsData'] ?? $snippet['functionArgsDict'] ?? [];
480 $updateParams['functionBehavior'] = $params['behavior'] ?? $snippet['functionBehavior'] ?? 'dynamic';
481 }
482
483 // Add scheduled-specific params if it's a scheduled snippet
484 if ( $scope === 'scheduled' ) {
485 $updateParams['intervalHours'] = $params['intervalHours'] ?? $snippet['intervalHours'] ?? 0;
486 $updateParams['intervalMinutes'] = $params['intervalMinutes'] ?? $snippet['intervalMinutes'] ?? 0;
487 }
488
489 $result = $this->core->add_snippet( $updateParams );
490
491 if ( $this->debug ) {
492 $this->core->log( sprintf( 'API [UpdateSnippet]: Success, ID=%d', $result['id'] ?? $id ) );
493 }
494
495 return $result;
496 }
497 catch ( Exception $e ) {
498 if ( $this->debug ) {
499 $this->core->log( '⚠️ API Error [UpdateSnippet]: ' . $e->getMessage() );
500 }
501 throw $e;
502 }
503 }
504
505 /**
506 * Delete a snippet by its ID.
507 *
508 * @param int $id Snippet ID.
509 * @return bool True if the snippet was deleted successfully.
510 * @throws Exception If the snippet cannot be deleted.
511 */
512 public function deleteSnippet( $id ) {
513 try {
514 if ( empty( $id ) ) {
515 throw new Exception( 'The snippet ID is required.' );
516 }
517
518 if ( $this->debug ) {
519 $this->core->log( sprintf( 'API [DeleteSnippet]: ID=%d', $id ) );
520 }
521
522 // Verify the snippet exists
523 $snippet = $this->snippet_module->select_one( $id );
524 if ( empty( $snippet ) ) {
525 throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
526 }
527
528 $params = [ 'id' => $id ];
529
530 // Delete function snippet data if it's a function
531 if ( $snippet['scope'] === 'function' ) {
532 $this->snippet_module->delete_function_snippet( $params );
533 }
534
535 // Delete scheduled snippet data if it's scheduled
536 if ( $snippet['scope'] === 'scheduled' ) {
537 $this->snippet_module->delete_interval_snippet( $params );
538 }
539
540 $result = $this->snippet_module->delete( $params );
541
542 if ( $this->debug ) {
543 $this->core->log( sprintf( 'API [DeleteSnippet]: Success, ID=%d', $id ) );
544 }
545
546 return !empty( $result );
547 }
548 catch ( Exception $e ) {
549 if ( $this->debug ) {
550 $this->core->log( '⚠️ API Error [DeleteSnippet]: ' . $e->getMessage() );
551 }
552 throw $e;
553 }
554 }
555
556 /**
557 * Delete a snippet by its name.
558 *
559 * @param string $name Snippet name.
560 * @return bool True if the snippet was deleted successfully.
561 * @throws Exception If the snippet cannot be deleted.
562 */
563 public function deleteSnippetByName( $name ) {
564 try {
565 if ( empty( $name ) ) {
566 throw new Exception( 'The snippet name is required.' );
567 }
568
569 if ( $this->debug ) {
570 $this->core->log( sprintf( 'API [DeleteSnippetByName]: Name=%s', $name ) );
571 }
572
573 // Try to find as function first
574 $snippet = $this->snippet_module->get_function_by_name( $name );
575 if ( !empty( $snippet ) && !empty( $snippet['snippetId'] ) ) {
576 return $this->deleteSnippet( $snippet['snippetId'] );
577 }
578
579 // If not found as function, search in all snippets
580 $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
581 foreach ( $allSnippets['data'] as $s ) {
582 if ( $s['name'] === $name ) {
583 return $this->deleteSnippet( $s['id'] );
584 }
585 }
586
587 throw new Exception( sprintf( 'Snippet with name "%s" not found.', $name ) );
588 }
589 catch ( Exception $e ) {
590 if ( $this->debug ) {
591 $this->core->log( '⚠️ API Error [DeleteSnippetByName]: ' . $e->getMessage() );
592 }
593 throw $e;
594 }
595 }
596 #endregion
597
598 #region Standard API (No REST API)
599
600 /**
601 * Check if a snippet exists by ID.
602 *
603 * @param int $id The snippet ID.
604 * @return bool True if the snippet exists.
605 */
606 public function snippetExists( $id ) {
607 try {
608 if ( empty( $id ) ) {
609 return false;
610 }
611
612 $snippet = $this->snippet_module->select_one( $id );
613 return !empty( $snippet );
614 }
615 catch ( Exception $e ) {
616 if ( $this->debug ) {
617 $this->core->log( '⚠️ API Error [SnippetExists]: ' . $e->getMessage() );
618 }
619 return false;
620 }
621 }
622
623 /**
624 * Check if a snippet exists by name.
625 *
626 * @param string $name The snippet name.
627 * @return bool True if the snippet exists.
628 */
629 public function snippetExistsByName( $name ) {
630 try {
631 if ( empty( $name ) ) {
632 return false;
633 }
634
635 // Check in functions first
636 $snippet = $this->snippet_module->get_function_by_name( $name );
637 if ( !empty( $snippet ) ) {
638 return true;
639 }
640
641 // Check in all snippets
642 $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
643 foreach ( $allSnippets['data'] as $s ) {
644 if ( $s['name'] === $name ) {
645 return true;
646 }
647 }
648
649 return false;
650 }
651 catch ( Exception $e ) {
652 if ( $this->debug ) {
653 $this->core->log( '⚠️ API Error [SnippetExistsByName]: ' . $e->getMessage() );
654 }
655 return false;
656 }
657 }
658
659 /**
660 * Validates snippet code syntax.
661 *
662 * @param string $code The snippet code to validate.
663 * @param string $target The target language ('php' or 'js').
664 * @return array ['valid' => bool, 'error' => string|null]
665 */
666 public function validateSnippetCode( $code, $target = 'php' ) {
667 try {
668 if ( empty( $code ) ) {
669 return [ 'valid' => false, 'error' => 'Code is empty.' ];
670 }
671
672 if ( !in_array( $target, ['php', 'js'] ) ) {
673 return [ 'valid' => false, 'error' => 'Invalid target language.' ];
674 }
675
676 if ( $target === 'php' ) {
677 // Use the core's validate_php_code method if available
678 if ( method_exists( $this->core, 'validate_php_code' ) ) {
679 $validation = $this->core->validate_php_code( $code );
680 return [
681 'valid' => $validation['valid'] ?? false,
682 'error' => $validation['error'] ?? null
683 ];
684 }
685 }
686
687 // Basic validation if no specific validator available
688 return [ 'valid' => true, 'error' => null ];
689 }
690 catch ( Exception $e ) {
691 return [ 'valid' => false, 'error' => $e->getMessage() ];
692 }
693 }
694 #endregion
695
696 #region Snippet Management
697
698 /**
699 * Activate a snippet by its ID.
700 *
701 * @param int $id The snippet ID.
702 * @return bool True if activated successfully.
703 * @throws Exception If the snippet cannot be activated.
704 */
705 public function activateSnippet( $id ) {
706 try {
707 if ( empty( $id ) ) {
708 throw new Exception( 'The snippet ID is required.' );
709 }
710
711 if ( $this->debug ) {
712 $this->core->log( sprintf( 'API [ActivateSnippet]: ID=%d', $id ) );
713 }
714
715 // Get the snippet
716 $snippet = $this->snippet_module->select_one( $id );
717 if ( empty( $snippet ) ) {
718 throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
719 }
720
721 // Update only the active status
722 $result = $this->updateSnippet( $id, [ 'active' => true ] );
723 return !empty( $result );
724 }
725 catch ( Exception $e ) {
726 if ( $this->debug ) {
727 $this->core->log( '⚠️ API Error [ActivateSnippet]: ' . $e->getMessage() );
728 }
729 throw $e;
730 }
731 }
732
733 /**
734 * Deactivate a snippet by its ID.
735 *
736 * @param int $id The snippet ID.
737 * @return bool True if deactivated successfully.
738 * @throws Exception If the snippet cannot be deactivated.
739 */
740 public function deactivateSnippet( $id ) {
741 try {
742 if ( empty( $id ) ) {
743 throw new Exception( 'The snippet ID is required.' );
744 }
745
746 if ( $this->debug ) {
747 $this->core->log( sprintf( 'API [DeactivateSnippet]: ID=%d', $id ) );
748 }
749
750 // Get the snippet
751 $snippet = $this->snippet_module->select_one( $id );
752 if ( empty( $snippet ) ) {
753 throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
754 }
755
756 // Update only the active status
757 $result = $this->updateSnippet( $id, [ 'active' => false ] );
758 return !empty( $result );
759 }
760 catch ( Exception $e ) {
761 if ( $this->debug ) {
762 $this->core->log( '⚠️ API Error [DeactivateSnippet]: ' . $e->getMessage() );
763 }
764 throw $e;
765 }
766 }
767
768 /**
769 * Get snippets by scope.
770 *
771 * @param string $scope The scope to filter by: 'backend', 'frontend', 'function', 'scheduled', 'persistent'.
772 * @param array $filters Additional filters: 'active' (bool), 'tags' (array).
773 * @return array The list of snippets matching the criteria.
774 * @throws Exception If the scope is invalid.
775 */
776 public function getSnippetsByScope( $scope, $filters = [] ) {
777 try {
778 $validScopes = ['function', 'backend', 'frontend', 'scheduled', 'persistent', 'content_js', 'content_php'];
779 if ( !in_array( $scope, $validScopes ) ) {
780 throw new Exception( sprintf( 'Invalid scope. Must be one of: %s', implode( ', ', $validScopes ) ) );
781 }
782
783 if ( $this->debug ) {
784 $this->core->log( sprintf( 'API [GetSnippetsByScope]: Scope=%s, Filters=%s', $scope, json_encode( $filters ) ) );
785 }
786
787 // For function scope, we can use the existing get_functions method
788 if ( $scope === 'function' && empty( $filters ) ) {
789 return $this->snippet_module->get_functions();
790 }
791
792 // For other scopes or with filters, we need to query all snippets
793 $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
794 $filtered = [];
795
796 foreach ( $allSnippets['data'] as $snippet ) {
797 // Filter by scope
798 if ( $snippet['scope'] !== $scope ) {
799 continue;
800 }
801
802 // Apply additional filters
803 if ( isset( $filters['active'] ) && $snippet['active'] != $filters['active'] ) {
804 continue;
805 }
806
807 if ( isset( $filters['tags'] ) && is_array( $filters['tags'] ) ) {
808 $snippetTags = is_array( $snippet['tags'] ) ? $snippet['tags'] : [];
809 $hasAllTags = true;
810 foreach ( $filters['tags'] as $tag ) {
811 if ( !in_array( $tag, $snippetTags ) ) {
812 $hasAllTags = false;
813 break;
814 }
815 }
816 if ( !$hasAllTags ) {
817 continue;
818 }
819 }
820
821 $filtered[] = $snippet;
822 }
823
824 return $filtered;
825 }
826 catch ( Exception $e ) {
827 if ( $this->debug ) {
828 $this->core->log( '⚠️ API Error [GetSnippetsByScope]: ' . $e->getMessage() );
829 }
830 throw $e;
831 }
832 }
833
834 /**
835 * Get all active snippets.
836 *
837 * @param string|null $scope Optional scope filter.
838 * @return array The list of active snippets.
839 */
840 public function getActiveSnippets( $scope = null ) {
841 try {
842 if ( $this->debug ) {
843 $this->core->log( sprintf( 'API [GetActiveSnippets]: Scope=%s', $scope ?? 'all' ) );
844 }
845
846 if ( $scope ) {
847 return $this->getSnippetsByScope( $scope, [ 'active' => true ] );
848 }
849
850 // Get all active snippets
851 $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
852 $active = [];
853
854 foreach ( $allSnippets['data'] as $snippet ) {
855 if ( $snippet['active'] ) {
856 $active[] = $snippet;
857 }
858 }
859
860 return $active;
861 }
862 catch ( Exception $e ) {
863 if ( $this->debug ) {
864 $this->core->log( '⚠️ API Error [GetActiveSnippets]: ' . $e->getMessage() );
865 }
866 throw $e;
867 }
868 }
869 #endregion
870 }
871