Author: Shivam Mathur (shivammathur)
Date: 2025-07-30T20:22:22+05:30

Commit: 
https://github.com/php/web-php/commit/9e571e931cc23ed1ad7f19bbd7c32de5ff8f1b26
Raw diff: 
https://github.com/php/web-php/commit/9e571e931cc23ed1ad7f19bbd7c32de5ff8f1b26.diff

Fix instructions for windows-native

Changed paths:
  A  install/windows.ps1
  M  include/download-instructions/windows-native.php


Diff:

diff --git a/include/download-instructions/windows-native.php 
b/include/download-instructions/windows-native.php
index 6477a4c715..52ef1412ea 100644
--- a/include/download-instructions/windows-native.php
+++ b/include/download-instructions/windows-native.php
@@ -2,5 +2,5 @@
 On the command line, run the following commands:
 </p>
 <pre><code class="language-powershell line-numbers">
-powershell -c "irm https://php.net/downloads/install.ps1 <?= $version; ?> | 
iex"
+powershell -c "& ([ScriptBlock]::Create((irm 
'https://php.net/install/windows.ps1'))) -Version <?= $version; ?>"
 </code></pre>
diff --git a/install/windows.ps1 b/install/windows.ps1
new file mode 100644
index 0000000000..d6ac1bad52
--- /dev/null
+++ b/install/windows.ps1
@@ -0,0 +1,228 @@
+<#
+.SYNOPSIS
+Downloads and sets up a specified PHP version on Windows.
+
+.PARAMETER Version
+Major.minor or full version (e.g., 7.4 or 7.4.30).
+
+.PARAMETER Path
+Destination directory (defaults to C:\php<Version>).
+
+.PARAMETER Arch
+Architecture: x64 or x86 (default: x64).
+
+.PARAMETER ThreadSafe
+ThreadSafe: download Thread Safe build (default: $False).
+
+.PARAMETER Timezone
+date.timezone string for php.ini (default: 'UTC').
+#>
+
+[CmdletBinding()]
+param(
+    [Parameter(Mandatory = $true, Position=0)]
+    [ValidatePattern('^\d+(\.\d+)?(\.\d+)?((alpha|beta|RC)\d*)?$')]
+    [string]$Version,
+    [Parameter(Mandatory = $false, Position=1)]
+    [string]$Path = "C:\php$Version",
+    [Parameter(Mandatory = $false, Position=2)]
+    [ValidateSet("x64", "x86")]
+    [string]$Arch = "x64",
+    [Parameter(Mandatory = $false, Position=3)]
+    [bool]$ThreadSafe = $False,
+    [Parameter(Mandatory = $false, Position=4)]
+    [string]$Timezone = 'UTC'
+)
+
+Function Get-File {
+    param (
+        [Parameter(Mandatory = $true, Position=0)]
+        [ValidateNotNullOrEmpty()]
+        [string] $Url,
+        [Parameter(Mandatory = $false, Position=1)]
+        [string] $FallbackUrl,
+        [Parameter(Mandatory = $false, Position=2)]
+        [string] $OutFile = '',
+        [Parameter(Mandatory = $false, Position=3)]
+        [int] $Retries = 3,
+        [Parameter(Mandatory = $false, Position=4)]
+        [int] $TimeoutSec = 0
+    )
+
+    for ($i = 0; $i -lt $Retries; $i++) {
+        try {
+            if($OutFile -ne '') {
+                Invoke-WebRequest -Uri $Url -OutFile $OutFile -TimeoutSec 
$TimeoutSec
+            } else {
+                Invoke-WebRequest -Uri $Url -TimeoutSec $TimeoutSec
+            }
+            break;
+        } catch {
+            if ($i -eq ($Retries - 1)) {
+                if($FallbackUrl) {
+                    try {
+                        if($OutFile -ne '') {
+                            Invoke-WebRequest -Uri $FallbackUrl -OutFile 
$OutFile -TimeoutSec $TimeoutSec
+                        } else {
+                            Invoke-WebRequest -Uri $FallbackUrl -TimeoutSec 
$TimeoutSec
+                        }
+                    } catch {
+                        throw "Failed to download the file from $Url and 
$FallbackUrl"
+                    }
+                } else {
+                    throw "Failed to download the file from $Url"
+                }
+            }
+        }
+    }
+}
+
+Function Get-Semver {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory = $true, Position=0)]
+        [ValidateNotNull()]
+        [ValidatePattern('^\d+\.\d+$')]
+        [string]$Version
+    )
+    $releases = Get-File -Url 
"https://downloads.php.net/~windows/releases/releases.json"; | ConvertFrom-Json
+    $semver = $releases.$Version.version
+    if($null -eq $semver) {
+        $semver = (Get-File -Url 
"https://downloads.php.net/~windows/releases/archives";).Links |
+                Where-Object { $_.href -match "php-($Version.[0-9]+).*" } |
+                ForEach-Object { $matches[1] } |
+                Sort-Object { [System.Version]$_ } -Descending |
+                Select-Object -First 1
+    }
+    if($null -eq $semver) {
+        throw "Unsupported PHP version: $Version"
+    }
+    return $semver
+}
+
+Function Get-VSVersion {
+    param(
+        [Parameter(Mandatory = $true, Position=0)]
+        [ValidateNotNull()]
+        [ValidatePattern('^\d+\.\d+$')]
+        [string]$Version
+    )
+    $map = @{
+        '5.2' = 'VC6'
+        '5.3' = 'VC9'; '5.4' = 'VC9'
+        '5.5' = 'VC11'; '5.6' = 'VC11'
+        '7.0' = 'VC14'; '7.1' = 'VC14'
+        '7.2' = 'VC15'; '7.3' = 'VC15'; '7.4' = 'vc15'
+        '8.0' = 'vs16'; '8.1' = 'vs16'; '8.2' = 'vs16'; '8.3' = 'vs16'
+        '8.4' = 'vs17'; '8.5' = 'vs17'
+    }
+
+    if ($map.ContainsKey($Version)) {
+        return $map[$Version]
+    }
+    throw "Unsupported PHP version: $Version"
+}
+
+Function Get-ReleaseType {
+    param(
+        [Parameter(Mandatory = $true, Position=0)]
+        [ValidateNotNull()]
+        [ValidatePattern('^\d+(\.\d+)?(\.\d+)?((alpha|beta|RC)\d*)?$')]
+        [string]$Version
+    )
+    if ($Version -match "[a-zA-Z]") {
+        return "qa"
+    } else {
+        return "releases"
+    }
+}
+
+Function Get-PhpFromUrl {
+    param(
+        [Parameter(Mandatory = $true, Position=0)]
+        [ValidateNotNull()]
+        [ValidatePattern('^\d+\.\d+$')]
+        [string]$Version,
+        [Parameter(Mandatory = $true, Position=1)]
+        [ValidateNotNull()]
+        [ValidatePattern('^\d+(\.\d+)?(\.\d+)?((alpha|beta|RC)\d*)?$')]
+        [string]$Semver,
+        [Parameter(Mandatory = $false, Position=2)]
+        [ValidateSet("x64", "x86")]
+        [string]$Arch = "x64",
+        [Parameter(Mandatory = $false, Position=3)]
+        [bool]$ThreadSafe = $false,
+        [Parameter(Mandatory = $true, Position=4)]
+        [ValidateNotNull()]
+        [ValidateLength(1, [int]::MaxValue)]
+        [string]$OutFile
+    )
+    $vs = Get-VSVersion $Version
+    $ts = if ($ThreadSafe) { "ts" } else { "nts" }
+    $zipName = if ($ThreadSafe) { "php-$Semver-Win32-$vs-$Arch.zip" } else { 
"php-$Semver-$ts-Win32-$vs-$Arch.zip" }
+    $type = Get-ReleaseType $Version
+
+    $base = "https://downloads.php.net/~windows/$type";
+    try {
+        Get-File -Url "$base/$zipName" -OutFile $OutFile
+    } catch {
+        try {
+            Get-File -Url "$base/archives/$zipName" -OutFile $OutFile
+        } catch {
+            throw "Failed to download PHP $Semver."
+        }
+    }
+}
+
+$tempFile = [IO.Path]::ChangeExtension([IO.Path]::GetTempFileName(), '.zip')
+try {
+    if ($Version -match "^\d+\.\d+$") {
+        $Semver = Get-Semver $Version
+    } else {
+        $Semver = $Version
+        $Semver -match '^(\d+\.\d+)' | Out-Null
+        $Version = $Matches[1]
+    }
+
+    if (-not (Test-Path $Path)) {
+        try {
+            New-Item -ItemType Directory -Path $Path -ErrorAction Stop | 
Out-Null
+        } catch {
+            throw "Failed to create directory $Path. $_"
+        }
+    } else {
+        $files = Get-ChildItem -Path $Path
+        if ($files.Count -gt 0) {
+            throw "The directory $Path is not empty. Please provide an empty 
directory."
+        }
+    }
+
+    if($Version -lt '5.5' -and $Arch -eq 'x64') {
+        $Arch = 'x86'
+        Write-Host "PHP version $Version does not support x64 architecture on 
Windows. Using x86 instead."
+    }
+
+    Write-Host "Downloading PHP $Semver to $Path"
+    Get-PhpFromUrl $Version $Semver $Arch $ThreadSafe $tempFile
+    Expand-Archive -Path $tempFile -DestinationPath $Path -Force -ErrorAction 
Stop
+
+    $phpIniProd = Join-Path $Path "php.ini-production"
+    if(-not(Test-Path $phpIniProd)) {
+        $phpIniProd = Join-Path $Path "php.ini-recommended"
+    }
+    $phpIni = Join-Path $Path "php.ini"
+    Copy-Item $phpIniProd $phpIni -Force
+    $extensionDir = Join-Path $Path "ext"
+    (Get-Content $phpIni) -replace '^extension_dir = "./"', "extension_dir = 
`"$extensionDir`"" | Set-Content $phpIni
+    (Get-Content $phpIni) -replace ';\s?extension_dir = "ext"', "extension_dir 
= `"$extensionDir`"" | Set-Content $phpIni
+    (Get-Content $phpIni) -replace ';\s?date.timezone =', "date.timezone = 
`"$Timezone`"" | Set-Content $phpIni
+
+    Write-Host "PHP $Semver downloaded to $Path"
+} catch {
+    Write-Error $_
+    Exit 1
+} finally {
+    if (Test-Path $tempFile) {
+        Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
+    }
+}

Reply via email to