Fusonic-Linq - Write Less Do More!

Linq (Language integrated query) in C# is one of the biggest time safer and productivity enhancement in recent programming language evolution history. With fusonic-linq we have implemented an opensource Linq2Object inspired library for PHP, with all the goodness you know and love from c#.

Filtering, aggregating, grouping and projecting enumerables (php: traversables/iterables) has always been a huge pain in php. You have to write a lot of nested foreach loops, updating local counter variables, messing arround with temporary arrays and spending a lot of time in the phpdocs, especially if you do not have all those inconsistent php functions in your head.

However, accomplishing these data querying tasks with a consistent and nice functional library like linq2object requires only some lines of code, and thanks to its fluent interface with logically and consistent namings, it is a simple no-brainer to even accomplish really complicated tasks.

The secret to this flexibility and simplicity is the way how you express your code. With Linq to objects you throw away the old fashined imperative approach where you had to write complex foreach loops that specified how to retrieve data from a collection. With the new approach, you write declarative code that describes what you want to retrieve, and not how you want to retreive it.

fusonic-linq - the write less do more library for php

In a nutshell, LINQ queries offer three main advantages over traditional foreach loops:

  • They are more concise and readable, especially when filtering multiple conditions.
  • They provide powerful filtering, ordering, and grouping capabilities with a minimum of application code.
  • In general, the more complex the operation you want to perform on the data, the more benefit you will realize by using LINQ instead of traditional iteration techniques.

Examples

1.Calculate the average file size of files in a directory

1
2
3
4
$source = glob("files/*");
Linq::from($source)
->select(function($i) { return filesize($i); })
->average();

2. Find all files bigger than 1024 bytes and return the fileinfo object.

1
2
3
4
$source = glob("files/*");
Linq::from($source)
->where(function($i) { return filesize($i) > 1024; })
->select(function($i) { return pathinfo($i); });

3. Search for all users containing “Max 1”, Skip 5 items, Take 2 items and select the property ID of each user.

1
2
3
4
5
$result = Linq::from($users)
->where(function (User $u) { return StringUtil::contains($u->surname, "Max 1"); })
->skip(5)
->take(2)
->select(function (User $u) { return $u->usrId; });

Simple, Consistent and Predictable

One important design goal was the principle of the least surprise. As PHP is a fully dynamic language with nearly no type-safety, it is common to shoot yourself into the foot because of accidentally mixing up incompatible types.

We protect you from these programing errors by asserting that every callback functions you supply to the library must return a correctly typed value. In addition, every supported aggregate function will throw an exception if you are accidentally mixing up incompatible types.

This means that we made this library totally predictable in what it does, and verified that every function has its defined exceptions which are thrown when certain operations fail, or if certain types are not correct.

Examples

1
2
3
4
5
6
7
8
9
/* Throws an UnexpectedValueException if the 
provided callback function does not return a boolean */
Linq::from(array("1", "1"))
->where(function($x) { return "NOT A BOOLEAN"; });

/* Throws an UnexpectedValueException if one of the values
is not convertible to a numeric value:*/
Linq::from(array(1, 2, "Not a numeric value"))
->sum();

Goals and none goals

With fusonic-linq we wanted to make it super easy to query in-memory collections, so the target scope is intentionally not a “full” LINQ-Stack like in C# where you have an abstract query provider capable of parsing and converting Linq expressions to other DSLs like SQL (Linq2Sql).

Although a similar full-LINQ provider like in c# would be possible, (and there are some working open source stacks out there) in a language like php this has some major disadvantages:

  • No build in language support for expression trees, so it would be necessary to use string expression to build an AST or to use reflection in combination with a php parser library to extract and parse the lambda contained within the executing php file - not a trivial task.
  • Performance: Parsing string expression is far slower than using compiler generated expression trees - although caching would be possible.
  • Complexity: The amount of code required for building a full linq provider is huge, which makes understanding and mainting the library for external contributors hard.
  • Generating a DSL out of a LINQ-Expression has lots of drawbacks (leaky abstraction, performance), so usally when performance matters, lots of programmers tend to use fast and simple micro ORMS instead of heavy weight ORMS anyway.

Open Source on Github

We have published this incredibly powerful, yet simple library on the following github project site: https://github.com/fusonic/fusonic-linq
There you find all the necessary instructions how to install and use it.

The source code is open sourced (MIT X11), so you can use this library in all open source and properitary projects.

All code is fully unit tested and well documented. We are also using the travis build service for continuous integration.