UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

377 lines (376 loc) 15.8 kB
import { BaseLanguageHandler } from './base.js'; import { getNodeText } from '../astAnalyzer.js'; import logger from '../../../logger.js'; export class PhpHandler extends BaseLanguageHandler { getFunctionQueryPatterns() { return [ 'function_definition', 'method_declaration', 'anonymous_function_creation_expression', 'arrow_function' ]; } getClassQueryPatterns() { return [ 'class_declaration', 'interface_declaration', 'trait_declaration' ]; } getImportQueryPatterns() { return [ 'namespace_use_declaration', 'namespace_use_clause', 'require_expression', 'include_expression' ]; } extractFunctionName(node, sourceCode, _options) { try { if (node.type === 'function_definition') { const nameNode = node.childForFieldName('name'); if (nameNode) { const name = getNodeText(nameNode, sourceCode); if (name.startsWith('test')) { return name; } return name; } } if (node.type === 'method_declaration') { const nameNode = node.childForFieldName('name'); if (nameNode) { const name = getNodeText(nameNode, sourceCode); if (name === '__construct') { return 'constructor'; } if (name.startsWith('__')) { return `magic_${name.slice(2)}`; } if (this.isLaravelControllerAction(node, sourceCode)) { return `action_${name}`; } return name; } } if (node.type === 'anonymous_function_creation_expression') { if (node.parent?.type === 'assignment_expression') { const leftNode = node.parent.childForFieldName('left'); if (leftNode) { return getNodeText(leftNode, sourceCode); } } if (node.parent?.type === 'argument' && node.parent.parent?.type === 'argument_list' && node.parent.parent.parent?.type === 'function_call_expression') { const funcNode = node.parent.parent.parent.childForFieldName('function'); if (funcNode) { const funcName = getNodeText(funcNode, sourceCode); if (['array_map', 'array_filter', 'array_reduce', 'usort'].includes(funcName)) { return `${funcName}_callback`; } } } return 'anonymous_function'; } if (node.type === 'arrow_function') { if (node.parent?.type === 'assignment_expression') { const leftNode = node.parent.childForFieldName('left'); if (leftNode) { return getNodeText(leftNode, sourceCode); } } return 'arrow_function'; } return 'anonymous'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP function name'); return 'anonymous'; } } isLaravelControllerAction(node, sourceCode) { try { let current = node.parent; while (current && current.type !== 'class_declaration') { current = current.parent; } if (current) { const baseClauseNode = current.childForFieldName('base_clause'); if (baseClauseNode) { const baseText = getNodeText(baseClauseNode, sourceCode); return baseText.includes('Controller'); } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if method is a Laravel controller action'); return false; } } extractClassName(node, sourceCode) { try { if (node.type === 'class_declaration' || node.type === 'interface_declaration' || node.type === 'trait_declaration') { const nameNode = node.childForFieldName('name'); if (nameNode) { return getNodeText(nameNode, sourceCode); } } return 'AnonymousClass'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP class name'); return 'AnonymousClass'; } } extractParentClass(node, sourceCode) { try { if (node.type === 'class_declaration') { const baseClauseNode = node.childForFieldName('base_clause'); if (baseClauseNode) { return getNodeText(baseClauseNode, sourceCode); } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP parent class'); return undefined; } } extractImplementedInterfaces(node, sourceCode) { try { if (node.type === 'class_declaration') { const implementsClauseNode = node.childForFieldName('implements_clause'); if (implementsClauseNode) { const interfaces = getNodeText(implementsClauseNode, sourceCode) .replace('implements', '') .split(',') .map(i => i.trim()); return interfaces.length > 0 ? interfaces : undefined; } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP implemented interfaces'); return undefined; } } extractImportPath(node, sourceCode) { try { if (node.type === 'namespace_use_declaration') { const clauseNode = node.childForFieldName('clauses'); if (clauseNode?.firstChild) { return getNodeText(clauseNode.firstChild, sourceCode); } } else if (node.type === 'namespace_use_clause') { return getNodeText(node, sourceCode); } else if (node.type === 'require_expression' || node.type === 'include_expression') { const argumentNode = node.childForFieldName('argument'); if (argumentNode) { const path = getNodeText(argumentNode, sourceCode); return path.replace(/^['"]|['"]$/g, ''); } } return 'unknown'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP import path'); return 'unknown'; } } extractImportedItems(node, sourceCode) { try { if (node.type === 'namespace_use_declaration') { const items = []; const clausesNode = node.childForFieldName('clauses'); if (clausesNode) { const importType = this.getUseDeclarationType(node, sourceCode); for (let i = 0; i < clausesNode.childCount; i++) { const clauseNode = clausesNode.child(i); if (clauseNode && clauseNode.type === 'namespace_use_clause') { const nameNode = clauseNode.childForFieldName('name'); const aliasNode = clauseNode.childForFieldName('alias'); if (nameNode) { const fullPath = getNodeText(nameNode, sourceCode); const parts = fullPath.split('\\'); const name = parts[parts.length - 1]; const alias = aliasNode ? getNodeText(aliasNode, sourceCode) : undefined; const isGroupUse = node.childForFieldName('prefix') !== null; let path = fullPath; if (isGroupUse) { const prefixNode = node.childForFieldName('prefix'); if (prefixNode) { const prefix = getNodeText(prefixNode, sourceCode); path = prefix + '\\' + fullPath; } } items.push({ name: name, path: path, alias: alias, isDefault: false, isNamespace: false, nodeText: clauseNode.text, importType: importType }); } } } } return items.length > 0 ? items : undefined; } else if (node.type === 'namespace_use_clause') { const nameNode = node.childForFieldName('name'); const aliasNode = node.childForFieldName('alias'); if (nameNode) { const fullPath = getNodeText(nameNode, sourceCode); const parts = fullPath.split('\\'); const name = parts[parts.length - 1]; const alias = aliasNode ? getNodeText(aliasNode, sourceCode) : undefined; const isGroupUse = node.parent?.parent?.childForFieldName('prefix') !== null; let path = fullPath; if (isGroupUse) { const prefixNode = node.parent?.parent?.childForFieldName('prefix'); if (prefixNode) { const prefix = getNodeText(prefixNode, sourceCode); path = prefix + '\\' + fullPath; } } const importType = this.getUseDeclarationType(node.parent?.parent, sourceCode); return [{ name: name, path: path, alias: alias, isDefault: false, isNamespace: false, nodeText: node.text, importType: importType }]; } } else if (node.type === 'require_expression' || node.type === 'include_expression') { const argumentNode = node.childForFieldName('argument'); if (argumentNode) { const path = getNodeText(argumentNode, sourceCode).replace(/^['"]|['"]$/g, ''); const parts = path.split('/'); const name = parts[parts.length - 1].replace('.php', ''); return [{ name: name, path: path, isDefault: false, isNamespace: false, nodeText: node.text, importType: node.type === 'require_expression' ? 'require' : 'include' }]; } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP imported items'); return undefined; } } getUseDeclarationType(node, sourceCode) { try { if (!node) return 'class'; const keywordNode = node.childForFieldName('kind'); if (keywordNode) { const keyword = getNodeText(keywordNode, sourceCode); if (keyword === 'function') return 'function'; if (keyword === 'const') return 'const'; } return 'class'; } catch (error) { logger.warn({ err: error }, 'Error getting PHP use declaration type'); return 'class'; } } extractFunctionComment(node, _sourceCode) { try { const current = node; let prev = current.previousNamedSibling; while (prev && prev.type !== 'comment') { prev = prev.previousNamedSibling; } if (prev && prev.type === 'comment' && prev.text.startsWith('/**')) { return this.parsePhpDocComment(prev.text); } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP function comment'); return undefined; } } extractClassComment(node, _sourceCode) { try { const current = node; let prev = current.previousNamedSibling; while (prev && prev.type !== 'comment') { prev = prev.previousNamedSibling; } if (prev && prev.type === 'comment' && prev.text.startsWith('/**')) { return this.parsePhpDocComment(prev.text); } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting PHP class comment'); return undefined; } } parsePhpDocComment(comment) { try { const text = comment.substring(3, comment.length - 2); const lines = text.split('\n') .map(line => line.trim().replace(/^\*\s*/, '')) .filter(line => !line.startsWith('@')); return lines.join(' ').trim(); } catch (error) { logger.warn({ err: error }, 'Error parsing PHPDoc comment'); return comment; } } detectFramework(sourceCode) { try { if (sourceCode.includes('Illuminate\\') || sourceCode.includes('extends Controller') || sourceCode.includes('use App\\Http\\Controllers\\Controller')) { return 'laravel'; } if (sourceCode.includes('Symfony\\') || sourceCode.includes('extends AbstractController') || sourceCode.includes('use Symfony\\Component\\HttpFoundation')) { return 'symfony'; } if (sourceCode.includes('add_action') || sourceCode.includes('add_filter') || sourceCode.includes('wp_enqueue_script')) { return 'wordpress'; } if (sourceCode.includes('extends CI_Controller') || sourceCode.includes('extends CI_Model') || sourceCode.includes('$this->load->view')) { return 'codeigniter'; } return null; } catch (error) { logger.warn({ err: error }, 'Error detecting PHP framework'); return null; } } }