Build and inspect a PHP PHAR archive with phar8.3
By the end of this guide you will have a compressed PHAR containing a small PHP application, a repeatable way to inspect it, and an extracted copy for testing. The commands target Ubuntu's installed PHP 8.3.6 CLI and its phar8.3 command. Allow about 10 minutes if PHP and your input files are already available.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
You need a shell, PHP 8.3 CLI, the PHAR command, and a directory of files you are willing to package. Check the installation first:
php8.3 -v
phar8.3 version
phar8.3 help-list
On this system, phar8.3 version reports PHP 8.3.6, PHAR extension 8.3.6, API 1.1.1, gzip support, and no bzip2 support. The aliases phar, phar.default, phar.phar, phar.phar.default, phar.phar8.3, and phar8.3.phar resolve to the same installed tool.
1. Make a small input tree
Use a temporary workspace while learning. The example creates one executable PHP entry point, a text file, and a file in a subdirectory. Replace these paths with your project directory when you are ready to package a real release.
work=$(mktemp -d /tmp/phar-demo.XXXXXX)
mkdir -p "$work/app/sub"
printf '%s\n' '<?php echo "hello from the archive\\n";' > "$work/app/index.php"
printf '%s\n' 'Files bundled in this release.' > "$work/app/readme.txt"
printf '%s\n' 'temporary input' > "$work/app/sub/temporary.txt"
Keep the variable in the current shell. It points to the workspace that later commands use.
2. Create the PHAR
PHAR executables are read-only by default. This is a deliberate PHP safety setting, so an ordinary create or update command stops with Creating phar files is disabled by ini setting 'phar.readonly'. For a controlled build, override the setting for this process only:
php8.3 -d phar.readonly=0 /usr/bin/phar8.3 pack \
-f "$work/app.phar" \
-a demo-app \
-c gzip \
"$work/app"
-f names the output archive, -a stores the alias, and -c gzip compresses files. The final argument is the input directory. The command adds its contents, including subdirectories, and writes the archive in the current workspace.
To select only matching input files, add -i. This is an inclusion filter, not an exclusion filter: when it is present, only files matching the regular expression are packed.
php8.3 -d phar.readonly=0 /usr/bin/phar8.3 pack \
-f "$work/php-only.phar" \
-i '.*\.php$' \
"$work/app"
Checkpoint
Both commands should exit without an error and create their named files. Confirm that the output exists before moving on:
ls -lh "$work"/*.phar
3. Inspect contents before extracting or running them
Listing is the safest first check. It shows archive paths without changing the archive or the filesystem:
phar8.3 list -f "$work/app.phar"
phar8.3 tree -f "$work/app.phar"
phar8.3 info -f "$work/app.phar"
list prints each entry, tree shows the directory structure, and info reports the alias, hash type, entry count, compression counts, sizes, and stub size. On the tested build, the gzip archive reported SHA-256 and all three input files as gzip-compressed.
Do not confuse an archive listing with a security decision. A PHAR can contain PHP code and a loader stub. Review files from an untrusted source before executing or extracting them.
4. Extract to a separate directory
Extraction defaults to the current directory, which is an easy way to scatter files into the wrong place. Always provide an explicit destination:
mkdir "$work/extracted"
phar8.3 extract -f "$work/app.phar" "$work/extracted"
find "$work/extracted" -type f -printf '%P\n' | sort
The verification output should include index.php, readme.txt, and sub/temporary.txt. Extraction writes files, so treat the destination as disposable when testing an archive. If you extracted into the wrong temporary directory, remove that directory using your normal file-management procedure and repeat with a fresh destination. Do not point this example at a live application directory.
5. Change an archive only on a copy
add, delete, compress, meta-set, sign, and stub-set modify the archive. Copy it first, then use the same one-process readonly override:
cp "$work/app.phar" "$work/edit.phar"
php8.3 -d phar.readonly=0 /usr/bin/phar8.3 delete \
-f "$work/edit.phar" -e 'sub/temporary.txt'
phar8.3 list -f "$work/edit.phar"
The final listing should no longer contain sub/temporary.txt. The original remains unchanged. The entry name must match the path inside the archive, not the source path on disk. Deletion is irreversible for that copy unless you restore the copy or rebuild the archive.
6. Handle signatures and stubs deliberately
sign -h sha256 selects a supported hash algorithm. It does not establish that a downloaded archive came from a trusted publisher. Verify a publisher's expected digest or public key through a separate trusted channel before use.
php8.3 -d phar.readonly=0 /usr/bin/phar8.3 sign \
-f "$work/edit.phar" -h sha256
phar8.3 info -f "$work/edit.phar" | grep -E 'Hash-type|Hash:'
Use stub-get with an explicit output file when you need to review a stub, rather than relying on its stdout behaviour:
phar8.3 stub-get -f "$work/app.phar" -s "$work/stub.php"
sed -n '1,8p' "$work/stub.php"
A PHAR stub is PHP and must end with __HALT_COMPILER();. Treat changes made by stub-set as code changes: review them, test them, and keep the original archive until the replacement has been verified.
Done means
phar8.3 versionidentifies the installed PHAR and PHP versions.packcreated the intended archive with a process-local readonly override.list,tree, andinfomatch the files and compression you expected.extractwas tested in an explicit, separate destination.- Any destructive edit was made to a copy, and signatures or stubs were reviewed separately from trust decisions.