What's New in v7
Pop PHP Framework v7.0.0 adds 278 features across the bundled components and one new package. This page is the guided version: what changed at the framework level, the additions worth reorganizing code around, and the patterns that recur often enough to name. The exhaustive list is NEW-FEATURES.md.
If you have a v6 application to move, read Upgrading from v6 alongside this. The two documents cover the same release from opposite directions.
Framework-Level Changes#
These are the changes that apply to the whole framework, and to any project that installs it.
There Is a New Installer Package#
popphp/framework is now the official way to start a project. In v6 you required popphp/popphp-framework
directly and inherited its 30-plus requirements into your own composer.json; in v7 the skeleton depends on
the metapackage for you, so your project file carries one line:
composer create-project popphp/framework my-app
The skeleton also brings the kettle script to the project root, and hooks into Composer to run pop:init
before the installer finishes. Installation walks through both paths,
including the core-only one.
There Is a New Component#
pop-parser parses free-form personal names and US or Canadian street addresses into their component parts,
with no third-party parsing dependency. It's bundled with the full framework. With popcorn and pop-ftp
dropped against that one addition, the v7 metapackage is one package smaller than v6's — see All
Components.
Every Component Requires PHP 8.4 and Declares strict_types=1#
There is no partial upgrade path and no component that still runs on 8.3. Strict mode is decided by the
calling file, so your own loosely-typed code is not retyped by this — but a loose value handed to a
component's strict internals is now a TypeError where v6 coerced it. Every component also gained PHPStan
coverage alongside its existing PHPUnit coverage.
Every component takes a major version bump as well. No breaking change ships behind a minor number, so a caret constraint on a v6 version refuses the v7 release rather than taking it quietly. That's deliberate: an accidental upgrade is the one failure mode a release this size cannot afford.
Two packages left the framework. popphp/popcorn is gone because the core absorbed it, and popphp/pop-ftp
is gone with no replacement — the guidance is to move that traffic to HTTPS or SSH. Three pop-auth
adapters went with them: Table moved to pop-db, Http was replaced by calling pop-http directly, and
Ldap was dropped. pop-form and pop-image both dropped their Captcha classes. Upgrading from
v6 covers what each removal means for existing code.
The Most Significant Additions#
278 features is more than anyone reads in order. These are the ones that change how you would write an application rather than adding to what it can do — the places where the v7 answer to a problem is a different answer, not a longer one.
The Core Absorbed Popcorn#
HTTP-verb routing was a separate package in v6 — popphp/popcorn — and in v7 it is Pop\Router\Match\Http
itself. Method-grouped route configs, a fluent verb API on Application, Router and the matcher, custom
verbs registered at runtime, and a real 405 with an Allow header when a path matches but the method does
not:
<?php
$autoloader = include __DIR__ . '/../vendor/autoload.php';
$app = new Pop\Application($autoloader);
$app->get('/orders[/:id]', function($id = null) {
echo ($id === null) ? 'All orders' : 'Order ' . $id;
})->post('/orders', function() {
echo 'Created';
});
$app->run();
A GET /orders/7 answers from the first route, a POST /orders from the second, and a PUT /orders gets a
405 Method Not Allowed carrying Allow: POST, GET — not the 404 v6 would have produced.
Alongside it the core grew PSR layers on three of its managers: PSR-11 on the service locator, PSR-14 on the
event manager, and a PSR-15 bridge on middleware. Pop\Dispatch decoupled dispatchability from controllers,
so a plain class, an invokable object or a CallableObject is a route target without extending anything.
And a new app.shutdown event fires from a finally inside run(), giving you teardown that runs whether
the request completed, aborted or threw. Routing and Error Handling
cover the routing half; Services, Events and
Middleware cover the rest.
pop-http Became a PSR-7/17/18 Client and Server#
Immutable messages, six factories, a PSR-18 sendRequest(), and — the part that changes how you write tests
— a Mock handler that answers from a canned script with no network involved. On top of that sits a
composable client middleware pipeline with retry-and-backoff and PSR-3 request logging, a native streaming
Body, and superglobal-free Server\Request construction, which is what makes a server-side request
testable at all. See HTTP Client and Requests &
Responses.
pop-db's Shorthand Syntax Became a Query Language#
In v6 the shorthand was a convenience over simple equality. In v7 it takes structured [OPERATOR, ...]
tuples, OR and AND groups, IN and EXISTS subqueries and JSON column paths, so a non-trivial WHERE no
longer forces you down to the builder. Records gained lifecycle hooks and mass-assignment protection,
relationships gained composite foreign keys and multi-path eager loading, and pop-auth's table adapter
moved here as Record\Auth with lockout and MFA. See Auth Records and Query
Builder.
pop-queue Became Crash-Safe#
The adapter contract was rewritten around leases: a worker reserves a job, then releases, deletes or buries
it. A worker that dies mid-job no longer loses it, and a job that keeps failing lands in a dead-letter
store. Add a worker registry with heartbeats, per-job delay and retry backoff, HMAC-signed payloads, daemon
mode with signal handling, and a queue:* command family to drive it. See Queues & Scheduled
Tasks.
pop-pdf Gained a Native PDF Reader#
Pop\Pdf\Extract is a full reading engine — cross-reference and object streams, every standard filter, CMap
and CID font decoding, and a repair pass for damaged files — replacing the third-party parser v6 leaned on.
Built on it: document merging, image-only page detection for routing scans to OCR, and HTML rendering that
now does real <table> layout with colspan, rowspan and repeating headers. CID font output means Cyrillic,
Greek and Arabic text renders from an embedded font. See PDFs.
pop-cache, pop-log and pop-code Caught up with Their Ecosystems#
pop-cache added PSR-6 and PSR-16 alongside its native API, plus remember() with probabilistic early
recomputation, tag-based invalidation, atomic counters and an injectable clock. pop-log became a genuine
PSR-3 logger with string levels, placeholder interpolation, pluggable formatters and JSON Lines output.
pop-code learned the PHP shipped since v6 — attributes, enums, readonly, constructor promotion, typed
constants, intersection types — in both directions. See Cache, Logging
and Code Generation.
Kettle Became More than Just a CLI Helper#
pop-kettle gained 16 features, more than any component except popphp and pop-http, and now houses the
skeleton install through pop:init — which deprecates the old popphp/popphp-skeleton and
popphp/popphp-tutorial repos. pop:init is a guided interview rather than a wall of flags, and it
registers your namespace in composer.json instead of writing v6's kettle.inc.php. It offers front-end
scaffolding — AlpineJS, Vue or React, each with Tailwind through Vite — driven afterward by the web:
commands. Commands can go to Kettle or to your own application namespace, and the built-in commands moved to
pop:* so app:* is yours. See Kettle.
pop-console Grew the Output Vocabulary a Real CLI Needs#
Table rendering, progress bars, a multi-select prompt, a non-exiting confirm(), and help screens that can
be narrowed to one command namespace. Underneath, commands became dispatchable objects with their own help
text, which is the mechanism Kettle's auto-discovery uses, and an injectable input stream makes a prompt
testable without a terminal. See Building Console Applications.
Mail Message Assembly Moved into pop-mime#
pop-mail's native message and part classes were replaced by refactored ones in pop-mime, which gained
part factories, RFC 5322 address parsing, RFC 2047 encoded words with no ext-imap, correct header folding
and lazy streamed attachment bodies. What pop-mail kept is the sending: batch-capable transports, a
pre-rendered body reused across repeated sends, injectable HTTP handlers on the API clients, and O(1) queue
de-duplication. See Mail and MIME.
pop-config Reads and Writes Dot Paths#
$config['database.host'] traverses nested arrays in both directions, isset() understands it, YAML moved
to symfony/yaml so it works without ext-yaml, and parse failures raise typed exceptions instead of
returning an empty array. See Config Objects.
Two more worth knowing about: pop-view can compile stream templates to plain PHP, turning every render
after the first into an include, and its multi-block layout inheritance works, where v6 resolved only a
block named header (Views & Templates). And pop-color added seven color spaces — HSV,
HSB, HWB, Lab, Lch, Oklab and Oklch — with colorimetrically correct conversions, CSS Color 4 parsing and
rendering, and hex with alpha (CSS & Color).
Smaller components got smaller versions of the same treatment. pop-acl gained wildcard permissions, role
and resource removal, and introspection (Authorization). pop-storage gained
streaming I/O, presigned URLs, and full pagination on S3 and Azure listings (File
Storage). pop-validator added 19 more Has* validators for asserting things about a
whole array (Forms & Validation). pop-csv got a streaming row reader and
formula-injection escaping (CSV), and pop-dir collapsed to a single filesystem walk (File
Storage).
Themes#
Five patterns run through the release. Recognizing them is the fastest way to guess what a component you have not read about yet probably gained.
PSR Interoperability#
Pop v7 drops into a wider PHP ecosystem, with the PSR surface sitting alongside the native API rather than
replacing it: PSR-3 in pop-log and pop-debug, PSR-6 and PSR-16 in pop-cache, PSR-7/17/18 in
pop-http, PSR-11 on the service locator, PSR-14 on events and PSR-15 on middleware. A library expecting a
LoggerInterface, CacheInterface or ClientInterface takes a Pop object without an adapter. The one
displacement is pop-log's levels, now strings to match PSR-3.
Testability Seams#
Every component that talks to the outside world grew a way to substitute that world: Client\Handler\Mock
in pop-http, the Memory adapter and Queue::fake() in pop-queue, Clock\MutableClock in pop-cache,
setInputStream() in pop-console, injectable HTTP handlers on pop-storage and pop-mail, and
superglobal-free request construction. A test suite that can run a queue, a cache and an HTTP call without
any of them existing is the largest practical difference from v6.
Security Hardening#
XChaCha20-Poly1305 and HKDF key separation in pop-crypt; per-field CSRF tokens compared in constant time
in pop-form; needsRehash() and hash_equals() in pop-auth; automatic redaction in pop-debug;
path-traversal rejection in pop-storage and pop-view; HMAC-signed job payloads in pop-queue;
formula-injection escaping in pop-csv; secure-by-default cookie flags in pop-session; and output
escaping in pop-dom, pop-nav, pop-paginator and pop-i18n. The escaping additions are the ones to
watch on an upgrade — see Upgrading from v6.
Streaming and Memory#
Large payloads stopped being buffered whole. Stream bodies and zero-copy multipart in pop-http,
putFileStream() and fetchFileStream() in pop-storage, a generator row reader in pop-csv, lazy
attachments through php://filter in pop-mime, one rendered body reused across a BCC loop in pop-mail,
compiled templates in pop-view, and a single filesystem walk in pop-dir where v6 made two or three.
Operational Visibility#
The framework got easier to run rather than easier to write: worker registries and dead-letter queues in
pop-queue, syslog, stdout and NDJSON output in pop-log, NDJSON debug storage in pop-debug, and the
queue:* commands in Kettle. Each is a line of configuration, so an application that doesn't want them pays
nothing.
See Also#
- Upgrading from v6 — the same release from the breaking-change side
- Changelog — the release history, including everything before v7
- All Components — every package, with its guide and its README