feat: implement PHP version management and extension configuration service with IPC support
This commit is contained in:
+13
-2
@@ -110,6 +110,9 @@ async function restartNginx(): Promise<{ success: boolean; message: string }> {
|
||||
|
||||
// Force kill Nginx to ensure it picks up new config on Windows
|
||||
await processManager.forceKillAll('nginx')
|
||||
await processManager.stopService('nginx')
|
||||
// Give OS time to release the port
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
// Start service
|
||||
const success = await processManager.startService('nginx', binPath, ['-p', nginxPrefix, '-c', configPath], false)
|
||||
@@ -187,9 +190,13 @@ export function registerIpcHandlers(): void {
|
||||
if (!fs.existsSync(sessionDir)) fs.mkdirSync(sessionDir, { recursive: true })
|
||||
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true })
|
||||
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
// --- 1. Get extensions from local php.ini ---
|
||||
let extensionsList = ''
|
||||
const localIniPath = path.join(phpDir, 'php.ini')
|
||||
const mustHaves = ['curl', 'mbstring', 'openssl', 'mysqli', 'pdo_mysql', 'gd', 'fileinfo', 'exif', 'intl', 'bz2', 'gettext', 'soap', 'xsl']
|
||||
|
||||
if (fs.existsSync(localIniPath)) {
|
||||
const localIniContent = fs.readFileSync(localIniPath, 'utf8')
|
||||
const extMatches = localIniContent.match(/^\s*extension\s*=.*$/gm)
|
||||
@@ -197,8 +204,10 @@ export function registerIpcHandlers(): void {
|
||||
extensionsList = extMatches.join('\n')
|
||||
}
|
||||
} else {
|
||||
// Fallback to minimal set if missing
|
||||
extensionsList = 'extension=curl\nextension=mbstring\nextension=openssl\nextension=mysqli\nextension=pdo_mysql'
|
||||
// Fallback formatting based on version
|
||||
const prefix = (phpVersion?.startsWith('5.') || phpVersion?.startsWith('7.')) ? 'php_' : ''
|
||||
const suffix = (phpVersion?.startsWith('5.') || phpVersion?.startsWith('7.')) ? '.dll' : ''
|
||||
extensionsList = mustHaves.map(m => `extension=${prefix}${m}${suffix}`).join('\n')
|
||||
}
|
||||
|
||||
const configPath = await configService.generateConfig('php.ini.template', `php_${phpVersion || 'default'}.ini`, {
|
||||
@@ -209,6 +218,8 @@ export function registerIpcHandlers(): void {
|
||||
EXTENSIONS: extensionsList
|
||||
})
|
||||
|
||||
// Add a small delay to ensure previous instance (if any) is fully killed
|
||||
await sleep(1000)
|
||||
const success = await processManager.startService(serviceName, binPath, ['-b', `127.0.0.1:${phpPort}`, '-c', configPath], false)
|
||||
|
||||
if (!success) {
|
||||
|
||||
@@ -212,23 +212,33 @@ export class PhpManagerService {
|
||||
let found = false
|
||||
const newLines = lines.map(line => {
|
||||
const trimmedLine = line.trim()
|
||||
// Pattern matches: ;extension=php_mysql.dll, extension=php_mysql.dll, ; extension = php_mysql.dll
|
||||
const pattern = new RegExp(`^\\s*;?\\s*extension\\s*=\\s*(?:php_)?${extName}(?:\\.dll)?\\s*$`, 'i')
|
||||
|
||||
if (pattern.test(trimmedLine)) {
|
||||
found = true
|
||||
return enabled ? `extension=php_${extName}.dll` : `;extension=php_${extName}.dll`
|
||||
// Keep the same format as found if possible, but for enabled we enforce a clean one
|
||||
// For Windows, extension=php_name.dll is safest even in newer versions,
|
||||
// but extension=name is cleaner. We'll use a slightly smarter approach:
|
||||
let extValue = extName
|
||||
if (version.startsWith('5.') || version.startsWith('7.')) {
|
||||
extValue = `php_${extName}.dll`
|
||||
}
|
||||
return enabled ? `extension=${extValue}` : `;extension=${extValue}`
|
||||
}
|
||||
return line
|
||||
})
|
||||
|
||||
if (!found && enabled) {
|
||||
// If not found in file, append it under [Extension] or at the end
|
||||
let extValue = extName
|
||||
if (version.startsWith('5.') || version.startsWith('7.')) {
|
||||
extValue = `php_${extName}.dll`
|
||||
}
|
||||
const newLine = `extension=${extValue}`
|
||||
const extSectionIndex = newLines.findIndex(l => l.trim().toLowerCase() === '; dynamic extensions')
|
||||
if (extSectionIndex !== -1) {
|
||||
newLines.splice(extSectionIndex + 1, 0, `extension=php_${extName}.dll`)
|
||||
newLines.splice(extSectionIndex + 1, 0, newLine)
|
||||
} else {
|
||||
newLines.push(`extension=php_${extName}.dll`)
|
||||
newLines.push(newLine)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1211,21 +1211,21 @@ function App(): JSX.Element {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: '#39A7FF' }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2, gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, minWidth: 0 }}>
|
||||
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: '#39A7FF', flexShrink: 0 }}>
|
||||
<FolderIcon />
|
||||
</Avatar>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, color: '#fff', lineHeight: 1.2 }}>
|
||||
<Box sx={{ minWidth: 0, flexGrow: 1 }}>
|
||||
<Typography variant="h6" noWrap sx={{ fontWeight: 700, color: '#fff', lineHeight: 1.2, textOverflow: 'ellipsis' }}>
|
||||
{project.name}
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: 'rgba(255, 255, 255, 0.4)', fontFamily: 'monospace' }}>
|
||||
<Typography variant="caption" noWrap sx={{ color: 'rgba(255, 255, 255, 0.4)', fontFamily: 'monospace', display: 'block', textOverflow: 'ellipsis' }}>
|
||||
{project.path.length > 40 ? '...' + project.path.slice(-37) : project.path}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
||||
<Box sx={{ display: 'flex', gap: 0.5, flexShrink: 0 }}>
|
||||
<Tooltip title="Düzenle">
|
||||
<IconButton size="small" onClick={() => {
|
||||
setNewProject(project)
|
||||
|
||||
Reference in New Issue
Block a user