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

Operating System Laravel Package

innmind/operating-system

Provides a portable abstraction over the host operating system for PHP, exposing filesystem, process, clock, environment, and related services through a consistent, testable API so applications can avoid direct reliance on platform-specific functions.

View on GitHub
Deep Wiki
Context7

Getting Started

innmind/operating-system is a single object that represents the machine your script is running on. Instead of reaching for file_get_contents(), exec(), date(), gethostname(), PHP_OS_FAMILY or pcntl_signal() scattered through your code, you ask one injected object for the capability you need. Everything OS-dependent becomes a dependency you can substitute.

composer require innmind/operating-system

Check composer.json for the PHP floor and for ext-posix / ext-pcntl (process and signal features lean on them on Unix). Those are usually suggest-level, so a missing extension turns into a failed capability rather than a hard install failure.

Where to look first: the Innmind\OperatingSystem\OperatingSystem interface. It is the map of the package — roughly eight accessor methods, each returning a sub-service:

  • filesystem() — the filesystem as the script sees it (mount points, temp dir, root), built on innmind/filesystem
  • clock() — time, from innmind/time-continuum
  • processes() — running and forking sub-processes
  • sockets() / http() / remote() — network capabilities
  • control() — signals, graceful halt
  • status/environment information (pid, user, whether you're already running, etc.)

Read that interface before the README; it tells you exactly what the abstraction covers.

First use case: a CLI command that reads a config file, shells out to a tool, timestamps a log line, and shuts down cleanly on Ctrl-C. Every one of those four things is a method on the OS object.

use Innmind\OperatingSystem\Factory;

$os = Factory::build();          // do this exactly once, at the composition root

$os->filesystem()
    ->get(Path::of('/etc/app/config.json'))
    ->match(
        fn($file) => $file->content()->toString(),
        fn() => throw new RuntimeException('config missing'),
    );

Build it in public/index.php or bin/console, then hand it (or, better, its parts) to whatever constructs your application objects. Never call Factory::build() deep inside a class.

Implementation Patterns

1. Inject the aggregate at the top, narrow interfaces below. The OS object is a service locator; don't let it become one in your domain code. Extract the two or three sub-services a class actually needs at the composition root:

final class ReportWriter
{
    public function __construct(
        private Filesystem $filesystem,
        private Clock $clock,
    ) {}
}

$os = Factory::build();
$writer = new ReportWriter($os->filesystem(), $os->clock());

2. Prefer capability attempts over platform sniffing. Don't branch on PHP_OS_FAMILY. Call the operation and handle the failure — most sub-services return Attempt / Maybe / Either, so "this machine can't do that" is a value you pattern-match on, not an exception you forgot to catch. This keeps one code path across Linux, macOS and Windows.

3. One instance per process. In a long-running worker or a queue consumer, build once in the bootstrap and reuse. The object is immutable and cheap to share.

4. Fake at the seam, not at the root. For unit tests, implement the narrow sub-interface (Filesystem, Clock, Processes) — they are a handful of methods each. Only implement the full OperatingSystem interface (~8 methods) when you want an end-to-end "fixed machine" for integration tests. That fake is genuinely small enough to maintain by hand.

5. Freeze the clock and relocate the filesystem in tests. Point the filesystem at a temp-dir mount and inject a fixed Clock. Retry/timeout logic and "write to a relative path" logic become deterministic and stop touching your real working directory.

6. Decorate to audit reach. Wrap the OperatingSystem in a decorator that records which sub-services were requested. It is a quick way to catch a class that quietly grew a dependency on HTTP when it should only need the filesystem.

7. In a framework, bind it as a singleton. Register OperatingSystem as a service and register each sub-interface as a factory resolving from it (fn($c) => $c->get(OperatingSystem::class)->clock()). Your controllers and commands then type-hint the narrow interfaces.

8. Daemon control through the abstraction. Use control() for signal handling and graceful shutdown instead of pcntl_* directly, so the same binary at least reports the capability as unavailable on platforms without it rather than fatal-erroring.

Gotchas and Tips

  • The interface is broad, and that's the trap. Type-hinting OperatingSystem everywhere makes tests need a fake for the entire machine. Always narrow to the sub-interface the class actually uses.
  • Factory::build() performs real I/O. It probes paths, resolves the temp directory and current user. Build it once at the top of the process; calling it in a loop or per request is a measurable cost.
  • Failures are values, not exceptions, in most places. If you're trained on try/catch, you will silently ignore failed Attempts. Decide explicitly at each call site whether to match() or unwrap() (which throws).
  • Unsupported capabilities exist. Some sub-services are effectively stubs on certain platforms. Check the concrete implementation for your target before assuming Linux/macOS/Windows feature parity — particularly around processes and signals.
  • Immutability bites. Mounts and similar configuration return new instances. $os->filesystem()->mount($path); discards the result. Always reassign.
  • Paths are absolute. innmind/filesystem does not resolve relative to the current working directory. If you need ./config, join getcwd() yourself before constructing the path.
  • Version pinning matters. Nine stars, one maintainer, and a fast release cadence mean the interface does change between majors. Pin exact versions and read the changelog before bumping.
  • No logging inside the package. Wrap the Factory::build() call in your bootstrap and log the detected platform and available capabilities once. Most "works on my machine" reports are answered by that single line.
  • Debugging tip: when something "just returns nothing", unwrap the Attempt in a scratch script and print the failure reason — the abstraction's error values are more informative than the empty result your application logic produced.
  • Extension points: the two clean ones are (a) a hand-written OperatingSystem implementation for tests, and (b) decorators around it or around individual sub-interfaces for logging, metrics, or retries. Subclassing the concrete classes is not the intended path.
  • Zero Packagist dependents means almost no public example code. Budget an afternoon with src/ and the tests — the package's own test suite is the best documentation of intended usage.
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.
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
noith/settings
kda/laravel-package