Download the PHP package denzyl/box without Composer

On this page you can find all versions of the php package denzyl/box. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package box

Box

Box is a modern, type-safe error handling library for PHP 8.2+. It replaces implicit, optional exceptions with an explicit Result type that forces callers to acknowledge both success and failure.

Inspired by Rust's Result<T, E> type, this library brings functional error handling and "railway oriented programming" to PHP with full support for generics (via PhpDoc), immutability, and composition.


Why a Result Type?

Traditional PHP error handling has two common patterns, both flawed:

Pattern Problem
Exceptions Uncaught exceptions crash the program. Nothing in the signature tells the caller an error can happen — you have to read the implementation or docs.
Returning null/false Silent failures. The caller can forget to check. No error context — you lose why it failed.

A Result solves both: the return type is always Result<T, E>, so the caller must handle success and failure. The error variant carries the full Throwable, so nothing is lost. And since PHP has no native generics, PhpDoc annotations make the chain traceable for your IDE and static analysers.


The Generic Chain

The library is designed around three components that work together:

This split exists by design: separating the "what could go wrong" (the Item that may throw) from the "what do I do with the result" (the Result). You keep your domain logic clean (just throw when something's wrong) and get a functional, type-safe result on the other side.

Item<T> — The Input Contract

The Item interface represents an operation that can produce a value of type T or throw. It's deliberately minimal: write your domain logic naturally, and let Box::put() convert it into a Result.

Why Item exists: Instead of wrapping every call in try/catch yourself, you declare "this is a fallible operation" by implementing Item. Box::put() handles the boilerplate of catching exceptions and wrapping them in an Error for you.

Box::put() — The Gateway

Box::put() calls grab() and wraps the result in a Result, catching any exceptions:

Why Box::put(): It's the bridge between imperative domain code (which throws) and the functional result pipeline. Your grab() method throws naturally when something's wrong — Box::put() captures that throwable and turns it into an Error variant, keeping the API honest without forcing you to manually construct Result::error() everywhere.

Result<T, E> — The Output

A Result is either Ok(T) holding a value or Error(E) holding a Throwable. All methods are annotated with generics so your static analyser (PHPStan, Psalm, PhpStorm) can track types through chains of map(), flatMap(), etc.

Why Result is immutable: Every transformation (map, filter, flatMap, etc.) returns a new Result instance. This means you can safely share results across functions without worrying about mutation bugs. The original value is never modified.

Wiring It Together

To preserve generic type information, your Item implementations must declare an @implements PhpDoc tag:

Now calling Box::put() returns Result<User, Throwable>:


Creating Results

Why choose one over the other?

Method Use when...
Result::ok() You already have a value and know it's valid
Result::error() You already have a Throwable and want to represent failure
Result::try() You have a callable that might throw and want to capture any exception automatically
Result::when() You have a boolean condition and want to branch into Ok or Error in one expression — more concise than if/else

Working with Results

There are three styles, each with a different trade-off. Pick the one that fits your context.

Pattern Matching (Explicit)

Why use this: When you need to handle both branches with completely different logic, or when your handler is complex enough that readability suffers from nested callbacks. The match expression forces you to enumerate all cases — the compiler won't let you forget the Error branch. Best for: controller-level code where you're translating domain results into HTTP responses or UI state.

Railway (Implicit / Chain)

Why use this: When you only care about the success path and have a reasonable fallback for failure. Each map() only runs if the result is Ok — errors pass through silently. This is "railway oriented programming": the happy path runs along one track, the error track bypasses all operations. Best for: data transformation pipelines (parsing, validation, cleanup) where you're threading a value through a series of steps.

Match (Functional)

Why use this: More explicit than railway, more concise than match ($result->state()). Unlike the railway pattern, both branches are visible in one place. Unlike pattern matching, you don't need to import ResultState. Best for: one-off handlers where you want both branches visible but the logic is simple enough that a full match statement feels verbose.

Compare the Three

Style Verbosity Both branches visible? Best for
Pattern matching High Yes Complex branching, API/UI handlers
Railway Low No (chain assumes success) Data pipelines, transformations
Functional match Medium Yes Simple handlers, callbacks

Safe Unwrapping

These methods extract the inner value. They differ in what happens when the result is an Error.

Why so many? Each serves a different failure-handling philosophy:

Method When to use
collect() When you know it must succeed (e.g. after filter + recover). Throws if you're wrong — it's a safety net for bugs in your logic, not a control flow mechanism.
unwrapOr($default) When you have a simple static fallback. Use for: "default config value", "empty string", "0".
unwrapOrElse(callable) When computing the fallback is expensive or requires the error context. The callable receives the Throwable so you can log it, format it, etc. Unlike unwrapOr, the fallback is lazy — it only runs if the result is an error.
expect($message) Like collect() but with a custom error message that helps debugging. Use when the default RuntimeException message isn't descriptive enough.

Side-Effects (Tap)

Sometimes you want to do something with the value without transforming it — logging, caching, metrics.

Aliases: inspect() = tapOk(), inspectErr() = tapErr().

Why tapOk / tapErr exist: map() implies transformation and should be pure. When you're just observing (logging, emitting events, incrementing counters), use tapOk/tapErr. They pass the result through unchanged — they're side-effect windows in an otherwise pure pipeline. The alias inspect() exists for familiarity if you come from Rust.

The key difference from map():


Transforming Collections

When your Result holds an iterable, these methods let you work with the elements.

mapEach(callable, $resetKeys = true) — transforms every element of the iterable. If $resetKeys is true, the returned array has sequential numeric keys (like array_values()). Set $resetKeys = false to preserve string or non-sequential keys.

filterEach(callable, $resetKeys = true) — keeps only elements where the predicate returns truthy.

Why seperate methods? Calling map() on a Result takes you from Result<T, E> to Result<TNew, E>. If T happens to be an array and you want to map its elements, you'd need to write ->map(fn(array $items) => array_map(...)) manually every time. mapEach/filterEach are convenience methods that skip the boilerplate. They also only work on iterables and throw LogicException otherwise — catching misuse early.


Transformation Reference

Method Input Output Description
map(callable) Result<T, E> Result<TNew, E> Transform the ok value, skip on error
mapOr(default, callable) Result<T, E> TNew Map ok value or return default
mapOrElse(errCb, okCb) Result<T, E> TNew Map either variant to a value
mapEach(callable) Result<iterable<T>, E> Result<array<TNew>, E> Map each element of the iterable
flatMap(callable) Result<T, E> Result<TNew, E> Chain another Result-returning operation
flatten() Result<Result<T, E>, E> Result<T, E> Flatten nested Results
mapError(callable) Result<T, E> Result<T, ENew> Transform the error
recover(callable) Result<T, E> Result<T, never> Recover from error (turns Error→Ok)
orElse(callable) Result<T, E> Result<TNew, ENew> Recover with another Result
filter(callable) Result<T, E> Result<T, E> Error if predicate fails
filterEach(callable) Result<iterable<T>, E> Result<array<T>, E> Filter elements of the iterable

Deep Dive: flatMap

Why flatMap over map? map wraps the callback's return in a new Ok. If your callback already returns a Result, you'd get Result<Result<TNew, ENew>, E> — a nested result. flatMap avoids this nesting by expecting the callback itself to return a Result. Use flatMap when your callback deliberately decides success/failure (the inner logic can also produce an error). Use map when the inner logic is infallible and only transforms.

Callback returns TNew Callback returns Result<TNew, ENew>
Use map Yes — wraps in Ok automatically Avoid — creates nesting
Use flatMap Errors (expects Result) Yes — no nesting

Deep Dive: recover vs orElse vs mapError

All three handle errors, but with different goals:

Method Ok passes through? Error becomes...
recover Yes (unchanged) New Ok value
orElse Yes (unchanged) Whatever Result the callback returns
mapError Yes (unchanged) Same state, transformed error

Batch Operations

When you have multiple independent results and need to combine them.

Why three different batch methods?

Method Returns Use when...
combine() Result<array, E> You need all results to proceed. Validation of a form with multiple fields — one field fails, the whole form fails.
any() Result<T, E> You only need one success. Trying multiple API endpoints or cache replicas — the first one that works is enough.
partition() {ok: array, error: array} You want to process both successes and failures independently. Bulk operations where you need to report partial failures.

Logical Composition

Why these matter:


Interoperability


Installation

Development

The release gate validates Composer metadata, checks Mago formatting, runs Mago linting, runs Phanalist against src/, and executes the PHPUnit suite. For a local Phanalist checkout, run:


Philosophy

"If an error can happen, the caller should have to look at it."

This library does not pretend PHP has checked exceptions. It simply provides a better failure model than throw-and-pray by making your API contracts honest and your code's intent explicit.

Because PHP lacks native generics, PhpDoc annotations are the glue that makes type inference work. Always add @implements Item<YourType> to your Item classes so your IDE and static analysers can trace the generic chain from Box::put() through to the final Result.


All versions of box with dependencies

PHP Build Version
Package Version
Requires php Version >=8.2
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package denzyl/box contains the following files

Loading the files please wait ...