Skip to content

First stab at a prototype for revamping the download instructions page #1287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/download-instructions/windows-native.php
Original file line number Diff line number Diff line change
Expand Up @@ -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; ?>"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be https://www.php.net/install/windows.ps1 to avoid redirects. I'm also thinking that instead of install, the directory should be include/download-instructions, like the other files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed the domain part of this now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can change the directory to include/download-instructions, my thought on adding the install directory was it will be short and would have recall value.

</code></pre>
228 changes: 228 additions & 0 deletions install/windows.ps1
Original file line number Diff line number Diff line change
@@ -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
}
}