Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Webdav Laravel Package

zetacomponents/webdav

Zeta Components WebDAV is a PHP library for building WebDAV servers and clients. It handles protocol requests, resource locking, property management, and response parsing, enabling file-management APIs over HTTP.

Deep Wiki
Context7

Getting Started

Install via Composer:

composer require zetacomponents/webdav

Composer will pull in zetacomponents/base. Classes are prefixed ezc* and the package registers Zeta's own autoloader alongside Composer's, so it coexists with Laravel/Symfony autoloading.

Read in this order: ezcWebdavServer → ezcWebdavServerConfiguration → ezcWebdavFileBackend → ezcWebdavBasicAuthenticator → ezcWebdavLockPlugin. Those five cover ~90% of day-to-day work.

Minimal endpoint (drop it in public/dav.php, or mount it as an isolated route — see below):

<?php
require __DIR__ . '/../vendor/autoload.php';

$server = ezcWebdavServer::getInstance();

$server->config = new ezcWebdavServerConfiguration(
    '/dav',                                   // URI prefix the endpoint is mounted at
    new ezcWebdavBasicAuthenticator(
        new ezcWebdavAuthStore([
            new ezcWebdavAuth('alice', 'secret'),
        ])
    )
);

$server->config->backend = new ezcWebdavFileBackend('/srv/dav-data');

$server->addPlugin(
    new ezcWebdavLockPlugin(new ezcWebdavLockPluginConfiguration())
);

$server->handleRequest();

First use case: expose a storage directory as a WebDAV share so macOS Finder, Cyberduck, Windows Explorer, or rclone can browse and edit files. Smoke-test with:

curl -i -X OPTIONS http://localhost/dav/
curl -i -X PROPFIND -H 'Depth: 1' http://localhost/dav/

Note: constructor signatures shifted between Zeta branches. The docs (and git blame) are your source of truth if an argument count doesn't line up.

Implementation Patterns

Filesystem share. ezcWebdavFileBackend gives you GET/PUT/DELETE/MKCOL/COPY/MOVE/PROPFIND/PROPPATCH plus lock state stored under the document root. This is the default choice; almost everything else is a special case.

Database or S3 backend. Implement ezcWebdavBackend (get, head, put, delete, mkcol, copy, move, propFind, propPatch, and the lock methods if you support locking). Use this when the "files" are records/objects you already manage. You also need a ezcWebdavLockManager and a ezcWebdavPropertyStorage implementation if you want locks or dead properties to persist.

Per-user permissions. Implement ezcWebdavAuthorizer:

class AppAuthorizer implements ezcWebdavAuthorizer
{
    public function authorize($user, $path, $access = ezcWebdavAuthorizer::ACCESS_READ)
    {
        // return bool based on user + path + access mode
    }
}

Wire it as the second argument to ezcWebdavServerConfiguration (or set it on the config) alongside a BasicAuthenticator. The auth store answers "is this credential valid?", the authorizer answers "may this user touch this path?".

Cross-cutting concerns via a plugin. Implement ezcWebdavPluginInterface with getHooks() returning event-name → callback. Use it to log requests, filter .DS_Store / ._* files, enforce quotas, or rewrite properties. This is the clean extension point — don't fork the backend for one-off rules.

Laravel integration. WebDAV verbs (PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK) are not covered by Route::any. Register them explicitly and bypass the response pipeline:

Route::match(
    ['get','put','delete','options','head','post',
     'propfind','proppatch','mkcol','copy','move','lock','unlock'],
    '/dav/{path?}',
    fn () => require base_path('dav/server.php')
)->where('path', '.*')
 ->withoutMiddleware([
     \Illuminate\Session\Middleware\StartSession::class,
     \Illuminate\View\Middleware\ShareErrorsFromSession::class,
     \App\Http\Middleware\VerifyCsrfToken::class,
 ]);

End dav/server.php with $server->handleRequest(); exit;. Do not return the output through a Laravel Response for general use: the server emits headers directly and streams GET bodies with flush(), which breaks buffering and corrupts large downloads. If you absolutely must produce a Response, it only works for small bodies: ob_start() before handleRequest(), then http_response_code(), headers_list(), ob_get_clean() — and treat it as a fallback.

Testing. ezcWebdavServer::getInstance()->handleRequest($request) accepts a manually constructed ezcWebdav*Request (e.g. new ezcWebdavPropFindRequest('/foo')). That lets you unit-test a custom backend or authorizer without an HTTP round-trip.

Gotchas and Tips

Path prefix must match REQUEST_URI. The configuration's first argument is the mount path the server strips. If you're behind nginx/Apache proxying or a subdirectory, make the prefix match what the client actually sees, and configure TrustProxies if Laravel is rewriting the path.

Nothing may be output before handleRequest(). No BOM, no trailing ?>, no dd(), no accidental blank line. Headers-already-sent means malformed multistatus XML or a status code that never reaches the client — the classic "Finder says the share is unavailable" symptom.

Web server method passthrough. nginx: raise client_max_body_size for PUT, and make sure the try_files/rewrite doesn't swallow the custom verbs. Apache: disable mod_dav if it's enabled (it fights for the same URIs), and confirm LimitExcept/<Directory> doesn't restrict PROPFIND. Symfony/Laravel routing needs explicit verb lists, as above.

Basic auth is cleartext. Terminate TLS at the proxy and use HTTPS everywhere. Digest auth exists (ezcWebdavDigestAuthenticator) but is painful through proxies. Windows Explorer will refuse Basic auth over HTTP unless the host is explicitly trusted — HTTPS fixes it.

Enable locking for Windows/Office. ezcWebdavLockPlugin is not on by default. Without LOCK/UNLOCK, Office and Explorer fail on save with 500s or "file in use" loops. With the file backend, lock state lives under the docroot — make sure it's writable and excluded from your backups.

Dead properties need storage. PROPPATCH from Office/Finder is silently dropped unless a ezcWebdavPropertyStorage is wired into the backend. With ezcWebdavFileBackend this is handled; with a custom backend, no-op is fine unless a client depends on it.

Depth: infinity is a CPU trap. A recursive PROPFIND on a large share will hammer the disk. Many servers return 403 for infinity per RFC 4918 — that's usually the right call. Even Depth: 1 on a directory with tens of thousands of entries is slow on the file backend.

Filenames and path traversal. Client-supplied UTF-8 paths must round-trip cleanly through the backend, and the backend must guarantee no escape from the docroot. This is especially fragile on Windows-hosted shares. Verify with .. and encoded %2e%2e probes before opening the endpoint.

Don't buffer GET. Large downloads are streamed by the file backend. Wrapping the endpoint in Laravel middleware that captures output, or in ob_start(), will hold entire files in memory. The exit-at-end pattern exists for a reason.

PHP 8 compatibility. Zeta Components are effectively frozen; expect create_function/each (removed), dynamic-property deprecations, and possible null-passing warnings. Pin a known version, run on a supported PHP line, and budget time for small patches or a maintained fork. Test the PUT path specifically — that's where the old stream code tends to break.

License. The repository reports NOASSERTION. Historical Zeta releases are permissive, but verify the actual license file before redistributing or bundling; it's a five-minute check that can save a compliance review.

Client quirks worth pre-empting.

  • macOS Finder drops .DS_Store and AppleDouble ._* files everywhere — filter them in a plugin.
  • Finder insists on HTTPS with a valid certificate; self-signed certs quietly fail.
  • Windows sends an extra PROPFIND immediately after MKCOL and expects DAV: 1,2 in OPTIONS.
  • An empty PROPFIND body means allprop; don't treat it as an error.
  • Some clients send Content-Type: text/xml; charset=utf-8 instead of application/xml; the parser is lenient but malformed XML yields 400. Log the raw body while debugging.

Debugging strategy. Almost every WebDAV failure is infrastructure — rewrite rules, a blocked verb, an auth loop — not protocol logic. Log method, path, status, and body for a week during rollout, and compare against the output of a plain curl -X PROPFIND. If curl works and Finder doesn't, the problem is auth, TLS, or a Depth: 1 follow-up the client expects.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels