PHPackages                             mapsight/pulp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. mapsight/pulp

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

mapsight/pulp
=============

Core library providing the stream-based processing engine.

v1.1.1(4w ago)01710MITPHPPHP ^8.2

Since May 22Pushed 1mo agoCompare

[ Source](https://github.com/open-mapsight/pulp)[ Packagist](https://packagist.org/packages/mapsight/pulp)[ Docs](https://github.com/open-mapsight/mapsight-pulp)[ RSS](/packages/mapsight-pulp/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (14)Versions (5)Used By (10)

Pulp
====

[](#pulp)

Stream-like file processing for PHP, inspired by Gulp.

Pulp moves `File` objects through a chain of handlers. Source handlers create files, transform handlers edit or replace them, branching handlers fan work out, and destination/result handlers write or collect the final output.

Features
--------

[](#features)

- **Pipeline API:** Compose file processing as `Pulp::start()->pipe(...)->run()`.
- **Virtual files:** Handlers pass `OpenMapsight\pulp\File` objects with `fileName`, `srcFileName`, and dynamic metadata.
- **Lazy file content:** Files from disk are not read until `$file->content` is accessed.
- **Stream access:** Large-file handlers can call `$file->stream()` to read without loading full content.
- **Branching:** Use `split`, `merge`, `shadow`, and `fileSwitch` for common flow-control patterns.
- **Extensible:** Implement handlers directly or build package-level helpers around them.

Quick Start
-----------

[](#quick-start)

```
use OpenMapsight\Pulp;
use OpenMapsight\pulp\File;

Pulp::start()
    ->pipe(Pulp::src('.*\.txt', __DIR__ . '/input'))
    ->pipe(Pulp::map(static function (File $file): File {
        $file->content = strtoupper($file->content);

        return $file;
    }))
    ->pipe(Pulp::dest(__DIR__ . '/output'))
    ->run();
```

Core Concepts
-------------

[](#core-concepts)

### Pipelines

[](#pipelines)

A pipeline is a chain of handlers:

```
Pulp::start()
    ->pipe(Pulp::src('.*\.json', __DIR__ . '/data'))
    ->pipe(/* handler */)
    ->pipe(Pulp::dest(__DIR__ . '/result'))
    ->run();
```

`run()` starts the chain. If no source handler is present, you can pass files directly:

```
$file = new File('example.txt');
$file->content = 'Hello';

$results = Pulp::start()
    ->pipe(Pulp::map(static fn(File $file): File => $file))
    ->run($file);
```

### Files

[](#files)

`OpenMapsight\pulp\File` represents a virtual file.

- `$file->fileName`: the pipeline-relative file name.
- `$file->srcFileName`: the original source name/path.
- `$file->content`: dynamic content property. For files sourced from disk, this lazy-loads the full file on first access.
- `$file->stream()`: returns a readable stream. For path-backed files this opens the source file directly; for generated string content it creates a temporary stream.
- Additional properties can be attached dynamically, for example `$file->stats` or `$file->isLogicallyEmpty`.

Use `$file->content` for normal transforms. Use `$file->stream()` in handlers that need to process large files without loading them completely.

API
---

[](#api)

### Pulp::start()

[](#pulpstart)

Creates an empty pipeline.

```
$pulp = Pulp::start();
```

### Pulp::src($patterns, $directory = '.')

[](#pulpsrcpatterns-directory--)

Creates files from matching paths.

- `$patterns`: a string, array of strings, file path, or `File`.
- `$directory`: base directory for recursive matching.

Patterns are regular expressions matched against relative file names.

```
Pulp::start()
    ->pipe(Pulp::src('.*\.csv', __DIR__ . '/data'))
    ->run();
```

### Pulp::srcFile($fileName, $aliasFileName = null, array $options = \[\])

[](#pulpsrcfilefilename-aliasfilename--null-array-options--)

Creates one file from a path. If `$aliasFileName` is provided, it becomes the virtual `fileName`.

### Pulp::srcHttp($method, $uri, array $guzzleOptions, $aliasFileName, array $options = \[\])

[](#pulpsrchttpmethod-uri-array-guzzleoptions-aliasfilename-array-options--)

Creates one file from an HTTP response body.

### Pulp::dest($directory, array $options = \[\])

[](#pulpdestdirectory-array-options--)

Writes incoming files into `$directory` using each file's `fileName`.

```
->pipe(Pulp::dest(__DIR__ . '/result'))
```

Common options:

- `flush`: call `fflush()` after writing.
- `skipExceptions`: log write errors and continue.
- `logSkipExceptions`: `stderr`, `stdout`, or `false`.

### Pulp::map($callback)

[](#pulpmapcallback)

Transforms or drops files.

```
->pipe(Pulp::map(static function (File $file): ?File {
    if ($file->fileName === 'skip.txt') {
        return null;
    }

    $file->content .= "\n";

    return $file;
}))
```

### Pulp::filter($callback)

[](#pulpfiltercallback)

Keeps only files where the callback returns truthy.

```
->pipe(Pulp::filter(static fn(File $file): bool => str_ends_with($file->fileName, '.json')))
```

### Pulp::results($callback)

[](#pulpresultscallback)

Collects all files that reach this handler and calls the callback at the end.

```
->pipe(Pulp::results(static function (array $files): void {
    // inspect results
}))
```

### Pulp::split(...$pipelines)

[](#pulpsplitpipelines)

Feeds the same incoming files into multiple branch pipelines and merges each branch's results back into the main pipeline.

Use this when one source should produce several outputs.

```
Pulp::start()
    ->pipe(Pulp::src('.*\.txt', __DIR__ . '/input'))
    ->pipe(Pulp::split(
        static fn(Pulp $p): Pulp => $p->pipe(Pulp::map(static function (File $file): File {
            $file->fileName = 'upper-' . $file->fileName;
            $file->content = strtoupper($file->content);

            return $file;
        })),
        static fn(Pulp $p): Pulp => $p->pipe(Pulp::map(static function (File $file): File {
            $file->fileName = 'lower-' . $file->fileName;
            $file->content = strtolower($file->content);

            return $file;
        })),
    ))
    ->pipe(Pulp::dest(__DIR__ . '/result'))
    ->run();
```

Branches may be `Pulp` instances or callbacks receiving a new `Pulp` instance.

### Pulp::merge(...$pulps)

[](#pulpmergepulps)

Runs independent pipelines and merges their output.

```
$a = Pulp::start()->pipe(Pulp::src('.*\.json', __DIR__ . '/a'));
$b = Pulp::start()->pipe(Pulp::src('.*\.json', __DIR__ . '/b'));

Pulp::start()
    ->pipe(Pulp::merge($a, $b))
    ->pipe(Pulp::dest(__DIR__ . '/result'))
    ->run();
```

Use `merge` for independent sources. Use `split` when you already have one incoming stream and want multiple outputs from it.

### Pulp::shadow($callback)

[](#pulpshadowcallback)

Taps the stream into a side pipeline without changing the main stream.

```
->pipe(Pulp::shadow(static fn(Pulp $p): Pulp => $p
    ->pipe(Pulp::debug())
))
```

`shadow` is useful for diagnostics or side effects. Its branch results are not merged back. Use `split` if branch output should continue downstream.

### Pulp::fileSwitch(array $patterns, ?callable $defaultCb = null)

[](#pulpfileswitcharray-patterns-callable-defaultcb--null)

Routes files into different sub-pipelines by file name.

```
->pipe(Pulp::fileSwitch([
    '.*\.json' => static fn(Pulp $p): Pulp => $p->pipe(/* JSON handlers */),
    '.*\.xml' => static fn(Pulp $p): Pulp => $p->pipe(/* XML handlers */),
], static fn(Pulp $p): Pulp => $p))
```

### Pulp::debug($length = 30)

[](#pulpdebuglength--30)

Prints a short preview for each file.

### Pulp::delete()

[](#pulpdelete)

Deletes each file's `srcFileName` from disk.

### Pulp::utf8encode() / Pulp::utf8decode()

[](#pulputf8encode--pulputf8decode)

Converts string content between ISO-8859-1 and UTF-8.

### Pulp::serveHttp()

[](#pulpservehttp)

Sends file content as an HTTP response.

Common Patterns
---------------

[](#common-patterns)

### Read, Transform, Write

[](#read-transform-write)

```
Pulp::start()
    ->pipe(Pulp::src('.*\.txt', __DIR__ . '/input'))
    ->pipe(Pulp::map(static function (File $file): File {
        $file->content = trim($file->content) . "\n";

        return $file;
    }))
    ->pipe(Pulp::dest(__DIR__ . '/output'))
    ->run();
```

### Fan Out One Source Into Multiple Outputs

[](#fan-out-one-source-into-multiple-outputs)

```
Pulp::start()
    ->pipe(Pulp::src('.*\.txt', __DIR__ . '/input'))
    ->pipe(Pulp::split(
        static fn(Pulp $p): Pulp => $p->pipe(/* branch A */),
        static fn(Pulp $p): Pulp => $p->pipe(/* branch B */),
    ))
    ->pipe(Pulp::dest(__DIR__ . '/output'))
    ->run();
```

### Process Large Files With Streams

[](#process-large-files-with-streams)

```
->pipe(Pulp::map(static function (File $file): File {
    $stream = $file->stream();

    try {
        while (($line = fgets($stream)) !== false) {
            // Process without loading the full file.
        }
    } finally {
        fclose($stream);
    }

    return $file;
}))
```

Writing Custom Handlers
-----------------------

[](#writing-custom-handlers)

Most handlers extend `OpenMapsight\pulp\AbstractHandler`.

```
use OpenMapsight\pulp\AbstractHandler;
use OpenMapsight\pulp\File;

class UppercaseHandler extends AbstractHandler
{
    public function onFile(File $file): void
    {
        $file->content = strtoupper($file->content);
        $this->pushFile($file);
    }
}
```

Handlers can override:

- `onStart()`: called before the first file.
- `onFile(File $file)`: called for each file.
- `onEnd()`: called when the stream ends.

Use `$this->pushFile($file)` to pass output to the next handler. A handler may emit zero, one, or many files.

Constructor arguments are declared with `getConstructorParamDefs()` and accessed via `$this->cp`.

```
class PrefixHandler extends AbstractHandler
{
    protected function getConstructorParamDefs(): array
    {
        return ['prefix'];
    }

    public function onFile(File $file): void
    {
        $file->content = $this->cp->prefix . $file->content;
        $this->pushFile($file);
    }
}
```

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance94

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 75% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~11 days

Total

4

Last Release

28d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/684458?v=4)[Paul Golmann](/maintainers/pjeweb)[@pjeweb](https://github.com/pjeweb)

---

Top Contributors

[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (3 commits)")[![pjeweb](https://avatars.githubusercontent.com/u/684458?v=4)](https://github.com/pjeweb "pjeweb (1 commits)")

---

Tags

streamprocessingenginemapsightpulp

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/mapsight-pulp/health.svg)

```
[![Health](https://phpackages.com/badges/mapsight-pulp/health.svg)](https://phpackages.com/packages/mapsight-pulp)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.8k543.8M20.5k](/packages/laravel-framework)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

751291.4k46](/packages/civicrm-civicrm-core)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M600](/packages/shopware-core)[shopware/platform

The Shopware e-commerce core

3.4k1.5M3](/packages/shopware-platform)[drupal/core

Drupal is an open source content management platform powering millions of websites and applications.

19566.0M1.8k](/packages/drupal-core)[illuminate/collections

The Illuminate Collections package.

27078.0M1.1k](/packages/illuminate-collections)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
