phpoffice/common
PHPOffice Common provides shared utility classes for PHPOffice projects, including XML helpers, drawing and font handling, file utilities, and compatibility code. It supports libraries such as PhpSpreadsheet and PHPWord.
phpoffice/php-common is a framework‑agnostic utility library (OLE compound‑file handling, XML reader/writer helpers, drawing/text utilities) that Laravel apps almost always get transitively — phpoffice/phpspreadsheet and phpoffice/phpword depend on it. You only composer require phpoffice/php-common directly if you need the OLE layer on its own.
# usually already present
composer show phpoffice/php-common
# 1.1.0 explicitly, if you consume OLE directly
composer require phpoffice/php-common:^1.1
Where to look first:
src/ — locate OLERead and the new OLEWrite (added in 1.1.0, the counterpart to OLERead). Everything else in this package is plumbing for PhpSpreadsheet/PHPWord.CHANGELOG.md / the 1.0.5 → 1.1.0 diff — the only runtime change is additive.composer.lock — confirm which version PhpSpreadsheet pulled in before you assume 1.1.0 features exist.First use case: you receive a legacy .xls/.doc/OLE‑embedded artefact upload and need to inspect or extract the streams inside it (e.g. Workbook, SummaryInformation), or you need to build such a container (embedding an object/file inside a generated spreadsheet or document). OLERead covers the first; OLEWrite now covers the second.
Resolve the version, then write to it. Because this is a transitive dependency, your app can silently sit on 1.0.5. If you plan to use OLEWrite, add an explicit constraint (^1.1) so a future composer update of PhpSpreadsheet can't drop you back.
Reading an OLE container from an upload. The API works on a real filesystem path, not a stream, so go through Laravel's temp path or Storage:
use PhpOffice\Common\OLERead; // confirm exact FQCN in src/ for your version
$path = $request->file('legacy')->getRealPath(); // or Storage::path($key)
$ole = new OLERead();
$ole->read($path);
$workbook = $ole->getStream('Workbook'); // binary string|null
Wrap this in a small service (App\Support\OleInspector) so the class name and API live in one place.
Writing a container (new in 1.1.0). OLEWrite is fresh and lightly documented — treat the public surface as unstable within 1.x. Add streams, then persist to a path or push bytes into a Laravel response:
$writer = new OLEWrite();
$writer->addStream('Workbook', $binaryPayload); // verify method names against src/
$writer->save(storage_path('app/objects/embed.ole'));
For a download, write to tmpfile()/Storage::path() first, then response()->download() or response()->streamDownload() — never echo binary before headers.
Integration with the PhpSpreadsheet/PHPWord workflow. Typical pattern: generate the spreadsheet/document normally, then post‑process the archive to inject or extract an embedded OLE object. Keep that post‑processing in a queued job — OLE parsing is CPU/IO heavy and does not belong in the request cycle for anything over a few MB.
Testing. Commit small binary fixtures under tests/Fixtures/ole/. Assert on stream presence and content, not on byte‑for‑byte output — OLEWrite output ordering/timestamps can vary. For round‑trips, write with OLEWrite and read back with OLERead in the same test.
Static analysis alignment. 1.1.0 raises the library's own PHPStan level to 7 and permits PHPStan 2.x. If you run Larastan/PHPStan against vendor/, keep your own PHPStan on 2.x-compatible versions to avoid duplicate‑constraint conflicts; if you're still on PHPStan 1.x, the widened constraint means composer will simply keep the 1.x line.
OLERead needs a seekable local file. Passing a stream wrapper URL (S3, php://input) will fail or behave oddly. Download to a temp path first — $file->getRealPath(), Storage::path(), or tempnam().OLERead loads the whole container; a 40 MB legacy file can blow memory_limit in a queue worker. Validate size before parsing and fail fast with a clear exception.OLEWrite is brand new. No docs, no backwards‑compatibility promise yet, and it ships in a minor release. Pin ^1.1 (and ideally a lock file), and isolate calls behind your own adapter so a 1.2.0 signature tweak is a one‑file fix.composer update phpoffice/php-common from 1.0.5 is safe.composer.json's require.php for the actual floor before planning your own runtime upgrade.addStream().phpoffice/php-common to require when PhpSpreadsheet already brings it in creates a second constraint to maintain. Require it directly only when you import its classes.OLERead/OLEWrite; compose around them.RuntimeException from the OLE reader with a terse message. Log the offending file's size, first 8 bytes (D0 CF 11 E0 A1 B1 1A E1 is the OLE magic), and the requested stream name — that trio resolves most "cannot find stream" reports.How can I help you explore Laravel packages today?