breznflow/includes/Features/NodeCategorizer.php
Michael fd83e4810b BreznFlow 1.0.0 — WordPress.org submission
Initial public release of BreznFlow, an n8n workflow renderer for WordPress.
Fully PHPCS-compliant (WordPress Coding Standards), security-hardened,
and ready for WordPress.org plugin review.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 11:27:36 +00:00

166 lines
3.4 KiB
PHP

<?php
/**
* Categorizes workflow nodes by type and detects AI nodes.
*
* @package BreznFlow
* @since 1.0.0
*/
namespace BreznFlow\Features;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Categorizes n8n workflow nodes by type (trigger, action, AI, etc.).
*
* @since 1.0.0
*/
class NodeCategorizer {
/** Keywords indicating AI-related nodes. */
const AI_KEYWORDS = array(
'openai',
'anthropic',
'claude',
'gemini',
'googleai',
'huggingface',
'langchain',
'agent',
'vectorstore',
'gpt',
'ollama',
'mistral',
'cohere',
'lmchat',
'chainllm',
'memorybuffer',
);
/**
* Categorizes nodes and computes counts.
*
* @param array $nodes Array of node objects from workflow data.
* @return array{counts: array, by_category: array, ai_nodes: array, has_ai: bool, total: int}
*/
public static function categorize( array $nodes ): array {
$counts = array();
$by_category = array();
$ai_nodes = array();
foreach ( $nodes as $node ) {
$type = isset( $node['type'] ) ? (string) $node['type'] : '';
$slug = NodeTypeRegistry::extract_slug( $type );
$entry = NodeTypeRegistry::lookup( $type );
$label = $entry['label'] ?? $slug;
// Count by display label.
if ( ! isset( $counts[ $label ] ) ) {
$counts[ $label ] = 0;
}
++$counts[ $label ];
// Check for AI nodes.
$slug_lower = strtolower( $slug );
foreach ( self::AI_KEYWORDS as $keyword ) {
if ( str_contains( $slug_lower, $keyword ) ) {
if ( ! in_array( $type, $ai_nodes, true ) ) {
$ai_nodes[] = $type;
}
break;
}
}
// Group by category.
$category = self::get_category( $slug );
if ( ! isset( $by_category[ $category ] ) ) {
$by_category[ $category ] = array();
}
$by_category[ $category ][] = $label;
}
arsort( $counts );
return array(
'counts' => $counts,
'by_category' => $by_category,
'ai_nodes' => $ai_nodes,
'has_ai' => ! empty( $ai_nodes ),
'total' => count( $nodes ),
);
}
/**
* Determines the category for a node slug.
*
* @since 1.0.0
* @param string $slug Node type slug.
* @return string Category name.
*/
private static function get_category( string $slug ): string {
$slug_lower = strtolower( $slug );
// AI check first.
foreach ( self::AI_KEYWORDS as $keyword ) {
if ( str_contains( $slug_lower, $keyword ) ) {
return 'ai';
}
}
$trigger_slugs = array( 'scheduletrigger', 'webhook', 'manualtrigger', 'formtrigger', 'emailreadimap', 'rssfeadread' );
foreach ( $trigger_slugs as $t ) {
if ( str_contains( $slug_lower, 'trigger' ) || str_contains( $slug_lower, 'webhook' ) ) {
return 'trigger';
}
}
$logic_slugs = array(
'if',
'switch',
'filter',
'merge',
'splitinbatches',
'splitout',
'sort',
'limit',
'removeduplicates',
'aggregate',
'comparedatasets',
);
foreach ( $logic_slugs as $l ) {
if ( $slug_lower === $l ) {
return 'logic';
}
}
$code_slugs = array(
'code',
'function',
'executeworkflow',
'set',
'editfields',
'html',
'xml',
'markdown',
'crypto',
'tofile',
'converttofile',
'extractfromfile',
);
foreach ( $code_slugs as $c ) {
if ( $slug_lower === $c ) {
return 'transform';
}
}
$db_slugs = array( 'mysql', 'postgres', 'redis', 'mongodb', 'sqlite', 'microsoftsql', 'supabase' );
foreach ( $db_slugs as $d ) {
if ( $slug_lower === $d ) {
return 'database';
}
}
return 'action';
}
}