|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | +/** |
| 5 | + * Copyright (c) The Magic , Distributed under the software license |
| 6 | + */ |
| 7 | +require_once __DIR__ . '/../vendor/autoload.php'; |
| 8 | + |
| 9 | +use Dtyq\PhpMcp\Client\McpClient; |
| 10 | +use Dtyq\PhpMcp\Shared\Kernel\Application; |
| 11 | +use Psr\Container\ContainerInterface; |
| 12 | +use Psr\EventDispatcher\EventDispatcherInterface; |
| 13 | +use Psr\Log\AbstractLogger; |
| 14 | +use Psr\Log\LoggerInterface; |
| 15 | + |
| 16 | +// Set timezone to Shanghai |
| 17 | +date_default_timezone_set('Asia/Shanghai'); |
| 18 | + |
| 19 | +// Function to detect PHP executable path |
| 20 | +function detectPhpPath(): string |
| 21 | +{ |
| 22 | + // Try to find PHP executable |
| 23 | + $possiblePaths = [ |
| 24 | + 'php', // System PATH |
| 25 | + '/usr/bin/php', // Linux standard |
| 26 | + '/usr/local/bin/php', // Common Linux location |
| 27 | + '/opt/homebrew/bin/php', // macOS Homebrew |
| 28 | + '/opt/homebrew/opt/[email protected]/bin/php', // macOS Homebrew PHP 8.3 |
| 29 | + '/opt/homebrew/opt/[email protected]/bin/php', // macOS Homebrew PHP 8.2 |
| 30 | + '/opt/homebrew/opt/[email protected]/bin/php', // macOS Homebrew PHP 8.1 |
| 31 | + 'C:\xampp\php\php.exe', // Windows XAMPP |
| 32 | + 'C:\php\php.exe', // Windows standalone PHP |
| 33 | + ]; |
| 34 | + |
| 35 | + foreach ($possiblePaths as $path) { |
| 36 | + if (is_executable($path)) { |
| 37 | + return $path; |
| 38 | + } |
| 39 | + } |
| 40 | + |
| 41 | + // Try using 'which' command |
| 42 | + $which = shell_exec('which php 2>/dev/null'); |
| 43 | + if ($which && is_executable(trim($which))) { |
| 44 | + return trim($which); |
| 45 | + } |
| 46 | + |
| 47 | + // Try using 'where' command on Windows |
| 48 | + $where = shell_exec('where php 2>NUL'); |
| 49 | + if ($where && is_executable(trim($where))) { |
| 50 | + return trim($where); |
| 51 | + } |
| 52 | + |
| 53 | + // Fallback to PHP_BINARY constant |
| 54 | + if (defined('PHP_BINARY') && is_executable(PHP_BINARY)) { |
| 55 | + return PHP_BINARY; |
| 56 | + } |
| 57 | + |
| 58 | + // Last resort |
| 59 | + return 'php'; |
| 60 | +} |
| 61 | + |
| 62 | +// Simple DI container implementation |
| 63 | +$container = new class implements ContainerInterface { |
| 64 | + /** @var array<string, object> */ |
| 65 | + private array $services = []; |
| 66 | + |
| 67 | + public function __construct() |
| 68 | + { |
| 69 | + $this->services[LoggerInterface::class] = new class extends AbstractLogger { |
| 70 | + /** |
| 71 | + * @param mixed $level |
| 72 | + * @param string $message |
| 73 | + */ |
| 74 | + public function log($level, $message, array $context = []): void |
| 75 | + { |
| 76 | + $timestamp = date('Y-m-d H:i:s'); |
| 77 | + $contextStr = empty($context) ? '' : ' ' . json_encode($context, JSON_UNESCAPED_SLASHES); |
| 78 | + |
| 79 | + // Ensure log directory exists |
| 80 | + $logDir = __DIR__ . '/../.log'; |
| 81 | + if (! is_dir($logDir)) { |
| 82 | + mkdir($logDir, 0755, true); |
| 83 | + } |
| 84 | + |
| 85 | + file_put_contents($logDir . '/env-stdio-client.log', "[{$timestamp}] {$level}: {$message}{$contextStr}\n", FILE_APPEND); |
| 86 | + } |
| 87 | + }; |
| 88 | + |
| 89 | + $this->services[EventDispatcherInterface::class] = new class implements EventDispatcherInterface { |
| 90 | + public function dispatch(object $event): object |
| 91 | + { |
| 92 | + return $event; |
| 93 | + } |
| 94 | + }; |
| 95 | + } |
| 96 | + |
| 97 | + public function get($id) |
| 98 | + { |
| 99 | + return $this->services[$id]; |
| 100 | + } |
| 101 | + |
| 102 | + public function has($id): bool |
| 103 | + { |
| 104 | + return isset($this->services[$id]); |
| 105 | + } |
| 106 | +}; |
| 107 | + |
| 108 | +// Create application |
| 109 | +$config = [ |
| 110 | + 'sdk_name' => 'php-mcp-env-demo-client', |
| 111 | +]; |
| 112 | +$app = new Application($container, $config); |
| 113 | + |
| 114 | +// Create client |
| 115 | +$client = new McpClient('env-demo-client', '1.0.0', $app); |
| 116 | + |
| 117 | +echo "=== PHP MCP Environment Variables Demo (Portable Version) ===\n\n"; |
| 118 | + |
| 119 | +// Detect PHP path |
| 120 | +$phpPath = detectPhpPath(); |
| 121 | +echo "Detected PHP executable: {$phpPath}\n\n"; |
| 122 | + |
| 123 | +// Connect to server with custom environment variables |
| 124 | +echo "1. Connecting to MCP server with custom environment variables...\n"; |
| 125 | +$session = $client->connect('stdio', [ |
| 126 | + 'command' => $phpPath, |
| 127 | + 'args' => [__DIR__ . '/env-stdio-server.php'], |
| 128 | + 'env' => [ |
| 129 | + // Custom environment variables that will be passed to the server |
| 130 | + 'DEMO_APP_NAME' => 'PHP MCP Environment Demo (Portable)', |
| 131 | + 'DEMO_VERSION' => '1.0.0', |
| 132 | + 'DEMO_ENVIRONMENT' => 'development', |
| 133 | + 'DEMO_DEBUG' => 'true', |
| 134 | + 'DEMO_API_KEY' => 'demo-key-12345', |
| 135 | + 'DEMO_DATABASE_URL' => 'postgres://localhost:5432/demo_db', |
| 136 | + 'DEMO_REDIS_URL' => 'redis://localhost:6379', |
| 137 | + 'OPENAPI_MCP_HEADERS' => '{"Authorization": "Bearer demo-token", "Content-Type": "application/json"}', |
| 138 | + 'NODE_ENV' => 'development', |
| 139 | + 'PHP_CUSTOM_VAR' => 'This is a custom PHP variable', |
| 140 | + 'DETECTED_PHP_PATH' => $phpPath, |
| 141 | + ], |
| 142 | + 'inherit_environment' => true, // Inherit parent environment and merge with custom vars |
| 143 | +]); |
| 144 | + |
| 145 | +$session->initialize(); |
| 146 | +echo " ✓ Connected and initialized with custom environment variables\n\n"; |
| 147 | + |
| 148 | +// Test environment variable passing |
| 149 | +echo "2. Testing environment variable passing:\n"; |
| 150 | +try { |
| 151 | + $result = $session->callTool('get_env', ['name' => 'DETECTED_PHP_PATH']); |
| 152 | + $content = $result->getContent(); |
| 153 | + if (is_array($content) && isset($content[0])) { |
| 154 | + $data = json_decode($content[0]->getText(), true); |
| 155 | + echo " PHP Path environment variable:\n"; |
| 156 | + echo " - Client detected: {$phpPath}\n"; |
| 157 | + echo ' - Server received: ' . ($data['value'] ?? 'not found') . "\n"; |
| 158 | + echo ' - Match: ' . (($data['value'] ?? '') === $phpPath ? 'YES' : 'NO') . "\n"; |
| 159 | + } |
| 160 | + echo "\n"; |
| 161 | +} catch (Exception $e) { |
| 162 | + echo " ✗ Failed to test environment variable passing: {$e->getMessage()}\n\n"; |
| 163 | +} |
| 164 | + |
| 165 | +// List available tools |
| 166 | +echo "3. Available Environment Tools:\n"; |
| 167 | +try { |
| 168 | + $tools = $session->listTools(); |
| 169 | + foreach ($tools->getTools() as $tool) { |
| 170 | + echo " - {$tool->getName()}: {$tool->getDescription()}\n"; |
| 171 | + } |
| 172 | + echo "\n"; |
| 173 | +} catch (Exception $e) { |
| 174 | + echo " ✗ Failed to list tools: {$e->getMessage()}\n\n"; |
| 175 | +} |
| 176 | + |
| 177 | +// Test all custom environment variables |
| 178 | +echo "4. Testing all custom environment variables:\n"; |
| 179 | +try { |
| 180 | + $result = $session->callTool('get_env', ['filter' => 'DEMO_']); |
| 181 | + $content = $result->getContent(); |
| 182 | + if (is_array($content) && isset($content[0])) { |
| 183 | + $data = json_decode($content[0]->getText(), true); |
| 184 | + echo " Found {$data['count']} DEMO_ variables:\n"; |
| 185 | + foreach ($data['variables'] as $name => $value) { |
| 186 | + echo " - {$name}: {$value}\n"; |
| 187 | + } |
| 188 | + } |
| 189 | + echo "\n"; |
| 190 | +} catch (Exception $e) { |
| 191 | + echo " ✗ Failed to get custom environment variables: {$e->getMessage()}\n\n"; |
| 192 | +} |
| 193 | + |
| 194 | +// Test comprehensive environment info |
| 195 | +echo "5. Getting comprehensive environment information:\n"; |
| 196 | +try { |
| 197 | + $result = $session->callTool('env_info'); |
| 198 | + $content = $result->getContent(); |
| 199 | + if (is_array($content) && isset($content[0])) { |
| 200 | + $data = json_decode($content[0]->getText(), true); |
| 201 | + echo " Process Information:\n"; |
| 202 | + echo ' - Process ID: ' . ($data['process_id'] ?? 'unknown') . "\n"; |
| 203 | + echo ' - PHP Version: ' . ($data['php_version'] ?? 'unknown') . "\n"; |
| 204 | + echo ' - PHP SAPI: ' . ($data['php_sapi'] ?? 'unknown') . "\n"; |
| 205 | + echo ' - Current User: ' . ($data['current_user'] ?? 'unknown') . "\n"; |
| 206 | + echo ' - Working Directory: ' . ($data['working_directory'] ?? 'unknown') . "\n"; |
| 207 | + echo ' - Memory Usage: ' . number_format((float) ($data['memory_usage'] ?? 0)) . " bytes\n"; |
| 208 | + echo ' - Total Environment Variables: ' . ($data['total_env_vars'] ?? 'unknown') . "\n"; |
| 209 | + |
| 210 | + if (isset($data['categories'])) { |
| 211 | + echo " Custom Environment Variables:\n"; |
| 212 | + foreach ($data['categories'] as $category => $vars) { |
| 213 | + if ($category === 'custom' && count($vars) > 0) { |
| 214 | + foreach ($vars as $name => $value) { |
| 215 | + if (strpos($name, 'DEMO_') === 0) { |
| 216 | + echo " - {$name}: {$value}\n"; |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | + } |
| 223 | + echo "\n"; |
| 224 | +} catch (Exception $e) { |
| 225 | + echo " ✗ Failed to get environment info: {$e->getMessage()}\n\n"; |
| 226 | +} |
| 227 | + |
| 228 | +// Test setting a runtime variable |
| 229 | +echo "6. Testing runtime variable setting:\n"; |
| 230 | +try { |
| 231 | + $result = $session->callTool('set_env', [ |
| 232 | + 'name' => 'DEMO_RUNTIME_VAR', |
| 233 | + 'value' => 'Set at runtime: ' . date('Y-m-d H:i:s') . ' on ' . php_uname('n'), |
| 234 | + ]); |
| 235 | + $content = $result->getContent(); |
| 236 | + if (is_array($content) && isset($content[0])) { |
| 237 | + $data = json_decode($content[0]->getText(), true); |
| 238 | + echo " Runtime variable set:\n"; |
| 239 | + echo " - Name: DEMO_RUNTIME_VAR\n"; |
| 240 | + echo ' - Value: ' . ($data['value'] ?? 'unknown') . "\n"; |
| 241 | + echo ' - Success: ' . (($data['success'] ?? false) ? 'YES' : 'NO') . "\n"; |
| 242 | + } |
| 243 | + echo "\n"; |
| 244 | +} catch (Exception $e) { |
| 245 | + echo " ✗ Failed to set runtime variable: {$e->getMessage()}\n\n"; |
| 246 | +} |
| 247 | + |
| 248 | +// Test searching for variables |
| 249 | +echo "7. Testing environment variable search:\n"; |
| 250 | +try { |
| 251 | + $result = $session->callTool('search_env', [ |
| 252 | + 'pattern' => '*DEMO*', |
| 253 | + 'search_in' => 'keys', |
| 254 | + ]); |
| 255 | + $content = $result->getContent(); |
| 256 | + if (is_array($content) && isset($content[0])) { |
| 257 | + $data = json_decode($content[0]->getText(), true); |
| 258 | + echo " Search Results for '*DEMO*':\n"; |
| 259 | + echo ' - Matches found: ' . ($data['matches_found'] ?? 0) . "\n"; |
| 260 | + |
| 261 | + if (isset($data['results']) && is_array($data['results'])) { |
| 262 | + foreach ($data['results'] as $match) { |
| 263 | + echo " - {$match['key']}: {$match['value']}\n"; |
| 264 | + } |
| 265 | + } |
| 266 | + } |
| 267 | + echo "\n"; |
| 268 | +} catch (Exception $e) { |
| 269 | + echo " ✗ Failed to search environment variables: {$e->getMessage()}\n\n"; |
| 270 | +} |
| 271 | + |
| 272 | +// Display session statistics |
| 273 | +echo "8. Session Summary:\n"; |
| 274 | +$stats = $client->getStats(); |
| 275 | +echo " - PHP Path: {$phpPath}\n"; |
| 276 | +echo ' - Connection attempts: ' . $stats->getConnectionAttempts() . "\n"; |
| 277 | +echo ' - Connection errors: ' . $stats->getConnectionErrors() . "\n"; |
| 278 | +echo ' - Status: ' . $stats->getStatus() . "\n"; |
| 279 | +echo ' - Session ID: ' . $session->getSessionId() . "\n"; |
| 280 | +echo "\n"; |
| 281 | + |
| 282 | +// Close client |
| 283 | +echo "9. Closing session...\n"; |
| 284 | +$client->close(); |
| 285 | +echo " ✓ Session closed\n\n"; |
| 286 | + |
| 287 | +echo "=== Environment Variables Demo completed successfully ===\n"; |
| 288 | +echo "\nThis portable demo automatically detected the PHP executable path\n"; |
| 289 | +echo "and successfully demonstrated environment variable passing functionality.\n"; |
| 290 | +echo "\nKey features tested:\n"; |
| 291 | +echo "1. Automatic PHP path detection\n"; |
| 292 | +echo "2. Custom environment variable passing\n"; |
| 293 | +echo "3. Environment variable retrieval and filtering\n"; |
| 294 | +echo "4. Runtime environment variable setting\n"; |
| 295 | +echo "5. Environment variable searching\n"; |
| 296 | +echo "6. Comprehensive environment information\n"; |
0 commit comments