WordPress Package Parser#
A small framework-agnostic PHP package for reading metadata from WordPress plugin, WordPress theme, and generic ZIP archives.
It extracts useful package data from plugin headers, theme style.css headers, and WordPress.org-style readme.txt files, then returns a normalized metadata array you can use in Laravel, plain PHP, package upload flows, or custom update systems.
Features#
- Parse WordPress plugin ZIP archives.
- Parse WordPress theme ZIP archives.
- Parse WordPress.org-style
readme.txtmetadata and sections. - Convert readme sections to HTML with Parsedown.
- Detect generic ZIP archives and generate a stable slug from the uploaded filename.
- Use PHP
ZipArchivewhen available, with a bundled PclZip fallback. - Work without Laravel service providers, facades, or framework bootstrapping.
Requirements#
- PHP 7.4 or newer
- Composer
The package can use the PHP zip extension when available. If ZipArchive is not loaded, it falls back to the bundled PclZip implementation.
Installation#
composer require hungnth/wordpress-package-parserQuick Start#
<?php
require __DIR__ . '/vendor/autoload.php';
use WpPackageParser\PackageMetadata;
$package = PackageMetadata::fromArchive('/absolute/path/to/my-plugin.zip', 'my-plugin.zip');$metadata = $package->getMetadata();If you only need the metadata array, use parse():
<?php
use WpPackageParser\PackageMetadata;
$metadata = PackageMetadata::parse('/absolute/path/to/my-plugin.zip', 'my-plugin.zip');Laravel Usage#
No Laravel-specific integration is required. Use it like any other Composer package:
<?php
use WpPackageParser\PackageMetadata;
$package = PackageMetadata::fromArchive( $this->uploadedFilePath($file), $storedFileName);
$metadata = $package->getMetadata();Or return the array directly:
$metadata = PackageMetadata::parse( $this->uploadedFilePath($file), $storedFileName);API#
PackageMetadata::fromArchive()#
PackageMetadata::fromArchive(string $filepath, ?string $originalFilename = null): PackageMetadataParses a ZIP archive and returns a parsed package metadata object.
$filepathis the readable path to the ZIP archive.$originalFilenameis optional and is used when generating the slug for generic ZIP archives.- Throws
WpPackageParser\InvalidPackageExceptionwhen the file cannot be parsed as a ZIP package.
PackageMetadata::parse()#
PackageMetadata::parse(string $filepath, ?string $originalFilename = null): arrayConvenience method that parses the archive and returns the metadata array directly.
$package->getMetadata()#
$package->getMetadata(): arrayReturns the parsed metadata array.
Returned Metadata#
Plugin archives can return keys such as:
[ 'name' => 'My Plugin', 'version' => '1.2.3', 'homepage' => 'https://example.com/my-plugin', 'author' => 'Jane Developer', 'author_homepage' => 'https://example.com', 'requires_php' => '7.4', 'depends' => ['dependency-one', 'dependency-two'], 'provides' => ['virtual-one'], 'requires' => '6.0', 'tested' => '6.5', 'sections' => [ 'description' => '<p>Full description.</p>', ], 'upgrade_notice' => 'Upgrade details.', 'slug' => 'my-plugin', 'type' => 'plugin', 'last_updated' => '2026-06-18 10:30:00',]Theme archives can return similar fields, plus details_url. When a theme does not define Details URI, details_url defaults to Theme URI.
Generic ZIP archives return package-level metadata:
[ 'slug' => 'custom-slug-generated-from-filename', 'type' => 'generic', 'last_updated' => '2026-06-18 10:30:00',]Note
Metadata keys depend on which headers and readme fields exist in the archive. Missing headers are not included in the returned array.
Supported Archive Layout#
The parser scans files at the archive root and one directory level below it, which matches common WordPress package layouts:
my-plugin.zip└── my-plugin/ ├── my-plugin.php └── readme.txtmy-theme.zip└── my-theme/ └── style.cssFor plugins, the main PHP file must include a valid Plugin Name header. For themes, style.css must include a valid Theme Name header.
Error Handling#
<?php
use WpPackageParser\InvalidPackageException;use WpPackageParser\PackageMetadata;
try { $metadata = PackageMetadata::parse('/absolute/path/to/package.zip', 'package.zip');} catch (InvalidPackageException $exception) { error_log($exception->getMessage());}InvalidPackageException is thrown when the archive path is missing, unreadable, or not a valid ZIP package.
Credits#
This package builds on ideas and code patterns from:
- YahnisElsts/wp-update-server for WordPress package parsing and update-server metadata concepts.
- erusev/parsedown for Markdown-to-HTML conversion of readme sections.
Development#
Install dependencies:
composer updateRun the test suite:
composer testRun syntax checks:
php -l src/PackageMetadata.phpphp -l src/PackageParser.phpImportant
The test suite creates ZIP fixtures with
ZipArchive, so local development tests require the PHPzipextension even though runtime parsing has a PclZip fallback.