rodneyrehm/plist
PHP library for reading and writing Apple property list (plist) files in both XML and binary formats. Convert plists to PHP arrays and back with a lightweight, straightforward API suited to macOS and iOS tooling, configuration, and data interchange.
Architecture fit. The capability is narrow and real: parsing and emitting Apple plist payloads in both XML and binary (bplist00) form. In a Laravel codebase this surfaces in a predictable set of places — enterprise iOS distribution manifests (manifest.plist), .mobileconfig MDM profiles, Info.plist extraction from uploaded IPA/app bundles, launchd/Keychain artifacts, and macOS/iOS backup metadata. The binary format is the differentiator: XML-only parsers (SimpleXML, spatie/array-to-xml) cannot read it, so if any workflow ingests a binary plist today, this package or a hand-rolled bplist reader is the only path. If no such workflow exists, the package is dead weight. That is the first question to answer before any code is written.
Integration feasibility. The library is pure PHP and Composer-installable, with no service provider, facade, or config — it is a codec, not a framework citizen. Integration is a ~20-line adapter: a PlistCodec contract in app/Support/Plist/, a concrete implementation wrapping the vendor classes, and a container binding in AppServiceProvider. This is genuinely low-effort and keeps the vendor surface confined to one directory, which is the right pattern for a dependency with this risk profile.
Technical risk. Several flags, one of them serious:
rodneyrehm/plist) and the linked repository (moodlehq/CFPropertyList) are two different codebases — rodneyrehm/plist is the older XML+binary plist library; moodlehq/CFPropertyList is a Moodle fork of the tarendai/CFPropertyList port. Before committing, resolve what composer require rodneyrehm/plist actually resolves to, confirm the source URL in composer.lock, and audit that specific tree. Do not accept the catalog metadata at face value.LIBXML_NONET, no LIBXML_NOENT) and whether binary parsing bounds nesting depth and element counts.Key questions. Which concrete Laravel workflow requires this? Does the resolved package disable XXE and bound recursion? Does it round-trip types faithfully — Date, Data blobs, UID/CF$UID, floats? What is peak memory on a 10 MB binary plist? Is the maintainer responsive to a security issue, and can we fork under our org if not?
Stack fit. Laravel 10–12, PHP 8.2+. No framework coupling, so it slots in cleanly behind the adapter. Keep the vendor namespace out of controllers, jobs, and models — everything touches PlistCodec, which returns plain arrays and exposes parse(string): array / serialize(array, Format): string. That contract is the seam that makes a later swap to a fork or an in-house reader a one-file change.
Migration path. Do not replace existing XML handling wholesale. If SimpleXML is in use for XML plists, leave it and adopt this only for the binary path — that minimizes blast radius to the code that actually needs the new capability. Gate the new path behind a Pennant feature flag per workflow, and write contract tests (Pest/PHPUnit) with a fixture corpus of real artifacts: a signed .mobileconfig, an OTA manifest.plist, an Info.plist from a shipped IPA, and a deliberately malformed binary file. Round-trip assertions (parse → serialize → parse) catch type-coercion regressions that unit tests over synthetic arrays will miss.
Compatibility. Verify composer.json platform constraints and required extensions (ext-libxml, ext-mbstring, possibly ext-zlib) against the runtime image. Run PHPStan at a high level over the adapter — the vendor tree will likely be untyped, so expect to baseline it rather than fix it.
Sequencing. (1) Spike: confirm provenance, run the security review against the actual locked tree, benchmark memory on a realistic binary plist. (2) Build the adapter plus fixture-based contract tests. (3) Wire one bounded use case — file upload of a .mobileconfig, validated by a FormRequest on MIME and size, parsed in a queued job writing to an S3 disk. (4) Only then broaden. If step 1 fails on XXE or PHP 8.4, stop and fork.
Maintenance. Treat this as a vendored dependency: pin an exact version, forbid transitive updates via composer.json constraint, and add it to the dependency-review list with a named owner. Given the provenance ambiguity, plan for a fork into a packages/ path repository — a pure-PHP codec is cheap to own, and the alternative is discovering abandonment mid-incident.
Support. No commercial backing, thin community, no Laravel-idiomatic docs. Your team is the support tier. Budget for reading Apple's plist format documentation directly when edge cases surface.
Scaling. Parsing is CPU- and memory-bound and fully stateless, so it scales horizontally without coordination. The operational controls that matter are per-request size caps, memory_limit headroom in the worker pool, and a hard timeout. Never parse untrusted uploads in a web request — push to a queue so a pathological file degrades one worker, not an entire FPM pool.
Failure modes. Ranked by severity: (1) silent round-trip corruption — a re-serialized MDM payload that omits a UID or flattens a Date can break device enrollment with no error at write time; guard with round-trip fixture tests and checksum comparison before writing. (2) XXE / entity expansion on the XML path if libxml flags are wrong. (3) Memory exhaustion on large binary plists producing a 500 in-process. (4) Unscoped exceptions — vendor code likely throws \Exception; wrap and translate to a domain exception so retries and alerting behave. (5) Encoding drift — XML plists are commonly UTF-16; confirm output encoding on write.
Ramp-up. Low for the happy path — a mid-level PHP developer is productive in half a day. The expensive part is not the API, it is domain knowledge: which Apple artifact types you touch, which fields are load-bearing, and what a valid round trip looks like. Assign that knowledge to whoever owns the iOS/macOS workflow, not to whoever installs the package.
How can I help you explore Laravel packages today?