{
"tool_response": {
"type": "update",
"filePath": "\/var\/www\/scripts\/interface-check.sh",
"content": "#!\/bin\/bash\n# Interface Binding Check - Validates DI container bindings\n# Runs BEFORE deployment to catch missing interface implementations\n\nset -e\nPROJECT_DIR=\"\/var\/www\/dev.campus.systemische-tools.de\"\ncd \"$PROJECT_DIR\"\n\necho \"[Interface Check] Validating DI container bindings...\"\n\nphp << 'PHPEOF'\n<?php\ndeclare(strict_types=1);\n\n$projectDir = '\/var\/www\/dev.campus.systemische-tools.de';\nchdir($projectDir);\nrequire_once $projectDir . '\/vendor\/autoload.php';\n$dotenv = Dotenv\\Dotenv::createImmutable($projectDir);\n$dotenv->load();\nrequire_once $projectDir . '\/config\/config.php';\nrequire_once $projectDir . '\/config\/autoload.php';\n\n$errors = [];\n$checked = 0;\n\n\/\/ Scan ServiceProviders for interface bindings\n$providers = glob($projectDir . '\/src\/ServiceProvider\/*ServiceProvider.php');\n\nforeach ($providers as $file) {\n $content = file_get_contents($file);\n\n \/\/ Find patterns: set(SomeInterface::class, fn ... get(SomeClass::class))\n preg_match_all(\n '\/\\$container->set\\(\\s*([A-Za-z\\\\\\\\]+Interface)::class\\s*,\\s*fn\\s*\\([^)]*\\)\\s*=>\\s*\\$c->get\\(\\s*([A-Za-z\\\\\\\\]+)::class\\s*\\)\/',\n $content,\n $matches,\n PREG_SET_ORDER\n );\n\n foreach ($matches as $match) {\n $interfaceShort = $match[1];\n $implShort = $match[2];\n\n \/\/ Skip if same class\n if ($interfaceShort === $implShort) continue;\n\n $checked++;\n\n \/\/ Resolve full class names\n $interfaceFqcn = resolveClassName($content, $interfaceShort);\n $implFqcn = resolveClassName($content, $implShort);\n\n \/\/ Check if class exists\n if (!class_exists($implFqcn) && !interface_exists($implFqcn)) {\n $errors[] = \"Class not found: $implFqcn\";\n continue;\n }\n\n \/\/ Check if interface exists\n if (!interface_exists($interfaceFqcn)) {\n $errors[] = \"Interface not found: $interfaceFqcn\";\n continue;\n }\n\n \/\/ Check if implementation implements the interface\n $implements = class_implements($implFqcn) ?: [];\n if (!in_array($interfaceFqcn, $implements, true)) {\n $errors[] = sprintf(\n \"MISSING: %s does NOT implement %s\",\n basename(str_replace('\\\\', '\/', $implFqcn)),\n basename(str_replace('\\\\', '\/', $interfaceFqcn))\n );\n }\n }\n}\n\nfunction resolveClassName(string $content, string $shortName): string {\n \/\/ Direct use statement\n if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+\\\\\\\\' . preg_quote($shortName, '\/') . ')\\s*;\/', $content, $m)) {\n return $m[1];\n }\n\n \/\/ Aliased use\n if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+)\\s+as\\s+' . preg_quote($shortName, '\/') . '\\s*;\/', $content, $m)) {\n return $m[1];\n }\n\n \/\/ Already FQN\n if (str_contains($shortName, '\\\\')) {\n return $shortName;\n }\n\n return $shortName;\n}\n\necho \"Checked $checked interface bindings\\n\";\n\nif (empty($errors)) {\n echo \"\\033[0;32m✓ All interface bindings are valid\\033[0m\\n\";\n exit(0);\n} else {\n echo \"\\033[0;31mErrors found:\\033[0m\\n\";\n foreach ($errors as $error) {\n echo \" - $error\\n\";\n }\n exit(1);\n}\nPHPEOF\n",
"structuredPatch": [
{
"oldStart": 8,
"oldLines": 30,
"newStart": 8,
"newLines": 28,
"lines": [
" ",
" echo \"[Interface Check] Validating DI container bindings...\"",
" ",
"-# Create temporary PHP script",
"-cat > \/tmp\/interface_check.php << PHPEOF",
"+php << 'PHPEOF'",
" <?php",
" declare(strict_types=1);",
" ",
"-\\$projectDir = '$PROJECT_DIR';",
"-chdir(\\$projectDir);",
"-require_once \\$projectDir . '\/vendor\/autoload.php';",
"-\\$dotenv = Dotenv\\Dotenv::createImmutable(\\$projectDir);",
"-\\$dotenv->load();",
"-require_once \\$projectDir . '\/config\/config.php';",
"-require_once \\$projectDir . '\/config\/autoload.php';",
"+$projectDir = '\/var\/www\/dev.campus.systemische-tools.de';",
"+chdir($projectDir);",
"+require_once $projectDir . '\/vendor\/autoload.php';",
"+$dotenv = Dotenv\\Dotenv::createImmutable($projectDir);",
"+$dotenv->load();",
"+require_once $projectDir . '\/config\/config.php';",
"+require_once $projectDir . '\/config\/autoload.php';",
" ",
" $errors = [];",
" $checked = 0;",
" ",
" \/\/ Scan ServiceProviders for interface bindings",
"-$providers = glob(__DIR__ . '\/src\/ServiceProvider\/*ServiceProvider.php');",
"+$providers = glob($projectDir . '\/src\/ServiceProvider\/*ServiceProvider.php');",
" ",
" foreach ($providers as $file) {",
" $content = file_get_contents($file);",
" ",
"- \/\/ Find patterns like: set(SomeInterface::class, fn ... get(SomeClass::class))",
"- \/\/ Pattern: Interface binding to implementation",
"+ \/\/ Find patterns: set(SomeInterface::class, fn ... get(SomeClass::class))",
" preg_match_all(",
" '\/\\$container->set\\(\\s*([A-Za-z\\\\\\\\]+Interface)::class\\s*,\\s*fn\\s*\\([^)]*\\)\\s*=>\\s*\\$c->get\\(\\s*([A-Za-z\\\\\\\\]+)::class\\s*\\)\/',",
" $content,"
]
},
{
"oldStart": 40,
"oldLines": 54,
"newStart": 38,
"newLines": 54,
"lines": [
" );",
" ",
" foreach ($matches as $match) {",
"- $interfaceFqcn = $match[1];",
"- $implFqcn = $match[2];",
"+ $interfaceShort = $match[1];",
"+ $implShort = $match[2];",
" ",
"- \/\/ Skip if same class (self-reference)",
"- if ($interfaceFqcn === $implFqcn) continue;",
"+ \/\/ Skip if same class",
"+ if ($interfaceShort === $implShort) continue;",
" ",
" $checked++;",
" ",
"- \/\/ Resolve full class names from use statements",
"- $interfaceResolved = resolveClassName($content, $interfaceFqcn);",
"- $implResolved = resolveClassName($content, $implFqcn);",
"+ \/\/ Resolve full class names",
"+ $interfaceFqcn = resolveClassName($content, $interfaceShort);",
"+ $implFqcn = resolveClassName($content, $implShort);",
" ",
"- \/\/ Check if implementation class exists",
"- if (!class_exists($implResolved) && !interface_exists($implResolved)) {",
"- $errors[] = \"Class not found: $implResolved (bound to $interfaceResolved)\";",
"+ \/\/ Check if class exists",
"+ if (!class_exists($implFqcn) && !interface_exists($implFqcn)) {",
"+ $errors[] = \"Class not found: $implFqcn\";",
" continue;",
" }",
" ",
" \/\/ Check if interface exists",
"- if (!interface_exists($interfaceResolved)) {",
"- $errors[] = \"Interface not found: $interfaceResolved\";",
"+ if (!interface_exists($interfaceFqcn)) {",
"+ $errors[] = \"Interface not found: $interfaceFqcn\";",
" continue;",
" }",
" ",
" \/\/ Check if implementation implements the interface",
"- $implements = class_implements($implResolved);",
"- if (!in_array($interfaceResolved, $implements, true)) {",
"+ $implements = class_implements($implFqcn) ?: [];",
"+ if (!in_array($interfaceFqcn, $implements, true)) {",
" $errors[] = sprintf(",
" \"MISSING: %s does NOT implement %s\",",
"- $implResolved,",
"- $interfaceResolved",
"+ basename(str_replace('\\\\', '\/', $implFqcn)),",
"+ basename(str_replace('\\\\', '\/', $interfaceFqcn))",
" );",
" }",
" }",
" }",
" ",
"-function resolveClassName(string $fileContent, string $shortName): string {",
"- \/\/ Check for direct use statement",
"- if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+\\\\\\\\' . preg_quote($shortName, '\/') . ')\\s*;\/', $fileContent, $m)) {",
"+function resolveClassName(string $content, string $shortName): string {",
"+ \/\/ Direct use statement",
"+ if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+\\\\\\\\' . preg_quote($shortName, '\/') . ')\\s*;\/', $content, $m)) {",
" return $m[1];",
" }",
" ",
"- \/\/ Check for aliased use",
"- if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+)\\s+as\\s+' . preg_quote($shortName, '\/') . '\\s*;\/', $fileContent, $m)) {",
"+ \/\/ Aliased use",
"+ if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+)\\s+as\\s+' . preg_quote($shortName, '\/') . '\\s*;\/', $content, $m)) {",
" return $m[1];",
" }",
" ",
"- \/\/ Check if it's already FQN",
"+ \/\/ Already FQN",
" if (str_contains($shortName, '\\\\')) {",
" return $shortName;",
" }"
]
},
{
"oldStart": 108,
"oldLines": 8,
"newStart": 106,
"newLines": 3,
"lines": [
" exit(1);",
" }",
" PHPEOF",
"-",
"-# Run the check",
"-php \/tmp\/interface_check.php",
"-",
"-rm -f \/tmp\/interface_check.php"
]
}
],
"originalFile": "#!\/bin\/bash\n# Interface Binding Check - Validates DI container bindings\n# Runs BEFORE deployment to catch missing interface implementations\n\nset -e\nPROJECT_DIR=\"\/var\/www\/dev.campus.systemische-tools.de\"\ncd \"$PROJECT_DIR\"\n\necho \"[Interface Check] Validating DI container bindings...\"\n\n# Create temporary PHP script\ncat > \/tmp\/interface_check.php << PHPEOF\n<?php\ndeclare(strict_types=1);\n\n\\$projectDir = '$PROJECT_DIR';\nchdir(\\$projectDir);\nrequire_once \\$projectDir . '\/vendor\/autoload.php';\n\\$dotenv = Dotenv\\Dotenv::createImmutable(\\$projectDir);\n\\$dotenv->load();\nrequire_once \\$projectDir . '\/config\/config.php';\nrequire_once \\$projectDir . '\/config\/autoload.php';\n\n$errors = [];\n$checked = 0;\n\n\/\/ Scan ServiceProviders for interface bindings\n$providers = glob(__DIR__ . '\/src\/ServiceProvider\/*ServiceProvider.php');\n\nforeach ($providers as $file) {\n $content = file_get_contents($file);\n\n \/\/ Find patterns like: set(SomeInterface::class, fn ... get(SomeClass::class))\n \/\/ Pattern: Interface binding to implementation\n preg_match_all(\n '\/\\$container->set\\(\\s*([A-Za-z\\\\\\\\]+Interface)::class\\s*,\\s*fn\\s*\\([^)]*\\)\\s*=>\\s*\\$c->get\\(\\s*([A-Za-z\\\\\\\\]+)::class\\s*\\)\/',\n $content,\n $matches,\n PREG_SET_ORDER\n );\n\n foreach ($matches as $match) {\n $interfaceFqcn = $match[1];\n $implFqcn = $match[2];\n\n \/\/ Skip if same class (self-reference)\n if ($interfaceFqcn === $implFqcn) continue;\n\n $checked++;\n\n \/\/ Resolve full class names from use statements\n $interfaceResolved = resolveClassName($content, $interfaceFqcn);\n $implResolved = resolveClassName($content, $implFqcn);\n\n \/\/ Check if implementation class exists\n if (!class_exists($implResolved) && !interface_exists($implResolved)) {\n $errors[] = \"Class not found: $implResolved (bound to $interfaceResolved)\";\n continue;\n }\n\n \/\/ Check if interface exists\n if (!interface_exists($interfaceResolved)) {\n $errors[] = \"Interface not found: $interfaceResolved\";\n continue;\n }\n\n \/\/ Check if implementation implements the interface\n $implements = class_implements($implResolved);\n if (!in_array($interfaceResolved, $implements, true)) {\n $errors[] = sprintf(\n \"MISSING: %s does NOT implement %s\",\n $implResolved,\n $interfaceResolved\n );\n }\n }\n}\n\nfunction resolveClassName(string $fileContent, string $shortName): string {\n \/\/ Check for direct use statement\n if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+\\\\\\\\' . preg_quote($shortName, '\/') . ')\\s*;\/', $fileContent, $m)) {\n return $m[1];\n }\n\n \/\/ Check for aliased use\n if (preg_match('\/use\\s+([A-Za-z\\\\\\\\]+)\\s+as\\s+' . preg_quote($shortName, '\/') . '\\s*;\/', $fileContent, $m)) {\n return $m[1];\n }\n\n \/\/ Check if it's already FQN\n if (str_contains($shortName, '\\\\')) {\n return $shortName;\n }\n\n return $shortName;\n}\n\necho \"Checked $checked interface bindings\\n\";\n\nif (empty($errors)) {\n echo \"\\033[0;32m✓ All interface bindings are valid\\033[0m\\n\";\n exit(0);\n} else {\n echo \"\\033[0;31mErrors found:\\033[0m\\n\";\n foreach ($errors as $error) {\n echo \" - $error\\n\";\n }\n exit(1);\n}\nPHPEOF\n\n# Run the check\nphp \/tmp\/interface_check.php\n\nrm -f \/tmp\/interface_check.php\n"
}
}