diff --git a/src/actions/ini/add.ps1 b/src/actions/ini/add.ps1
index 2c15ecf6..a6480570 100644
--- a/src/actions/ini/add.ps1
+++ b/src/actions/ini/add.ps1
@@ -45,71 +45,6 @@ function Select-ExtensionPackageLink {
return ($extensionLinks | Where-Object -FilterScript { $_.index -eq $packageIndex })
}
-function Get-XdebugConfigV2 {
- param ($XDebugPath)
-
- return @(
- '[xdebug]'
- ";zend_extension='$XDebugPath'"
- 'xdebug.remote_enable=1'
- 'xdebug.remote_host=127.0.0.1'
- 'xdebug.remote_port=9000'
- )
-}
-
-function Get-XdebugConfigV3 {
- param ($XDebugPath)
-
- return @(
- '[xdebug]'
- ";zend_extension='$XDebugPath'"
- 'xdebug.mode=debug'
- 'xdebug.client_host=127.0.0.1'
- 'xdebug.client_port=9003'
- )
-}
-
-function Get-XDebugFromUrl {
- param ($url, $version)
-
- try {
- $html = Invoke-WebRequestWrapper -uri $url
- $links = $html.Links
-
- # Return the filtered links (PHP version names)
- $formattedList = @()
- $links | ForEach-Object -Process {
- if (-not $_.href) { return }
-
- $fileName = [System.IO.Path]::GetFileName($_.href)
-
- if ($fileName -notmatch '^php_xdebug-.*\.dll$') { return }
-
- if ($fileName -notmatch "php_xdebug-[\d\.a-zA-Z]+-$version-") { return }
-
- $xDebugVersion = '2.0'
- if ($fileName -match 'php_xdebug-([^-]+)') {
- $xDebugVersion = $matches[1]
- }
-
- $formattedList += @{
- href = "$($PVMConfig.links.xdebugBase)$($_.href)"
- version = $version
- extVersion = $xDebugVersion;
- arch = if ($fileName -match '(x86_64|x64)(?=\.dll$)') { 'x64' } else { 'x86' }
- buildType = if ($fileName -match '(?i)(?:^|-)nts(?:-|\.dll$)') { 'NTS' } else { 'TS' }
- compiler = if ($fileName -match '(?i)\b(vs|vc)\d+\b') { $matches[0].ToUpper() } else { 'unknown' }
- fileName = $fileName
- }
- }
-
- return $formattedList
- } catch {
- $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to fetch xdebug versions from $url"; exception = $_ }
- return @()
- }
-}
-
function Get-PrereleaseSortKey {
param ($Name)
@@ -130,87 +65,6 @@ function Get-PrereleaseSortKey {
return ($versionScore * 100000) + ($weight * 10000) + $number
}
-function Install-XDebugExtension {
- param ($iniPath, $skipConfirmation = $false)
-
- try {
- $currentVersionObj = Get-CurrentPHPVersion
- $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1'
- $xDebugList = Get-OrUpdateCache -cacheFileName "packages_links_for_xdebug_php_$($currentVersion)_xdebug" -compute {
- return Show-SpinnerWhileJob -argumentList @($currentVersion) -scriptBlock {
- param ($currentVersion)
-
- $data = Get-XDebugFromUrl -url $PVMConfig.links.xdebugHistorical -version $currentVersion
- return @{ pvmData = $data }
- } -rethrow $true
- }
-
- if ($null -eq $xDebugList -or $xDebugList.Count -eq 0) {
- Show-Error -message "`nNo match was found, check the '$($PVMConfig.paths.files.logError)' for any potentiel errors"
- return -1
- }
-
- $xDebugList = $xDebugList | Where-Object -FilterScript {
- if ($null -ne $currentVersionObj.arch) {
- if ($_.arch -ne $currentVersionObj.arch) { return $false }
- }
-
- if ($null -ne $currentVersionObj.buildType) {
- if ($_.buildType -ne $currentVersionObj.buildType) { return $false }
- }
-
- return $true
- }
-
- Show-Info -message "`nThis is a partial list. For a complete list, visit: $($PVMConfig.links.xdebugHistorical)"
- $chosenItem = Select-ExtensionPackageLink -extName 'Xdebug' -extensionLinks $xDebugList
-
- if (-not $chosenItem) {
- Show-Error -message "`nYou chose the wrong index"
- return -1
- }
-
- $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php
- $phpPath = Split-Path -Path $iniPath -Parent
-
- if (-not $skipConfirmation) {
- if (Test-FileExists -path "$phpPath\ext\$($chosenItem.fileName)") {
- $response = Read-HostWrapper -prompt "`n$($chosenItem.fileName) already exists. Would you like to overwrite it? (y/n)"
- if (Test-NoResponse -response $response) {
- Remove-ItemWrapper -path "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)"
- Write-Gray -message "`nInstallation cancelled"
- return -1
- }
- }
- }
-
- Move-ItemWrapper -path "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)" -destination "$phpPath\ext"
- $xDebugConfig = Get-XdebugConfigV2 -XDebugPath $($chosenItem.fileName)
- if ($chosenItem.extVersion -like '3.*') {
- $xDebugConfig = Get-XdebugConfigV3 -XDebugPath $($chosenItem.fileName)
- }
-
- $code = Add-MissingPHPExtensionToIni -iniPath $iniPath -extFileName $chosenItem.fileName -enable $false
- if ($code -ne 0) {
- Show-Error -message "`nFailed to add XDebug"
- return -1
- } else {
- $iniContent = Get-ContentWrapper -path $iniPath
- if ($iniContent -notcontains '[xdebug]') {
- $xDebugConfig = "`n$($xDebugConfig -join "`n")"
- Add-ContentWrapper -path $iniPath -value $xDebugConfig
- }
- }
-
- Show-Success -message "`nXDebug installed successfully"
-
- return 0
- } catch {
- $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to install extension 'xdebug'"; exception = $_ }
- return -1
- }
-}
-
function Add-MissingPHPExtensionToIni {
param ($iniPath, $extFileName, $enable = $true)
@@ -235,15 +89,15 @@ function Add-MissingPHPExtensionToIni {
return -1
}
- $matchesList = Get-MatchingPHPExtensionsStatus -iniPath $iniPath -extName $extFileName -includeIniOnly $true -addToIniFileIfMissing $false
- if ($matchesList.Length -gt 0) {
+ $matchesList = (Get-MatchingPHPExtensionsStatus -iniPath $iniPath -extName $extFileName -includeIniOnly $true -addToIniFileIfMissing $false) | Select-Object -First 1
+ if ($matchesList.Length -gt 0 -and $matchesList.LineNumber -gt 0) {
Show-Warning -message "- Extension '$extFileName' already exists in php.ini"
return 0
}
$lines = Get-ContentWrapper -path $iniPath
$commented = if ($enable) { '' } else { ';' }
- $isZendExtension = Get-ZendExtensionsList | Where-Object -FilterScript { $extFileName -like "*$_*" }
+ $isZendExtension = [bool](Get-ZendExtensionsList | Where-Object -FilterScript { $extFileName -like "*$_*" } | Select-Object -First 1)
if ($isZendExtension) {
$lines += "`n$commented" + "zend_extension=$extFileName"
} else {
@@ -263,9 +117,46 @@ function Install-Extension {
param ($iniPath, $extName, $skipConfirmation = $false)
try {
- $currentVersionObj = Get-CurrentPHPVersion
- $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1'
- $extensionLinksObj = Get-ExtensionPackages -extName $extName -version $currentVersion
+ New-Line
+ $sourceHandlers = (Get-ExtensionHandlers).SourceHandlers
+ $sourceNames = @($sourceHandlers.Keys) | Sort-Object
+ $index = 0
+ $sourceNames | Foreach-Object -Process {
+ Show-Message -message "[$index] $_"
+ $index++
+ }
+ $selectedIndex = Read-HostWrapper -prompt "`nEnter the [number] of your selection"
+ if ([string]::IsNullOrWhiteSpace($selectedIndex)) {
+ Write-Gray -message "`nInstallation cancelled"
+ return -1
+ }
+ $choice = $null
+ if (-not [int]::TryParse($selectedIndex, [ref]$choice)) {
+ Show-Warning -message "`nYou answer is invalid!"
+ return -1
+ }
+ if ($choice -lt 0 -or $choice -gt $sourceHandlers.Count - 1) {
+ Show-Warning -message "Number must be between 0 and $($sourceHandlers.Count - 1)."
+ return -1
+ }
+
+ $selectedSource = $sourceNames[$choice]
+ $handler = Get-SourceHandler -sourceUrl $selectedSource
+
+ if (-not $handler) {
+ Show-Error -message "`nNo handler found for source: $source"
+ return -1
+ }
+
+ if ($handler.SupportedExtensions -and $handler.SupportedExtensions -notcontains '*') {
+ $normalizedExtName = ConvertTo-ExtensionId -name $extName
+ if ($handler.SupportedExtensions -notcontains $normalizedExtName) {
+ Show-Error -message "`nSource '$selectedSource' does not support extension '$extName'. Supported extensions: $($handler.SupportedExtensions -join ', ')"
+ return -1
+ }
+ }
+
+ $extensionLinksObj = & $handler.ResolveLinks -extName $extName
if (($null -eq $extensionLinksObj) -or ($extensionLinksObj.Count -eq 0) -or ($null -eq $extensionLinksObj.data) -or ($extensionLinksObj.data.Count -eq 0)) {
$extName = if ($extensionLinksObj) { $extensionLinksObj.extName } else { $extName }
@@ -273,6 +164,7 @@ function Install-Extension {
return -1
}
+ $currentVersionObj = Get-CurrentPHPVersion
$extensionLinks = $extensionLinksObj.data | Where-Object -FilterScript {
if ($null -ne $currentVersionObj.arch) {
if ($_.arch -ne $currentVersionObj.arch) { return $false }
@@ -291,10 +183,17 @@ function Install-Extension {
}
$extName = $extensionLinksObj.extName
+ $source = $extensionLinksObj.source
+
if ($extensionLinks.Length -eq 1) {
$chosenItem = $($extensionLinks)
} else {
- Show-Info -message "`nThis is a partial list. For a complete list, visit: $($PVMConfig.links.peclPackageRoot)/$extName"
+ $moreInfoUrl = if ($handler.MoreInfoUrl -is [scriptblock]) {
+ & $handler.MoreInfoUrl $extName
+ } else {
+ $handler.MoreInfoUrl
+ }
+ Show-Info -message "`nThis is a partial list. For a complete list, visit: $moreInfoUrl"
$chosenItem = Select-ExtensionPackageLink -extName $extName -extensionLinks $extensionLinks
}
@@ -303,39 +202,31 @@ function Install-Extension {
return -1
}
- $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php
- $fileNamePath = $chosenItem.fileName -replace '.zip$', ''
- $extractPath = "$($PVMConfig.paths.directories.php)\$fileNamePath"
- Expand-Zip -zipPath "$extractPath.zip" -extractPath $extractPath -deleteZipAfter $true
- $files = Get-ChildItemWrapper -path $extractPath
- $extFile = $files | Where-Object -FilterScript {
- ($_.Name -match "^php_$extName.*\.dll$")
+ $phpPath = Split-Path -Path $iniPath -Parent
+
+ $downloadParams = @{
+ chosenItem = $chosenItem
+ phpPath = $phpPath
+ skipConfirmation = $skipConfirmation
+ }
+ if ($source -eq 'pecl.php.net') {
+ $downloadParams.extName = $extName
}
+
+ $extFile = & $handler.Download @downloadParams
if (-not $extFile) {
- Show-Error -message "`nFailed to find $extName"
+ Show-Error -message "`nFailed to download $extName"
return -1
}
- $phpPath = Split-Path -Path $iniPath -Parent
-
- if (-not $skipConfirmation) {
- if (Test-FileExists -path "$phpPath\ext\$($extFile.Name)") {
- $response = Read-HostWrapper -prompt "`n$($extFile.Name) already exists. Would you like to overwrite it? (y/n)"
- if (Test-NoResponse -response $response) {
- Remove-ItemWrapper -path "$($PVMConfig.paths.directories.php)\$fileNamePath"
- Write-Gray -message "`nInstallation cancelled"
- return -1
- }
- }
- }
+ $configHandler = Get-ExtensionConfigHandler -extName $extFile.Name
- Move-ItemWrapper -path $extFile.FullName -destination "$phpPath\ext"
- Remove-ItemWrapper -path $extractPath
- $code = Add-MissingPHPExtensionToIni -iniPath $iniPath -extFileName $extFile.Name -enable $false
- if ($code -ne 0) {
- Show-Error -message "`nFailed to add $extName"
+ $configCode = & $configHandler -iniPath $iniPath -fileName $extFile.Name -extVersion $chosenItem.extVersion
+ if ($configCode -ne 0) {
+ Show-Error -message "`nFailed to apply configuration for $extName"
return -1
}
+
Show-Success -message "`n$extName installed successfully"
return 0
@@ -354,16 +245,13 @@ function Install-IniExtension {
return -1
}
- $overallCode = 0
+ $codes = @()
foreach ($extName in $extNames) {
- if ($extName -like '*xdebug*') {
- $overallCode = Install-XDebugExtension -iniPath $iniPath -skipConfirmation $skipConfirmation
- } else {
- $overallCode = Install-Extension -iniPath $iniPath -extName $extName -skipConfirmation $skipConfirmation
- }
+ $codes += Install-Extension -iniPath $iniPath -extName $extName -skipConfirmation $skipConfirmation
}
- return $overallCode
+ if ($codes | Where-Object -FilterScript { $_ -ne 0 }) { return -1 }
+ return 0
} catch {
$null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to install '$($extNames -join ', ')'"; exception = $_ }
return -1
diff --git a/src/actions/ini/helpers/fetch.ps1 b/src/actions/ini/helpers/fetch.ps1
index 286f1e43..d2c1a080 100644
--- a/src/actions/ini/helpers/fetch.ps1
+++ b/src/actions/ini/helpers/fetch.ps1
@@ -1,4 +1,262 @@
+function Get-ExtensionHandlers {
+ return @{
+ SourceHandlers = @{
+ 'xdebug.org' = @{
+ SupportedExtensions = @('xdebug')
+ ResolveLinks = {
+ param ($extName)
+
+ $currentVersionObj = Get-CurrentPHPVersion
+ $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1'
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+ if (-not $handler) {
+ return @{ extName = 'xdebug'; data = $null; source = 'xdebug.org' }
+ }
+
+ $formattedList = & $handler.GetPackages -version $currentVersion
+ if ($null -eq $formattedList) {
+ return @{ extName = 'xdebug'; data = $null; source = 'xdebug.org' }
+ }
+
+ return @{
+ extName = $extName
+ data = $formattedList
+ source = 'xdebug.org'
+ }
+ }
+ GetPackages = {
+ param ($version)
+ return Get-OrUpdateCache -cacheFileName "packages_links_for_xdebug_php_$($version)_xdebug" -compute {
+ return Show-SpinnerWhileJob -argumentList @($version) -scriptBlock {
+ param ($version)
+ $data = Get-XDebugFromUrl -url $PVMConfig.links.xdebugHistorical -version $version
+ return @{ pvmData = $data }
+ } -rethrow $true
+ }
+ }
+ Download = {
+ param ($chosenItem, $phpPath, $skipConfirmation)
+
+ try {
+ $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php
+ $extFile = @{
+ Name = $chosenItem.fileName
+ FullName = "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)"
+ }
+
+ if (-not $skipConfirmation) {
+ if (Test-FileExists -path "$phpPath\ext\$($extFile.Name)") {
+ $response = Read-HostWrapper -prompt "`n$($extFile.Name) already exists. Would you like to overwrite it? (y/n)"
+ if (Test-NoResponse -response $response) {
+ Remove-ItemWrapper -path $extFile.FullName
+ Write-Gray -message "`nInstallation cancelled"
+ return $null
+ }
+ }
+ }
+
+ Move-ItemWrapper -path $extFile.FullName -destination "$phpPath\ext"
+ return $extFile
+ } catch {
+ $null = Add-LogEntry -data @{ header = "Xdebug.org Handler - Failed to download extension"; exception = $_ }
+ return $null
+ }
+ }
+ MoreInfoUrl = $PVMConfig.links.xdebugHistorical
+ }
+ 'pecl.php.net' = @{
+ SupportedExtensions = @('*')
+ ResolveLinks = {
+ param ($extName)
+
+ $currentVersionObj = Get-CurrentPHPVersion
+ $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1'
+
+ return (Get-ExtensionPackages -extName $extName -version $currentVersion)
+ }
+ GetPackages = {
+ param ($version, $linksObj)
+ if (($null -eq $linksObj.links) -or ($linksObj.links.Count -eq 0)) {
+ return $null
+ }
+ return Get-OrUpdateCache -cacheFileName "packages_links_for_$($linksObj.extName)_php_$($version)_pecl" -compute {
+ return Show-SpinnerWhileJob -argumentList @($linksObj, $version) -scriptBlock {
+ param ($linksObj, $version)
+ $data = Get-PackagesFromSourceLinks -extName $linksObj.extName -version $version -links $linksObj.links
+ return @{ pvmData = $data }
+ } -rethrow $true
+ }
+ }
+ Download = {
+ param ($chosenItem, $phpPath, $skipConfirmation, $extName)
+
+ try {
+ $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php
+ $fileNamePath = $chosenItem.fileName -replace '.zip$', ''
+ $extractPath = "$($PVMConfig.paths.directories.php)\$fileNamePath"
+ Expand-Zip -zipPath "$extractPath.zip" -extractPath $extractPath -deleteZipAfter $true
+ $files = Get-ChildItemWrapper -path $extractPath
+ $extFile = $files | Where-Object -FilterScript {
+ ($_.Name -match "^php_$extName.*\.dll$")
+ }
+
+ if (-not $extFile) {
+ Remove-ItemWrapper -path $extractPath
+ return $null
+ }
+
+ if (-not $skipConfirmation) {
+ if (Test-FileExists -path "$phpPath\ext\$($extFile.Name)") {
+ $response = Read-HostWrapper -prompt "`n$($extFile.Name) already exists. Would you like to overwrite it? (y/n)"
+ if (Test-NoResponse -response $response) {
+ Remove-ItemWrapper -path $extractPath
+ Write-Gray -message "`nInstallation cancelled"
+ return $null
+ }
+ }
+ }
+
+ Move-ItemWrapper -path $extFile.FullName -destination "$phpPath\ext"
+ Remove-ItemWrapper -path $extractPath
+ return $extFile
+ } catch {
+ $null = Add-LogEntry -data @{ header = "PECL Handler - Failed to download extension"; exception = $_ }
+ return $null
+ }
+ }
+ MoreInfoUrl = {
+ param ($extName)
+ return "$($PVMConfig.links.peclPackageRoot)/$extName"
+ }
+ }
+ }
+ ExtensionConfigHandlers = @{
+ 'xdebug' = {
+ param ($iniPath, $fileName, $extVersion)
+
+ try {
+ $xDebugConfig = Get-XdebugConfigV2 -XDebugPath $fileName
+ if ($extVersion -like '3.*') {
+ $xDebugConfig = Get-XdebugConfigV3 -XDebugPath $fileName
+ }
+
+ $iniContent = Get-ContentWrapper -path $iniPath
+ if ($iniContent -notcontains '[xdebug]') {
+ $xDebugConfig = "`n$($xDebugConfig -join "`n")"
+ Add-ContentWrapper -path $iniPath -value $xDebugConfig
+ }
+
+ return 0
+ } catch {
+ $null = Add-LogEntry -data @{ header = "Xdebug Config Handler - Failed to apply configuration"; exception = $_ }
+ return -1
+ }
+ }
+ 'default' = {
+ param ($iniPath, $fileName, $extVersion)
+
+ return (Add-MissingPHPExtensionToIni -iniPath $iniPath -extFileName $extFile.Name -enable $false)
+ }
+ }
+ }
+}
+
+function Get-SourceHandler {
+ param ($sourceUrl)
+
+ $handlers = Get-ExtensionHandlers
+ $sourceHandlers = $handlers.SourceHandlers
+
+ if ($sourceHandlers.ContainsKey($sourceUrl)) {
+ return $sourceHandlers[$sourceUrl]
+ }
+
+ # Default to PECL handler for unknown sources
+ return $sourceHandlers['pecl.php.net']
+}
+
+function Get-ExtensionConfigHandler {
+ param ($extName)
+
+ $handlers = Get-ExtensionHandlers
+ $configHandlers = $handlers.ExtensionConfigHandlers
+
+ $baseExtName = ConvertTo-ExtensionId -name $extName
+
+ if ($configHandlers.ContainsKey($baseExtName)) {
+ return $configHandlers[$baseExtName]
+ }
+
+ # Default handler - no special configuration needed
+ return $configHandlers['default']
+}
+
+function Get-XDebugFromUrl {
+ param ($url, $version)
+
+ try {
+ $html = Invoke-WebRequestWrapper -uri $url
+ $links = $html.Links
+
+ $formattedList = @()
+ $links | ForEach-Object -Process {
+ if (-not $_.href) { return }
+
+ $fileName = [System.IO.Path]::GetFileName($_.href)
+
+ if ($fileName -notmatch '^php_xdebug-.*\.dll$') { return }
+
+ if ($fileName -notmatch "php_xdebug-[\d\.a-zA-Z]+-$version-") { return }
+
+ $xDebugVersion = '2.0'
+ if ($fileName -match 'php_xdebug-([^-]+)') {
+ $xDebugVersion = $matches[1]
+ }
+
+ $formattedList += @{
+ href = "$($PVMConfig.links.xdebugBase)$($_.href)"
+ version = $version
+ extVersion = $xDebugVersion;
+ arch = if ($fileName -match '(x86_64|x64)(?=\.dll$)') { 'x64' } else { 'x86' }
+ buildType = if ($fileName -match '(?i)(?:^|-)nts(?:-|\.dll$)') { 'NTS' } else { 'TS' }
+ compiler = if ($fileName -match '(?i)\b(vs|vc)\d+\b') { $matches[0].ToUpper() } else { 'unknown' }
+ fileName = $fileName
+ }
+ }
+
+ return $formattedList
+ } catch {
+ $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to fetch xdebug versions from $url"; exception = $_ }
+ return @()
+ }
+}
+
+function Get-XdebugConfigV2 {
+ param ($XDebugPath)
+
+ return @(
+ '[xdebug]'
+ ";zend_extension='$XDebugPath'"
+ 'xdebug.remote_enable=1'
+ 'xdebug.remote_host=127.0.0.1'
+ 'xdebug.remote_port=9000'
+ )
+}
+
+function Get-XdebugConfigV3 {
+ param ($XDebugPath)
+
+ return @(
+ '[xdebug]'
+ ";zend_extension='$XDebugPath'"
+ 'xdebug.mode=debug'
+ 'xdebug.client_host=127.0.0.1'
+ 'xdebug.client_port=9003'
+ )
+}
+
function Get-ExtensionCategoriesByPage {
param ($extCategory, $link, $page = 1)
@@ -237,6 +495,7 @@ function Resolve-ExtensionLinks {
$links = Get-OrUpdateCache -cacheFileName "available_$($extName)_versions_$($version)_pecl" -compute {
return Get-ExtensionAvailableReleasesLinks -extName $extName
}
+ $source = 'pecl.php.net'
} catch {
Show-Message -message "`nDirect link for extension '$extName' not found, Loading matching extensions..."
@@ -252,15 +511,23 @@ function Resolve-ExtensionLinks {
if (-not $chosenItem) { return $null }
$extName = $chosenItem.extName
+ $source = $chosenItem.source
Show-Message -message "`nLoading links for '$extName'..."
- $links = Get-OrUpdateCache -cacheFileName "available_$($extName)_versions_$($version)_pecl" -compute {
- return Get-ExtensionAvailableReleasesLinks -extName $extName
+
+ # Only get PECL links if the source is pecl.php.net
+ if ($source -eq 'pecl.php.net') {
+ $links = Get-OrUpdateCache -cacheFileName "available_$($extName)_versions_$($version)_pecl" -compute {
+ return Get-ExtensionAvailableReleasesLinks -extName $extName
+ }
+ } else {
+ $links = @()
}
}
return @{
extName = $extName
links = $links
+ source = $source
}
}
@@ -269,22 +536,28 @@ function Get-ExtensionPackages {
$linksObj = Resolve-ExtensionLinks -extName $extName -version $version
- if (($null -eq $linksObj) -or ($linksObj.Count -eq 0) -or ($null -eq $linksObj.links) -or ($linksObj.links.Count -eq 0)) {
- $extName = if ($linksObj -and $linksObj.extName) { $linksObj.extName } else { $extName }
- return @{ extName = $extName; data = $null }
+ if ($null -eq $linksObj) {
+ return @{ extName = $extName; data = $null; source = 'unknown' }
+ }
+
+ $handler = Get-SourceHandler -sourceUrl $linksObj.source
+ if (-not $handler) {
+ return @{ extName = $linksObj.extName; data = $null; source = $linksObj.source }
}
- $formattedList = Get-OrUpdateCache -cacheFileName "packages_links_for_$($linksObj.extName)_php_$($version)_pecl" -compute {
- return Show-SpinnerWhileJob -argumentList @($linksObj, $version) -scriptBlock {
- param ($linksObj, $version)
+ $formattedList = if ($linksObj.source -eq 'pecl.php.net') {
+ & $handler.GetPackages -version $version -linksObj $linksObj
+ } else {
+ & $handler.GetPackages -version $version
+ }
- $data = Get-PackagesFromSourceLinks -extName $linksObj.extName -version $version -links $linksObj.links
- return @{ pvmData = $data }
- } -rethrow $true
+ if ($null -eq $formattedList) {
+ return @{ extName = $linksObj.extName; data = $null; source = $linksObj.source }
}
return @{
extName = $linksObj.extName
data = $formattedList
+ source = $linksObj.source
}
}
diff --git a/tests/actions/ini.tests.ps1 b/tests/actions/ini.tests.ps1
index 62a1e95f..ab3c0f5b 100644
--- a/tests/actions/ini.tests.ps1
+++ b/tests/actions/ini.tests.ps1
@@ -304,8 +304,7 @@ extension=php_curl.dll
Mock Expand-Zip { }
Mock Remove-ItemWrapper { }
Mock Move-ItemWrapper { }
- Mock Install-Extension { return 0 }
- Mock Install-XDebugExtension { return 0 }
+ Mock Install-IniExtension { return 0 }
}
It "Installs extension" {
@@ -331,7 +330,7 @@ extension=php_curl.dll
$result = Invoke-IniAction -action 'add' -params @('pdo_mysql', '-y')
$result | Should -Be 0
- Should -Invoke Install-Extension -Times 1 -ParameterFilter {
+ Should -Invoke Install-IniExtension -Times 1 -ParameterFilter {
$skipConfirmation -eq $true
}
}
@@ -341,7 +340,7 @@ extension=php_curl.dll
$result = Invoke-IniAction -action 'add' -params @('xdebug', '-y')
$result | Should -Be 0
- Should -Invoke Install-XDebugExtension -Times 1 -ParameterFilter {
+ Should -Invoke Install-IniExtension -Times 1 -ParameterFilter {
$skipConfirmation -eq $true
}
}
diff --git a/tests/actions/ini/add.tests.ps1 b/tests/actions/ini/add.tests.ps1
index d7c88c50..eabe0a29 100644
--- a/tests/actions/ini/add.tests.ps1
+++ b/tests/actions/ini/add.tests.ps1
@@ -18,6 +18,7 @@ BeforeAll {
$script:PECL_PACKAGE_ROOT_URL = $PVMConfig.links.peclPackageRoot
$script:PECL_WIN_EXT_DOWNLOAD_URL = $PVMConfig.links.peclWinExtDownload
+ Mock New-Line {}
Mock Show-Warning {}
Mock Show-Message {}
Mock Show-Error {}
@@ -95,407 +96,165 @@ AfterAll {
$Global:PVMConfig = $PVMConfigBackup
}
-Describe "Get-XDebugFromUrl Tests" {
- BeforeAll {
- function Reset-MockState {
- $script:MockRegistryThrowException = $false
- $script:MockFileSystem.DownloadFails = $false
- $script:MockFileSystem.WebResponses = @{}
- $script:MockFileSystem.Files = @{}
- $script:MockFileSystem.Directories = @()
- }
+Describe "Get-ExtensionHandlers Tests" {
+ It "Returns unified handler registry with both source and config handlers" {
+ $handlers = Get-ExtensionHandlers
- function Set-MockWebResponse {
- param ($url, $content, $links = @())
- $script:MockFileSystem.WebResponses[$url] = @{
- Content = $content
- Links = $links
- }
- }
+ $handlers.SourceHandlers | Should -Not -BeNullOrEmpty
+ $handlers.ExtensionConfigHandlers | Should -Not -BeNullOrEmpty
+ $handlers.SourceHandlers.ContainsKey('xdebug.org') | Should -Be $true
+ $handlers.SourceHandlers.ContainsKey('pecl.php.net') | Should -Be $true
+ $handlers.ExtensionConfigHandlers.ContainsKey('xdebug') | Should -Be $true
}
- BeforeEach {
- Reset-MockState
- }
-
- It "Should parse XDebug versions correctly" {
- $mockLinks = @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86_64.dll" },
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x86_64.dll" },
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll" },
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll" },
- @{ href = "$XDEBUG_BASE_URL/download/php_random.dll" }
- )
- Set-MockWebResponse -url 'https://test.com' -links $mockLinks
+}
- $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1'
+Describe "Get-SourceHandler Tests" {
+ It "Returns correct handler for xdebug.org source" {
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
- $result.Count | Should -Be 4
- $result[0].extVersion | Should -Be '3.1.0'
- $result[1].extVersion | Should -Be '2.9.0'
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
}
- It "Should handle network errors" {
- $script:MockFileSystem.DownloadFails = $true
-
- $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1'
+ It "Returns correct handler for pecl.php.net source" {
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
- $result | Should -Be @()
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
}
- It "Should parse xdebug with x86 architecture and unknown compiler" {
- $mockLinks = @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-x86.dll" },
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-x86.dll" }
- )
- Set-MockWebResponse -url 'https://test.com' -links $mockLinks
-
- $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1'
+ It "Returns default PECL handler for unknown sources" {
+ $handler = Get-SourceHandler -sourceUrl 'unknown.source.com'
- $result.Count | Should -Be 2
- $result[0].arch | Should -Be 'x86'
- $result[0].compiler | Should -Be 'unknown'
- $result[1].arch | Should -Be 'x86'
- $result[1].compiler | Should -Be 'unknown'
+ $handler | Should -Not -BeNullOrEmpty
+ # Should return the pecl.php.net handler as default
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
}
}
-Describe "Install-XDebugExtension" {
- BeforeAll {
- Mock Show-SpinnerWhileJob {
- param ($scriptBlock, $message, $noClear, $argumentList, $rethrow)
- $result = & $scriptBlock @argumentList
- return $result.pvmData
- }
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "php_xdebug-3.1.0-8.1-vs16-x64.dll" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '2.9.0'; fileName = 'php_xdebug-2.9.0-8.1-vs16-x86_64.dll'; outerHTML = "php_xdebug-2.9.0-8.1-vs16-x86_64.dll" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'nts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll'; outerHTML = "php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-vc16-x64.dll"; arch = 'x64'; buildType = 'nts'; version = '8.1'; extVersion = '2.9.0'; fileName = 'php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll'; outerHTML = "php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll" }
- )
- }
- Mock Read-HostWrapper {
- param ($Prompt)
- if ($Prompt -eq "`nEnter the [number] of your selection") {
- return ''
- }
- }
-
- function Reset-MockState {
- $script:MockRegistryThrowException = $false
- $script:MockFileSystem.DownloadFails = $false
- $script:MockFileSystem.WebResponses = @{}
- $script:MockFileSystem.Files = @{}
- $script:MockFileSystem.Directories = @()
- }
-
- function Add-ContentWrapper {
- param ($Path, $Value)
- if ($script:MockFileSystem.Files.ContainsKey($Path)) {
- $script:MockFileSystem.Files[$Path] += "`n$Value"
- }
- else {
- $script:MockFileSystem.Files[$Path] = $Value
- }
- }
-
- function Set-MockWebResponse {
- param ($url, $content, $links = @())
- $script:MockFileSystem.WebResponses[$url] = @{
- Content = $content
- Links = $links
- }
- }
- }
-
- BeforeEach {
- $script:MockFileSystem.Directories += "$TEST_DRIVE\php"
- $script:MockFileSystem.Directories += "$TEST_DRIVE\php\ext"
- $script:MockFileSystem.Files["$TEST_DRIVE\php\php.ini"] = @"
-;extension_dir = "ext"
-zend_extension = opcache
-opcache.enable = 1
-"@
- Reset-MockState
- $mockLinks = @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll" }
- )
- Set-MockWebResponse -url $XDEBUG_HISTORICAL_URL -links $mockLinks
- }
+Describe "Get-ExtensionConfigHandler Tests" {
+ It "Returns xdebug config handler for xdebug extension" {
+ $handler = Get-ExtensionConfigHandler -extName 'php_xdebug.dll'
- It "Returns -1 when user does not choose a dll extension version to install" {
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be -1
+ $handler | Should -Not -BeNullOrEmpty
+ # Should return the xdebug config handler scriptblock
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
}
- It "Returns -1 when user does choose a non valid dll extension version to install" {
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '-10' }
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be -1
- }
-
- It "Returns -1 when user does not want to overwrite existing dll extension version" {
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Mock Test-Path { return $true }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nphp_xdebug-3.1.0-8.1-vs16-x64.dll already exists. Would you like to overwrite it? (y/n)" } -MockWith { return 'n' }
- Mock Remove-ItemWrapper { }
+ It "Returns xdebug config handler for xdebug without prefix" {
+ $handler = Get-ExtensionConfigHandler -extName 'xdebug.dll'
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be -1
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
}
- It "Returns 0 when user wants to overwrite existing dll extension version" {
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-2.9.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Test-FileExists -ParameterFilter { $path -eq "$TEST_DRIVE\ext\php_xdebug-3.1.0-8.1-vs16-x64.dll" } -MockWith { return $true }
- Mock Read-HostWrapper -ParameterFilter { $prompt -like '*already exists. Would you like to overwrite it*' } -MockWith { return 'y' }
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Add-ContentWrapper { }
- Mock Remove-ItemWrapper { }
- Mock Move-ItemWrapper { }
+ It "Returns xdebug config handler for xdebug with version" {
+ $handler = Get-ExtensionConfigHandler -extName 'php_xdebug-3.1.0-8.1-vs16-x64.dll'
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
}
- It "Handles exception gracefully" {
- Mock Sort-Object { throw 'Error' }
+ It "Returns default handler for unknown extensions" {
+ $handler = Get-ExtensionConfigHandler -extName 'php_unknown.dll'
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be -1
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
}
- It "Returns -1 when no compatible extension version is found" {
- Mock Test-CanUseCache { return $false }
- Mock Get-XDebugFromUrl { return @() }
+ It "Returns default handler for empty input" {
+ $handler = Get-ExtensionConfigHandler -extName ''
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be -1
- }
-
- It "Filters xdebug versions by architecture" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x86'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86.dll"; arch = 'x86'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86.dll'; outerHTML = "test" }
- )
- }
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Invoke-WebRequestWrapper { }
- Mock Move-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Set-ContentWrapper { }
- Mock Remove-ItemWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
}
+}
- It "Filters xdebug versions by build type" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'nts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-ts-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-ts-x64.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-x64.dll"; arch = 'x64'; buildType = 'nts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-nts-x64.dll'; outerHTML = "test" }
- )
+Describe "Install-Extension Tests" {
+ BeforeAll {
+ Mock Show-SpinnerWhileJob {
+ param ($scriptBlock, $message, $noClear, $argumentList, $rethrow)
+ $result = & $scriptBlock @argumentList
+ return $result.pvmData
}
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Invoke-WebRequestWrapper { }
- Mock Move-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Set-ContentWrapper { }
- Mock Remove-ItemWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
- }
-
- It "Sorts prerelease versions correctly (alpha, beta, rc)" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0rc1-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0rc1'; fileName = 'php_xdebug-3.1.0rc1-8.1-vs16-x64.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0beta1-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0beta1'; fileName = 'php_xdebug-3.1.0beta1-8.1-vs16-x64.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0alpha1-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0alpha1'; fileName = 'php_xdebug-3.1.0alpha1-8.1-vs16-x64.dll'; outerHTML = "test" }
- )
+ Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } }
+ Mock Read-HostWrapper {
+ param ($Prompt)
+ if ($Prompt -eq "`nEnter the [number] of your selection") {
+ return '0'
+ }
}
Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
Mock Invoke-WebRequestWrapper { }
Mock Move-ItemWrapper { }
Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Set-ContentWrapper { }
- Mock Remove-ItemWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
- }
-
- It "Returns -1 if fails to add xdebug to ini" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" }
- )
- }
- Mock Add-MissingPHPExtensionToIni { return -1 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Invoke-WebRequestWrapper { }
- Mock Move-ItemWrapper { }
- Mock Remove-ItemWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be -1
- Should -Invoke Show-Error -ParameterFilter { $message -like '*Failed to add XDebug*' }
- }
-
- It "Adds xdebug v3 config when no existing xdebug found" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" }
- )
- }
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Invoke-WebRequestWrapper { }
- Mock Move-ItemWrapper { }
- Mock Remove-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache`nopache.enable = 1" }
Mock Add-ContentWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
-
- # Verify Add-ContentWrapper was called for xdebug config
- Should -Invoke Add-ContentWrapper -Times 1 -ParameterFilter { $Path -eq $testIniPath }
}
- It "Adds xdebug v2 config when version 2.x is selected" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '2.9.0'; fileName = 'php_xdebug-2.9.0-8.1-vs16-x64.dll'; outerHTML = "test" }
- )
- }
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Invoke-WebRequestWrapper { }
+ It "Successfully installs extension using source handler" {
+ Mock Invoke-WebRequestWrapper { return $null }
+ Mock Expand-Zip { }
Mock Move-ItemWrapper { }
Mock Remove-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache`nopache.enable = 1" }
- Mock Add-ContentWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
-
- # Verify Add-ContentWrapper was called with v2 config
- Should -Invoke Add-ContentWrapper -Times 1 -ParameterFilter {
- $Path -eq $testIniPath -and $Value -match 'xdebug.remote_enable'
+ $mockFile = @{ Name = 'php_curl.dll'; FullName = "$TEST_DRIVE\extracted\php_curl.dll" }
+ Mock Get-ChildItemWrapper { return @( $mockFile ) }
+ Mock Add-MissingPHPExtensionToIni { return 0 }
+ Mock Get-ExtensionPackages {
+ return @{
+ extName = 'curl'
+ source = 'pecl.php.net'
+ data = @(
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ )
+ }
}
- }
- It "Handles x86_64 architecture in sorting" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = $null; buildType = $null; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86.dll"; arch = 'x86'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86_64.dll"; arch = 'x86_64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86_64.dll'; outerHTML = "test" }
- )
- }
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Invoke-WebRequestWrapper { }
- Mock Move-ItemWrapper { }
- Mock Remove-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Add-ContentWrapper { }
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl' -skipConfirmation $true
- $code = Install-XDebugExtension -iniPath $testIniPath
$code | Should -Be 0
- }
-
- It "Handles unknown architecture in sorting" {
- Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = $null; buildType = $null; path = "$TEST_DRIVE\php\8.1.0" } }
- Mock Get-XDebugFromUrl {
- return @(
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86.dll"; arch = 'x86'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86.dll'; outerHTML = "test" }
- @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-arm64.dll"; arch = 'arm64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-arm64.dll'; outerHTML = "test" }
- )
+ Should -Invoke Add-MissingPHPExtensionToIni -Exactly 1
+ }
+
+ It "Uses extension config handler for configuration" {
+ $mockFile = @{ Name = 'php_curl.dll'; FullName = "$TEST_DRIVE\extracted\php_curl.dll" }
+ Mock Get-ChildItemWrapper { return @( $mockFile ) }
+ Mock Get-ExtensionConfigHandler {
+ param ($extName)
+ return {
+ param ($iniPath, $fileName, $extVersion)
+ # Mock config handler
+ return 0
+ }
+ }
+ Mock Get-ExtensionPackages {
+ return @{
+ extName = 'xdebug'
+ source = 'xdebug.org'
+ data = @(
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll' }
+ )
+ }
}
- Mock Add-MissingPHPExtensionToIni { return 0 }
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' }
- Mock Invoke-WebRequestWrapper { }
- Mock Move-ItemWrapper { }
- Mock Remove-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Add-ContentWrapper { }
-
- $code = Install-XDebugExtension -iniPath $testIniPath
- $code | Should -Be 0
- }
-
- It "Skips overwrite prompt and installs when skipConfirmation is true and file exists" {
- Mock Test-CanUseCache { return $false }
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-2.9.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Test-FileExists { return $true }
- Mock Remove-ItemWrapper { }
- Mock Move-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Add-ContentWrapper { }
- Mock Add-MissingPHPExtensionToIni { return 0 }
- $code = Install-XDebugExtension -iniPath $testIniPath -skipConfirmation $true
+ $code = Install-Extension -iniPath $testIniPath -extName 'xdebug'
$code | Should -Be 0
- Should -Invoke Read-HostWrapper -Exactly 0 -ParameterFilter {
- $Prompt -like '*already exists*'
- }
+ Should -Invoke Get-ExtensionConfigHandler -Exactly 1
}
- It "Prompts overwrite when skipConfirmation is false and file exists and user cancels" {
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Test-FileExists { return $true }
- Mock Read-HostWrapper -ParameterFilter { $prompt -like '*already exists*' } -MockWith { return 'n' }
- Mock Remove-ItemWrapper { }
+ It "Returns -1 when no packages found" {
+ Mock Get-ExtensionPackages { return @{ extName = 'xdebug'; data = $null; source = 'xdebug.org' } }
- $code = Install-XDebugExtension -iniPath $testIniPath -skipConfirmation $false
+ $code = Install-Extension -iniPath $testIniPath -extName 'xdebug'
$code | Should -Be -1
- Should -Invoke Read-HostWrapper -Exactly 1 -ParameterFilter {
- $Prompt -like '*already exists*'
- }
- }
-
- It "Prompts overwrite when skipConfirmation is false and file exists and user confirms" {
- Mock Test-CanUseCache { return $false }
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-2.9.0-8.1-vs16-x64.dll" -content 'XDebug DLL content'
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
- Mock Test-FileExists { return $true }
- Mock Read-HostWrapper -ParameterFilter { $prompt -like '*already exists*' } -MockWith { return 'y' }
- Mock Remove-ItemWrapper { }
- Mock Move-ItemWrapper { }
- Mock Get-ContentWrapper { return "zend_extension=opcache" }
- Mock Add-ContentWrapper { }
- Mock Add-MissingPHPExtensionToIni { return 0 }
-
- $code = Install-XDebugExtension -iniPath $testIniPath -skipConfirmation $false
-
- $code | Should -Be 0
- Should -Invoke Read-HostWrapper -Exactly 1 -ParameterFilter {
- $Prompt -like '*already exists*'
- }
+ Should -Invoke Show-Error -Exactly 1
}
}
@@ -513,7 +272,17 @@ Describe "Add-MissingPHPExtensionToIni" {
}
It "Adds and configures xdebug in ini file" {
- Mock Get-MatchingPHPExtensionsStatus { return @( @{ name = 'xdebug'; status = 'Enabled'; enabled = $true; color = 'DarkGreen' } )}
+ Mock Get-MatchingPHPExtensionsStatus { return @( @{ name = 'xdebug'; status = 'Enabled'; enabled = $true; color = 'DarkGreen'; LineNumber = 0 } )}
+ Mock Test-Path { return $true }
+ $result = Add-MissingPHPExtensionToIni -iniPath $testIniPath -extFileName 'php_xdebug.dll'
+ $result | Should -Be 0
+ Should -Invoke Show-Success -Times 1 -ParameterFilter {
+ $message -like "- 'php_xdebug.dll' added successfully."
+ }
+ }
+
+ It "Returns 0 and shows warning when extension already exists in ini file" {
+ Mock Get-MatchingPHPExtensionsStatus { return @( @{ name = 'xdebug'; status = 'Enabled'; enabled = $true; color = 'DarkGreen'; LineNumber = 150 } )}
Mock Test-Path { return $true }
$result = Add-MissingPHPExtensionToIni -iniPath $testIniPath -extFileName 'php_xdebug.dll'
$result | Should -Be 0
@@ -707,6 +476,52 @@ Describe "Install-Extension" {
}
}
+ It "Returns -1 when user cancels the extension installation" {
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { '' }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl'
+
+ $code | Should -Be -1
+ Should -Invoke Write-Gray -Times 1 -ParameterFilter { $message -like '*Installation cancelled*' }
+ }
+
+ It "Returns -1 when user enters an invalid selection" {
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { 'unknown' }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl'
+
+ $code | Should -Be -1
+ Should -Invoke Show-Warning -Times 1 -ParameterFilter { $message -like '*You answer is invalid*' }
+ }
+
+ It "Returns -1 when user enters a negative selection" {
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { -1 }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl'
+
+ $code | Should -Be -1
+ Should -Invoke Show-Warning -Times 1 -ParameterFilter { $message -like '*Number must be between 0 and 1*' }
+ }
+
+ It "Returns -1 when user enters a selection outside the valid range" {
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { 5 }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl'
+
+ $code | Should -Be -1
+ Should -Invoke Show-Warning -Times 1 -ParameterFilter { $message -like '*Number must be between 0 and 1*' }
+ }
+
+ It "Returns -1 when no handler is found for the selected source" {
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { 0 }
+ Mock Get-SourceHandler { return $null }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl'
+
+ $code | Should -Be -1
+ Should -Invoke Show-Error -Times 1 -ParameterFilter { $message -like '*No handler found for source*' }
+ }
+
It "Returns -1 when gets empty list from extension" {
$code = Install-Extension -iniPath $testIniPath -extName 'nonexistent_ext'
$code | Should -Be -1
@@ -790,6 +605,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "8.2 Thread Safe (TS) x86" }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-nts-vs16-x86.zip'; outerHTML = "8.2 Non Thread Safe (NTS) x86" }
@@ -888,6 +704,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'courierauth'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/courierauth/1.4.0/php_courierauth-1.4.0-8.2-ts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_courierauth-1.4.0-8.2-ts-vs16-x64.zip'; outerHTML = "8.2 Thread Safe (TS) x64" }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/courierauth/1.4.0/php_courierauth-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_courierauth-1.4.0-8.2-nts-vs16-x64.zip'; outerHTML = "8.2 Non Thread Safe (NTS) x64" }
@@ -923,9 +740,15 @@ Describe "Install-Extension" {
)
}
}
- Mock Select-ExtensionPackageLink { return $null }
+ $script:callCount = 0
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith {
+ $script:callCount++
+ if ($script:callCount -eq 1) { return '0' }
+ return ''
+ }
$code = Install-Extension -iniPath $testIniPath -extName 'courierauth'
$code | Should -Be -1
+ Should -Invoke Show-Error -Times 1 -ParameterFilter { $message -like '*You chose the wrong index*' }
}
}
@@ -940,6 +763,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.5.0/php_curl-1.5.0-8.2-ts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.5.0'; compiler = 'vs16' }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.5.0rc1/php_curl-1.5.0rc1-8.2-ts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.5.0rc1'; compiler = 'vs16' }
@@ -962,6 +786,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86_64.zip"; arch = 'x86_64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' }
@@ -981,13 +806,14 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-arm64.zip"; arch = 'arm64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' }
)
}
}
- Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' }
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' }
Mock Test-Path { return $false }
Mock Add-MissingPHPExtensionToIni { return 0 }
@@ -1000,6 +826,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1020,6 +847,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1041,6 +869,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1064,6 +893,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1086,6 +916,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts'; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1112,6 +943,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts'; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1137,6 +969,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts'; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" }
)
@@ -1164,6 +997,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
@@ -1183,6 +1017,7 @@ Describe "Install-Extension" {
Mock Get-ExtensionPackages {
return @{
extName = 'curl'
+ source = 'pecl.php.net'
data = @(
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
@{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
@@ -1190,7 +1025,13 @@ Describe "Install-Extension" {
)
}
}
- Mock Read-HostWrapper { '-1' }
+
+ $script:callCount = 0
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith {
+ $script:callCount++
+ if ($script:callCount -eq 1) { return '0' }
+ return '-1'
+ }
$code = Install-Extension -iniPath $testIniPath -extName 'curl'
$code | Should -Be -1
@@ -1205,57 +1046,116 @@ Describe "Install-Extension" {
$code | Should -Be -1
Should -Invoke Add-LogEntry
}
+
+ It "Returns -1 when no source supports the extension" {
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'curl'
+
+ $code | Should -Be -1
+ Should -Invoke Show-Error -ParameterFilter { $message -like "*Source 'xdebug.org' does not support extension 'curl'*" }
+ }
+
+ It "Shows the more info url" {
+ Mock Get-CurrentPHPVersion { return @{ version = '8.2.0'; path = "$TEST_DRIVE\php\8.2.0" } }
+ Mock Get-SourceHandler {
+ param ($sourceUrl)
+ return @{
+ ResolveLinks = {
+ return @{
+ extName = 'xdebug'
+ source = 'xdebug.org'
+ data = @(
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-ts-vs16-x86.dll"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-nts-vs16-x86.dll"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.0-8.2-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ )
+ }
+ }
+ GetPackages = {
+ return @(
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-ts-vs16-x86.dll"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-nts-vs16-x86.dll"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.0-8.2-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ )
+ }
+ Download = { return @{ Name = 'php_xdebug.dll'; FullName = "$TEST_DRIVE\extracted\php_xdebug.dll" } }
+ MoreInfoUrl = $XDEBUG_HISTORICAL_URL
+ }
+ }
+ Mock Get-ExtensionConfigHandler {
+ param ($extName)
+ return { return 0 }
+ }
+ Mock Get-ExtensionPackages {
+ return @{
+ extName = 'xdebug'
+ source = 'xdebug.org'
+ data = @(
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-ts-vs16-x86.dll"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-nts-vs16-x86.dll"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.0-8.2-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ )
+ }
+ }
+ Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' }
+
+ $code = Install-Extension -iniPath $testIniPath -extName 'xdebug'
+
+ $code | Should -Be 0
+ Should -Invoke Show-Info -ParameterFilter { $message -like "*This is a partial list. For a complete list, visit: $XDEBUG_HISTORICAL_URL*" } -Times 1
+ }
}
Describe "Install-IniExtension" {
It "Handles null extension name" {
- $code = Install-IniExtension -iniPath $testIniPath -extName $null
+ $code = Install-IniExtension -iniPath $testIniPath -extNames $null
$code | Should -Be -1
}
It "Installs xdebug" {
- Mock Install-XDebugExtension { return 0 }
- $code = Install-IniExtension -iniPath $testIniPath -extName 'xdebug'
+ Mock Install-Extension { return 0 }
+ $code = Install-IniExtension -iniPath $testIniPath -extNames 'xdebug'
$code | Should -Be 0
}
- It "Installs extension" {
+ It "Installs pecl extension" {
Mock Install-Extension { return 0 }
- $code = Install-IniExtension -iniPath $testIniPath -extName 'curl'
+ $code = Install-IniExtension -iniPath $testIniPath -extNames 'curl'
$code | Should -Be 0
}
It "Returns -1 on error" {
Mock Install-Extension { return -1 }
- $code = Install-IniExtension -iniPath $testIniPath -extName 'curl'
+ $code = Install-IniExtension -iniPath $testIniPath -extNames 'curl'
$code | Should -Be -1
}
It "Handles thrown exception" {
Mock Add-LogEntry { return 0 }
Mock Install-Extension { throw 'Network error' }
- $code = Install-IniExtension -iniPath $testIniPath -extName 'curl'
+ $code = Install-IniExtension -iniPath $testIniPath -extNames 'curl'
$code | Should -Be -1
}
- It "Passes skipConfirmation true to Install-XDebugExtension" {
- Mock Install-XDebugExtension { return 0 }
+ It "Passes skipConfirmation true to Install-IniExtension" {
+ Mock Install-Extension { return 0 }
$code = Install-IniExtension -iniPath $testIniPath -extNames @('xdebug') -skipConfirmation $true
$code | Should -Be 0
- Should -Invoke Install-XDebugExtension -Exactly 1 -ParameterFilter {
+ Should -Invoke Install-Extension -Exactly 1 -ParameterFilter {
$skipConfirmation -eq $true
}
}
- It "Passes skipConfirmation false to Install-XDebugExtension by default" {
- Mock Install-XDebugExtension { return 0 }
+ It "Passes skipConfirmation false to Install-IniExtension by default" {
+ Mock Install-Extension { return 0 }
$code = Install-IniExtension -iniPath $testIniPath -extNames @('xdebug')
$code | Should -Be 0
- Should -Invoke Install-XDebugExtension -Exactly 1 -ParameterFilter {
+ Should -Invoke Install-Extension -Exactly 1 -ParameterFilter {
$skipConfirmation -eq $false
}
}
@@ -1281,6 +1181,19 @@ Describe "Install-IniExtension" {
$skipConfirmation -eq $false
}
}
+
+ It "Returns -1 if one extension fails to install" {
+ Mock Install-Extension {
+ param ($extName)
+
+ if ($extName -eq 'unknown') { return -1 }
+ return 0
+ }
+
+ $code = Install-IniExtension -iniPath $testIniPath -extNames @('curl', 'unknown')
+
+ $code | Should -Be -1
+ }
}
Describe "Get-PrereleaseSortKey" {
diff --git a/tests/actions/ini/helpers/fetch.tests.ps1 b/tests/actions/ini/helpers/fetch.tests.ps1
index 846ffe98..d7a0f546 100644
--- a/tests/actions/ini/helpers/fetch.tests.ps1
+++ b/tests/actions/ini/helpers/fetch.tests.ps1
@@ -4,6 +4,9 @@ BeforeAll {
$script:TEST_DRIVE = "$($PVMConfig.paths.directories.fakeStorage)\fetch-drive"
$PVMConfig.test.setFakePaths.Invoke($TEST_DRIVE)
+ $script:testIniPath = "$TEST_DRIVE\php.ini"
+ $script:testPhpPath = "$TEST_DRIVE\php"
+ $script:XDEBUG_HISTORICAL_URL = $PVMConfig.links.xdebugHistorical
$script:PECL_BASE_URL = $PVMConfig.links.peclBase
$script:PECL_PACKAGES_URL = $PVMConfig.links.peclPackages
$script:PECL_PACKAGE_ROOT_URL = $PVMConfig.links.peclPackageRoot
@@ -52,6 +55,507 @@ AfterAll {
$Global:PVMConfig = $PVMConfigBackup
}
+Describe "Get-ExtensionHandlers Tests" {
+ It "Returns unified handler registry with both source and config handlers" {
+ $handlers = Get-ExtensionHandlers
+
+ $handlers.SourceHandlers | Should -Not -BeNullOrEmpty
+ $handlers.ExtensionConfigHandlers | Should -Not -BeNullOrEmpty
+ $handlers.SourceHandlers.ContainsKey('xdebug.org') | Should -Be $true
+ $handlers.SourceHandlers.ContainsKey('pecl.php.net') | Should -Be $true
+ $handlers.ExtensionConfigHandlers.ContainsKey('xdebug') | Should -Be $true
+ }
+}
+
+Describe "Get-SourceHandler Tests" {
+ It "Returns correct handler for xdebug.org source" {
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+ }
+
+ It "Returns correct handler for pecl.php.net source" {
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+ }
+
+ It "Returns default PECL handler for unknown sources" {
+ $handler = Get-SourceHandler -sourceUrl 'unknown.source.com'
+
+ $handler | Should -Not -BeNullOrEmpty
+ # Should return the pecl.php.net handler as default
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+ }
+
+ Context "When running source actions (download, configure and link) from xdebug source handler" {
+ BeforeEach {
+ Mock Show-SpinnerWhileJob {
+ param ($scriptBlock, $message, $noClear, $argumentList, $rethrow)
+ $result = & $scriptBlock @argumentList
+ return $result.pvmData
+ }
+ }
+
+ It "Returns data null when no handler found for xdebug" {
+ Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } }
+ Mock Get-OrUpdateCache { return $null }
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ Mock Get-SourceHandler { return $null }
+ $result = & $handler.ResolveLinks -extName 'xdebug'
+
+ $result.data | Should -BeNullOrEmpty
+ }
+
+ It "Returns data null when no packages found" {
+ Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } }
+ Mock Get-OrUpdateCache { return $null }
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ $result = & $handler.ResolveLinks -extName 'xdebug'
+
+ $result.data | Should -BeNullOrEmpty
+ }
+
+ It "Resolves and returns xdebug links" {
+ Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } }
+ Mock Get-OrUpdateCache {
+ return @{
+ extName = 'curl'
+ data = @(
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ )
+ }
+ }
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ $result = & $handler.ResolveLinks -extName 'xdebug'
+
+ $result.source | Should -Be 'xdebug.org'
+ $result.data.Count | Should -Be 2
+ $result.extName | Should -Be 'xdebug'
+ }
+
+ It "Returns null when user cancels" {
+ Mock Get-XDebugFromUrl { return $null }
+ Mock Invoke-WebRequestWrapper { return $null }
+ Mock Test-FileExists { return $true }
+ $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; }
+ Mock Read-HostWrapper -ParameterFilter { $prompt -like "*$($chosenItem.fileName) already exists. Would you like to overwrite it?*" } -MockWith { return 'n' }
+ Mock Remove-ItemWrapper { }
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Be $XDEBUG_HISTORICAL_URL
+
+ $null = & $handler.GetPackages -version '8.5'
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $false
+
+ $result | Should -BeNullOrEmpty
+ Should -Invoke Get-XDebugFromUrl -Times 1
+ Should -Invoke Invoke-WebRequestWrapper -Times 1
+ Should -Invoke Write-Gray -ParameterFilter { $message -like '*Installation cancelled*' }
+ }
+
+ It "Returns downloaded file" {
+ Mock Get-XDebugFromUrl { return $null }
+ Mock Invoke-WebRequestWrapper { return $null }
+ $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; }
+ Mock Move-ItemWrapper { }
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Be $XDEBUG_HISTORICAL_URL
+
+ $null = & $handler.GetPackages -version '8.5'
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true
+
+ $result | Should -Not -BeNullOrEmpty
+ $result.Name | Should -Be $chosenItem.fileName
+ $result.FullName | Should -Be "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)"
+ Should -Invoke Get-XDebugFromUrl -Times 1
+ Should -Invoke Invoke-WebRequestWrapper -Times 1
+ Should -Invoke Move-ItemWrapper -Times 1
+ }
+
+ It "Handles exception gracefully" {
+ Mock Get-XDebugFromUrl { return $null }
+ Mock Invoke-WebRequestWrapper { throw 'Error' }
+ Mock Add-LogEntry { return 0 }
+
+ $handler = Get-SourceHandler -sourceUrl 'xdebug.org'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Be $XDEBUG_HISTORICAL_URL
+
+ $null = & $handler.GetPackages -version '8.5'
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true
+
+ $result | Should -BeNullOrEmpty
+ Should -Invoke Get-XDebugFromUrl -Times 1
+ Should -Invoke Invoke-WebRequestWrapper -Times 1
+ Should -Invoke Add-LogEntry -Times 1
+ }
+ }
+
+ Context "When running source actions (download and configure) from pecl source handler" {
+ BeforeEach {
+ Mock Show-SpinnerWhileJob {
+ param ($scriptBlock, $message, $noClear, $argumentList, $rethrow)
+ $result = & $scriptBlock @argumentList
+ return $result.pvmData
+ }
+ }
+
+ It "Resolves and returns extension links" {
+ Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } }
+ Mock Get-ExtensionPackages {
+ return @{
+ extName = 'curl'
+ source = 'pecl.php.net'
+ data = @(
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' }
+ @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' }
+ )
+ }
+ }
+
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
+
+ $result = & $handler.ResolveLinks -extName 'curl'
+
+ $result.source | Should -Be 'pecl.php.net'
+ $result.data.Count | Should -Be 3
+ $result.extName | Should -Be 'curl'
+ }
+
+ It "Returns null when no dll file found in downloaded zip" {
+ Mock Get-PackagesFromSourceLinks { return $null }
+ Mock Invoke-WebRequestWrapper { return $null }
+ $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; }
+ Mock Expand-Zip { }
+ Mock Get-ChildItemWrapper { return @() }
+ Mock Remove-ItemWrapper { }
+
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+
+ $links = @{
+ extName = 'xdebug'
+ source = 'pecl.php.net'
+ links = @(
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" }
+ )
+ }
+
+ $null = & $handler.GetPackages -version '8.5' -linksObj $links
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true -extName 'xdebug'
+
+ $result | Should -BeNullOrEmpty
+ Should -Invoke Get-PackagesFromSourceLinks -Times 1
+ }
+
+ It "Returns null when user cancels" {
+ Mock Get-PackagesFromSourceLinks { return $null }
+ Mock Invoke-WebRequestWrapper { return $null }
+ Mock Expand-Zip { }
+ $mockFile = @{ Name = 'php_xdebug.dll'; FullName = "$TEST_DRIVE\extracted\php_xdebug.dll" }
+ Mock Get-ChildItemWrapper { return @( $mockFile ) }
+ $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; }
+ Mock Test-FileExists { return $true }
+ Mock Read-HostWrapper -ParameterFilter { $prompt -like "*$($mockFile.Name) already exists. Would you like to overwrite it?*" } -MockWith { return 'n' }
+ Mock Remove-ItemWrapper { }
+
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+
+ $links = @{
+ extName = 'xdebug'
+ source = 'pecl.php.net'
+ links = @(
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" }
+ )
+ }
+ $null = & $handler.GetPackages -version '8.5' -linksObj $links
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $false -extName 'xdebug'
+ $link = & $handler.MoreInfoUrl -extName 'xdebug'
+
+ $result | Should -BeNullOrEmpty
+ $link | Should -Be "$PECL_PACKAGE_ROOT_URL/xdebug"
+ Should -Invoke Write-Gray -ParameterFilter { $message -like '*Installation cancelled*' }
+ Should -Invoke Get-PackagesFromSourceLinks -Times 1
+ }
+
+ It "Returns downloaded file" {
+ Mock Get-PackagesFromSourceLinks { return $null }
+ Mock Invoke-WebRequestWrapper { return $null }
+ Mock Expand-Zip { }
+ Mock Move-ItemWrapper { }
+ Mock Remove-ItemWrapper { }
+ $mockFile = @{ Name = 'php_xdebug.dll'; FullName = "$TEST_DRIVE\extracted\php_xdebug.dll" }
+ Mock Get-ChildItemWrapper { return @( $mockFile ) }
+ $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; }
+
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+
+ $links = @{
+ extName = 'xdebug'
+ source = 'pecl.php.net'
+ links = @(
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" }
+ )
+ }
+ $null = & $handler.GetPackages -version '8.5' -linksObj $links
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true -extName 'xdebug'
+
+ $result.FullName | Should -Be $mockFile.FullName
+ $result.Name | Should -Be $mockFile.Name
+ Should -Invoke Get-PackagesFromSourceLinks -Times 1
+ }
+
+ It "Handles exception gracefully" {
+ Mock Get-PackagesFromSourceLinks { return $null }
+ Mock Invoke-WebRequestWrapper { throw 'Error' }
+ Mock Add-LogEntry { return 0 }
+
+ $handler = Get-SourceHandler -sourceUrl 'pecl.php.net'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetPackages | Should -Not -BeNullOrEmpty
+ $handler.Download | Should -Not -BeNullOrEmpty
+ $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty
+
+ $links = @{
+ extName = 'xdebug'
+ source = 'pecl.php.net'
+ links = @(
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" },
+ @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" }
+ )
+ }
+ $null = & $handler.GetPackages -version '8.5' -linksObj $links
+ $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true -extName 'xdebug'
+
+ $result | Should -BeNullOrEmpty
+ Should -Invoke Get-PackagesFromSourceLinks -Times 1
+ Should -Invoke Invoke-WebRequestWrapper -Times 1
+ Should -Invoke Add-LogEntry -Times 1
+ }
+ }
+}
+
+Describe "Get-ExtensionConfigHandler Tests" {
+ It "Returns xdebug config handler for xdebug extension" {
+ $handler = Get-ExtensionConfigHandler -extName 'php_xdebug.dll'
+
+ $handler | Should -Not -BeNullOrEmpty
+ # Should return the xdebug config handler scriptblock
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
+ }
+
+ It "Returns xdebug config handler for xdebug without prefix" {
+ $handler = Get-ExtensionConfigHandler -extName 'xdebug.dll'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
+ }
+
+ It "Returns xdebug config handler for xdebug with version" {
+ $handler = Get-ExtensionConfigHandler -extName 'php_xdebug-3.1.0-8.1-vs16-x64.dll'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
+ }
+
+ It "Returns default handler for unknown extensions" {
+ Mock Add-MissingPHPExtensionToIni { 0 }
+
+ $handler = Get-ExtensionConfigHandler -extName 'php_unknown.dll'
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
+ $code = & $handler $iniPath $null $fileName $null $extVersion $null
+ $code | Should -Be 0
+ Should -Invoke Add-MissingPHPExtensionToIni -Times 1
+ }
+
+ It "Returns default handler for empty input" {
+ $handler = Get-ExtensionConfigHandler -extName ''
+
+ $handler | Should -Not -BeNullOrEmpty
+ $handler.GetType().Name | Should -Be 'ScriptBlock'
+ }
+
+ Context "When running config actions from selected config handler" {
+ It "Configures xdebug in ini file" {
+ Mock Get-ContentWrapper { return '' }
+ Mock Add-ContentWrapper { }
+
+ $configHandler = Get-ExtensionConfigHandler -extName 'xdebug'
+
+ $result = & $configHandler -iniPath $testIniPath -fileName 'php_xdebug.dll' -extVersion '3.5'
+
+ $configHandler | Should -Not -BeNullOrEmpty
+ $result | Should -Be 0
+ Should -Invoke Add-ContentWrapper -Times 1
+ }
+
+ It "Handles exception gracefully" {
+ Mock Get-ContentWrapper { throw 'Error' }
+ Mock Add-LogEntry { return 0 }
+
+ $configHandler = Get-ExtensionConfigHandler -extName 'xdebug'
+
+ $result = & $configHandler -iniPath $testIniPath -fileName 'php_xdebug.dll' -extVersion '3.5'
+
+ $configHandler | Should -Not -BeNullOrEmpty
+ $result | Should -Be -1
+ Should -Invoke Add-LogEntry -Times 1
+ }
+
+ It "Configures curl in ini file" {
+ Mock Add-MissingPHPExtensionToIni { return 0 }
+
+ $configHandler = Get-ExtensionConfigHandler -extName 'curl'
+
+ $result = & $configHandler -iniPath $testIniPath -fileName 'php_curl.dll' -extVersion '1.5'
+
+ $configHandler | Should -Not -BeNullOrEmpty
+ $result | Should -Be 0
+ Should -Invoke Add-MissingPHPExtensionToIni -Times 1
+ }
+ }
+}
+
+Describe "Get-XDebugFromUrl Tests" {
+ BeforeAll {
+ function Reset-MockState {
+ $script:MockRegistryThrowException = $false
+ $script:MockFileSystem.DownloadFails = $false
+ $script:MockFileSystem.WebResponses = @{}
+ $script:MockFileSystem.Files = @{}
+ $script:MockFileSystem.Directories = @()
+ }
+
+ function Set-MockWebResponse {
+ param ($url, $content, $links = @())
+ $script:MockFileSystem.WebResponses[$url] = @{
+ Content = $content
+ Links = $links
+ }
+ }
+ }
+ BeforeEach {
+ Reset-MockState
+ }
+
+ It "Should parse XDebug versions correctly" {
+ $mockLinks = @(
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86_64.dll" },
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x86_64.dll" },
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll" },
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll" },
+ @{ href = "$XDEBUG_BASE_URL/download/php_random.dll" }
+ )
+ Set-MockWebResponse -url 'https://test.com' -links $mockLinks
+
+ $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1'
+
+ $result.Count | Should -Be 4
+ $result[0].extVersion | Should -Be '3.1.0'
+ $result[1].extVersion | Should -Be '2.9.0'
+ }
+
+ It "Should handle network errors" {
+ $script:MockFileSystem.DownloadFails = $true
+
+ $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1'
+
+ $result | Should -Be @()
+ }
+
+ It "Should parse xdebug with x86 architecture and unknown compiler" {
+ $mockLinks = @(
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-x86.dll" },
+ @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-x86.dll" }
+ )
+ Set-MockWebResponse -url 'https://test.com' -links $mockLinks
+
+ $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1'
+
+ $result.Count | Should -Be 2
+ $result[0].arch | Should -Be 'x86'
+ $result[0].compiler | Should -Be 'unknown'
+ $result[1].arch | Should -Be 'x86'
+ $result[1].compiler | Should -Be 'unknown'
+ }
+}
+
+Describe "Get-XdebugConfigV2 Tests" {
+ It "Fetchs xdebug v2 config" {
+ $res = Get-XdebugConfigV2 -XDebugPath 'php_xdebug.dll'
+
+ $res[0] | Should -Be '[xdebug]'
+ $res[1] | Should -Be ";zend_extension='php_xdebug.dll'"
+ $res[2] | Should -Be 'xdebug.remote_enable=1'
+ $res[3] | Should -Be 'xdebug.remote_host=127.0.0.1'
+ $res[4] | Should -Be 'xdebug.remote_port=9000'
+ }
+}
+
+Describe "Get-XdebugConfigV2 Tests" {
+ It "Fetchs xdebug v3 config" {
+ $res = Get-XdebugConfigV3 -XDebugPath 'php_xdebug.dll'
+
+ $res[0] | Should -Be '[xdebug]'
+ $res[1] | Should -Be ";zend_extension='php_xdebug.dll'"
+ $res[2] | Should -Be 'xdebug.mode=debug'
+ $res[3] | Should -Be 'xdebug.client_host=127.0.0.1'
+ $res[4] | Should -Be 'xdebug.client_port=9003'
+ }
+}
+
Describe "Get-ExtensionCategoriesByPage Tests" {
It "Returns extensions links by page" {
Mock Invoke-WebRequestWrapper -ParameterFilter { $Uri -eq "$($PECL_PACKAGES_URL)?catpid=3&catname=Caching&pageID=1" } -MockWith {
@@ -183,9 +687,9 @@ Describe "Get-ExtensionAvailableReleasesLinks" {
$result = Get-ExtensionAvailableReleasesLinks -extName 'memcache'
$result.Count | Should -Be 3
- $result[0].href | Should -Be "$($PVMConfig.links.peclBase)/package/memcache/3.4.0/windows"
- $result[1].href | Should -Be "$($PVMConfig.links.peclBase)/package/memcache/3.3.0/windows"
- $result[2].href | Should -Be "$($PVMConfig.links.peclBase)/package/memcache/3.2.0/windows"
+ $result[0].href | Should -Be "$PECL_BASE_URL/package/memcache/3.4.0/windows"
+ $result[1].href | Should -Be "$PECL_BASE_URL/package/memcache/3.3.0/windows"
+ $result[2].href | Should -Be "$PECL_BASE_URL/package/memcache/3.2.0/windows"
}
}
@@ -669,13 +1173,25 @@ Describe "Resolve-ExtensionLinks Tests" {
}
It "Takes the only link found" {
- Mock Get-ExtensionMatchingCategories { return @( @{ href = '/package/memcache'; extName = 'memcache' } ) }
+ Mock Get-ExtensionMatchingCategories { return @( @{ href = '/package/memcache'; extName = 'memcache'; source = 'pecl.php.net' } ) }
$result = Resolve-ExtensionLinks -extName 'mem' -version '8.2'
$result.extName | Should -Be 'memcache'
$result.links.Count | Should -Be 3
}
+
+ It "Should return empty links for sources other than pecl" {
+ Mock Get-ExtensionMatchingCategories { return @(
+ @{ href = "$XDEBUG_HISTORICAL_URL"; extName = 'xdebug'; source = 'xdebug.org' }
+ )
+ }
+
+ $result = Resolve-ExtensionLinks -extName 'debug' -version '8.2'
+
+ $result.extName | Should -Be 'xdebug'
+ $result.links.Count | Should -Be 0
+ }
}
It "Handles defensive check when chosen item is null" {
@@ -711,6 +1227,7 @@ Describe "Get-ExtensionPackages Tests" {
Mock Resolve-ExtensionLinks {
return @{
extName = 'memcache'
+ source = 'pecl.php.net'
links = @(
@{ href = '/package/memcache/3.4.0/windows' },
@{ href = '/package/memcache/3.3.0/windows' },
@@ -739,11 +1256,12 @@ Describe "Get-ExtensionPackages Tests" {
$result = Get-ExtensionPackages -extName 'cache' -version '8.2'
$result.data | Should -Be $null
+ $result.source | Should -Be 'unknown'
}
It "Uses extName from linksObj when links are empty" {
Mock Resolve-ExtensionLinks {
- return @{ extName = 'memcache'; links = @() }
+ return @{ extName = 'memcache'; source = 'pecl.php.net'; links = @() }
}
$result = Get-ExtensionPackages -extName 'mem' -version '8.2'
@@ -751,4 +1269,31 @@ Describe "Get-ExtensionPackages Tests" {
$result.extName | Should -Be 'memcache'
$result.data | Should -Be $null
}
+
+ It "Returns null when no handler found" {
+ Mock Resolve-ExtensionLinks {
+ return @{ extName = 'memcache'; source = 'pecl.php.net'; links = @() }
+ }
+ Mock Get-SourceHandler { return $null }
+
+ $result = Get-ExtensionPackages -extName 'mem' -version '8.2'
+
+ $result.extName | Should -Be 'memcache'
+ $result.data | Should -Be $null
+ $result.source | Should -Be 'pecl.php.net'
+ }
+
+ It "Runs GetPackages for packages from sources other than pecl" {
+ Mock Get-OrUpdateCache { return $null }
+ Mock Resolve-ExtensionLinks {
+ return @{ extName = 'xdebug'; source = 'xdebug.org'; links = @() }
+ }
+
+ $result = Get-ExtensionPackages -extName 'mem' -version '8.2'
+
+ $result.extName | Should -Be 'xdebug'
+ $result.data | Should -Be $null
+ $result.source | Should -Be 'xdebug.org'
+ Should -Invoke Get-OrUpdateCache -Times 1
+ }
}
diff --git a/tests/actions/ini/set.tests.ps1 b/tests/actions/ini/set.tests.ps1
index b3e6afd6..5984249d 100644
--- a/tests/actions/ini/set.tests.ps1
+++ b/tests/actions/ini/set.tests.ps1
@@ -181,4 +181,13 @@ memory_limit=2G
$code = Set-IniSetting -iniPath $testIniPath -keys @('memory_limit=256M')
$code | Should -Be -1
}
+
+ It "Returns -1 if no match is found for any setting" {
+ @"
+memory_limit=2G
+"@ | Set-ContentWrapper -path $testIniPath
+
+ $code = Set-IniSetting -iniPath $testIniPath -keys @('memory_limit=256M', 'unknown')
+ $code | Should -Be -1
+ }
}
diff --git a/tests/actions/ini/status.tests.ps1 b/tests/actions/ini/status.tests.ps1
index 2f067afb..5ccd5c25 100644
--- a/tests/actions/ini/status.tests.ps1
+++ b/tests/actions/ini/status.tests.ps1
@@ -97,4 +97,17 @@ Describe "Get-IniExtensionStatus" {
$code = Get-IniExtensionStatus -iniPath $testIniPath -extNames @('curl')
$code | Should -Be -1
}
+
+ It "Returns -1 if no match is found for any extension" {
+ Mock Get-MatchingPHPExtensionsStatus {
+ param ($extName)
+
+ if ($extName -eq 'unknown') { return @() }
+ return @( @{ name = 'curl'; id='curl'; status='Enabled'; color='DarkGreen'; line=0; lineNamber=0; source='ext,ini' } )
+ }
+
+ $code = Get-IniExtensionStatus -iniPath $testIniPath -extNames @('curl', 'unknown')
+
+ $code | Should -Be -1
+ }
}