private/Install-PhpFromUrl.ps1

function Install-PhpFromUrl() {
    <#
    .Synopsis
    Installs PHP, fetching its binary archive from an URL.
 
    .Parameter Url
    The URL where the binary archive can be downloaded from.
 
    .Parameter Path
    The path where the archive should be extracted to.
 
    .Parameter PhpVersion
    The instance of PphVersion we are going to install.
 
    .Parameter InstallVCRedist
    Install the Visual C++ Redistributables if they are missing?
    #>

    [OutputType()]
    param (
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'The URL where the binary archive can be downloaded from')]
        [ValidateNotNull()]
        [ValidateLength(1, [int]::MaxValue)]
        [string] $Url,
        [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'The path where the archive should be extracted to')]
        [ValidateNotNull()]
        [ValidateLength(1, [int]::MaxValue)]
        [string] $Path,
        [Parameter(Mandatory = $true, Position = 2, HelpMessage = 'The instance of PphVersion we are going to install')]
        [ValidateNotNull()]
        [PhpVersionDownloadable] $PhpVersion,
        [Parameter(Mandatory = $true, Position = 3, HelpMessage = 'Install the Visual C++ Redistributables if they are missing?')]
        [bool] $InstallVCRedist
    )
    begin {
    }
    process {
        $temporaryFile, $keepTemporaryFile = Get-FileFromUrlOrCache -Url $Url
        try {
            $temporaryDirectory = New-TempDirectory
            try {
                Write-Verbose "Extracting $temporaryFile to temporary directory"
                try {
                    Expand-ArchiveWith7Zip -ArchivePath $temporaryFile -DestinationPath $temporaryDirectory -Overwrite
                } catch {
                    $keepTemporaryFile = $false
                    throw
                }
                $exePath = Join-Path -Path $temporaryDirectory -ChildPath php.exe
                if (-Not(Test-Path -Path $exePath -PathType Leaf)) {
                    throw "Unable to find php.exe in the downloaded archive"
                }
                try {
                    $exeOutput = & $exePath @('-n', '-v') 2>&1 | Out-String
                } catch {
                    $exeOutput = $_.Exception.Message
                }
                $exeExitCode = $LASTEXITCODE
                if ($exeExitCode -eq $Script:STATUS_DLL_NOT_FOUND -or $exeExitCode -eq $Script:ENTRYPOINT_NOT_FOUND  -or $exeExitCode -eq $Script:STATUS_INVALID_IMAGE_FORMAT -or $exeOutput -match 'vcruntime.*is not compatible with this PHP build') {
                    switch ($PhpVersion.VCVersion) {
                        6 { $redistName = '6' } # PHP 5.2, PHP 5.3
                        7 { $redistName = '2002' }
                        7.1 { $redistName = '2003' }
                        8 { $redistName = '2005' }
                        9 { $redistName = '2008' } # PHP 5.4
                        10 { $redistName = '2010' }
                        11 { $redistName = '2012' } # PHP 5.5, PHP 5.6
                        12 { $redistName = '2013' }
                        14 { $redistName = '2015' } # PHP 7.0, PHP 7.1
                        15 { $redistName = '2017' } # PHP 7.2, PHP 7.3
                        16 { $redistName = '2019' } # PHP 7.4, PHP 8.0, PHP 8.1, PHP 8.2, PHP 8.3
                        17 { $redistName = '2022' } # PHP 8.4
                        default {
                            throw ('The Visual C++ ' + $PhpVersion.VCVersion + ' Redistributable seems to be missing: you have to install it manually (we can''t recognize its version)')
                        }
                    }
                    if (-Not($InstallVCRedist)) {
                        throw "The Visual C++ $redistName Redistributable seems to be missing: you have to install it manually"
                    }
                    $vcRedistModule = Get-Module -Name VcRedist
                    if (-Not($vcRedistModule)) {
                        $vcRedistModule = Get-Module -ListAvailable | Where-Object { $_.Name -eq 'VcRedist' }
                        if (-Not($vcRedistModule)) {
                            throw "The Visual C++ $redistName Redistributable seems to be missing: you have to manually install it (if you install the VcRedist PowerShell module we could try to install it automatically)"
                        }
                    }
                    $vcListKind = 'Supported'
                    $vcList = Get-VcList -Export $vcListKind | Where-Object { $_.Release -eq $redistName -and $_.Architecture -eq $PhpVersion.Architecture }
                    if (-Not($vcList)) {
                        $vcListKind = 'All'
                        $vcList = Get-VcList -Export $vcListKind | Where-Object { $_.Release -eq $redistName -and $_.Architecture -eq $PhpVersion.Architecture }
                        if (-Not($vcList)) {
                            throw "The Visual C++ $redistName Redistributable seems to be missing: you have to manually install it (the VcRedist PowerShell module doesn't support it)"
                        }
                    }
                    Write-Verbose "Downloading the Visual C++ $redistName Redistributable (it's required by this version of PHP)"
                    $temporaryDirectory2 = New-TempDirectory
                    try {
                        if ($vcRedistModule.Version -lt '2.0') {
                            $vcRedistCommand = 'Get-VcRedist'
                        } else {
                            $vcRedistCommand = 'Save-VcRedist'
                        }
                        $vcList | & $vcRedistCommand -Path $temporaryDirectory2
                        Write-Verbose "Installing the Visual C++ $redistName Redistributable"
                        $currentUser = [System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()
                        if ($currentUser.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
                            $vcList | Install-VcRedist -Path $temporaryDirectory2
                        } else {
                            $exeCommand = "Get-VcList -Export $vcListKind"
                            $exeCommand += " | Where-Object { `$_.Release -eq '$redistName' -and `$_.Architecture -eq '" + $PhpVersion.Architecture + "' }"
                            $exeCommand += " | Install-VcRedist -Path '" + ($temporaryDirectory2 -replace "'", "''") + "'"
                            Start-Process -FilePath 'powershell.exe' -ArgumentList "-Command ""$exeCommand""" -Verb RunAs -Wait
                        }
                    } finally {
                        try {
                            Remove-Item -Path $temporaryDirectory2 -Recurse -Force
                        } catch {
                            Write-Debug 'Failed to remove temporary folder'
                        }
                    }
                } elseif ($exeExitCode -ne 0) {
                    throw "Running PHP resulted in the following error:`n$exeOutput"
                }
            } finally {
                try {
                    Remove-Item -Path $temporaryDirectory -Recurse -Force
                } catch {
                    Write-Debug 'Failed to remove temporary folder'
                }
            }
            Write-Verbose "Extracting $temporaryFile to destination directory"
            Expand-ArchiveWith7Zip -ArchivePath $temporaryFile -DestinationPath $Path -Overwrite
            try {
                $mostRecentApacheFile = $null
                $mostRecentApacheFileVersion = $null
                $apacheDlls = @(Get-ChildItem -LiteralPath $Path -Filter 'php*apache*.dll')
                $apacheDlls = $apacheDlls | Where-Object { $_.BaseName -match '^php(\d+(_\d+)*)apache(\d+(_\d+)*)$' }
                foreach ($apacheDll in $apacheDlls) {
                    $match = $apacheDll.BaseName | Select-String -Pattern '^php(\d+(?:_\d+)*)apache(\d+(?:_\d+)*)$'
                    if ($match) {
                        $apacheFile = [System.IO.Path]::Combine($Path, 'Apache' + $match.Matches[0].Groups[2].Value + '.conf')
                        $apacheFileVersion = [System.Version](($match.Matches[0].Groups[2].Value -replace '_', '.') + '.0.0')
                        if (-Not(Test-Path -LiteralPath $apacheFile)) {
                            if ($match.Matches[0].Groups[1].Value -match '^[1-7]($|_)') {
                                $moduleName = 'php' + $match.Matches[0].Groups[1].Value + '_module'
                            } else {
                                $moduleName = 'php_module'
                            }
                            $fullName = $apacheDll.FullName
                            Set-Content -LiteralPath $apacheFile -Value "LoadModule $moduleName ""$fullName"""
                        }
                        if ($null -eq $mostRecentApacheFile -or $mostRecentApacheFileVersion -lt $apacheFileVersion) {
                            $mostRecentApacheFile = $apacheFile
                            $mostRecentApacheFileVersion = $apacheFileVersion
                        }
                    }
                }
                $genericApacheFile = [System.IO.Path]::Combine($Path, 'Apache.conf')
                if ($null -ne $mostRecentApacheFile -and -Not(Test-Path -LiteralPath $genericApacheFile)) {
                    Copy-Item -Path $mostRecentApacheFile -Destination $genericApacheFile
                }
            } catch {
                Write-Debug 'Failed to configure the Apache.conf file'
            }
        } finally {
            if (-Not($keepTemporaryFile)) {
                try {
                    Remove-Item -Path $temporaryFile
                } catch {
                    Write-Debug 'Failed to remove temporary file'
                }
            }
        }
    }
    end {
    }
}