Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ backend/mirror.gif
backend/mirror.png
backend/mirror.jpg
backend/GeoIP.dat
include/download-instructions/releases.json
node_modules/
/test-results/
/playwright-report/
Expand Down
134 changes: 131 additions & 3 deletions include/download-instructions/windows-downloads.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,131 @@
<p>
You can find the downloads for Windows on the <a href="https://windows.php.net/download#php-<?= $version; ?>">dedicated downloads page</a>.
</p>
<?php
$cacheFile = (sys_get_temp_dir() ?? __DIR__) . '/releases.json';
$releasesUrl = 'https://downloads.php.net/~windows/releases/releases.json';
$fallbackReleasesUrl = 'https://downloads.internal.php.net/~windows/releases/releases.json';
$maxAge = 3600;

$needFetch = true;
if (file_exists($cacheFile)) {
$mtime = @filemtime($cacheFile) ?: 0;
if (time() - $mtime < $maxAge) {
$needFetch = false;
}
}

if ($needFetch) {
$context = stream_context_create([
'http' => [
'header' => "User-Agent: web-php/1.1",
'timeout' => 5,
],
'ssl' => [
'allow_self_signed' => true,
'verify_peer_name' => false,
],
]);
$json = @file_get_contents($releasesUrl, false, $context);
if ($json === false || $json === null) {
$json = @file_get_contents($fallbackReleasesUrl, false, $context);
}
if ($json !== false && $json !== null) {
$tmp = $cacheFile . '.tmp';
if (@file_put_contents($tmp, $json) !== false) {
@rename($tmp, $cacheFile);
}
}
}

$dataStr = @file_get_contents($cacheFile);
$releases = $dataStr ? json_decode($dataStr ?? $json, true) : null;

if (!is_array($releases)) {
echo '<p>Windows release index is temporarily unavailable.</p>';
if (file_exists($cacheFile)) {
@unlink($cacheFile);
}
return;
}

if (!isset($releases[$version]) || !is_array($releases[$version])) {
echo '<p>No Windows builds found for PHP ' . htmlspecialchars($version) . '.</p>';
return;
}

$verBlock = $releases[$version];
$fullVersion = isset($verBlock['version']) ? $verBlock['version'] : $version;

$baseDownloads = 'https://downloads.php.net/~windows/releases/';

function ws_build_label(string $k, array $entry): string {
$tool = 'VS';
if (strpos($k, 'vs17') !== false) { $tool .= '17'; }
elseif (strpos($k, 'vs16') !== false) { $tool .= '16'; }
elseif (strpos($k, 'vc15') !== false) { $tool = 'VC15'; }

$arch = (strpos($k, 'x64') !== false) ? 'x64' : ((strpos($k, 'x86') !== false) ? 'x86' : '');
$ts = (strpos($k, 'nts') !== false) ? 'Non Thread Safe' : 'Thread Safe';
if (strncmp($k, 'nts-', 4) === 0) {
$ts = 'Non Thread Safe';
} elseif (strncmp($k, 'ts-', 3) === 0) {
$ts = 'Thread Safe';
}

$mtime = isset($entry['mtime']) ? strtotime($entry['mtime']) : 0;
$mt = $mtime ? gmdate('Y-M-d H:i:s', $mtime) : '';
return trim(($tool ? $tool . ' ' : '') . ($arch ? $arch . ' ' : '') . $ts . ($mt ? ' <span class="time">' . $mt . ' UTC</span>' : ''));
}

echo '<h3>PHP ' . htmlspecialchars($version) . ' (' . $fullVersion . ')</h3>';

if (!empty($verBlock['source']['path'])) {
echo '<p><strong><a href="' . $baseDownloads . $verBlock['source']['path'] . '">Download source code</a></strong> <span class="size">' . $verBlock['source']['size'] ?? '' . '</span></p>';
}
if (!empty($verBlock['test_pack']['path'])) {
echo '<p><strong><a href="' . $baseDownloads . $verBlock['test_pack']['path'] . '">Download tests package (phpt)</a></strong> <span class="size">' . $verBlock['test_pack']['size'] ?? '' . '</span></p>';
}

$buckets = [
'nts-x64' => [],
'ts-x64' => [],
'nts-x86' => [],
'ts-x86' => [],
];

$package_names = [
'zip' => 'Zip',
'debug_pack' => 'Debug Pack',
'devel_pack' => 'Development package (SDK to develop PHP extensions)',
];

foreach ($verBlock as $k => $entry) {
if (!is_array($entry)) { continue; }
if (in_array($k, ['version', 'source', 'test_pack'], true)) { continue; }

$isNts = (strncmp($k, 'nts-', 4) === 0) || (strpos($k, 'nts') !== false);
$arch = (strpos($k, 'x64') !== false) ? 'x64' : ((strpos($k, 'x86') !== false) ? 'x86' : '');
$bucketKey = ($isNts ? 'nts' : 'ts') . '-' . ($arch !== '' ? $arch : 'other');
if (!isset($buckets[$bucketKey])) { $bucketKey = 'other'; }
$buckets[$bucketKey][] = [$k, $entry];
}

foreach (['nts-x64', 'ts-x64', 'nts-x86', 'ts-x86'] as $bk) {
foreach ($buckets[$bk] as [$k, $entry]) {
$label = ws_build_label($k, $entry);
if ($label === '') { continue; }

echo '<div class="win-build">';
echo '<h4>' . $label . '</h4>';

foreach(['zip', 'debug_pack', 'devel_pack'] as $type) {
if (!empty($entry[$type]['path'])) {
$p = $entry[$type]['path'];
echo '<p><strong><a href="' . $baseDownloads . $p . '">' . $package_names[$type] . '</a></strong> <span class="size">' . $entry[$type]['size'] . '</span><br>';
echo '<span class="sha256"><strong>sha256:</strong> ' . $entry[$type]['sha256'] ?? '' . '</span></p>';
}
}

echo '</div>';
}
}

?>
2 changes: 1 addition & 1 deletion include/version.inc
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function show_source_releases()
</li>
<?php endforeach; ?>
<li>
<a href="https://windows.php.net/download#php-<?php echo urlencode($mver); ?>">
<a href="/downloads.php?os=windows&osvariant=windows-downloads&version=<?php echo urlencode($mver); ?>">
Windows downloads
</a>
</li>
Expand Down
25 changes: 25 additions & 0 deletions styles/code-syntax.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,28 @@ button.copy-to-clipboard-button:hover {
border-color: var(--dark-magenta-color) !important;
}

.win-build {
background: rgba(39, 40, 44, 0.05);
margin: 20px 0;
padding: 20px 20px 10px 20px;
border-top: 4px solid var(--dark-blue-color, #4F5B93);
}

.win-build h4 {
line-height: 1.5rem;
margin-bottom: 0;
}

.instructions .size {
background: #dcdcdc;
border-radius: 6px;
padding: 3px 7px;
font-size: 0.8rem;
font-weight: 600;
}

.instructions .time {
color: #555;
font-size: 0.8rem;
display: block;
}