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

Smb Bundle Laravel Package

isklv/smb-bundle

SMB Bundle adds SMB/CIFS network share support to Laravel/PHP applications, offering simple configuration and a familiar API for reading, writing, listing, and managing files on remote SMB servers. Simplify remote file operations and avoid custom integration glue code. Ideal for NAS and file-server...

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before anything else. isklv/smb-bundle is a Symfony2 bundle from 2015, not a Laravel package. There is no service provider, no facade, no auto-discovery, and the last release predates Laravel 5.2. In Laravel you will not be composer require-ing this and calling it a day — you will be either (a) extracting the underlying SMB client and wiring it yourself, or (b) reading the bundle's service definitions as a specification for your own adapter. Plan for that from the first hour; it changes the estimate from "afternoon" to "day plus a spike."

Where to look first, in this order. All inside vendor/isklv/smb-bundle/:

  1. composer.json — read the PHP floor and the SMB client dependency. This tells you what actually does the work (typically a icewind/smb-style client or an smbclient extension wrapper). That library is your real dependency, not the bundle.
  2. DependencyInjection/*Extension.php — the config tree. This is the honest documentation of what settings exist, since the README won't be.
  3. Resources/config/services.xml (or .yml) — what services are defined, what's injected, what the share service looks like.
  4. The filesystem wrapper class itself — the public methods: read, write, delete, listDirectory/ls, exists, mkdir, rename. Confirm the return types; 2015-era SMB wrappers frequently return false on failure instead of throwing.

Host prerequisite. Most PHP SMB clients of this vintage bind to the PECL smbclient extension (libsmbclient) — a C library. composer install will succeed without it and your code will fail at runtime. Verify with:

php -m | grep -i smbclient

If it's absent, check whether the client falls back to a pure-PHP transport (phpseclib) before assuming you can install it in your container.

First use case — one share, read and write. Don't start with multi-share config. Get a single share working end-to-end:

// app/Services/Smb/Share.php
interface Share
{
    public function list(string $dir): array;
    public function read(string $path): string;
    public function write(string $path, string $contents): void;
    public function delete(string $path): void;
}

Bind one implementation to one share in a service provider, drive it from a Tinker session or an Artisan command against a known test share, and confirm you can list, read, and write. Everything after that is configuration.

Implementation Patterns

Config-driven share registry. The bundle's core idea is sound and worth keeping: shares are named in config, credentials live there, and application code asks for a share by name. Mirror it in config/smb.php:

return [
    'shares' => [
        'reports' => [
            'host'      => env('SMB_REPORTS_HOST'),
            'share'     => env('SMB_REPORTS_SHARE'),
            'user'      => env('SMB_REPORTS_USER'),
            'password'  => env('SMB_REPORTS_PASS'),
            'domain'    => env('SMB_REPORTS_DOMAIN', 'WORKGROUP'),
            'timeout'   => 10,
        ],
        'archive' => [ /* ... */ ],
    ],
];

Never call env() outside this file — see Gotchas.

Manager + per-share client. Resolve share clients lazily from a manager so you don't open an SMB connection on every request:

class ShareManager
{
    private array $resolved = [];

    public function __construct(private array $config, private ClientFactory $factory) {}

    public function share(string $name): Share
    {
        return $this->resolved[$name] ??= $this->factory->make($this->config[$name]);
    }
}

Register it as a singleton, and register Share::class only if you have a genuine "default share." Prefer injecting ShareManager and naming the share at the call site; a default-share binding quietly couples every consumer to one config key.

Wrap it, always. This is the single most important pattern given the package's age. Your application depends on your Share interface, not on the bundle's class. The wrapper is where you normalise the 2015 API into something modern:

final class SmbShare implements Share
{
    public function __construct(private $client) {}

    public function write(string $path, string $contents): void
    {
        $ok = $this->client->put($this->normalise($path), $contents);

        if ($ok === false) {
            throw new SmbException("Write failed: {$path}");
        }
    }

    private function normalise(string $path): string
    {
        return ltrim(str_replace('\\', '/', $path), '/');
    }
}

The wrapper converts false returns into exceptions, normalises path separators, adds retries, and — critically — is the only file you rewrite when you eventually fork or replace the library.

Queue the work, don't do it in the request. SMB round-trips are tens to hundreds of milliseconds and are entirely outside your control. A request that lists a 2,000-file directory and reads three of them is a request that times out when the file server is busy. Pattern: HTTP request validates and dispatches a job; the job uses ShareManager; results land in your DB or local disk.

class ImportReportJob implements ShouldQueue
{
    public int $timeout = 300;
    public int $tries = 3;
    public array $backoff = [10, 60, 300];

    public function handle(ShareManager $shares): void
    {
        $share = $shares->share('reports');
        $tmp = tempnam(sys_get_temp_dir(), 'smb');
        $share->download('monthly/2024-11.csv', $tmp);
        // ... parse, persist, unlink
    }
}

Stream, don't buffer. Reading a 400 MB file into a PHP string is how you discover your memory_limit. Use stream or file-to-file methods where the client offers them (get/put with a resource or path), and only fall back to string reads for genuinely small files. Same on the write side.

Testing. Because the client is injected, fake the interface, not the network:

$this->mock(Share::class, function ($m) {
    $m->shouldReceive('list')->with('inbound')->andReturn(['a.csv', 'b.csv']);
});

Keep one integration test, tagged and skipped by default, that hits a real share in CI — that's how you catch transport changes that unit tests can't see.

Local-disk fallback adapter. A useful second implementation of Share backed by Storage::disk('local') lets you run the whole test suite and local dev without any SMB server at all. It also gives you a safe path if the file server is unreachable in a non-critical flow.

Gotchas and Tips

It is not a Laravel package — there is no auto-discovery. If you install it expecting IsKlv\SmbBundle\SmbBundle to register itself, you'll get a confusing "class not found" for services that were never bound. Either write a thin SmbServiceProvider that translates the bundle's services.xml into container bindings, or skip the bundle entirely and depend on the underlying client. For a 0-star, 2015, Symfony2 package, the second option is usually correct.

PECL smbclient is a hard host dependency. No composer.json will install libsmbclient for you. Your Dockerfile needs libsmbclient-dev plus pecl install smbclient, and your production image needs to match your dev image. This is the most common reason the package "works locally, not in prod."

2015 code on a modern PHP. Expect PHP 5.5/5.6 idioms: array() syntax, no return types, no ??, possibly each() or other removed functions. Run PHPStan/Larastan over it immediately — you want to know before you write against it which methods actually exist and what they return. If it breaks on PHP 8, that's a fork, not a patch.

SMB1 is dead on modern servers. A 2015 client may negotiate SMB1 by default; Windows Server 2019+ and current Samba ship with SMB1 disabled. If you get connection failures but credentials are correct, that's the cause. Look for a protocol/version option in the client, and be prepared to move to a maintained client (icewind/smb v3+, or smbclient with explicit -m SMB3).

Failures return false. Old SMB wrappers almost never throw. $client->get(...) returning false and your code writing it to a file produces a zero-byte file that passes downstream validation. Every wrapper method must check the return and throw. Also check file_exists before read — many of these clients emit a PHP warning rather than returning false.

Path and case semantics are Windows, not Linux. Backslashes vs forward slashes differ per method. Directory listings come back with names in arbitrary/uppercase-preserved form, and lookups are case-insensitive on the server but not in your array comparisons. Normalise both directions in the wrapper, and never build paths with DIRECTORY_SEPARATOR.

Non-ASCII filenames. SMB uses UTF-16 on the wire; the extension converts per locale. If your filenames contain accents or CJK, set a UTF-8 locale in the container (LC_ALL=C.UTF-8) or expect mojibake in list() results — and a file not found when you try to read them back.

Timeouts and connection reuse. Set an explicit timeout in config; the default is often "forever," which is how a queue worker hangs silently. Connections are typically opened per client instance — don't build a new client per file in a loop, and don't share one across forked queue workers. If you use pcntl or a forking worker, reconnect after fork.

Config caching will bite you. If you translate bundle config into config/smb.php, remember Laravel caches config: any env() call outside a config file returns null in production. All credential reads must be in the config file only.

No locking worth relying on. SMB share modes are advisory in practice. Two workers writing the same filename will interleave. Write to a temp name and rename, or use unique filenames and a reconcile step.

Debugging. Turn on the client's own logging first (most have a debug flag or accept a logger) — a false return with no detail is undebuggable. At the shell level, verify the share independently before blaming PHP:

smbclient //host/share -U 'DOMAIN\user%pass' -m SMB3 -c 'ls'

If that works and PHP doesn't, it's the extension; if that fails, it's network, credentials, or protocol version.

Extension points to plan for. The bundle gives you essentially one: swap the client service. That's enough — but it means your adapter is the real extension point going forward. Budget for a fork: the repo has zero stars and its last commit is a decade old, so treat it as reference material you own a copy of, not a dependency you'll get patches to.

Version pinning. If you do install the bundle, pin it exactly, not ^0.1 — under SemVer a 0.x minor bump may break, and there is no changelog to read before you upgrade.

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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource