diff --git a/config/php.ini.template b/config/php.ini.template index ef44245..7ae54e2 100644 --- a/config/php.ini.template +++ b/config/php.ini.template @@ -47,17 +47,7 @@ sys_temp_dir = "{{TEMP_DIR}}" extension_dir = "{{EXT_DIR}}" [ExtensionList] -extension=curl -extension=fileinfo -extension={{GD_EXT}} -extension=gettext -extension=mbstring -extension=exif -extension=mysqli -extension=openssl -extension=pdo_mysql -extension=pdo_sqlite -extension=sqlite3 +{{EXTENSIONS}} [Date] date.timezone = UTC diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index 52aceff..7bb39bf 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -187,11 +187,26 @@ export function registerIpcHandlers(): void { if (!fs.existsSync(sessionDir)) fs.mkdirSync(sessionDir, { recursive: true }) if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true }) + // --- 1. Get extensions from local php.ini --- + let extensionsList = '' + const localIniPath = path.join(phpDir, 'php.ini') + if (fs.existsSync(localIniPath)) { + const localIniContent = fs.readFileSync(localIniPath, 'utf8') + const extMatches = localIniContent.match(/^\s*extension\s*=.*$/gm) + if (extMatches) { + extensionsList = extMatches.join('\n') + } + } else { + // Fallback to minimal set if missing + extensionsList = 'extension=curl\nextension=mbstring\nextension=openssl\nextension=mysqli\nextension=pdo_mysql' + } + const configPath = await configService.generateConfig('php.ini.template', `php_${phpVersion || 'default'}.ini`, { EXT_DIR: extDir, GD_EXT: gdExtName, SESSION_DIR: sessionDir, - TEMP_DIR: tempDir + TEMP_DIR: tempDir, + EXTENSIONS: extensionsList }) const success = await processManager.startService(serviceName, binPath, ['-b', `127.0.0.1:${phpPort}`, '-c', configPath], false) @@ -370,6 +385,29 @@ export function registerIpcHandlers(): void { return result }) + ipcMain.handle('php:config:get', async (_event, version: string) => { + const phpDir = path.join(configService.getBasePath(), 'bin', `php-${version}`) + const iniPath = path.join(phpDir, 'php.ini') + if (fs.existsSync(iniPath)) { + return fs.readFileSync(iniPath, 'utf8') + } + return '' + }) + + ipcMain.handle('php:config:save', async (_event, { version, content }) => { + const phpDir = path.join(configService.getBasePath(), 'bin', `php-${version}`) + const iniPath = path.join(phpDir, 'php.ini') + fs.writeFileSync(iniPath, content) + + // Return a result that indicates a restart is needed + const serviceName = `php:${version}` + const isRunning = processManager.isServiceRunning(serviceName) + if (isRunning) { + await processManager.stopService(serviceName) + } + return { success: true, message: `php.ini saved.${isRunning ? ' Current PHP service stopped. Please restart to apply.' : ''}` } + }) + // MariaDB Versions ipcMain.handle('mariadb:versions', async () => { return await mariaDbManagerService.getVersions() diff --git a/src/renderer/src/components/PhpExtensionManager.tsx b/src/renderer/src/components/PhpExtensionManager.tsx index 8f978d9..399be16 100644 --- a/src/renderer/src/components/PhpExtensionManager.tsx +++ b/src/renderer/src/components/PhpExtensionManager.tsx @@ -15,10 +15,9 @@ import { Box, Typography, Chip, - IconButton, - Alert + IconButton } from '@mui/material' -import { Search as SearchIcon, Close as CloseIcon, Refresh as RefreshIcon } from '@mui/icons-material' +import { Search as SearchIcon, Close as CloseIcon, Refresh as RefreshIcon, EditNote as RawIcon, Save as SaveIcon } from '@mui/icons-material' import Swal from 'sweetalert2' interface PhpExtension { @@ -38,6 +37,9 @@ const PhpExtensionManager: React.FC = ({ open, onClose const [loading, setLoading] = useState(false) const [searchTerm, setSearchTerm] = useState('') const [saving, setSaving] = useState(null) + const [rawConfig, setRawConfig] = useState('') + const [isRawEditorOpen, setIsRawEditorOpen] = useState(false) + const [isSavingRaw, setIsSavingRaw] = useState(false) const fetchExtensions = async () => { if (!phpVersion) return @@ -52,6 +54,39 @@ const PhpExtensionManager: React.FC = ({ open, onClose } } + const fetchRawConfig = async () => { + if (!phpVersion) return + try { + const data = await window.api.invoke('php:config:get', phpVersion) + setRawConfig(data) + setIsRawEditorOpen(true) + } catch (error) { + console.error('Failed to fetch raw config:', error) + } + } + + const handleSaveRaw = async () => { + setIsSavingRaw(true) + try { + const result = await window.api.invoke('php:config:save', { version: phpVersion, content: rawConfig }) + if (result.success) { + Swal.fire({ + title: 'Başarılı', + text: result.message, + icon: 'success', + background: '#1e1e1e', + color: '#fff' + }) + setIsRawEditorOpen(false) + fetchExtensions() // Refresh extension list too + } + } catch (error: any) { + Swal.fire({ title: 'Hata', text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' }) + } finally { + setIsSavingRaw(false) + } + } + useEffect(() => { if (open && phpVersion) { fetchExtensions() @@ -101,106 +136,171 @@ const PhpExtensionManager: React.FC = ({ open, onClose ) return ( - - - - PHP {phpVersion} Eklentileri - php.ini üzerinden eklentileri yönetin - - - - - - - - setSearchTerm(e.target.value)} - InputProps={{ - startAdornment: ( - - - - ), - sx: { color: '#fff', bgcolor: 'rgba(0,0,0,0.2)' } - }} - /> - - - {loading ? ( - - + <> + + + + PHP {phpVersion} Eklentileri + php.ini üzerinden eklentileri yönetin - ) : ( - - {filteredExtensions.length === 0 ? ( - - - - ) : ( - filteredExtensions.map((ext) => ( - - ) : ( - handleToggle(ext.name, e.target.checked)} - color="primary" - /> - ) - } - > + + + + + + + setSearchTerm(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + sx: { color: '#fff', bgcolor: 'rgba(0,0,0,0.2)' } + }} + /> + + + + {loading ? ( + + + + ) : ( + + {filteredExtensions.length === 0 ? ( + - {ext.name} - {ext.enabled && } - - } - secondary={ext.dll} - secondaryTypographyProps={{ sx: { color: 'text.secondary', fontSize: '0.75rem' } }} + primary={searchTerm ? "Eklenti bulunamadı." : "Hiç eklenti yok."} + sx={{ textAlign: 'center', color: 'text.secondary' }} /> - )) - )} - - )} - - - - - - + ) : ( + filteredExtensions.map((ext) => ( + + ) : ( + handleToggle(ext.name, e.target.checked)} + color="primary" + /> + ) + } + > + + {ext.name} + {ext.enabled && } + + } + secondary={ext.dll} + secondaryTypographyProps={{ sx: { color: 'text.secondary', fontSize: '0.75rem' } }} + /> + + )) + )} + + )} + + + + + + + + setIsRawEditorOpen(false)} + maxWidth="md" + fullWidth + PaperProps={{ + sx: { + bgcolor: '#1a1a1a', + color: '#fff', + borderRadius: 2, + } + }} + > + + php.ini Düzenle ({phpVersion}) + setIsRawEditorOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}> + + + + + setRawConfig(e.target.value)} + variant="filled" + InputProps={{ + sx: { + fontFamily: 'monospace', + fontSize: '0.85rem', + color: '#d4d4d4', + bgcolor: '#1e1e1e', + '& textarea': { p: 2 } + }, + disableUnderline: true + }} + /> + + + + + + + ) }