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

Objectstorage Laravel Package

softlayer/objectstorage

PHP client for IBM SoftLayer Object Storage (OpenStack Swift). Provides a simple API for authentication, container management, object uploads/downloads, and metadata, making it easy to add Swift-based object storage to PHP and Laravel apps via Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the official-but-dormant SoftLayer Object Storage client — a thin wrapper over the OpenStack Swift (v1) REST API, built on top of php-opencloud's ObjectStore resources. Before you write a line of feature code, confirm it is still the right tool for you: the repo has low adoption (29 stars, 0 dependent packages) and no recent maintenance. If you can use league/flysystem-aws-s3-v3 against a Swift/S3-compatible gateway, or the modern php-opencloud/openstack SDK, do that instead. If you are locked into a SoftLayer/IBM Cloud Object Storage Swift account and need the X-Auth-Token + X-Storage-Url flow it handles, read on.

Install and sanity-check compatibility

composer require softlayer/objectstorage

Expect dependency friction on modern PHP. The package predates PHP 8 and pulls an old Guzzle/php-opencloud stack, so you may need --ignore-platform-reqs, a version pin, or a vendored fork. Run composer why-not php 8.2 and inspect composer.json in the package before committing to it in composer.json.

Auth model — this is not IAM

SoftLayer Object Storage uses your SoftLayer username plus an API key to obtain a Swift auth token and storage endpoint. The two relevant entry points:

  • SoftLayer\Common\Token::getToken($username, $apiKey) — performs the auth handshake.
  • SoftLayer\ObjectStorage\Client::getClient($token, $endpoint) — returns the storage client.
use SoftLayer\Common\Token;
use SoftLayer\ObjectStorage\Client;

$token  = Token::getToken(config('objectstorage.username'), config('objectstorage.api_key'));
$client = Client::getClient($token, config('objectstorage.endpoint'));
// endpoint example: https://syd01.objectstorage.softlayer.net/auth/v1.0/

Where to look first

  1. src/SoftLayer/ObjectStorage/Client.php — the factory and the container-level methods (getContainers, getContainer, createContainer).
  2. src/SoftLayer/Common/Token.php — cache behavior and expiry handling.
  3. The underlying OpenStack\ObjectStore\v1\Resource\Container / StorageObject classes (installed via Composer) — these hold the actual object CRUD methods.

First use case

The classic day-one win is moving user-generated uploads off the app server: create a container per environment (app-prod-uploads, app-staging-uploads), write uploaded files into it, and keep only the object key in your database. Start there before touching CDN or lifecycle features.

Implementation Patterns

Bind the client as a singleton; don't build it per request call

Put credentials and endpoint in config/objectstorage.php fed by env vars, and register a singleton in a service provider so the token handshake happens at most once per process:

$this->app->singleton(\SoftLayer\ObjectStorage\Client::class, function () {
    $token = \Cache::remember('sl_obj_token', 60 * 60 * 12, function () {
        return \SoftLayer\Common\Token::getToken(
            config('objectstorage.username'),
            config('objectstorage.api_key')
        );
    });

    return \SoftLayer\ObjectStorage\Client::getClient($token, config('objectstorage.endpoint'));
});

Inject Client into the services that need it (an ObjectStorageService, a filesystem adapter, a job), never call getClient() from controllers.

A storage service, not scattered calls

Wrap container/object operations in one class so the rest of the app speaks in your domain terms (putAvatar, avatarUrl, deleteExport) and the package-specific types stay in one file. This is also where you swap in a different backend later — the biggest risk with this package is lock-in.

Uploads: stream, don't buffer

StorageObject::setContent() accepts a stream or string. For anything a user can upload, pass a stream so you don't load a 50 MB video into memory:

$container = $client->getContainer('app-prod-uploads');
$object    = $container->getObject("avatars/{$user->id}.png");
$object->setContent(fopen($request->file('avatar')->getRealPath(), 'r'));
$object->create(['contentType' => $request->file('avatar')->getMimeType()]);

Note the API shape: create the object on the container, set the content, set content type, then persist. Verify the exact call (create() vs save()) against the installed php-opencloud version — it varies.

Path prefixes instead of containers

Swift containers are flat; / in object names is a pseudo-directory. Keep one bucket per environment and namespace by prefix (users/42/avatar.png, exports/2024-06/...). Listing with a prefix and paginating by marker is cheaper than spinning up containers per feature, and it maps cleanly onto Eloquent if you store object_key plus container on a media table.

Queue the write path

Auth + upload latency makes synchronous uploads a bad fit for request/response. Dispatch a job after the user upload completes locally (or straight from the temp file), and store pending → stored status on the model. This also gives you a natural retry point when Swift returns transient errors.

Filesystem integration options

  • Best-effort: implement a small Flysystem adapter (League\Flysystem\FilesystemAdapter) over Container/StorageObject and register it as a Laravel disk in config/filesystems.php. Then Storage::disk('softlayer')->put(...) works and the rest of the app is provider-agnostic.
  • Pragmatic: a thin service with putPath/getPath/url/delete and no Flysystem dependency. Given the package's age, the smaller the surface area, the better.

Serving files

Swift containers can be public, or you proxy through a route (authorize, get the object stream, stream back with the right headers). Public containers give you CDN-friendly URLs; private containers plus signed/proxy routes keep access control in Laravel. Decide up front — retrofitting access checks onto public URLs is painful.

Testing

Bind a fake against the same interface your service exposes. For real integration tests, run a local Swift container (docker run an OpenStack Swift dev image) and point the endpoint at it. That's more reliable than mocking php-opencloud's object graph.

Gotchas and Tips

Token expiry is your first 401

Auth tokens are short-lived (historically ~24 h). If you cache the token as shown above, make sure the TTL is under the expiry and that you refresh on a 401 — a cached expired token produces confusing 401s that look like credential failures. Also, the auth endpoint and storage endpoint are different hosts; passing the wrong one yields 401 or 404.

Abandoned dependency = platform risk

Assume no upstream fixes. Pin the version, and vendor/fork if you must patch it. Check composer.json PHP and Guzzle constraints early — this is where most "can't install" issues come from. The NOASSERTION license identifier means the license wasn't machine-detectable; confirm licensing before shipping commercially.

Container deletion requires empty

deleteContainer fails on a non-empty container. Delete objects first (with pagination — listing returns pages), then the container. Same ordering matters for teardown scripts and test cleanup.

Metadata and content type defaults

Swift stores custom metadata as X-Object-Meta-* headers. The wrapper handles the prefixing, but keys are case-sensitive and hyphens are constrained. Content-Type defaults to application/octet-stream if not set — always set it explicitly or browsers will download PNGs instead of rendering them.

Errors are Guzzle exceptions

Wrap calls and distinguish 404 (missing object) from 401/403 (auth) from 5xx (transient, retry). Don't let raw Guzzle exceptions bubble into controllers; map them to your domain exceptions in the service layer.

Large objects

Single-request uploads have size limits and can time out on slow links. Swift supports segmented uploads (manifest + parts), but this client's support is thin — for very large files, either accept the limit or drop to direct Swift API calls for the segment flow. Set sane Guzzle timeouts either way.

Config quirks worth checking

  • Endpoint must be the Swift auth URL for the region, not the regional API host.
  • Some accounts expose both private and public (CDN) endpoints; pick deliberately.
  • Region changes require re-auth against the new endpoint; a stale token will 401.

Extension points

The clean seams are: your own storage service interface (swap providers), a custom Flysystem adapter (swap filesystem semantics), and a token provider you control (swap auth). Nothing in the package is designed for extension, so build these wrappers rather than subclassing Client.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console