php-ini-automation
Version:
Ultimate Laravel-optimized PHP configuration tool that automatically detects and configures PHP installations across all environments with Laravel 10+ specific extensions, performance settings, and security configurations
1,149 lines • 44.1 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.scanPhpInstallations = scanPhpInstallations;
exports.determinePhpIniPaths = determinePhpIniPaths;
exports.getDetailedPhpDetection = getDetailedPhpDetection;
exports.validatePhpInstallation = validatePhpInstallation;
exports.findBestPhpInstallation = findBestPhpInstallation;
const child_process_1 = require("child_process");
const fs_extra_1 = __importDefault(require("fs-extra"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
// ANSI color codes for consistent styling
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
white: '\x1b[37m'
};
// Cross-platform PHP environment configurations
const getEnvironmentConfigs = () => {
const isWindows = process.platform === 'win32';
const phpExe = isWindows ? 'php.exe' : 'php';
const configs = [];
if (isWindows) {
// Windows-specific environments
configs.push({
name: 'Laragon',
priority: 1,
deepScan: true,
maxDepth: 3,
basePaths: [
process.env.LARAGON_PATH || '',
'C:/laragon',
'D:/laragon',
'E:/laragon',
'F:/laragon',
path_1.default.join(os_1.default.homedir(), 'laragon'),
'C:/laragon/bin',
'D:/laragon/bin'
],
iniPattern: ['php', '{version}', 'php.ini'],
extPattern: ['php', '{version}', 'ext'],
phpExePattern: ['php', '{version}', phpExe],
versionPattern: ['php', '*']
}, {
name: 'PVM',
priority: 2,
deepScan: true,
maxDepth: 4,
basePaths: [
process.env.PVM_PATH || '',
'C:/tools/php',
'C:/pvm',
'D:/pvm',
path_1.default.join(os_1.default.homedir(), 'pvm'),
path_1.default.join(os_1.default.homedir(), '.pvm'),
'C:/Users/*/pvm',
'C:/dev/php',
'C:/tools/pvm'
],
iniPattern: ['sym', 'php.ini'],
extPattern: ['php', '{version}', 'ext'],
phpExePattern: ['sym', phpExe],
versionPattern: ['php', '*']
}, {
name: 'WAMP',
priority: 3,
deepScan: true,
maxDepth: 4,
basePaths: [
process.env.WAMP_PATH || '',
process.env.WAMPP_PATH || '',
'C:/wamp',
'C:/wamp64',
'D:/wamp',
'D:/wamp64',
'E:/wamp64',
'C:/wampserver',
'D:/wampserver'
],
iniPattern: ['bin', 'php', 'php{version}', 'php.ini'],
extPattern: ['bin', 'php', 'php{version}', 'ext'],
phpExePattern: ['bin', 'php', 'php{version}', phpExe],
versionPattern: ['bin', 'php', 'php*']
}, {
name: 'XAMPP',
priority: 4,
deepScan: false,
basePaths: [
process.env.XAMPP_PATH || '',
'C:/xampp',
'D:/xampp',
'E:/xampp',
'C:/xampp/php',
'D:/xampp/php'
],
iniPattern: ['php.ini'],
extPattern: ['ext'],
phpExePattern: [phpExe]
}, {
name: 'MAMP',
priority: 5,
deepScan: true,
maxDepth: 3,
basePaths: [
'C:/MAMP',
'D:/MAMP',
'C:/Applications/MAMP'
],
iniPattern: ['bin', 'php', 'php{version}', 'conf', 'php.ini'],
extPattern: ['bin', 'php', 'php{version}', 'lib', 'php', 'extensions'],
phpExePattern: ['bin', 'php', 'php{version}', phpExe],
versionPattern: ['bin', 'php', 'php*']
}, {
name: 'Uniform Server',
priority: 6,
deepScan: true,
maxDepth: 3,
basePaths: [
'C:/UniServer',
'D:/UniServer',
'C:/UniServerZ'
],
iniPattern: ['core', 'php{version}', 'php.ini'],
extPattern: ['core', 'php{version}', 'ext'],
phpExePattern: ['core', 'php{version}', phpExe],
versionPattern: ['core', 'php*']
}, {
name: 'Bitnami',
priority: 7,
deepScan: true,
maxDepth: 4,
basePaths: [
'C:/Bitnami',
'D:/Bitnami',
'C:/Program Files/Bitnami'
],
iniPattern: ['php', 'etc', 'php.ini'],
extPattern: ['php', 'lib', 'php', 'extensions'],
phpExePattern: ['php', 'bin', phpExe],
versionPattern: ['*']
}, {
name: 'Custom/System',
priority: 8,
deepScan: true,
maxDepth: 2,
basePaths: [
process.env.DEFAULT_PATH || '',
'C:/php',
'D:/php',
'C:/Program Files/PHP',
'C:/Program Files (x86)/PHP',
'C:/tools/php',
'C:/dev/php',
path_1.default.join(os_1.default.homedir(), 'php')
],
iniPattern: ['php.ini'],
extPattern: ['ext'],
phpExePattern: [phpExe]
});
}
else {
// Linux/Unix-specific environments
configs.push({
name: 'System Package Manager',
priority: 1,
deepScan: false,
basePaths: [
'/usr/bin',
'/usr/local/bin',
'/opt/php',
'/etc/php'
],
iniPattern: ['php.ini'],
extPattern: ['extensions'],
phpExePattern: [phpExe]
}, {
name: 'Ubuntu/Debian APT',
priority: 2,
deepScan: true,
maxDepth: 3,
basePaths: [
'/etc/php',
'/usr/lib/php',
'/usr/bin'
],
iniPattern: ['{version}', 'cli', 'php.ini'],
extPattern: ['{version}', 'mods-available'],
phpExePattern: [phpExe],
versionPattern: ['*']
}, {
name: 'CentOS/RHEL',
priority: 3,
deepScan: true,
maxDepth: 2,
basePaths: [
'/etc',
'/usr/lib64/php',
'/usr/share/php'
],
iniPattern: ['php.ini'],
extPattern: ['modules'],
phpExePattern: [phpExe]
}, {
name: 'Homebrew',
priority: 4,
deepScan: true,
maxDepth: 4,
basePaths: [
'/usr/local/etc/php',
'/opt/homebrew/etc/php',
'/usr/local/Cellar/php',
'/opt/homebrew/Cellar/php'
],
iniPattern: ['{version}', 'php.ini'],
extPattern: ['{version}', 'pecl'],
phpExePattern: ['{version}', 'bin', phpExe],
versionPattern: ['*']
}, {
name: 'Custom/Compiled',
priority: 5,
deepScan: true,
maxDepth: 3,
basePaths: [
process.env.PHP_PATH || '',
'/usr/local/php',
'/opt/php',
path_1.default.join(os_1.default.homedir(), 'php'),
path_1.default.join(os_1.default.homedir(), '.local/php')
],
iniPattern: ['php.ini'],
extPattern: ['lib', 'php', 'extensions'],
phpExePattern: ['bin', phpExe]
}, {
name: 'System Package Manager',
priority: 1,
deepScan: false,
basePaths: [
'/usr/bin',
'/usr/local/bin',
'/opt/php',
'/etc/php'
],
iniPattern: ['php.ini'],
extPattern: ['extensions'],
phpExePattern: [phpExe]
}, {
name: 'Ubuntu/Debian APT',
priority: 2,
deepScan: true,
maxDepth: 3,
basePaths: [
'/etc/php',
'/usr/lib/php',
'/var/lib/php'
],
iniPattern: ['{version}', 'apache2', 'php.ini'],
extPattern: ['{version}', 'mods-available'],
phpExePattern: ['{version}', 'bin', phpExe],
versionPattern: ['*']
}, {
name: 'CentOS/RHEL YUM/DNF',
priority: 3,
deepScan: true,
maxDepth: 3,
basePaths: [
'/etc/php.ini',
'/etc/php.d',
'/usr/lib64/php',
'/usr/share/php'
],
iniPattern: ['php.ini'],
extPattern: ['modules'],
phpExePattern: [phpExe]
}, {
name: 'Homebrew (macOS/Linux)',
priority: 4,
deepScan: true,
maxDepth: 4,
basePaths: [
'/usr/local/etc/php',
'/opt/homebrew/etc/php',
'/usr/local/Cellar/php',
'/opt/homebrew/Cellar/php',
path_1.default.join(os_1.default.homedir(), '.brew/etc/php')
],
iniPattern: ['{version}', 'php.ini'],
extPattern: ['{version}', 'pecl'],
phpExePattern: ['{version}', 'bin', phpExe],
versionPattern: ['*']
}, {
name: 'Docker/Container',
priority: 5,
deepScan: true,
maxDepth: 2,
basePaths: [
'/usr/local/etc/php',
'/etc/php',
'/var/www/html'
],
iniPattern: ['php.ini'],
extPattern: ['extensions'],
phpExePattern: [phpExe]
}, {
name: 'Compiled/Custom',
priority: 6,
deepScan: true,
maxDepth: 3,
basePaths: [
process.env.PHP_PATH || '',
'/usr/local/php',
'/opt/php',
'/home/*/php',
path_1.default.join(os_1.default.homedir(), 'php'),
path_1.default.join(os_1.default.homedir(), '.local/php'),
'/usr/local/src/php'
],
iniPattern: ['php.ini'],
extPattern: ['lib', 'php', 'extensions'],
phpExePattern: ['bin', phpExe],
versionPattern: ['*']
});
}
return configs;
};
/**
* Gets php.ini path directly from PHP executable
*/
function getPhpIniPathFromExecutable(phpPath) {
try {
const isWindows = process.platform === 'win32';
const nullDevice = isWindows ? '2>nul' : '2>/dev/null';
// Get loaded configuration file path
const result = (0, child_process_1.execSync)(`"${phpPath}" -r "echo php_ini_loaded_file();" ${nullDevice}`, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore']
});
const iniPath = result.trim();
if (iniPath && iniPath !== '' && fs_extra_1.default.existsSync(iniPath)) {
return [iniPath];
}
// Try alternative method
const configResult = (0, child_process_1.execSync)(`"${phpPath}" --ini ${nullDevice}`, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore']
});
const loadedMatch = configResult.match(/Loaded Configuration File:\s*(.+)/);
if (loadedMatch && loadedMatch[1] && loadedMatch[1].trim() !== '(none)') {
const loadedPath = loadedMatch[1].trim();
if (fs_extra_1.default.existsSync(loadedPath)) {
return [loadedPath];
}
}
// Extract scan directories
const scanMatch = configResult.match(/Scan for additional \.ini files in:\s*(.+)/);
if (scanMatch && scanMatch[1] && scanMatch[1].trim() !== '(none)') {
const scanDirs = scanMatch[1].trim().split(/[,;:]/);
return scanDirs.map(dir => path_1.default.join(dir.trim(), 'php.ini')).filter(p => fs_extra_1.default.existsSync(p));
}
}
catch {
// Failed to get ini path from PHP
}
return [];
}
/**
* Gets extension directory path directly from PHP executable
*/
function getPhpExtensionDirFromExecutable(phpPath) {
try {
const isWindows = process.platform === 'win32';
const nullDevice = isWindows ? '2>nul' : '2>/dev/null';
// Get extension directory from PHP
const result = (0, child_process_1.execSync)(`"${phpPath}" -r "echo ini_get('extension_dir');" ${nullDevice}`, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore']
});
const extDir = result.trim();
if (extDir && extDir !== '' && fs_extra_1.default.existsSync(extDir)) {
return [extDir];
}
}
catch {
// Failed to get extension dir from PHP
}
return [];
}
/**
* Gets comprehensive PHP information from executable (cross-platform)
*/
function getPhpInfoFromExecutable(phpPath) {
try {
const isWindows = process.platform === 'win32';
const nullDevice = isWindows ? '2>nul' : '2>/dev/null';
// Suppress PHP warnings and errors during detection
const versionResult = (0, child_process_1.execSync)(`"${phpPath}" -v ${nullDevice}`, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore'] // Ignore stderr
});
const configResult = (0, child_process_1.execSync)(`"${phpPath}" -i ${nullDevice}`, {
encoding: 'utf8',
timeout: 10000,
stdio: ['pipe', 'pipe', 'ignore'] // Ignore stderr
});
// Extract version
const versionMatch = versionResult.match(/PHP (\d+\.\d+\.\d+)/);
const version = versionMatch ? versionMatch[1] : 'unknown';
// Extract additional info
const architectureMatch = configResult.match(/Architecture => (.+)/);
const threadSafetyMatch = configResult.match(/Thread Safety => (.+)/);
const buildDateMatch = configResult.match(/Build Date => (.+)/);
const configureMatch = configResult.match(/Configure Command => (.+)/);
return {
version,
architecture: architectureMatch ? architectureMatch[1].trim() : undefined,
threadSafety: threadSafetyMatch ? threadSafetyMatch[1].trim() === 'enabled' : undefined,
buildDate: buildDateMatch ? buildDateMatch[1].trim() : undefined,
configureCommand: configureMatch ? configureMatch[1].trim() : undefined
};
}
catch {
// Fallback to simple version detection
try {
const isWindows = process.platform === 'win32';
const nullDevice = isWindows ? '2>nul' : '2>/dev/null';
const result = (0, child_process_1.execSync)(`"${phpPath}" -v ${nullDevice}`, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore']
});
const match = result.match(/PHP (\d+\.\d+\.\d+)/);
return match ? { version: match[1] } : null;
}
catch {
return null;
}
}
}
/**
* Detects PHP installations from Windows Registry (Windows only)
*/
function detectFromRegistry() {
const installations = [];
if (process.platform !== 'win32')
return installations;
try {
// Check common registry paths for PHP installations
const registryPaths = [
'HKEY_LOCAL_MACHINE\\SOFTWARE\\PHP',
'HKEY_CURRENT_USER\\SOFTWARE\\PHP',
'HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\PHP'
];
for (const regPath of registryPaths) {
try {
const result = (0, child_process_1.execSync)(`reg query "${regPath}" /s 2>nul`, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore'] // Suppress error output
});
// Parse registry output for PHP paths
const pathMatches = result.match(/InstallDir\s+REG_SZ\s+(.+)/g);
if (pathMatches) {
for (const match of pathMatches) {
const pathMatch = match.match(/InstallDir\s+REG_SZ\s+(.+)/);
if (pathMatch) {
const phpPath = pathMatch[1].trim();
const installation = createInstallationFromPath(phpPath, 'Registry');
if (installation)
installations.push(installation);
}
}
}
}
catch {
// Continue with next registry path silently
}
}
}
catch {
// Registry detection failed, continue with other methods
}
return installations;
}
/**
* Detects PHP from system PATH (cross-platform)
*/
function detectFromPath() {
const installations = [];
try {
const isWindows = process.platform === 'win32';
const command = isWindows ? 'where php 2>nul' : 'which php 2>/dev/null';
const result = (0, child_process_1.execSync)(command, {
encoding: 'utf8',
timeout: 5000,
stdio: ['pipe', 'pipe', 'ignore'] // Suppress error output
});
const phpPaths = result.split('\n').filter(line => line.trim());
for (const phpPath of phpPaths) {
const cleanPath = phpPath.trim();
if (fs_extra_1.default.existsSync(cleanPath)) {
const installation = createInstallationFromPath(path_1.default.dirname(cleanPath), 'System PATH');
if (installation) {
installation.isActive = true; // PATH PHP is considered active
installations.push(installation);
}
}
}
}
catch {
// PATH detection failed silently
}
return installations;
}
/**
* Deep recursive scan for PHP installations (cross-platform)
*/
function deepScanDirectory(basePath, maxDepth = 3, currentDepth = 0) {
const phpPaths = [];
if (currentDepth >= maxDepth || !fs_extra_1.default.existsSync(basePath)) {
return phpPaths;
}
try {
const entries = fs_extra_1.default.readdirSync(basePath);
const isWindows = process.platform === 'win32';
const phpExeName = isWindows ? 'php.exe' : 'php';
for (const entry of entries) {
const fullPath = path_1.default.join(basePath, entry);
try {
const stat = fs_extra_1.default.statSync(fullPath);
if (stat.isDirectory()) {
// Check if this directory contains PHP executable
const phpExePath = path_1.default.join(fullPath, phpExeName);
if (fs_extra_1.default.existsSync(phpExePath)) {
phpPaths.push(fullPath);
}
// Recursively scan subdirectories
if (currentDepth < maxDepth - 1) {
phpPaths.push(...deepScanDirectory(fullPath, maxDepth, currentDepth + 1));
}
}
}
catch {
// Skip inaccessible directories
}
}
}
catch {
// Skip inaccessible directories
}
return phpPaths;
}
/**
* Creates installation object from a directory path (cross-platform)
*/
function createInstallationFromPath(phpDir, environment) {
try {
const isWindows = process.platform === 'win32';
const phpExeName = isWindows ? 'php.exe' : 'php';
const phpExePath = path_1.default.join(phpDir, phpExeName);
if (!fs_extra_1.default.existsSync(phpExePath))
return null;
const phpInfo = getPhpInfoFromExecutable(phpExePath);
if (!phpInfo || !phpInfo.version)
return null;
// Find php.ini (cross-platform paths)
const possibleIniPaths = isWindows ? [
path_1.default.join(phpDir, 'php.ini'),
path_1.default.join(phpDir, 'conf', 'php.ini'),
path_1.default.join(phpDir, 'etc', 'php.ini'),
path_1.default.join(phpDir, '..', 'php.ini')
] : [
// Try to get actual php.ini path from PHP itself
...getPhpIniPathFromExecutable(phpExePath),
// Common Linux/Unix paths
`/etc/php/${phpInfo.version}/apache2/php.ini`,
`/etc/php/${phpInfo.version}/cli/php.ini`,
`/etc/php/${phpInfo.version}/fpm/php.ini`,
`/etc/php/${phpInfo.version}/php.ini`,
'/etc/php.ini',
'/usr/local/etc/php.ini',
`/usr/local/etc/php/${phpInfo.version}/php.ini`,
path_1.default.join(phpDir, 'php.ini'),
path_1.default.join(phpDir, '..', 'etc', 'php.ini'),
path_1.default.join(phpDir, '..', 'lib', 'php.ini')
];
let iniPath = '';
for (const iniCandidate of possibleIniPaths) {
if (fs_extra_1.default.existsSync(iniCandidate)) {
iniPath = iniCandidate;
break;
}
}
// Find extension directory (cross-platform)
const possibleExtDirs = isWindows ? [
path_1.default.join(phpDir, 'ext'),
path_1.default.join(phpDir, 'extensions'),
path_1.default.join(phpDir, 'lib', 'php', 'extensions'),
path_1.default.join(phpDir, 'modules')
] : [
// Try to get actual extension dir from PHP
...getPhpExtensionDirFromExecutable(phpExePath),
// Common Linux/Unix extension paths
`/usr/lib/php/${phpInfo.version}`,
`/usr/lib/php/${phpInfo.version.split('.').slice(0, 2).join('.')}`,
'/usr/lib/php/modules',
'/usr/lib/php/extensions',
`/usr/local/lib/php/extensions/no-debug-non-zts-${phpInfo.version}`,
`/usr/local/lib/php/extensions`,
path_1.default.join(phpDir, '..', 'lib', 'php', 'extensions'),
path_1.default.join(phpDir, '..', 'lib', 'php', 'modules'),
path_1.default.join(phpDir, 'extensions'),
path_1.default.join(phpDir, 'modules')
];
let extensionDir = '';
for (const extCandidate of possibleExtDirs) {
if (fs_extra_1.default.existsSync(extCandidate)) {
extensionDir = extCandidate;
break;
}
}
return {
version: phpInfo.version,
path: phpDir,
iniPath,
extensionDir,
environment,
phpExecutable: phpExePath,
isActive: false,
architecture: phpInfo.architecture,
threadSafety: phpInfo.threadSafety,
buildDate: phpInfo.buildDate,
configureCommand: phpInfo.configureCommand,
priority: environment === 'System PATH' ? 1 : 5
};
}
catch {
return null;
}
}
/**
* Comprehensive PHP installation scanner with streamlined feedback
*/
function scanPhpInstallations() {
const installations = [];
const foundPaths = new Set(); // Prevent duplicates
const foundExecutables = new Set(); // Prevent duplicate executables
console.log('🔍 Scanning for PHP installations...');
// Method 1: Detect from system PATH (highest priority)
const pathInstallations = detectFromPath();
for (const installation of pathInstallations) {
const key = `${installation.path}|${installation.phpExecutable}`;
if (!foundPaths.has(key) && !foundExecutables.has(installation.phpExecutable)) {
installations.push(installation);
foundPaths.add(key);
foundExecutables.add(installation.phpExecutable);
}
}
// Method 2: Detect from Windows Registry (Windows only)
if (process.platform === 'win32') {
const registryInstallations = detectFromRegistry();
for (const installation of registryInstallations) {
const key = `${installation.path}|${installation.phpExecutable}`;
if (!foundPaths.has(key) && !foundExecutables.has(installation.phpExecutable)) {
installations.push(installation);
foundPaths.add(key);
foundExecutables.add(installation.phpExecutable);
}
}
}
// Method 3: Scan known environment configurations (limited on Linux to avoid duplicates)
const ENVIRONMENT_CONFIGS = getEnvironmentConfigs();
const isLinux = process.platform === 'linux';
for (const config of ENVIRONMENT_CONFIGS) {
// Skip problematic configs on Linux to avoid duplicates
if (isLinux && (config.name.includes('CentOS') ||
config.name.includes('X11') ||
config.name.includes('Ubuntu/Debian APT'))) {
continue;
}
for (const basePath of config.basePaths) {
if (!basePath || basePath.includes('*')) {
// Handle wildcard paths
if (basePath.includes('*')) {
const expandedPaths = expandWildcardPath(basePath);
for (const expandedPath of expandedPaths) {
scanEnvironmentPath(expandedPath, config, installations, foundPaths, foundExecutables);
}
}
continue;
}
scanEnvironmentPath(basePath, config, installations, foundPaths, foundExecutables);
}
}
// Method 4: Deep scan common directories (cross-platform, limited to avoid duplicates)
const isWindows = process.platform === 'win32';
if (isWindows) {
// Only deep scan on Windows where it's more useful
const commonDirs = ['C:/', 'D:/', 'E:/'];
for (const dir of commonDirs) {
if (fs_extra_1.default.existsSync(dir)) {
const deepPaths = deepScanDirectory(dir, 2);
for (const phpPath of deepPaths) {
if (!foundPaths.has(phpPath)) {
const installation = createInstallationFromPath(phpPath, 'Deep Scan');
if (installation && installation.iniPath) {
installations.push(installation);
foundPaths.add(phpPath);
}
}
}
}
}
}
// Sort by priority and version
installations.sort((a, b) => {
if (a.priority !== b.priority)
return a.priority - b.priority;
return b.version.localeCompare(a.version, undefined, { numeric: true });
});
// Mark the most likely active installation
if (installations.length > 0 && !installations.some(i => i.isActive)) {
installations[0].isActive = true;
}
return installations;
}
/**
* Expands wildcard paths like C:/Users/star/pvm (where star = *)
*/
function expandWildcardPath(wildcardPath) {
const paths = [];
const parts = wildcardPath.split(path_1.default.sep);
try {
let currentPaths = [''];
for (const part of parts) {
if (part === '*') {
const newPaths = [];
for (const currentPath of currentPaths) {
if (fs_extra_1.default.existsSync(currentPath)) {
const entries = fs_extra_1.default.readdirSync(currentPath);
for (const entry of entries) {
const fullPath = path_1.default.join(currentPath, entry);
if (fs_extra_1.default.statSync(fullPath).isDirectory()) {
newPaths.push(fullPath);
}
}
}
}
currentPaths = newPaths;
}
else {
currentPaths = currentPaths.map(p => path_1.default.join(p, part));
}
}
paths.push(...currentPaths.filter(p => fs_extra_1.default.existsSync(p)));
}
catch {
// Wildcard expansion failed
}
return paths;
}
/**
* Scans a specific environment path
*/
function scanEnvironmentPath(basePath, config, installations, foundPaths, foundExecutables) {
if (!fs_extra_1.default.existsSync(basePath))
return;
try {
if (config.versionPattern) {
// Multi-version environment
const versionDirs = findVersionDirectories(basePath, config.versionPattern);
for (const versionDir of versionDirs) {
const installation = createInstallation(basePath, versionDir, config);
if (installation) {
const key = `${installation.path}|${installation.phpExecutable}`;
if (!foundPaths.has(key) && !foundExecutables.has(installation.phpExecutable)) {
installations.push(installation);
foundPaths.add(key);
foundExecutables.add(installation.phpExecutable);
}
}
}
}
else {
// Single version environment
const installation = createInstallation(basePath, '', config);
if (installation) {
const key = `${installation.path}|${installation.phpExecutable}`;
if (!foundPaths.has(key) && !foundExecutables.has(installation.phpExecutable)) {
installations.push(installation);
foundPaths.add(key);
foundExecutables.add(installation.phpExecutable);
}
}
}
// Deep scan if enabled
if (config.deepScan) {
const deepPaths = deepScanDirectory(basePath, config.maxDepth || 3);
for (const phpPath of deepPaths) {
const installation = createInstallationFromPath(phpPath, config.name);
if (installation && installation.iniPath) {
const key = `${installation.path}|${installation.phpExecutable}`;
if (!foundPaths.has(key) && !foundExecutables.has(installation.phpExecutable)) {
installations.push(installation);
foundPaths.add(key);
foundExecutables.add(installation.phpExecutable);
}
}
}
}
}
catch {
// Continue with next path
}
}
/**
* Enhanced version directory finder with intelligent pattern matching
*/
function findVersionDirectories(basePath, versionPattern) {
const versions = [];
try {
let currentPath = basePath;
// Navigate to the directory containing version folders
for (let i = 0; i < versionPattern.length - 1; i++) {
currentPath = path_1.default.join(currentPath, versionPattern[i]);
if (!fs_extra_1.default.existsSync(currentPath))
return versions;
}
const pattern = versionPattern[versionPattern.length - 1];
const entries = fs_extra_1.default.readdirSync(currentPath);
for (const entry of entries) {
const fullPath = path_1.default.join(currentPath, entry);
try {
if (fs_extra_1.default.statSync(fullPath).isDirectory()) {
let matches = false;
if (pattern === '*') {
// Accept any directory that looks like a PHP version
matches = /^(php)?[\d\.\-]+/i.test(entry) ||
/^\d+\.\d+/.test(entry) ||
entry.toLowerCase().includes('php');
}
else {
// Use pattern matching
const regex = new RegExp(pattern.replace('*', '.*'), 'i');
matches = regex.test(entry);
}
if (matches) {
// Verify this directory actually contains PHP
const phpExePath = findPhpExecutableInDir(fullPath);
if (phpExePath) {
versions.push(entry);
}
}
}
}
catch {
// Skip inaccessible directories
}
}
}
catch {
// Return empty array if scanning fails
}
// Sort versions in descending order (newest first)
return versions.sort((a, b) => {
const versionA = extractVersionNumber(a);
const versionB = extractVersionNumber(b);
return versionB.localeCompare(versionA, undefined, { numeric: true });
});
}
/**
* Extracts version number from directory name
*/
function extractVersionNumber(dirName) {
const match = dirName.match(/(\d+\.\d+(?:\.\d+)?)/);
return match ? match[1] : dirName;
}
/**
* Enhanced installation creator with better detection
*/
function createInstallation(basePath, version, config) {
try {
const iniPath = buildPath(basePath, config.iniPattern, version);
const extensionDir = buildPath(basePath, config.extPattern, version);
// More flexible ini path detection
let finalIniPath = iniPath;
if (!fs_extra_1.default.existsSync(iniPath)) {
// Try alternative ini locations
const alternatives = [
path_1.default.join(path_1.default.dirname(iniPath), 'php.ini-development'),
path_1.default.join(path_1.default.dirname(iniPath), 'php.ini-production'),
path_1.default.join(basePath, 'php.ini'),
path_1.default.join(basePath, 'conf', 'php.ini')
];
for (const alt of alternatives) {
if (fs_extra_1.default.existsSync(alt)) {
finalIniPath = alt;
break;
}
}
// If still no ini file found, skip this installation
if (!fs_extra_1.default.existsSync(finalIniPath))
return null;
}
// Find PHP executable with enhanced patterns
const phpExecutable = findPhpExecutable(basePath, version, config);
if (!phpExecutable)
return null;
// Get comprehensive PHP info
const phpInfo = getPhpInfoFromExecutable(phpExecutable);
const detectedVersion = phpInfo?.version || version || 'unknown';
return {
version: detectedVersion,
path: basePath,
iniPath: finalIniPath,
extensionDir,
environment: config.name,
phpExecutable,
isActive: false,
architecture: phpInfo?.architecture,
threadSafety: phpInfo?.threadSafety,
buildDate: phpInfo?.buildDate,
configureCommand: phpInfo?.configureCommand,
priority: config.priority
};
}
catch {
return null;
}
}
/**
* Enhanced path builder with smart replacements
*/
function buildPath(basePath, pattern, version) {
const parts = pattern.map(part => {
let result = part;
// Replace version placeholders
result = result.replace('{version}', version);
result = result.replace('*', version);
// Handle php{version} pattern (like php8.2)
if (result.includes('php{') || result.includes('php*')) {
result = result.replace('php{version}', `php${version}`);
result = result.replace('php*', `php${version}`);
}
return result;
});
return path_1.default.join(basePath, ...parts);
}
/**
* Enhanced PHP executable finder with comprehensive search patterns
*/
function findPhpExecutable(basePath, version, config) {
// Build paths from config patterns first
const configPaths = [];
if (config.phpExePattern) {
configPaths.push(buildPath(basePath, config.phpExePattern, version));
}
// Common PHP executable locations
const possiblePaths = [
...configPaths,
path_1.default.join(basePath, 'php.exe'),
path_1.default.join(basePath, 'bin', 'php.exe'),
path_1.default.join(basePath, 'php', version, 'php.exe'),
path_1.default.join(basePath, 'php', `php${version}`, 'php.exe'),
path_1.default.join(basePath, 'bin', 'php', `php${version}`, 'php.exe'),
path_1.default.join(basePath, 'sym', 'php.exe'),
path_1.default.join(basePath, 'core', `php${version}`, 'php.exe'),
path_1.default.join(basePath, version, 'php.exe'),
path_1.default.join(basePath, `php-${version}`, 'php.exe')
];
for (const phpPath of possiblePaths) {
if (fs_extra_1.default.existsSync(phpPath)) {
return phpPath;
}
}
// Deep search in the base directory
return findPhpExecutableInDir(basePath);
}
/**
* Recursively searches for PHP executable in a directory (cross-platform)
*/
function findPhpExecutableInDir(dir, maxDepth = 2, currentDepth = 0) {
if (currentDepth >= maxDepth || !fs_extra_1.default.existsSync(dir)) {
return '';
}
try {
const isWindows = process.platform === 'win32';
const phpExeName = isWindows ? 'php.exe' : 'php';
const phpExePath = path_1.default.join(dir, phpExeName);
if (fs_extra_1.default.existsSync(phpExePath)) {
return phpExePath;
}
const entries = fs_extra_1.default.readdirSync(dir);
for (const entry of entries) {
const fullPath = path_1.default.join(dir, entry);
try {
if (fs_extra_1.default.statSync(fullPath).isDirectory()) {
const found = findPhpExecutableInDir(fullPath, maxDepth, currentDepth + 1);
if (found)
return found;
}
}
catch {
// Skip inaccessible directories
}
}
}
catch {
// Directory not accessible
}
return '';
}
/**
* Determines the appropriate paths for php.ini and extensions directory.
* Enhanced version with intelligent matching and fallbacks.
*/
function determinePhpIniPaths(version = '') {
const installations = scanPhpInstallations();
if (installations.length === 0) {
throw new Error('No PHP installations found. Please install PHP or set environment variables.');
}
// If version specified, find best matching installation
if (version) {
// Try exact version match first
let matching = installations.find(inst => inst.version === version);
// Try version prefix match
if (!matching) {
matching = installations.find(inst => inst.version.startsWith(version));
}
// Try partial version match
if (!matching) {
matching = installations.find(inst => inst.version.includes(version));
}
// Try path-based match
if (!matching) {
matching = installations.find(inst => inst.path.toLowerCase().includes(version.toLowerCase()));
}
if (matching) {
return {
iniPath: matching.iniPath,
extensionDir: matching.extensionDir
};
}
}
// Return highest priority installation (active or first)
const activeInstallation = installations.find(inst => inst.isActive);
const selectedInstallation = activeInstallation || installations[0];
return {
iniPath: selectedInstallation.iniPath,
extensionDir: selectedInstallation.extensionDir
};
}
/**
* Gets detailed detection results including active and system PHP
*/
function getDetailedPhpDetection() {
const installations = scanPhpInstallations();
const activePhp = installations.find(inst => inst.isActive);
const systemPhp = installations.find(inst => inst.environment === 'System PATH');
return {
installations,
activePhp,
systemPhp
};
}
/**
* Validates a PHP installation with automatic permission handling
*/
function validatePhpInstallation(installation) {
const issues = [];
const suggestions = [];
let needsSudo = false;
// Check PHP executable
if (!installation.phpExecutable || !fs_extra_1.default.existsSync(installation.phpExecutable)) {
issues.push('PHP executable not found');
suggestions.push('Reinstall PHP or check installation integrity');
}
// Check php.ini
if (!installation.iniPath || !fs_extra_1.default.existsSync(installation.iniPath)) {
issues.push('php.ini file not found');
suggestions.push('Create php.ini file or copy from php.ini-development');
}
// Check extension directory
if (!installation.extensionDir || !fs_extra_1.default.existsSync(installation.extensionDir)) {
issues.push('Extension directory not found');
suggestions.push('Check PHP installation or create extensions directory');
}
// Check if php.ini is writable (with automatic permission handling for Unix)
if (installation.iniPath && fs_extra_1.default.existsSync(installation.iniPath)) {
try {
fs_extra_1.default.accessSync(installation.iniPath, fs_extra_1.default.constants.W_OK);
}
catch {
const isUnix = process.platform !== 'win32';
if (isUnix) {
// On Unix systems, we can use sudo
needsSudo = true;
console.log(`${colors.yellow}ℹ️ php.ini requires elevated permissions - will use sudo automatically${colors.reset}`);
}
else {
issues.push('php.ini file is not writable');
suggestions.push('Run as administrator or change file permissions');
}
}
}
return {
isValid: issues.length === 0,
issues,
suggestions,
needsSudo
};
}
/**
* Finds the best PHP installation for a specific use case
*/
function findBestPhpInstallation(criteria) {
const installations = scanPhpInstallations();
let candidates = installations;
// Filter by criteria
if (criteria.version) {
candidates = candidates.filter(inst => inst.version.startsWith(criteria.version) ||
inst.version.includes(criteria.version));
}
if (criteria.environment) {
candidates = candidates.filter(inst => inst.environment.toLowerCase().includes(criteria.environment.toLowerCase()));
}
if (criteria.minVersion) {
candidates = candidates.filter(inst => inst.version.localeCompare(criteria.minVersion, undefined, { numeric: true }) >= 0);
}
if (criteria.architecture) {
candidates = candidates.filter(inst => inst.architecture?.toLowerCase().includes(criteria.architecture.toLowerCase()));
}
if (criteria.threadSafety !== undefined) {
candidates = candidates.filter(inst => inst.threadSafety === criteria.threadSafety);
}
// Return highest priority candidate
return candidates.length > 0 ? candidates[0] : null;
}
//# sourceMappingURL=phpEnvironmentUtils.js.map