• Stars
    star
    280
  • Rank 147,143 (Top 3 %)
  • Language
    PHP
  • License
    MIT License
  • Created over 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Method chaining for any value using any method.

PHP Pipe Operator

Latest stable release Software license Build status Total downloads Total stars

Read my blog View my other packages and projects Follow @sebastiaanluca on Twitter Share this package on Twitter

Method chaining (or fluent expressions) for any value using any method.

Table of contents

Requirements

  • PHP 8.1 or 8.2

How to install

Via Composer:

composer require sebastiaanluca/php-pipe-operator

How to use

The basics

The basic gist of the package is that it takes a value and performs one or more actions on it. A simple example:

use SebastiaanLuca\PipeOperator\Pipe;

Pipe::from('hello')->strtoupper()->get();

// "HELLO"

A few alternatives to create the same instance:

take('hello')->strtoupper()->get();

// "HELLO"

pipe('hello')->strtoupper()->get();

// "HELLO"

Of course that's not very useful since you could've just used strtoupper('hello') and be done with it, but the goal is to make multi-method calls on a value easier to read and write:

$subdomain = Pipe::from('https://blog.sebastiaanluca.com')
    ->parse_url()
    ->end()
    ->explode('.', PIPED_VALUE)
    ->reset()
    ->get();

// "blog"

Note that in comparison to the original RFC, there's no need to pass the initial value to methods that receive the value as first parameter and have no other required parameters. The previous value is always passed as first parameter. In effect, both of the following examples will work:

Pipe::from('hello')->strtoupper()->get();

// "HELLO"

Pipe::from('hello')->strtoupper(PIPED_VALUE)->get();

// "HELLO"

In contrast, if a method takes e.g. a setting before the previous value, we need to set it manually using the replacement identifier (the globally available PIPED_VALUE constant). This identifier can be placed anywhere in the method call, it will simply be replaced by the previous value.

Pipe::from(['key' => 'value'])
    ->array_search('value', PIPED_VALUE)
    ->get();

// "key"

Using first class callable syntax (enabling IDE autocompletion)

Since PHP 8.1, you can use a first class callable syntax, or simply put an anonymous function, to pipe the value through. This enables full method autocompletion.

take('STRING')
    ->pipe(strtolower(...))
    ->get()

// "string"

Or using parameters:

Pipe::from('https://sebastiaanluca.com/blog')
    ->pipe(parse_url(...))
    ->end()
    ->pipe(substr(...), PIPED_VALUE, 3)
    ->pipe(strtoupper(...))
    ->get(),

// "OG"

Using closures

Sometimes standard methods don't cut it and you need to perform a custom operation on a value in the process. You can do so using a closure:

Pipe::from('string')
    ->pipe(fn(string $value): string => 'prefixed-' . $value)
    ->get();

// "prefixed-string"

Using class methods

The same is possible using a class method (regardless of visibility):

class MyClass
{
    public function __construct()
    {
        Pipe::from('HELLO')
            ->pipe($this)->lowercase()
            ->get();

        // "hello"
    }

    /**
     * @param string $value
     *
     * @return string
     */
    private function lowercase(string $value) : string
    {
        return mb_strtolower($value);
    }
}

Class method alternatives

If you don't want to use the internal pipe proxy and pass $this, there are two other ways you can use class methods.

Using first class callable syntax:

class MyClass
{
    public function __construct()
    {
        Pipe::from('HELLO')
            ->pipe($this->lowercase(...))
            ->get();

        // "hello"
    }

    /**
     * @param string $value
     *
     * @return string
     */
    public function lowercase(string $value) : string
    {
        return mb_strtolower($value);
    }
}

Using an array (for public methods only):

class MyClass
{
    public function __construct()
    {
        Pipe::from('HELLO')
            ->pipe([$this, 'lowercase'])
            ->get();

        // "hello"
    }

    /**
     * @param string $value
     *
     * @return string
     */
    public function lowercase(string $value) : string
    {
        return mb_strtolower($value);
    }
}

By parsing the callable method to a closure:

use Closure;

class MyClass
{
    public function __construct()
    {
        Pipe::from('HELLO')
            ->pipe(Closure::fromCallable([$this, 'lowercase']))
            ->get();

        // "hello"
    }

    /**
     * @param string $value
     *
     * @return string
     */
    private function lowercase(string $value) : string
    {
        return mb_strtolower($value);
    }
}

What does it solve?

This package is based on the pipe operator RFC by Sara Golemon (2016), who explains the problem as:

A common PHP OOP pattern is the use of method chaining, or what is also known as โ€œFluent Expressionsโ€. [โ€ฆ] This works well enough for OOP classes which were designed for fluent calling, however it is impossible, or at least unnecessarily arduous, to adapt non-fluent classes to this usage style, harder still for functional interfaces.

Coming across the proposal, I also blogged about it.

A simple example

Say you want to get the subdomain from a URL, you end up with something like this:

$subdomain = 'https://blog.sebastiaanluca.com/';
$subdomain = parse_url($subdomain, PHP_URL_HOST);
$subdomain = explode('.', $subdomain);
$subdomain = reset($subdomain);

// "blog"

This works, of course, but it's quite verbose and repetitive.

Another way of writing

Same result, different style:

$subdomain = explode('.', parse_url('https://blog.sebastiaanluca.com/', PHP_URL_HOST))[0];

// "blog"

This might be the worst of all solutions, as it requires you to start reading from the center, work your way towards the outer methods, and keep switching back and forth. The more methods and variants, the more difficult to get a sense of what's going on.

More examples of the issue at hand

See Sara's RFC for more complex and real-world examples.

Notes

While this packages makes a good attempt at bringing the pipe operator to PHP, it unfortunately does not offer autocompletion on chained methods. For that to work we need the real deal, so make some noise and get the people in charge to vote for Sara's RFC!

License

This package operates under the MIT License (MIT). Please see LICENSE for more information.

Change log

Please see CHANGELOG for more information what has changed recently.

Testing

composer install
composer test

Contributing

Please see CONTRIBUTING and CONDUCT for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

About

My name is Sebastiaan and I'm a freelance Laravel developer specializing in building custom Laravel applications. Check out my portfolio for more information, my blog for the latest tips and tricks, and my other packages to kick-start your next project.

Have a project that could use some guidance? Send me an e-mail at [email protected]!

More Repositories

1

laravel-helpers

THIS PACKAGE HAS BEEN DEPRECATED โ€” An extensive set of Laravel framework helper functions and collection macros.
PHP
418
star
2

laravel-router

THIS PACKAGE HAS BEEN DEPRECATED โ€” An organized approach to handling routes in Laravel.
PHP
129
star
3

laravel-module-loader

THIS PACKAGE HAS BEEN DEPRECATED โ€” A lightweight package to organize your code into contextual modules.
PHP
77
star
4

laravel-auto-morph-map

THIS PACKAGE HAS BEEN DEPRECATED โ€” Automatically alias and map the polymorphic types of Eloquent models.
PHP
54
star
5

laravel-boolean-dates

Automatically convert Eloquent model boolean attributes to dates (and back).
PHP
35
star
6

php-helpers

THIS PACKAGE HAS BEEN DEPRECATED โ€” An extensive set of PHP helper functions and classes.
PHP
27
star
7

laravel-4-eventcron

THIS PACKAGE HAS BEEN DEPRECATED โ€” Laravel 4.x database event queue
PHP
27
star
8

laravel-conditional-providers

THIS PACKAGE HAS BEEN DEPRECATED โ€” Load Laravel service providers and facades based on the current environment.
PHP
26
star
9

laravel-skeleton-deprecated-2

THIS PACKAGE HAS BEEN DEPRECATED โ€” A ready-to-go Laravel skeleton application.
PHP
21
star
10

laravel-route-model-autobinding

THIS PACKAGE HAS BEEN DEPRECATED โ€” Automatically bind Eloquent models as route segment variables.
PHP
14
star
11

php-stub-generator

THIS PACKAGE HAS BEEN DEPRECATED โ€” Easily generate files and classes from stubs.
PHP
10
star
12

laravel-unbreakable-migrations

THIS PACKAGE HAS BEEN DEPRECATED โ€” Prevent your Laravel database migrations from failing by wrapping them in transactions.
PHP
9
star
13

laravel-blog

THIS PACKAGE HAS BEEN DEPRECATED โ€” A simple-to-use blog system you can pull into any existing Laravel project and immediately get writing.
PHP
5
star
14

php-codesniffer-ruleset

THIS PACKAGE HAS BEEN DEPRECATED โ€” An opinionated custom coding standard
4
star
15

laravel-changelog

THIS PACKAGE HAS BEEN DEPRECATED โ€” Show your project's changelog in your application.
PHP
4
star
16

laravel-goodies

THIS PACKAGE HAS BEEN DEPRECATED โ€” A curated list of Laravel packages and other goodies.
3
star
17

laravel-validator

THIS PACKAGE HAS BEEN DEPRECATED โ€” Properly handle user input by using your validation rules as source of truth
PHP
3
star
18

laravel-presets

THIS PACKAGE HAS BEEN DEPRECATED โ€” Various plug-and-play presets to scaffold your existing project.
PHP
3
star
19

Gmail-Tracker-Extension

THIS PACKAGE HAS BEEN DEPRECATED โ€” Chrome extension for Gmail to enable tracking emails.
JavaScript
2
star
20

alarmclock

NodeJS alarm clock for the Raspberry Pi.
JavaScript
2
star
21

laravel-skeleton

An opinionated fresh Laravel project to help you get started.
PHP
1
star
22

laravel-resource-flow

THIS PACKAGE HAS BEEN DEPRECATED โ€” Quickly scaffold access to a resource.
PHP
1
star
23

P5RuntimeFullScreen

THIS PACKAGE HAS BEEN DEPRECATED โ€” Fullscreen mode for Processing that actually lets you switch (on the fly) between a windowed and a fullscreen sketch. With support for Processing 2 (beta), customisable hotkeys, OpenGL, Proscene, Eclipse, โ€ฆ
Java
1
star