data types and standard library

Archives: Zip and Phar

Archives package files together. PHP projects use ZIP files for exports, downloads, imports, backups, generated reports, and receiving batches from other systems. Phar archives are PHP application archives, often used for distributing command-line tools.

Archive handling crosses a filesystem boundary, so safe filenames, size limits, extraction paths, and extension availability matter.

Check ZIP support

ZipArchive comes from the ZIP extension, which may not be installed everywhere.

PHP example
<?php

declare(strict_types=1);

echo class_exists(ZipArchive::class) ? 'ZIP available' : 'ZIP missing';
echo PHP_EOL;

If your application creates or reads ZIP files, make this a deployment requirement.

Create a ZIP archive

Use ZipArchive to add files under controlled archive names.

PHP example
<?php

declare(strict_types=1);

$path = tempnam(sys_get_temp_dir(), 'zip_');
$zip = new ZipArchive();

if ($zip->open($path, ZipArchive::OVERWRITE) !== true) {
    throw new RuntimeException('Could not create zip file.');
}

$zip->addFromString('readme.txt', 'Archive created');
$zip->close();

$zip->open($path);
echo $zip->getFromName('readme.txt') . PHP_EOL;
$zip->close();

unlink($path);

// Prints:
// Archive created

This example writes to a temporary file, reads the archived entry back, and removes the archive.

Validate archive entry names

Archive entries are paths inside the archive. Do not allow absolute paths, parent-directory traversal, or empty names.

PHP example
<?php

declare(strict_types=1);

function safeArchiveEntryName(string $name): string
{
    $name = str_replace('\\', '/', trim($name));

    if ($name === '' || str_starts_with($name, '/') || str_contains($name, '../') || str_contains($name, "\0")) {
        throw new InvalidArgumentException('Archive entry name is invalid.');
    }

    return $name;
}

echo safeArchiveEntryName('reports/daily.txt') . PHP_EOL;

// Prints:
// reports/daily.txt

This matters when archive contents are based on user uploads, database values, or external systems.

List archive contents before extraction

Inspect entries before extracting an uploaded archive.

PHP example
<?php

declare(strict_types=1);

$path = tempnam(sys_get_temp_dir(), 'zip_');
$zip = new ZipArchive();
$zip->open($path, ZipArchive::OVERWRITE);
$zip->addFromString('reports/daily.txt', 'Daily report');
$zip->addFromString('reports/weekly.txt', 'Weekly report');
$zip->close();

$zip->open($path);

for ($index = 0; $index < $zip->numFiles; $index++) {
    echo $zip->getNameIndex($index) . PHP_EOL;
}

$zip->close();
unlink($path);

// Prints:
// reports/daily.txt
// reports/weekly.txt

Do validation before extraction: entry names, total file count, total uncompressed size, allowed extensions, and destination path.

Be careful with extraction

Extracting an archive can overwrite files or write outside the intended directory if entry names are unsafe. Prefer extracting only validated entries to a controlled temporary directory.

PHP example
<?php

declare(strict_types=1);

function canExtractEntry(string $entryName): bool
{
    try {
        safeArchiveEntryName($entryName);
        return true;
    } catch (InvalidArgumentException) {
        return false;
    }
}

echo canExtractEntry('../config.php') ? 'safe' : 'blocked';
echo PHP_EOL;

// Prints:
// blocked

Archive upload features need stricter review than ordinary single-file uploads.

Understand where Phar fits

Phar packages PHP code and resources into a single archive. Tools such as PHPStan, Psalm, and Composer plugins may be distributed as Phar files.

PHP example
<?php

declare(strict_types=1);

echo class_exists(Phar::class) ? 'Phar available' : 'Phar missing';
echo PHP_EOL;

Creating Phar files has runtime configuration requirements, and running unknown Phar files is a security decision. Treat them as executable code, not as ordinary document archives.

What to remember

ZIP is for packaging files. Phar is for packaging PHP applications. Validate archive entry names, inspect contents before extraction, use controlled temporary directories, check required extensions, and treat uploaded archives as high-risk input.

Practice

Task: Create and inspect a report ZIP

Write a small example that creates a ZIP archive and inspects its contents.

Requirements

  • Use declare(strict_types=1);.
  • Create a temporary ZIP archive with ZipArchive.
  • Add two report files with safe archive entry names.
  • Close and reopen the archive.
  • List the archived file names.
  • Read one archived file back.
  • Delete the temporary archive at the end.
  • Show one invalid archive entry name by catching the exception.
  • Include expected output comments for the deterministic lines.

The example should validate archive entry names before adding them.

Show solution
PHP example
<?php

declare(strict_types=1);

function safeArchiveEntryName(string $name): string
{
    $name = str_replace('\\', '/', trim($name));

    if ($name === '' || str_starts_with($name, '/') || str_contains($name, '../') || str_contains($name, "\0")) {
        throw new InvalidArgumentException('Archive entry name is invalid.');
    }

    return $name;
}

$path = tempnam(sys_get_temp_dir(), 'reports_');
$zip = new ZipArchive();

if ($zip->open($path, ZipArchive::OVERWRITE) !== true) {
    throw new RuntimeException('Could not create ZIP archive.');
}

$zip->addFromString(safeArchiveEntryName('reports/daily.txt'), 'Daily total: 120');
$zip->addFromString(safeArchiveEntryName('reports/weekly.txt'), 'Weekly total: 560');
$zip->close();

$zip->open($path);

for ($index = 0; $index < $zip->numFiles; $index++) {
    echo $zip->getNameIndex($index) . PHP_EOL;
}

echo $zip->getFromName('reports/daily.txt') . PHP_EOL;
$zip->close();
unlink($path);

try {
    safeArchiveEntryName('../config.php');
} catch (InvalidArgumentException $exception) {
    echo $exception->getMessage() . PHP_EOL;
}

// Prints:
// reports/daily.txt
// reports/weekly.txt
// Daily total: 120
// Archive entry name is invalid.

The solution validates names before adding files, inspects the finished archive, and cleans up the temporary ZIP. The same validation habit matters even more when extracting archives supplied by users.