phpscript: extending the language with LINQ

In this article I’m spending a few of my own grey-matter tokens to investigate the idea of adding LINQ syntax extensions to the PHP language. I’ll go through a quick description of what LINQ is, and some of my reasoning towards answering the question…

What would LINQ look like as an extension of phpscript?

Feature context

LINQ

LINQ (Language Integrated Query) is a C# feature that lets you search, filter, sort, and transform data using simple, readable code, like asking a question about a list. It runs inside your application, querying collections, databases, XML, or other data sources. Microsoft Docs.

// Filter a list
var adults = people.Where(p => p.Age >= 18);
// Select names and sort alphabetically
var names = people.Select(p => p.Name).OrderBy(n => n);

Think of LINQ as SQL-like querying built into C#: instead of manually looping through data, you describe what you want, and LINQ handles the querying.

Linq syntax and abstraction

I’ve encountered some other kinds of samples of LINQ in the wild, notably a reddit comment that had this bare style for the following syntax:

$result = from $c in $customers
          where $c->Age > 30
          select new { $c->Name, $c->City };

With a small adjustment for phpscript, the variables and syntax could look like the above. Even with the lack of braces however, there’s a certain disconnect from PHP. Consider this is basically a collection mechanism and you can approximate it with PHP syntax itself:

$result = array()
foreach $customers as $c
	if $c->age > 30
		$result []= new { $c->name, $c->city }

To adjust phpscript to allow for a more LINQ like syntax, the foreach should function as a value-returning closure. The select keyword adds something to the return, that something may be typed or as in the example just be a stdClass and get collected into a collection returned by foreach.

PHP and phpscript both should support the existing loop constructs to be used in this way. Opening and closing braces can be omitted for a single line statement, which gets you pretty near to LINQ syntax.

Alternatives considered / discarded

PHP is a special language due to the marking of variables with the $ prefix. Hate it or love it, it’s a quick cheat for static analysis. As long as I can get the statement as a string, it allows me to extract variables. It also allows me to implement adjustments to the runtime. In terms of data model and APIs, I can already envision:

  • Extend(rt *Runtime, findToken string, evaluate EvaluateFunc)
  • EvaluateFunc(stmt string, vars map[string]any)
  • Extend(rt, "from", evaluateLinqFrom)

Some extensions to the parser/compiler to detect $var = <findToken>*; and properly slurp a possibly multi-line statement without a trailing delimiter, e.g. should the ; be required as part of an extension?

$result = array()  
foreach $customers as $c if $c->age > 30 $result[] = $c

This is what we’re talking about if we optimize the whitespace a bit. And it requires nobody to have some kind of extension binding. It doesn’t even need a language extension. I’ve briefly considered just adding the individual tokens for from, select into phpscript and discarded the idea. Every iteration syntax in all of programming languages, ever, compiles to the same assembly. There’s no need to have a language that just expresses the iterations like the Eskimo people express 20 different words for snow.

Conclusions

The given example of a foreach closure returning values can be challenged as insufficient. The LINQ syntax provides enough additional tokens to have it’s own AST, so the PHP/phpscript syntax itself is not sufficient. Let’s tokenize the following expression.

FROM orders
WHERE <expression>
GROUP BY <field>[, <expression>...]
HAVING <expression>
SELECT <field>, <expression> AS <literal>
ORDER BY <field>, <literal>, <expression> <enum ASC|DESC>
LIMIT 10

The language itself, just from observation:

  • Tokens: FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, ASC, DESC, AS
  • More tokens: IN, SET, FIELD,…
  • CTEs, Window functions (extrapolated): SUM, COUNT, AVG, P50, P95…
  • Expressions like <, >, = comparisons, math column * column
  • Values taken in pretty much any part of the query (resolution by reflection)

The implementation may more complicated to do right. The only valuable takeaway was the idea that foreach could return a closure somehow - but invalidated because phpscript itself would still need to add additional syntax. Since I’d like not to write my own database and continue writing phpscript, adding LINQ syntax doesn’t seem to be the move. The Language Integrated acronym should have been a give-away.

I hope you enjoyed the investigative writeup.

0 comments

No comments yet.

Sign in to comment.