PluginProbe
Code Engine – PHP Snippets, AI Functions & Automation for WordPress / trunk
Code Engine – PHP Snippets, AI Functions & Automation for WordPress vtrunk
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
← All changes | classes/api.php +846 -37 0.2.9trunk View file →
@@ -1,69 +1,878 @@
1 1 <?php
2 2
3 -class Meow_MWCODE_API
4 -{
5 - private $core = null;
6 - private $snippet = null;
3 +class Meow_MWCODE_API {
4 + public $core;
5 + private $snippet_module;
6 + private $debug = false;
7 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 + }
8 13
9 - public function __construct( $core, $snippet ) {
10 - $this->core = $core;
11 - $this->snippet = $snippet;
12 - }
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 + }
13 58
14 59 /**
15 - * Get a function by its ID.
60 + * Get a snippet by its name.
16 61 *
17 - * The options are used to filter the functions:
62 + * The options are used to filter the snippets:
18 63 * - 'php_ready_args' (bool): If false, the arguments will not be formatted for PHP. (no $ before the names).
19 64 *
20 - * @param $id
21 - * @param array $options
65 + * @param string $name The name of the snippet to be retrieved.
66 + * @param array $options Options for filtering.
22 67 *
23 - * @return mixed
68 + * @return array|null The snippet data or null if not found.
69 + * @throws Exception If the snippet cannot be retrieved.
24 70 */
25 - public function get_function( $id, $options = [] ) {
26 - $snippet = $this->snippet->get_function( $id, $options );
27 - return $snippet;
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 + }
28 94 }
29 95
30 96 /**
31 - * Get all functions.
97 + * Get all snippets.
32 98 *
33 - * @return mixed
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.
34 154 *
155 + * @param bool $safe Whether to filter out snippets with invalid names.
156 + * @return array The list of function snippets only.
35 157 */
36 158 public function get_functions( $safe = true ) {
37 - $snippets = $this->snippet->get_functions();
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 + }
38 163
39 - if ( $safe ) {
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 + }
40 190
41 - $snippets = array_filter( $snippets, function( $snippet ) {
42 - $name = $snippet['name'];
43 - if ( !preg_match( '/^[a-zA-Z0-9_-]{1,64}$/', $name ) ) {
44 - return false;
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', 'content_php', 'content_js'.
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 + * - mcp: Expose this function as its own MCP tool in AI Engine (for function snippets)
299 + * - tags: Array of tags
300 + * - priority: Execution priority
301 + * - active: Active status (true/false)
302 + * - endpoint: REST endpoint
303 + * - method: HTTP method (GET/POST)
304 + * - intervalHours: Hours for scheduled snippets
305 + * - intervalMinutes: Minutes for scheduled snippets
306 + *
307 + * @return array The created snippet data.
308 + * @throws Exception If the snippet cannot be created.
309 + */
310 + public function createSnippet( $name, $code, $scope = 'function', $options = [] ) {
311 +
312 + try {
313 + if ( empty( $name ) ) {
314 + throw new Exception( 'The snippet name is required.' );
315 + }
316 +
317 + if ( empty( $code ) ) {
318 + throw new Exception( 'The snippet code is required.' );
319 + }
320 +
321 + $validScopes = ['function', 'backend', 'frontend', 'scheduled', 'persistent', 'content_php', 'content_js'];
322 + if ( !in_array( $scope, $validScopes ) ) {
323 + throw new Exception( sprintf( 'Invalid scope. Must be one of: %s', implode( ', ', $validScopes ) ) );
324 + }
325 +
326 + // Extract options with defaults
327 + $target = $options['target'] ?? 'php';
328 + $description = $options['description'] ?? '';
329 + $args = $options['args'] ?? [];
330 + $argsData = $options['argsData'] ?? [];
331 + $behavior = $options['behavior'] ?? 'dynamic';
332 + $mcp = $options['mcp'] ?? false;
333 + $tags = $options['tags'] ?? ['api'];
334 + $priority = $options['priority'] ?? 10;
335 + $active = $options['active'] ?? true;
336 + $endpoint = $options['endpoint'] ?? '';
337 + $method = $options['method'] ?? 'POST';
338 + $intervalHours = $options['intervalHours'] ?? 0;
339 + $intervalMinutes = $options['intervalMinutes'] ?? 0;
340 +
341 + // Validate function-specific options
342 + if ( $scope === 'function' ) {
343 + if ( !in_array( $target, ['php', 'js'] ) ) {
344 + throw new Exception( 'The target must be either "php" or "js" for function snippets.' );
45 345 }
346 + if ( !in_array( $behavior, ['dynamic', 'static'] ) ) {
347 + throw new Exception( 'The behavior must be either "dynamic" or "static" for function snippets.' );
348 + }
349 + }
350 +
351 + if ( $this->debug ) {
352 + $shortCode = substr( $code, 0, 100 );
353 + $this->core->log( sprintf( 'API [CreateSnippet]: Name=%s, Scope=%s, Options=%s',
354 + $name, $scope, json_encode( $options ) ) );
355 + }
356 +
357 + $params = [
358 + // Core values
359 + 'id' => null,
360 + 'name' => $name,
361 + 'code' => $code,
362 + 'scope' => $scope,
363 + 'active' => $active ? 1 : 0,
364 + 'priority' => $priority,
365 + 'tags' => $tags,
366 + 'description' => $description,
367 + 'endpoint' => $endpoint,
368 + 'method' => $method,
369 + ];
370 +
371 + // Add function-specific params
372 + if ( $scope === 'function' ) {
373 + $params['functionName'] = $name;
374 + $params['functionTarget'] = $target;
375 + $params['functionArgs'] = $args;
376 + $params['functionArgsDict'] = $argsData;
377 + $params['functionBehavior'] = $behavior;
378 + $params['functionMcp'] = $mcp;
379 + }
380 +
381 + // Add scheduled-specific params
382 + if ( $scope === 'scheduled' ) {
383 + $params['intervalHours'] = $intervalHours;
384 + $params['intervalMinutes'] = $intervalMinutes;
385 + }
386 +
387 + $result = $this->core->add_snippet( $params );
388 +
389 + if ( $this->debug ) {
390 + $this->core->log( sprintf( 'API [CreateSnippet]: Success, ID=%d', $result['id'] ?? 0 ) );
391 + }
392 +
393 + return $result;
394 + }
395 + catch ( Exception $e ) {
396 + if ( $this->debug ) {
397 + $this->core->log( '⚠️ API Error [CreateSnippet]: ' . $e->getMessage() );
398 + }
399 + throw $e;
400 + }
401 + }
46 402
47 - return true;
48 - } );
403 + /**
404 + * Update a snippet by its ID.
405 + * All parameters except ID are optional. If not specified, the current values will be used.
406 + *
407 + * @param int $id ID of the snippet to update.
408 + * @param array $params Parameters to update. Can include:
409 + * - name: Snippet name
410 + * - code: Snippet code
411 + * - scope: Snippet scope
412 + * - description: Description
413 + * - active: Active status
414 + * - priority: Execution priority
415 + * - tags: Array of tags
416 + * - endpoint: REST endpoint
417 + * - method: HTTP method
418 + * - target: 'php' or 'js' (for function snippets)
419 + * - args: Arguments (for function snippets)
420 + * - argsData: Argument data (for function snippets)
421 + * - behavior: 'dynamic' or 'static' (for function snippets)
422 + * - mcp: Expose this function as its own MCP tool in AI Engine (for function snippets)
423 + * - intervalHours: Hours (for scheduled snippets)
424 + * - intervalMinutes: Minutes (for scheduled snippets)
425 + *
426 + * @return array The updated snippet data.
427 + * @throws Exception If the snippet cannot be updated.
428 + */
429 + public function updateSnippet( $id, $params = [] ) {
430 + try {
431 + if ( empty( $id ) ) {
432 + throw new Exception( 'The snippet ID is required.' );
433 + }
49 434
435 + if ( $this->debug ) {
436 + $this->core->log( sprintf( 'API [UpdateSnippet]: ID=%d, Params=%s', $id, json_encode( $params ) ) );
437 + }
438 +
439 + // Get existing snippet
440 + $snippet = $this->snippet_module->select_one( $id );
441 + if ( empty( $snippet ) ) {
442 + throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
443 + }
444 +
445 + // Validate scope if provided
446 + if ( isset( $params['scope'] ) ) {
447 + $validScopes = ['function', 'backend', 'frontend', 'scheduled', 'persistent', 'content_php', 'content_js'];
448 + if ( !in_array( $params['scope'], $validScopes ) ) {
449 + throw new Exception( sprintf( 'Invalid scope. Must be one of: %s', implode( ', ', $validScopes ) ) );
450 + }
451 + }
452 +
453 + // Validate function-specific parameters if provided
454 + $scope = $params['scope'] ?? $snippet['scope'];
455 + if ( $scope === 'function' ) {
456 + if ( isset( $params['target'] ) && !in_array( $params['target'], ['php', 'js'] ) ) {
457 + throw new Exception( 'The target must be either "php" or "js" for function snippets.' );
458 + }
459 + if ( isset( $params['behavior'] ) && !in_array( $params['behavior'], ['dynamic', 'static'] ) ) {
460 + throw new Exception( 'The behavior must be either "dynamic" or "static" for function snippets.' );
461 + }
462 + }
463 +
464 + // Build update params - merge with existing values
465 + $updateParams = [
466 + 'id' => $id,
467 + 'name' => $params['name'] ?? $snippet['name'],
468 + 'code' => $params['code'] ?? $snippet['code'],
469 + 'scope' => $scope,
470 + 'active' => isset( $params['active'] ) ? ( $params['active'] ? 1 : 0 ) : $snippet['active'],
471 + 'priority' => $params['priority'] ?? $snippet['priority'],
472 + 'tags' => $params['tags'] ?? $snippet['tags'],
473 + 'description' => $params['description'] ?? $snippet['description'] ?? '',
474 + 'endpoint' => $params['endpoint'] ?? $snippet['endpoint'] ?? '',
475 + 'method' => $params['method'] ?? $snippet['method'] ?? 'POST',
476 + ];
477 +
478 + // Add function-specific params if it's a function snippet
479 + if ( $scope === 'function' ) {
480 + $updateParams['functionName'] = $params['name'] ?? $snippet['name'];
481 + $updateParams['functionTarget'] = $params['target'] ?? $snippet['functionTarget'] ?? 'php';
482 + $updateParams['functionArgs'] = $params['args'] ?? $snippet['functionArgs'] ?? [];
483 + $updateParams['functionArgsDict'] = $params['argsData'] ?? $snippet['functionArgsDict'] ?? [];
484 + $updateParams['functionBehavior'] = $params['behavior'] ?? $snippet['functionBehavior'] ?? 'dynamic';
485 + // select_one() doesn't carry function metadata, so read the current MCP flag
486 + // from the stored function to preserve it when the caller doesn't set it.
487 + $existingFn = $this->snippet_module->get_function( $id );
488 + $updateParams['functionMcp'] = $params['mcp'] ?? ( $existingFn['mcp'] ?? false );
489 + }
490 +
491 + // Add scheduled-specific params if it's a scheduled snippet
492 + if ( $scope === 'scheduled' ) {
493 + $updateParams['intervalHours'] = $params['intervalHours'] ?? $snippet['intervalHours'] ?? 0;
494 + $updateParams['intervalMinutes'] = $params['intervalMinutes'] ?? $snippet['intervalMinutes'] ?? 0;
495 + }
496 +
497 + $result = $this->core->add_snippet( $updateParams );
498 +
499 + if ( $this->debug ) {
500 + $this->core->log( sprintf( 'API [UpdateSnippet]: Success, ID=%d', $result['id'] ?? $id ) );
501 + }
502 +
503 + return $result;
50 504 }
505 + catch ( Exception $e ) {
506 + if ( $this->debug ) {
507 + $this->core->log( '⚠️ API Error [UpdateSnippet]: ' . $e->getMessage() );
508 + }
509 + throw $e;
510 + }
511 + }
51 512
52 - return $snippets;
513 + /**
514 + * Delete a snippet by its ID.
515 + *
516 + * @param int $id Snippet ID.
517 + * @return bool True if the snippet was deleted successfully.
518 + * @throws Exception If the snippet cannot be deleted.
519 + */
520 + public function deleteSnippet( $id ) {
521 + try {
522 + if ( empty( $id ) ) {
523 + throw new Exception( 'The snippet ID is required.' );
524 + }
525 +
526 + if ( $this->debug ) {
527 + $this->core->log( sprintf( 'API [DeleteSnippet]: ID=%d', $id ) );
528 + }
529 +
530 + // Verify the snippet exists
531 + $snippet = $this->snippet_module->select_one( $id );
532 + if ( empty( $snippet ) ) {
533 + throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
534 + }
535 +
536 + $params = [ 'id' => $id ];
537 +
538 + // Delete function snippet data if it's a function
539 + if ( $snippet['scope'] === 'function' ) {
540 + $this->snippet_module->delete_function_snippet( $params );
541 + }
542 +
543 + // Delete scheduled snippet data if it's scheduled
544 + if ( $snippet['scope'] === 'scheduled' ) {
545 + $this->snippet_module->delete_interval_snippet( $params );
546 + }
547 +
548 + $result = $this->snippet_module->delete( $params );
549 +
550 + if ( $this->debug ) {
551 + $this->core->log( sprintf( 'API [DeleteSnippet]: Success, ID=%d', $id ) );
552 + }
553 +
554 + return !empty( $result );
555 + }
556 + catch ( Exception $e ) {
557 + if ( $this->debug ) {
558 + $this->core->log( '⚠️ API Error [DeleteSnippet]: ' . $e->getMessage() );
559 + }
560 + throw $e;
561 + }
53 562 }
54 563
55 564 /**
56 - * Execute a function by its ID.
57 - * The arguments should be an associative array with the argument names as keys.
58 - * Example: [ "$city" => " 'Tokyo' ", "$date" => "1999" ]
565 + * Delete a snippet by its name.
59 566 *
60 - * @param $id
61 - * @param $args
62 - *
63 - * @return mixed
567 + * @param string $name Snippet name.
568 + * @return bool True if the snippet was deleted successfully.
569 + * @throws Exception If the snippet cannot be deleted.
64 570 */
65 - public function execute_function( $id, $args ) {
66 - $output = $this->core->run_snippet( $id, $args );
67 - return $output;
571 + public function deleteSnippetByName( $name ) {
572 + try {
573 + if ( empty( $name ) ) {
574 + throw new Exception( 'The snippet name is required.' );
575 + }
576 +
577 + if ( $this->debug ) {
578 + $this->core->log( sprintf( 'API [DeleteSnippetByName]: Name=%s', $name ) );
579 + }
580 +
581 + // Try to find as function first
582 + $snippet = $this->snippet_module->get_function_by_name( $name );
583 + if ( !empty( $snippet ) && !empty( $snippet['snippetId'] ) ) {
584 + return $this->deleteSnippet( $snippet['snippetId'] );
585 + }
586 +
587 + // If not found as function, search in all snippets
588 + $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
589 + foreach ( $allSnippets['data'] as $s ) {
590 + if ( $s['name'] === $name ) {
591 + return $this->deleteSnippet( $s['id'] );
592 + }
593 + }
594 +
595 + throw new Exception( sprintf( 'Snippet with name "%s" not found.', $name ) );
596 + }
597 + catch ( Exception $e ) {
598 + if ( $this->debug ) {
599 + $this->core->log( '⚠️ API Error [DeleteSnippetByName]: ' . $e->getMessage() );
600 + }
601 + throw $e;
602 + }
68 603 }
604 + #endregion
605 +
606 + #region Standard API (No REST API)
607 +
608 + /**
609 + * Check if a snippet exists by ID.
610 + *
611 + * @param int $id The snippet ID.
612 + * @return bool True if the snippet exists.
613 + */
614 + public function snippetExists( $id ) {
615 + try {
616 + if ( empty( $id ) ) {
617 + return false;
618 + }
619 +
620 + $snippet = $this->snippet_module->select_one( $id );
621 + return !empty( $snippet );
622 + }
623 + catch ( Exception $e ) {
624 + if ( $this->debug ) {
625 + $this->core->log( '⚠️ API Error [SnippetExists]: ' . $e->getMessage() );
626 + }
627 + return false;
628 + }
629 + }
630 +
631 + /**
632 + * Check if a snippet exists by name.
633 + *
634 + * @param string $name The snippet name.
635 + * @return bool True if the snippet exists.
636 + */
637 + public function snippetExistsByName( $name ) {
638 + try {
639 + if ( empty( $name ) ) {
640 + return false;
641 + }
642 +
643 + // Check in functions first
644 + $snippet = $this->snippet_module->get_function_by_name( $name );
645 + if ( !empty( $snippet ) ) {
646 + return true;
647 + }
648 +
649 + // Check in all snippets
650 + $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
651 + foreach ( $allSnippets['data'] as $s ) {
652 + if ( $s['name'] === $name ) {
653 + return true;
654 + }
655 + }
656 +
657 + return false;
658 + }
659 + catch ( Exception $e ) {
660 + if ( $this->debug ) {
661 + $this->core->log( '⚠️ API Error [SnippetExistsByName]: ' . $e->getMessage() );
662 + }
663 + return false;
664 + }
665 + }
666 +
667 + /**
668 + * Validates snippet code syntax.
669 + *
670 + * @param string $code The snippet code to validate.
671 + * @param string $target The target language ('php' or 'js').
672 + * @return array ['valid' => bool, 'error' => string|null]
673 + */
674 + public function validateSnippetCode( $code, $target = 'php' ) {
675 + try {
676 + if ( empty( $code ) ) {
677 + return [ 'valid' => false, 'error' => 'Code is empty.' ];
678 + }
679 +
680 + if ( !in_array( $target, ['php', 'js'] ) ) {
681 + return [ 'valid' => false, 'error' => 'Invalid target language.' ];
682 + }
683 +
684 + if ( $target === 'php' ) {
685 + // Use the core's validate_php_code method if available
686 + if ( method_exists( $this->core, 'validate_php_code' ) ) {
687 + $validation = $this->core->validate_php_code( $code );
688 + return [
689 + 'valid' => $validation['valid'] ?? false,
690 + 'error' => $validation['error'] ?? null
691 + ];
692 + }
693 + }
694 +
695 + // Basic validation if no specific validator available
696 + return [ 'valid' => true, 'error' => null ];
697 + }
698 + catch ( Exception $e ) {
699 + return [ 'valid' => false, 'error' => $e->getMessage() ];
700 + }
701 + }
702 + #endregion
703 +
704 + #region Snippet Management
705 +
706 + /**
707 + * Activate a snippet by its ID.
708 + *
709 + * @param int $id The snippet ID.
710 + * @return bool True if activated successfully.
711 + * @throws Exception If the snippet cannot be activated.
712 + */
713 + public function activateSnippet( $id ) {
714 + try {
715 + if ( empty( $id ) ) {
716 + throw new Exception( 'The snippet ID is required.' );
717 + }
718 +
719 + if ( $this->debug ) {
720 + $this->core->log( sprintf( 'API [ActivateSnippet]: ID=%d', $id ) );
721 + }
722 +
723 + // Get the snippet
724 + $snippet = $this->snippet_module->select_one( $id );
725 + if ( empty( $snippet ) ) {
726 + throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
727 + }
728 +
729 + // Update only the active status
730 + $result = $this->updateSnippet( $id, [ 'active' => true ] );
731 + return !empty( $result );
732 + }
733 + catch ( Exception $e ) {
734 + if ( $this->debug ) {
735 + $this->core->log( '⚠️ API Error [ActivateSnippet]: ' . $e->getMessage() );
736 + }
737 + throw $e;
738 + }
739 + }
740 +
741 + /**
742 + * Deactivate a snippet by its ID.
743 + *
744 + * @param int $id The snippet ID.
745 + * @return bool True if deactivated successfully.
746 + * @throws Exception If the snippet cannot be deactivated.
747 + */
748 + public function deactivateSnippet( $id ) {
749 + try {
750 + if ( empty( $id ) ) {
751 + throw new Exception( 'The snippet ID is required.' );
752 + }
753 +
754 + if ( $this->debug ) {
755 + $this->core->log( sprintf( 'API [DeactivateSnippet]: ID=%d', $id ) );
756 + }
757 +
758 + // Get the snippet
759 + $snippet = $this->snippet_module->select_one( $id );
760 + if ( empty( $snippet ) ) {
761 + throw new Exception( sprintf( 'Snippet with ID %d not found.', $id ) );
762 + }
763 +
764 + // Update only the active status
765 + $result = $this->updateSnippet( $id, [ 'active' => false ] );
766 + return !empty( $result );
767 + }
768 + catch ( Exception $e ) {
769 + if ( $this->debug ) {
770 + $this->core->log( '⚠️ API Error [DeactivateSnippet]: ' . $e->getMessage() );
771 + }
772 + throw $e;
773 + }
774 + }
775 +
776 + /**
777 + * Get snippets by scope.
778 + *
779 + * @param string $scope The scope to filter by: 'backend', 'frontend', 'function', 'scheduled', 'persistent', 'content_php', 'content_js'.
780 + * @param array $filters Additional filters: 'active' (bool), 'tags' (array).
781 + * @return array The list of snippets matching the criteria.
782 + * @throws Exception If the scope is invalid.
783 + */
784 + public function getSnippetsByScope( $scope, $filters = [] ) {
785 + try {
786 + $validScopes = ['function', 'backend', 'frontend', 'scheduled', 'persistent', 'content_js', 'content_php'];
787 + if ( !in_array( $scope, $validScopes ) ) {
788 + throw new Exception( sprintf( 'Invalid scope. Must be one of: %s', implode( ', ', $validScopes ) ) );
789 + }
790 +
791 + if ( $this->debug ) {
792 + $this->core->log( sprintf( 'API [GetSnippetsByScope]: Scope=%s, Filters=%s', $scope, json_encode( $filters ) ) );
793 + }
794 +
795 + // For function scope, we can use the existing get_functions method
796 + if ( $scope === 'function' && empty( $filters ) ) {
797 + return $this->snippet_module->get_functions();
798 + }
799 +
800 + // For other scopes or with filters, we need to query all snippets
801 + $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
802 + $filtered = [];
803 +
804 + foreach ( $allSnippets['data'] as $snippet ) {
805 + // Filter by scope
806 + if ( $snippet['scope'] !== $scope ) {
807 + continue;
808 + }
809 +
810 + // Apply additional filters
811 + if ( isset( $filters['active'] ) && $snippet['active'] != $filters['active'] ) {
812 + continue;
813 + }
814 +
815 + if ( isset( $filters['tags'] ) && is_array( $filters['tags'] ) ) {
816 + $snippetTags = is_array( $snippet['tags'] ) ? $snippet['tags'] : [];
817 + $hasAllTags = true;
818 + foreach ( $filters['tags'] as $tag ) {
819 + if ( !in_array( $tag, $snippetTags ) ) {
820 + $hasAllTags = false;
821 + break;
822 + }
823 + }
824 + if ( !$hasAllTags ) {
825 + continue;
826 + }
827 + }
828 +
829 + $filtered[] = $snippet;
830 + }
831 +
832 + return $filtered;
833 + }
834 + catch ( Exception $e ) {
835 + if ( $this->debug ) {
836 + $this->core->log( '⚠️ API Error [GetSnippetsByScope]: ' . $e->getMessage() );
837 + }
838 + throw $e;
839 + }
840 + }
841 +
842 + /**
843 + * Get all active snippets.
844 + *
845 + * @param string|null $scope Optional scope filter.
846 + * @return array The list of active snippets.
847 + */
848 + public function getActiveSnippets( $scope = null ) {
849 + try {
850 + if ( $this->debug ) {
851 + $this->core->log( sprintf( 'API [GetActiveSnippets]: Scope=%s', $scope ?? 'all' ) );
852 + }
853 +
854 + if ( $scope ) {
855 + return $this->getSnippetsByScope( $scope, [ 'active' => true ] );
856 + }
857 +
858 + // Get all active snippets
859 + $allSnippets = $this->snippet_module->select( 0, 9999, [], [] );
860 + $active = [];
861 +
862 + foreach ( $allSnippets['data'] as $snippet ) {
863 + if ( $snippet['active'] ) {
864 + $active[] = $snippet;
865 + }
866 + }
867 +
868 + return $active;
869 + }
870 + catch ( Exception $e ) {
871 + if ( $this->debug ) {
872 + $this->core->log( '⚠️ API Error [GetActiveSnippets]: ' . $e->getMessage() );
873 + }
874 + throw $e;
875 + }
876 + }
877 + #endregion
69 878 }