Download the PHP package wexample/symfony-live without Composer

On this page you can find all versions of the php package wexample/symfony-live. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package symfony-live

symfony-live

Version: 5.5.0

symfony-live is the server half of Wex live updates: it publishes to a Mercure hub, hands subscribers a token scoped to the topics they asked for, and owns the topic grammar and the payload envelope both halves have to agree on. The browser half stays where it is — LiveUpdatesService in symfony-loader, MercureLiveUpdatesDriver in js-api — and this package never depends on it.

What the two halves agree on

Three things cross the wire, and all three are written here.

A topic is entity/<kebab-entity-name>/<action>/<id>, built by LiveTopicHelper::entity(). The browser rebuilds the same string segment by segment through renderNode.liveUpdatesTopic(), so a change to the grammar is a change on both sides. LiveTopicHelper::join() prefixes a topic when an API version has to scope it.

The last segment is (string) $entity->getId(), the same expression the normalizers use for the id they serialize — and the helper takes no identifier argument, so an app cannot pass a second one. A subscriber only ever holds what the API served it: give it a topic built on anything else and the two halves subscribe and publish past each other, silently.

An update payload is {"event": "...", "data": ...}, produced by LivePublisherService::publishEvent(). A subscriber reads the event name to decide what to do, instead of guessing from the shape of the data.

Subscriber info is {hubUrl, jwt, topics, expiresAt}, produced by LiveSubscriberTokenService::buildSubscriberInfo() and consumed by the browser driver as its Mercure configuration. Keys are camelCase and fixed.

Publishing without calling anything

An entity marked #[LiveEntity] publishes itself. LiveEntityPublishListener watches Doctrine and pushes create, update and delete on the entity's own topics — a service that changes one has nothing to call.

Publication happens on postFlush, not from inside the transaction: a change announced and then rolled back is a change no subscriber can un-hear.

What goes in data is the output of the entity's live normalizer, which an app declares by adding an interface to a normalizer it already has:

Nothing else is needed: getEntityClassName() is already on AbstractEntityNormalizer, and the interface is autoconfigured. An entity usually has several normalizers — legacy, summary, export — and only the one carrying the interface goes on the wire. With none, data is {"id": "..."} and the subscriber refetches.

A delete always carries only the id: the entity is still in memory but its relations are gone, and normalizing it would walk into proxies of rows that no longer exist.

Publication is scoped by the same actions list that the token grants, so an entity declaring only EVENT is not auto-published — its events stay the app's to send.

Publishing by hand

publish() is there for a payload that is not an event envelope. Both return the update id the hub answers with.

Handing a subscriber its token

Mark the entity and the endpoint exists:

Then include the bundle's routes once, in config/routes.yaml:

GET /live/subscribe-info/session/<id> now answers the {hubUrl, jwt, topics, expiresAt} above. LiveEntityRegistryService finds the entity by reading #[LiveEntity] off the Doctrine mapping — there is no name-to-class map to keep in a config file, and the name in the URL is the same kebab name as in the topic.

Two things are worth knowing about what it grants:

The token is replayable until it expires, so it grants exactly those topics and carries no publish claim at all. An app that needs something else in the envelope — an API version prefix on the topics, an internal variant of the route — calls LiveSubscriberTokenService::buildSubscriberInfo() from its own controller instead.

Configuration

The bundle prepends a default Mercure hub, so an app that ships the env vars needs no config/packages/mercure.yaml of its own:

The three carry a default pointing at http://localhost/.well-known/mercure with the placeholder secret of the Mercure recipe, set on the variables themselves rather than on a config key. An app installing the bundle therefore boots — cache:clear, a route dump, an image build — before anyone has declared a hub, and so does the config/packages/mercure.yaml the recipe writes. Nothing reaches a hub with those values: a deployment overrides them.

Everything else has a default and is only worth setting to change the token lifetime:

Table of Contents

Where the cut is

Live updates have two halves. The browser half already exists and works: LiveUpdatesService in symfony-loader is an AppService of that registry, wired to EventsService, ConnectionStatusService and the RenderNode mount hooks; LiveUpdatesConnection and MercureLiveUpdatesDriver in js-api sit under it. None of that moves here — pulling it out would make symfony-live depend on symfony-loader and symfony-design-system, which is the layering upside down.

What this package takes instead is the server half and the contract: the topic grammar, the update envelope and the subscriber-info payload. Those three were written twice before — once in an app's message handlers, once in the browser driver — with nothing holding them together. They now live in Helper/LiveTopicHelper, Service/LivePublisherService and Class/LiveSubscriberInfo, and the browser is a consumer that already reads exactly those keys.

Layout

Publishing waits for postFlush

postPersist, postUpdate and postRemove only note what happened; the listener publishes in postFlush. Pushing from inside the transaction would announce a change a rollback then undoes, and there is no retraction a subscriber could hear.

Finding the normalizer is the one thing that could not be derived. An entity has several — legacy, summary, export — none of which is more canonical than the others from outside the app, and symfony-helpers normalizers are injected by class rather than resolved through the serializer, so asking the serializer would have picked whichever matched first. Marking one with LiveEntityNormalizerInterface is the app naming it, and costs a single implements because getEntityClassName() is already there. The interface is autoconfigured onto a tag in the extension and the listener takes them all through #[AutowireIterator] — no compiler pass, and no dependency on symfony-api.

The entity name is derived, never declared

LiveEntityRegistryService builds its name-to-class map by walking getMetadataFactory()->getAllMetadata() and keeping the classes carrying #[LiveEntity]. There is no config file to keep in step, and no name to invent: the key is ClassHelper::getKebabName(), which is getSnakeShortClassName() with dashes instead of underscores — the same string the topic's second segment already uses, and the same one the browser produces with stringToKebab(getEntityName()).

The endpoint takes an entity name and an id, never a topic. A caller that could hand over a topic list would be a caller deciding what it may listen to; here the topics are built by the same LiveTopicHelper::entity() call the publisher makes, and authorisation is a voter on the loaded entity. With no voter supporting it, Symfony denies — which is the direction a mistake should fall in.

One identifier

LiveTopicHelper::entity() reads the id off the entity and takes no identifier argument. That is deliberate: an app given the choice takes it per call site, and the browser has no choice at all — it subscribes on the id the API served it. The two only meet if the topic carries (string) $entity->getId(), which is what AbstractEntityNormalizer serializes.

Apps carrying a second identifier from before uuids — a secureId, a slug — cannot publish on it: the migration is theirs to finish, not the helper's to accommodate.

Two tokens, not one

The hub JWT configured on the default hub is the server's: it carries publish: '*' and no subscribe claim, and never leaves the server. Subscriber tokens are minted per request by LiveSubscriberTokenService from the same secret, carry a subscribe claim listing the exact topics and no publish claim at all — LcobucciFactory::create() omits the claim entirely when publish is null, where an empty array would grant publication on the empty selector.

Both are signed with MERCURE_JWT_SECRET. The hub must be started with that same value in MERCURE_PUBLISHER_JWT_KEY and MERCURE_SUBSCRIBER_JWT_KEY.

No dependency on the browser half

symfony-live requires symfony/mercure, symfony/mercure-bundle, lcobucci/jwt and the Wexample helpers, and nothing from symfony-loader. An API-only app can use it as it is; an app rendering pages through the loader wires the browser side itself, from the same subscribe-info response.

Integration in the Suite

This package is part of the Wexample Suite — a collection of high-quality, modular tools designed to work seamlessly together across multiple languages and environments.

Related Packages

The suite includes packages for configuration management, file handling, prompts, and more. Each package can be used independently or as part of the integrated suite.

Visit the Wexample Suite documentation for the complete package ecosystem.

Dependencies

Versioning & Compatibility Policy

Wexample packages follow Semantic Versioning (SemVer):

We maintain backward compatibility within major versions and provide clear migration guides for breaking changes.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Free to use in both personal and commercial projects.

About us

Wexample stands as a cornerstone of the digital ecosystem — a collective of seasoned engineers, researchers, and creators driven by a relentless pursuit of technological excellence. More than a media platform, it has grown into a vibrant community where innovation meets craftsmanship, and where every line of code reflects a commitment to clarity, durability, and shared intelligence.

This packages suite embodies this spirit. Trusted by professionals and enthusiasts alike, it delivers a consistent, high-quality foundation for modern development — open, elegant, and battle-tested. Its reputation is built on years of collaboration, refinement, and rigorous attention to detail, making it a natural choice for those who demand both robustness and beauty in their tools.

Wexample cultivates a culture of mastery. Each package, each contribution carries the mark of a community that values precision, ethics, and innovation — a community proud to shape the future of digital craftsmanship.

Migration Notes

When upgrading between major versions, refer to the migration guides in the documentation.

Breaking changes are clearly documented with upgrade paths and examples.


All versions of symfony-live with dependencies

PHP Build Version
Package Version
Requires php Version >=8.5
lcobucci/jwt Version ^4.0 || ^5.0
symfony/http-client Version ^7.4 || ^8.0
symfony/mercure Version ^0.6
symfony/mercure-bundle Version ^0.3
symfony/uid Version ^7.4 || ^8.0
wexample/php-helpers Version >=7.0.4
wexample/symfony-helpers Version >=19.1.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package wexample/symfony-live contains the following files

Loading the files please wait ...