ikadoc/kcfinder-bundle
Integrates KCFinder, the open-source web file manager, into Symfony applications. Provides bundle setup and routing so you can browse, upload, and manage files from CKEditor or standalone, using a familiar PHP backend.
The first thing a Laravel developer needs to know: ikadoc/kcfinder-bundle is a Symfony bundle, not a Laravel package. It has no service provider, no config/ publish tag, no container bindings you can resolve from Laravel, and no routes Laravel can register. composer require will download it into vendor/, but nothing boots it — Laravel has no bundle/kernel registration step. Your "day-to-day leverage" therefore starts with accepting that on a Laravel stack you are not using the bundle; you are using the legacy KCFinder file manager it wraps.
That reframes the two real starting paths:
If you are genuinely on Symfony (e.g., a team running both stacks):
composer require ikadoc/kcfinder-bundle
Register it in config/bundles.php (Symfony 4.4+) or AppKernel::registerBundles() (≤4.3):
Ikadoc\KCFinderBundle\KCFinderBundle::class => ['all' => true],
Then publish/copy KCFinder's PHP distribution into public/kcfinder (manual — expect no console command), and point CKEditor at it.
If you are on Laravel (the common case): the realistic workflow is to skip the bundle and vendor KCFinder directly — clone its PHP files into public/vendor/kcfinder or, better, a non-public directory behind a proxy controller you own. The bundle contributes essentially no reusable code for this.
Where to look first:
conf/config.php — uploadDir, per-type extension allowlists, maxSize, thumbnails. This file, not any Symfony config, is where behaviour actually lives.DependencyInjection/ extension and Resources/config/ — only to see how it injects an upload path, if you're porting that idea.First working use case — the only thing this does — a "Browse Server"/"Upload" dialog in the editor that drops a file into a directory and inserts a relative URL. For CKEditor 4:
CKEDITOR.replace('body', {
filebrowserBrowseUrl: '/kcfinder/browse.php?opener=ckeditor&type=files',
filebrowserUploadUrl: '/kcfinder/upload.php?opener=ckeditor&type=files',
filebrowserImageBrowseUrl: '/kcfinder/browse.php?opener=ckeditor&type=images',
filebrowserImageUploadUrl: '/kcfinder/upload.php?opener=ckeditor&type=images'
});
Note: CKEditor 5 does not read filebrowser* URLs at all — it needs a custom upload adapter. If you're on CKEditor 5, this package is irrelevant to you.
Before committing, sanity-check runtime: KCFinder is PHP 5-era and will likely break on PHP 8.1+ (Laravel 10/11). Prove it runs on your target before planning around it.
Treat whatever you build as legacy upload plumbing isolated behind a boundary you control, never as a native Laravel media layer.
Proxy every request through Laravel. The single most important pattern: do not let /kcfinder/*.php be served directly as static files from public/. That bypasses Laravel's web middleware, CSRF, auth guards, policies, and rate limiting — a parallel trust boundary. Instead route through a controller that enforces auth, then hands off:
Route::middleware(['auth', 'can:manage-media'])->group(function () {
Route::any('/media/browse', [KcfinderProxyController::class, 'browse']);
Route::post('/media/upload', [KcfinderProxyController::class, 'upload']);
});
The proxy includes the vendored KCFinder entrypoints after setting the session/env they expect. This is the closest you'll get to "day-to-day" ergonomics, and it's where all your real work happens.
Auth bridging is the actual integration cost. KCFider runs as plain PHP outside the framework pipeline, so it does not see Auth::user() or your middleware. Two workable strategies:
auth_request or Apache Require in front of the upload/browse endpoints, so only authenticated sessions reach them at all.session_name match config('session.cookie') and share the save path, so its built-in session check sees the same authenticated session. This is fiddly and a common source of "works in dev, denies/anonymous in prod" bugs.Config layering — keep it minimal on the framework side. Only two things belong in Laravel: where KCFinder lives and where uploads go. Everything else — max size, allowed/denied extensions, thumbnail dimensions — belongs in conf/config.php. Commit that file to your repo and diff it on every bump of the vendored code; that diff is the only upgrade trail you get.
Mirror KCFinder's type param (files / images / flash). Restrict extensions per type, and only enable the image plugin in the editor where images are permitted. This is your cheapest content-type segregation.
Persist relative paths, resolve at render. Store the path KCFinder returns, not an absolute URL, and resolve through asset() / Storage::url() / a media route so CDN and thumbnail generation stay consistent:
$post->image_path = '/uploads/images/'.$filename; // relative, stored
// blade:
<img src="{{ asset($post->image_path) }}">
If you want thumbnails, add a maintained filter (Glide, spatie/image) rather than KCFinder's built-in thumbnailer.
Migration path worth planning for now: for most Laravel teams the correct pattern is a maintained media layer (spatie/laravel-medialibrary, or UniSharp/laravel-filemanager for a full browser UI) plus a CKEditor 5 upload adapter (~50–100 lines). Keep that as the target and treat KCFinder as a stopgap inside one internal admin tool, if at all.
php artisan vendor:publish, no container bindings. Expecting these wastes an afternoon. The package is Symfony-only; on Laravel you're vendoring KCFinder yourself.conf/config.php) and two session models (Laravel vs KCFinder) produce recurring "uploads silently denied" or worse "anonymous upload allowed" bugs. Verify the session name and save path match exactly; a mismatch is a security issue, not just a convenience one.conf/config.php; front it with a WAF; keep it behind authenticated, trusted-author access.Validator, no Storage/Flysystem, no authorization policies, no MIME sniffing. If you persist what KCFinder returns, you've created files your app doesn't know about. Deletes in your app won't delete the file; plan a reconciliation/cleanup job.filebrowser* config is CKEditor 4 only. On CKEditor 5 (the maintained editor) you must write a custom upload adapter — which makes this package pointless for that path.uploadDir, whether uploadDir is an absolute path, PHP upload limits (upload_max_filesize/post_max_size), session visibility inside KCFinder, and the type allowlist in conf/config.php. KCFinder swallows a lot of errors; enable its own debug flags.spatie/laravel-medialibrary plus a CKEditor 5 upload adapter, or UniSharp/laravel-filemanager if you need a full browser UI. Only reach for KCFinder when an existing internal Symfony admin panel already runs CKEditor 4, the users are trusted, and you've accepted ownership of the fork.How can I help you explore Laravel packages today?