If you’ve ever used the Backbone framework for JavaScript, you’ll already be familiar with Underscore. Indeed, it’s become incredibly useful for JavaScript developers in general. But did you know that it’s been ported to PHP?
In this article I’ll take a look at Underscore, what it can do, and provide some examples of where it might be useful.
What is Underscore?
Underscore describes itself as a “utility belt library for JavaScript that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects. It’s the tie to go along with jQuery’s tux, and Backbone.js’s suspenders.”
Most notably, Underscore provides a bunch of utilities for working with collections and arrays, some for working with objects, basic templating functionality and a number of other useful functions.
The functions which operate on collections and arrays can be particularly useful when dealing with JSON, which makes it great for handling responses from web services.
Installation / Download
The easiest way to download the library is using Composer:
{
"require": {
"underscore/underscore.php": "dev-master"
}
}
Alternatively, you can download it manually, or clone it from Github – you need only require
one file, underscore.php
.
Syntax of PHP Underscore
In the original JavaScript library, all Underscore’s functions are prefixed with an underscore and a dot; e.g. _.each
, _.map
, _.reduce
. In PHP, the underscore is generally reserved as an alias of gettext()
(for translating strings), so instead it uses a double-underscore.
All the functions that make up the library are available as static methods of a class called __
– i.e., a double-underscore.
So, if we wish to map the JavaScript functions to their PHP equivalent:
JavaScript PHP
_.each __::each
_.map __::map
_.reduce __::reduce
…and so on.
You can also use it in an object-oriented fashion;
__(array(1, 2, 3))->map(function($n) { return $n * 2; });
Is equivalent to:
__::map(array(1, 2, 3), function($n) { return $n * 2; });
For the purposes of this article, I’m going to use the static method style.
Working with Collections and Arrays
Each
You can iterate through an array, applying a function to each item using __::each
.
For example:
$items = array(1, 2, 3, 4, 5);
__::each($items, function($item) { print $item; });
Produces:
12345
Here’s a (slightly) more useful example:
$student_records = array(
array(
'name' => 'Joe Bloggs',
'id' => 1,
'grade' => 72,
'class' => 'A',
),
array(
'name' => 'Jack Brown',
'id' => 2,
'grade' => 67,
'class' => 'B',
),
array(
'name' => 'Jill Beaumont',
'id' => 3,
'grade' => 81,
'class' => 'B',
),
);
__::each($student_records, function($record) {
print $record['name'] . ' ' . $record['grade'] . '<br />';
});
This produces:
Joe Bloggs A
Jack Brown B
Jill Beaumont B
Later, we’ll look at some better ways of doing something like this, when we delve into templating.
Pluck
If you have a multi-dimensional array and you wish to “pluck” certain values out and flatten them into a single-dimensional array, you can use __::pluck
.
The Facebook API provides a real-world example of when this might be useful. When you request a list of a Facebook user’s friends, the result (when json_decode
‘d into a multi-dimensional array) looks like this:
$response = array(
'data' => array(
array(
'name' => 'Joe Bloggs',
'id' => 123456789,
),
array(
'name' => 'Jack Brown',
'id' => 987654321,
),
)
...
If we want to extract the Facebook user ID’s into a single-dimensional array, we can do this:
$ids = __::pluck($response['data'], 'id');
// array(123456789, 98765432)
Minimum and Maximum
Based on our sample student records from earlier, we could find the student with the highest grade using __::max
:
__::max($student_records, function($student) { return $student['grade']; });
// returns array('name' => 'Jill Beaumont', 'id' => 3, 'grade' => 81, 'class' => 'B')
Or the lowest, by using __::min
:
__::min($student_records, function($student) { return $student['grade']; });
// returns array('name' => 'Jack Brown', 'id' => 2, 'grade' => 67, 'class' => 'B')
As you can see, rather than simply return the highest or lowest grade, these functions return the corresponding item from the array – i.e., the student record.
Filter and Reject
The filter
method runs a truth test on a collection or array, and returns only those items for which it passes.
As an example, let’s go back to our student records from earlier. Suppose the pass-mark is a grade of 70 or above. We can use __::filter
to apply a simple function, so that we can find out which of the students have passed:
$passed = __::filter($student_records, function($student) { return $student['grade'] >= 70; });
The reject
function is simply the opposite of filter
; it excludes the items that pass the truth test.
In other words, the following two functions produce the same result:
__::filter($student_records, function($student) { return $student['grade'] >= 70; });
__::reject($student_records, function($student) { return $student['grade'] < 70; });
sortBy
The sortBy
function orders an array – in ascending order – using an iterator function. Here’s a simple example:
$scores = array(476, 323, 1010, 567, 723, 1009, 600);
$sorted = __::sortBy($scores, function($score) { return $score; });
To sort in descending order, simply negate the value. So to get a list of records, starting with the student who scored highest:
$ordered = __::sortBy($student_records, function($student) { return -$student['grade']; });
groupBy
Suppose we want to re-organise our array of students according to the class they are in.
This is where groupBy
comes in. We can do this:
var_dump( __::groupBy($student_records, 'class') );
Which will produce the following output:
array(2) {
["A"]=>
array(1) {
[0]=>
array(4) {
["name"]=>
string(10) "Joe Bloggs"
["id"]=>
int(1)
["grade"]=>
int(72)
["class"]=>
string(1) "A"
}
}
["B"]=>
array(2) {
[0]=>
array(4) {
["name"]=>
string(10) "Jack Brown"
["id"]=>
int(2)
["grade"]=>
int(67)
["class"]=>
string(1) "B"
}
[1]=>
array(4) {
["name"]=>
string(13) "Jill Beaumont"
["id"]=>
int(3)
["grade"]=>
int(81)
["class"]=>
string(1) "B"
}
}
}
Reduce
The reduce
function is used to reduce a collection into a single value.
For example, to get the sum of a single-dimensional array:
__::reduce(array(1, 2, 3), function($first, $second) { return $first + $second; }, 0); // 6
If we combine reduce
with pluck
on our student records, we can find out the average grade:
$average = round( ( __::reduce(__::pluck($student_records, 'grade'), function($first, $second) { return $first + $second; }, 0) / count($student_records) ), 2);
What we’re doing here is extracting the grades as a single dimensional array using pluck
, reducing them to a single value using a simple additive iterator function, dividing it by the number of records, then rounding it to two digits.
Find
The find
function iterates through an array, executing a function against each item until the function returns true – in other words, it returns the first matching “record”.
For example, to find the first student with a grade below 70, you could do this:
__::find($student_records, function($student) { return $student['grade'] < 70; })
This should produce the following, if you var_dump
the results:
array(4) {
["name"]=>
string(10) "Jack Brown"
["id"]=>
int(2)
["grade"]=>
int(67)
["class"]=>
string(1) "B"
}
Suppose we want to find a student record by ID. We can do this:
function findById($records, $id) {
return __::find($records, function($record) use ($id) { return ($record['id'] == $id); });
}
If you run:
var_dump(findById($student_records, 2));
You should get this:
array(4) {
["name"]=>
string(10) "Jack Brown"
["id"]=>
int(2)
["grade"]=>
int(67)
["class"]=>
string(1) "B"
}
Note the inclusion of the use
keyword, so that $id
is in scope.
Templating
One area where Backbone uses Underscore extensively is through its simple templating functionality.
It’s generally much cleaner than, say, string concatenation, and you can combine it with other Underscore functions such as __::each
to make it even more powerful.
Within a template string, you can echo values like this:
<%= $student['name'] %>
You can execute code using this syntax:
<% __::each($records, student) { %> … <% }) %>
There are two common methods for templating. One is to define it as a string, using the syntax above to inject values or code, and run it through the __::template()
function:
$welcome = 'Hello <%= $name %>, welcome back!';
print __::template($welcome, array('name' => 'Jack'));
Alternatively, you can “compile” a template by defining a variable and assigning the result of the __::template
function, with a template defined as a single, string argument.
The following is equivalent to the previous example:
$compiled = __::template('Hello <%= $name %>, welcome back!');
print $compiled(array('name'=>'Jack'));
// Hello Jack, welcome back!
Here’s how you might create a simple template to output an unordered list, by combining __::template
with __::each
:
$ul = __::template('<ul><% __::each($items, function($item) { %><li><%= $item %></li><% }); %></ul>');
print $ul(array('items' => array('one', 'two', 'three')));
Let’s build a compiled template which takes a set of student records, and creates an unordered list of their names:
$list_students = __::template('<ul><% __::each($records, function($student) { %><li><%= $student["name"] %></li><% }); %></ul>');
Then, to render it:
print $list_students(array('records' => $student_records));
You should get the following output:
<ul>
<li>Joe Bloggs</li>
<li>Jack Brown</li>
<li>Jill Beaumont</li>
</ul>
Or a template to produce a table of students and their grades:
$grades_table = __::template('<table><thead><tr><td>Student</td><td>Grade</td></tr></thead><tbody><% __::each($records, function($student) { %><tr><td><%= $student["name"] %></td><td><%= $student["grade"] %>%</td></tr><% }); %></tbody></table>');
print $grades_table(array('records' => $student_records));
You can of course pass multiple arguments in, so we could add a header to the table, for example:
$grades_table = __::template('<h4><%= $title %></h4><table><thead><tr><td>Student</td><td>Grade</td></tr></thead><tbody><% __::each($records, function($student) { %><tr><td><%= $student["name"] %></td><td><%= $student["grade"] %>%</td></tr><% }); %></tbody></table>');
print $grades_table(array('title' => $title, 'records' => $student_records));
Extending Underscore
You can even create your own functions using mixins.
__::mixin(array(
'capitalize'=> function($string) { return ucwords($string); },
'shout' => function($string) { return strtoupper($string); }
));
__::capitalize('joe bloggs'); // 'Joe Bloggs'
__::shout('Joe bloggs'); // 'JOE BLOGGS'
Summary
In this article, I’ve introduced you to the PHP port of the popular “utility belt” library, Underscore. I’ve gone through some of the available features; however there’s much more to explore. Have a browse through the documentation and play around!
Frequently Asked Questions (FAQs) about PHP Underscore
What is the primary function of PHP underscore?
The PHP underscore, also known as the underscore function, is a localization function in PHP. It is used to translate a string into a different language. This function is particularly useful when you are developing a website that needs to support multiple languages. It works by looking for the translation of the string in a text domain that has been loaded into the PHP script.
How do I use the PHP underscore function in my code?
To use the PHP underscore function, you need to wrap the string you want to translate with the underscore function like this: _(‘Hello World’). This will translate the string ‘Hello World’ into the language that has been set in your PHP script. Remember, the translation will only work if you have the appropriate language file loaded into your script.
How do I load a language file into my PHP script?
To load a language file into your PHP script, you can use the bindtextdomain() and textdomain() functions. The bindtextdomain() function specifies the location of the language files, while the textdomain() function sets the default text domain. Here’s an example:bindtextdomain("myDomain", "/path/to/language/files");
textdomain("myDomain");
What is the purpose of the Underscore.php library?
The Underscore.php library is a utility library that provides over 100 functions that support both the usual functional suspects (map, filter, invoke) as well as more specialized helpers like function binding, javascript templating, creating quick indexes, deep equality testing, and so on. It’s a powerful tool that can help you write cleaner and more efficient PHP code.
How do I install the Underscore.php library?
The Underscore.php library can be installed using Composer, a dependency management tool for PHP. You can install it by running the following command in your terminal:composer require anahkiasen/underscore-php
How do I use the functions provided by the Underscore.php library?
To use the functions provided by the Underscore.php library, you first need to include the library in your PHP script. After that, you can use the functions like this:use Underscore\Types\Arrays;
$array = Arrays::from([1, 2, 3, 4, 5]);
$result = $array->filter(function($value) { return $value % 2 == 0; });
Can I use the Underscore.php library with Laravel?
Yes, the Underscore.php library can be used with Laravel, a popular PHP framework. In fact, the library was originally developed as a part of the Laravel framework. To use it with Laravel, you just need to include the library in your Laravel project using Composer.
What are some alternatives to the Underscore.php library?
There are several alternatives to the Underscore.php library, including Lodash, Ramda, and Functional PHP. These libraries also provide a collection of utility functions for PHP, but they each have their own unique features and advantages.
Is the Underscore.php library still maintained?
As of now, the Underscore.php library is not actively maintained. The last update was made several years ago. However, the library is still functional and can be used in your projects. If you encounter any issues, you may need to fix them yourself or find an alternative library.
Can I contribute to the Underscore.php library?
Yes, you can contribute to the Underscore.php library. The library is open-source, which means that anyone can contribute to its development. If you have a bug fix or a new feature that you want to add, you can create a pull request on the library’s GitHub page.
Lukas is a freelance web and mobile developer based in Manchester in the North of England. He's been developing in PHP since moving away from those early days in web development of using all manner of tools such as Java Server Pages, classic ASP and XML data islands, along with JavaScript - back when it really was JavaScript and Netscape ruled the roost. When he's not developing websites and mobile applications and complaining that this was all fields, Lukas likes to cook all manner of World foods.