jims/editor-hub-bundle
Editor Hub Bundle provides a centralized hub for editor integrations in PHP applications, helping you organize editor configuration and related workflows in one place. It offers a configurable, lightweight approach to managing editor setup and keeping features consistent across projects.
First, verify the package identity and platform. Despite the "Laravel developer" framing, jims/editor-hub-bundle is a Symfony bundle (Symfony 2/3/4) wrapping Baidu's UEditor/UMEditor. There is no ServiceProvider, no config/editor-hub.php, no facade — so on a Laravel codebase the realistic path is to treat it as a reference implementation and port the ~200 lines you actually need, or to fork it. Either way, start by reading the source, not the docs:
composer show jims/editor-hub-bundle --all # check php + symfony/* constraints first
If you see php: >=5.5 and symfony/symfony: ~2.8|~3.0, it will not install alongside modern Symfony or Laravel — clone the repo and read it instead of fighting Composer.
Where to look first (in order):
DependencyInjection/Configuration.php — every tunable option (upload paths, allowed extensions, editor flavour).Controller/* — the UEditor server endpoint (action=config, uploadimage, catchimage, …). This is the piece you are really porting.Resources/config/routing.yml — the route the editor's serverUrl must point at. Note this route is the protocol surface — UEditor hard-codes a single catch-all endpoint and multiplies operations through an action query string.Resources/views/ (or the Twig extension) — the snippet that emits <script> + UE.getEditor(...) and merges the config blob. Translate to Blade.First use case — get GET /ueditor/server?action=config returning the JSON config blob, then render an editor instance pointing at it. That is the whole protocol handshake. Once config works, uploads are the only meaningful remaining work. On Laravel:
// routes/web.php
Route::any('/ueditor/server', [UEditorController::class, 'handle'])
->middleware(['auth', 'can:edit-content']);
// Blade
<div id="editor"></div>
<script src="/vendor/ueditor/ueditor.config.js"></script>
<script src="/vendor/ueditor/ueditor.all.js"></script>
<script>
UE.getEditor('editor', { serverUrl: '/ueditor/server' });
</script>
If instead you are on a real Symfony 4.4 app with Chinese-speaking authors, composer require jims/editor-hub-bundle, register the bundle in config/bundles.php, and consume the Twig helper. That is the intended path — but only for Symfony ≤4.4.
Treat the endpoint as a protocol, not a REST API. UEditor multiplexes every operation through one URL + action query parameter. Do not try to be RESTful about it.
public function handle(Request $request)
{
return match ($request->query('action')) {
'config' => response()->json($this->config()),
'uploadimage' => $this->storeImage($request),
'uploadfile' => $this->storeFile($request),
'catchimage' => $this->fetchRemote($request), // if you must
default => response()->json(['state' => 'unknown action'], 400),
};
}
Mirror the response shape exactly. UEditor expects a specific JSON envelope — {"state": "SUCCESS", "url": "...", "title": "...", "original": "...", "type": "..."} for uploads and {"state": "ERROR: ..."} on failure. Emit that shape from your own controller and the front-end will "just work" regardless of backend.
Centralize config in PHP, not in the vendor JS. The config action should return the full config.json blob so the editor self-configures. Keep a single source of truth:
protected function config(): array
{
return [
'imageActionName' => 'uploadimage',
'imageFieldName' => 'upfile',
'imageAllowFiles' => ['.png', '.jpg', '.jpeg', '.gif', '.webp'],
'imageMaxSize' => 5 * 1024 * 1024,
'imageUrlPrefix' => '',
'imagePathFormat' => '/uploads/editor/{yyyy}{mm}{dd}/{rand:8}',
// ...same block for fileActionName, scrawlActionName, etc.
];
}
Sanitize on write AND on read. UEditor is a stored-HTML editor. Run HTMLPurifier (or mews/purifier on Laravel) on what you save and again on what you render, because legacy content authored before your sanitizer existed still flows through. This is a security invariant, not a nicety.
Lock down the upload handler explicitly. Do not port the original's extension-string validation. Enforce: size cap from config, MIME sniff (finfo / Laravel's image/mimes rule), non-executable storage location (outside public/, or S3), randomized filenames, and no HTML/SVG/JS in the allowed set.
Symfony-side, if you genuinely use the bundle: register editors as tagged services (editor_hub.editor tag) and let the registry discover them. Shared config profiles let you define one toolbar and reuse across editors; the Twig helper emits both the <script> includes and the UE.getEditor() call. Add a custom adapter by implementing the bundle's editor adapter interface — that is the documented extension point.
Assets. Under Symfony ≤4.4 the bundle uses the old asset conventions (possibly Assetic). If you port to Laravel, drop assets under public/vendor/ueditor/ and copy them via a publish command; under Vite, copy them as static files rather than trying to bundle them — the editor assumes a global UE and a runtime-computed serverUrl, so bundling breaks path resolution.
Testing. Write a smoke test per action (config, upload success, upload rejection, oversized upload). These are the ones that fail silently later.
It will not install on modern stacks. Symfony 4.4 is EOL (Nov 2023), Symfony 2/3 long dead. Laravel 10/11 needs PHP 8.1+, which conflicts on symfony/http-foundation/symfony/routing. composer require will fail loudly or force --ignore-platform-reqs and a cascade of patches. Decide up front whether you are porting or using — do not drift into a half-fork.
Zero dependents, one maintainer, 10 stars. No security advisories will ever be published for this fork. Treat every byte as unmaintained code you now own. Pin the commit, vendor the assets, and open it in the browser on Chrome and Safari before committing — the editor is iframe-based and depends on deprecated DOM APIs that nobody has tested against current browsers.
The upload endpoint is historically the weak point. UEditor's canonical PHP connector has a poor security record (extension-based validation, web-reachable storage, path traversal). Do not call the bundle's handler as-is. Write your own using your framework's file validation, and store files where nothing executes.
serverUrl must be absolute from the browser's view. If the editor is served from /admin/ and the endpoint resolves relatively, config and uploads break with opaque errors. Log the resolved URL server-side once and verify it.
Silent failure modes to instrument:
config action returning malformed JSON — the editor loads but every toolbar is empty."SUCCESS" — the editor treats anything else as failure, including "success".Add a /ueditor/server?action=config health check to your uptime monitor; it is the cheapest signal that the whole integration is alive.
Config quirks. UEditor merges the config blob over defaults — if a key is missing it falls back, if it is present-but-wrong-type it breaks silently. Keep imagePathFormat/filePathFormat in the exact {yyyy}{mm}{dd}{rand:8} placeholder syntax; a near-miss produces a valid but colliding filename.
Extension points. On Symfony, the adapter interface + tagged services are the sanctioned way to add a second editor. On Laravel, skip all of that — the protocol is the interface; anything that answers action=config and action=upload* in the right envelope is a valid backend.
Ramp-up cost is real and mostly sunk. Budget 1–2 weeks to become productive, and roughly that again when you next upgrade PHP. That is the dominant lifecycle cost and it does not appear in the integration estimate. If you have a legacy UEditor corpus, run a content audit first (sample stored HTML, count sanitizer-affected records) before committing to either path.
Prefer replacement if the requirement is "any WYSIWYG." TipTap/ProseMirror, CKEditor 5, and TinyMCE all have current Laravel and Symfony integrations plus a security process. The only defensible reason to stay on this stack is byte-compatible rendering of existing UEditor-authored content, or an existing Symfony ≤4.4 admin panel with trained Chinese-speaking authors. Otherwise, decline and fund the exit now.
How can I help you explore Laravel packages today?