Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

JavaScript and Underscore.js Type Methods in PHP

DZone's Guide to

JavaScript and Underscore.js Type Methods in PHP

Do you prefer to code in JavaScript, but have to use PHP for certain projects? Read on to learn how to combine the two using an interesting library.

· Web Dev Zone ·
Free Resource

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

I've been working with PHP since 2000 and with JavaScript even longer. Over the last decade, JavaScript has evolved drastically. It had a promising design from the very beginning and gained its current incredible shape with the shift to ES2015 (Harmony). PHP, though, didn't change that much. Apparently, it's doomed always to suffer from its own legacy. It arrived in the mid-90s as a simple CGI app to maintain Rasmus Lerdorf's home page and have been more like spontaneously developing ever since. That resulted in syntax inconsistency, a mix of functional and OOP-based assets, and absence of a decent language specification. So every time I switch to PHP I miss the rationality, predictability, and flexibility of JavaScript. This made me consider a library which would bring a JavaScript-like development experience to PHP. Eventually, I came up with Extras, which extends PHP types with JavaScript (other than Underscore) methods, unlocks chaining, and introduces the PlainObject type representing an object-literal.

JavaScript Meets PHP

The library enriches with JavaScript and Underscore.js methods the following PHP types: array (both sequential and associative), string, number (integer, float, NaN), function (callable), collection (Iterable, ArrayObject, Iterator) and boolean. Every added method is a static method of the class representing the type. So we can access any directly, without the creation of a chain like Arrays::map.

What is good about that? Let's examine the following snippet:

<?php
use \Dsheiko\Extras\Arrays;

function numToArray(int $value, int $index, array $array): array
{
  return [$value];
}
$res = Arrays::map(range(1,3), "numToArray"); // [[1],[2],[3]]

Unlike \array_map, the library method has an interface consistent with any other array extras. The first argument is always the source array, next iterate/predicate a callback, then anything else if needed. What is more? With PHP array functions, we need to remember which one accepts only references for sources or for callbacks. Arrays::* methods always accept standalone values as well as references.

Have you noticed the callback parameter list? Yes, it's extended according to ECMAScript specification. Besides the value, it always receives the element index (key in an associative array) and the initial array.

In addition, all the added methods can be chained:

<?php
use \Dsheiko\Extras\Arrays;

$res = Arrays::chain([1, 2, 3])
    ->map(function($num){ return $num + 1; })
    ->filter(function($num){ return $num > 1; })
    ->reduce(function($carry, $num){ return $carry + $num; }, 0)
    ->value();

To me, it looks cleaner than the following:

$array = [1, 2, 3];
$array = \array_map(function($num){ return $num + 1; }, $array);
$array = \array_filter($array, function($num){ return $num > 1; });
$array = \array_reduce($array, function($carry, $num){ return $carry + $num; }, 0);

What about any other type? If we take a string, it works the same way:

<?php
use \Dsheiko\Extras\Strings;
function sanitizeSearchTerm(string $value, int $maxLength): string
{
    return Strings::chain($value)
            ->trim()
            ->replace('`[^0-9\-_ \p{L}]`u', ' ')
            ->replace('` +`', ' ')
            ->substr(0, $maxLength)
            ->value();
}

Furthermore, we can also manipulate multiple types in the same chain:

<?php
use \Dsheiko\Extras\Arrays;
$res = Arrays::chain(["foo" => "FOO", "bar" => "BAR"])
    // input: array, output: string
    ->reduce(function($carry, $val, $key){ return $carry . " {$key}:{$val} "; }, "")
    // input: string, output: integer
    ->indexOf("bar:")    
    // input: integer, output: double
    ->toFixed(2)    
    ->value();

Actually, you do not even need to guess the type of the entry point. Instead, you can go with Any type:

<?php
use \Dsheiko\Extras\Any;
$res = Any::chain(new \ArrayObject([1,2,3]))
    ->toArray() // value is [1,2,3]
    ->map(function($num){ return [ "num" => $num ]; })
    // value is [[ "num" => 1, ..]]
    ->reduce(function($carry, $arr){
        $carry .= $arr["num"];
        return $carry;

    }, "") // value is "123"
    ->replace("/2/", "") // value is "13"
    ->then(function($value){
      if (empty($value)) {
        throw new \Exception("Empty value");
      }
      return $value;
    })
    ->value();

As you can see in this example, I also use the chaining method then that works similar to one of Promise APIs. It accepts the actual value of the chain and passes it further based on whether the chain was modified or not.

Underscore.js Meets PHP

Not persuaded? Well, we do a lot of manipulations with types and especially with arrays. Array functions help to isolate iteration scope and improve performance, but they are not that handy when it comes to showing developer intent. In fact, we often solve similar logical tasks where we could simply reuse existing patterns. The Extras library incorporates a collection of Underscore.js methods. And it makes a difference. Just imagine you have an array of key-value objects (actually associative arrays) carrying data about all the plays performed in a theater. According to user-applied filters, you need to search for the matching plays. Instead of re-creating this every time a callback is used for \array_filter, you can use Arrays:where:

$listOfPlays = [
    ["title" => "Cymbeline", "author" => "Shakespeare", "year" => 1611],
    ["title" => "The Tempest", "author" => "Shakespeare", "year" => 1611],
    ["title" => "Hamlet", "author" => "Shakespeare", "year" => 1603]
];
$res = Arrays::where($listOfPlays, ["author" => "Shakespeare", "year" => 1611]);
// [
//    ["title" => "Cymbeline", "author" => "Shakespeare", "year" => 1611],
//    ["title" => "The Tempest", "author" => "Shakespeare", "year" => 1611],
// ]

Or, let's say you have a key-value data structure describing a product. You need to extract a smaller object (here, an associative array) containing the only pairs you choose:

$res = Arrays::pick([
    'name' => 'moe',
    'age' => 50,
    'userid' => 'moe1',
     //… 100 more lines
  ], 'name', 'age');
// ['name' => 'moe', 'age' => 50, ]

As you probably noted, Extras reflects both JavaScript type arrays and objects in PHP arrays (sequential and associative, respectively). Besides, one can get from it a type that is similar to a JavaScript plain object:

<?php
use \Dsheiko\Extras\Arrays;

$po = Arrays::object(["foo" => "FOO", "bar" => ["baz" => "BAZ"]]);
echo $po->foo; // FOO
echo $po->bar->baz; // BAZ

The type inherits Underscore Object methods:

<?php
use \Dsheiko\Extras\Arrays;
$po = Arrays::object([
    "start" => 5,
    "end" => 12,
]);
$res = $po->mapObject(function($val){
        return $val + 5;
    }) // PlainObject{ "start": 10, "end": 17 }
    ->invert(); // PlainObject{ 10: "start", 17: "end" }

Recap

Extras is a PHP package available via composer:

composer require "dsheiko/extras"

It is designed to improve the developing experience of PHP. Namely, it provides a type manipulation utility-belt with consistent naming conventions and consistent parameter order. Any of the delivered methods are chainable. In addition to PHP generic types, the package brings PlainObject, which is similar to JavaScript plain objects. Package methods adhere to the syntax and logic of corresponding JavaScript and Underscore.js/Lodash methods.

The project is accompanied by online documentation in the style of Underscore:

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Topics:
web dev ,javascript ,php ,web application development

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}