Unzip with PHP on Shared Hosting: A Safer Way to Extract ZIP Files

Illustrated infographic summarizing: Unzip with PHP on Shared Hosting: A Safer Way to Extract ZIP Files

By Greg Nowak. Updated 7 September 2026.

Uploading one ZIP archive can save time when a website release contains thousands of small files. If your shared hosting has no SSH access and its file manager cannot extract archives, PHP can handle the job.

The extraction itself is only one step. For a business website, the useful outcome is a complete release, a checked customer journey and a clear way back if something fails. Here is how to organise that work without overwriting the live site halfway through an update.

Choose the simplest available route

Check the hosting panel before adding a script. Its extraction feature may be sufficient, provided you can choose a separate destination. For an agency handover, record who uploads the package, who activates it and who checks the result.

Hosting situation Practical approach Before proceeding
File manager supports ZIP extraction Use the hosting panel Select a fresh release directory
No SSH or panel extraction; PHP ZIP is available Use a temporary PHP helper Protect access and staging files
PHP ZIP is missing Ask the host to enable it or extract the package Confirm support for the site's PHP runtime
Large archives or repeated timeouts Ask for a host-managed or command-line operation Check disk quota and execution limits
Choose the extraction method around the hosting account's actual capabilities.

Check PHP, space and access first

With shell access, check ZIP support using:

php -m | grep -i zip

The command-line runtime may differ from the website's PHP configuration. For a browser helper, check the hosting panel or temporarily run var_dump(class_exists('ZipArchive')); behind access protection, then delete it. The PHP installation documentation explains the extension requirements; shared-hosting changes usually belong with the host.

Use a supported PHP branch with current patches. On 7 September 2026, PHP 8.2–8.5 remain supported; 8.2 and 8.3 receive security fixes only. PHP 8.2 support ends on 31 December 2026, according to the official support schedule.

Allow space for the ZIP, expanded release and previous release, plus working headroom. Check the account's file-count quota too. A small compressed file can contain many entries.

Before uploading the helper, protect its URL with hosting-panel authentication over HTTPS or an appropriate IP restriction. An obscure filename is insufficient. Keep the archive and staging directory outside the public document root where possible; otherwise block web access to both. Filesystem permissions alone do not stop the web server serving files it can read.

A bounded ZipArchive helper for trusted releases

This example is for a team-produced, reviewed archive on Linux shared hosting. Place it in the protected maintenance workspace described above. It uses fixed paths, rejects questionable entry names and requires a new destination. Adjust the example limits to the expected package.

<?php
$archive = __DIR__ . '/site-release.zip';
$target = __DIR__ . '/release-2026-09-07';
$maxEntries = 2000;
$maxBytes = 200 * 1024 * 1024; // 200 MiB, declared size

if (!class_exists('ZipArchive')) {
    exit('ZIP support is unavailable.');
}
if (!is_file($archive)) {
    exit('Archive not found.');
}
if (file_exists($target) || is_link($target)) {
    exit('Use a new release directory.');
}

$zip = new ZipArchive();
$result = $zip->open(
    $archive,
    ZipArchive::RDONLY | ZipArchive::CHECKCONS
);
if ($result !== true) {
    exit('Could not open archive. Error code: ' . $result);
}

$oldUmask = umask(0077);
try {
    if ($zip->numFiles > $maxEntries) {
        throw new RuntimeException('Too many archive entries.');
    }
    $total = 0;
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $stat = $zip->statIndex($i);
        if ($stat === false) {
            throw new RuntimeException('Cannot inspect entry.');
        }
        $path = $stat['name'];
        if ($path === '' || $path[0] === '/'
            || str_contains($path, '\\')
            || str_contains($path, ':')
            || preg_match('/[\x00-\x1F\x7F]/', $path)
            || preg_match('#(^|/)\.{1,2}(/|$)#', $path)) {
            throw new RuntimeException('Unsupported archive path.');
        }
        $size = $stat['size'];
        if (!is_int($size) || $size < 0
            || $size > $maxBytes - $total) {
            throw new RuntimeException('Declared size exceeds limit.');
        }
        $total += $size;
    }
    if (!mkdir($target, 0700)) {
        throw new RuntimeException('Cannot create release directory.');
    }
    if (!$zip->extractTo($target)) {
        throw new RuntimeException('Extraction failed; inspect partial release.');
    }
    echo 'Extracted. Validate before activation.';
} catch (RuntimeException $e) {
    http_response_code(500);
    echo htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
} finally {
    umask($oldUmask);
    $zip->close();
}

The strict !== true check matters because ZipArchive::open() can return an integer error code. Consistency checking does not establish that the contents are trustworthy.

The size check is preflight validation, not a hard extraction quota. statIndex() supplies archive metadata; this helper does not count bytes as they are written. It is unsuitable for processing arbitrary customer uploads or defending against hostile archives.

The restrictive umask keeps staging permissions narrow. PHP documents that extractTo() does not restore original permissions. Before activation, apply the host's required file and directory permissions, including any executable scripts. Avoid blanket chmod 777 fixes.

Validate the release before switching traffic

Check the directory layout: an extra parent folder can put index.php one level below where the site expects it. Confirm dependencies, environment configuration and writable directories. Keep uploads, secrets and generated content outside the replaceable package wherever practical.

Agree the activation method before starting. Some hosts allow a document-root change; others require a maintenance window and directory changes. Do not assume switching will be instantaneous. Keep the previous release and test the homepage, a key enquiry or purchase journey, and the error log after activation.

If the release changes the database, restoring old files may not restore the application. Plan compatible migrations or a tested database recovery procedure, accounting for orders and submissions received since the backup.

Close the maintenance job properly

A timeout or failed extraction can leave a partial directory. Inspect it, remove the failed release and retry into a fresh destination. Delete the temporary helper and uploaded ZIP after the job; record the release, checks and rollback location.

If these deployments depend on one person's memory, Greg can help document and coordinate a repeatable release workflow that fits your hosting, team and budget.

Related on GrN.dk

Need help with this kind of work?

Discuss your website deployment workflow Get in touch with Greg.

Sources

Seneste artikler

Sådan automatiserer danske virksomheder Gmail og Microsoft 365 med hurtig sortering, begrænsede rettigheder og menneskelig godkendelse.

Samme kunde på flere kort i HubSpot? Se, hvordan CVR-match, AI-forslag og menneskelig godkendelse kan bruges til at rydde op med styr på felter, relationer og kundehistorik.

Få en ugentlig marketingrapport fra GA4 og Google Ads med kontrollerede beregninger, tydelige dataforbehold og et kort AI-udkast, der hjælper jer på mandagsmødet.

Brug AI til webshoppens alt-tekster med en overskuelig pilot: kortlæg billederne, få danske forslag, og kontrollér resultatet i WordPress og WooCommerce.

AI-baseret ticketanalyse kan afsløre gentagne klager, produktfejl og huller i dokumentationen – uden at virksomheden behøver endnu en chatbot.

OpenSSH 10 fjerner DSA og advarer om nøgleudveksling, der ikke er post-kvantesikker. Her får du en metode til at afgrænse SFTP-oprydningen uden at svække alle SSH-forbindelser.

Botforespørgsler overstiger nu menneskelig webtrafik. Lær at auditere AI-crawlere, fastsætte regler på stiniveau, håndhæve robots.txt og måle det forretningsmæssige afkast.

Cloudflares Tunnel-opdateringer fra 2026 forbedrer kortlægning, overvågning af replikaer, logstreaming og overdragelse – men synliggør samtidig svagt ejerskab og mangelfuld praksis for failover og logging.

Sådan bruger du AI til mødenoter og opfølgning, mens faste regler beskytter CRM-data, kundematch og pipeline mod fejl og forhastede ændringer.

Drupal 10 når end of life den 9. december 2026. Brug denne praktiske kortlægning til at afgrænse arbejdet med Drupal 11-parathed, Composer-efterslæb, moduler og custom code.