Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

LINQ(Language Integrated Query) for PHP

すみません。LINQって初めて見ました(多分)。C#方面の機能(?)みたいです。で、これをPHPで部分的に実装してみた方(ベルギーの方ですかね)がいらっしゃるようです。


Perhaps you have already heard of C# 3.5's "LINQ" component. LINQ, or Language Integrated Query, is a component inside the .NET framework which enables you to perform queries on a variety of data sources like arrays, XML, SQL server, ... These queries are defined using a syntax which is very similar to SQL.

There is a problem with LINQ though... If you start using this, you don't want to access data sources differently anymore. Since I'm also a PHP developer, I thought of creating a similar concept for PHP.

LINQとは「リレーショナルデータベースや XML に対する操作をプログラミング言語に統合するもの」だそうです。

要は、DBやXMLなどのデータソースから同じ構文でを使ってデータを抽出するためのもの、という事でしょうかね。上のURLで「LINQの全体像」にある図が分かりやすいです。

と、ここまで前置き。

で、さっくり試してみました。まずは配列からデータを抽出する例。

<?php
ini_set('include_path', ini_get('include_path') . ':./Classes/');
require_once 'PHPLinq/LinqToObjects.php';

// Create data source
$names = array("John", "Peter", "Joe", "Patrick", "Donald", "Eric");

$result = from('$name')->in($names)
            ->where('$name => strlen($name) < 5')
            ->select('$name');
var_dump($result);

fromのところからがLINQの部分ですが、何となくSQLっぽい感じです。さしずめ、

SELECT $name FROM $names WHERE strlen($name) < 5;

といったところでしょうか。で、これを実行すると、

$ php phplinq01.php
array(3) {
  [0]=>
  string(4) "John"
  [1]=>
  string(3) "Joe"
  [2]=>
  string(4) "Eric"
}
$ 

のように出力されます。分かりますかね?LINQの部分を素直に書けば、

<?php$result = array();
foreach ($names as $name) {
    if (strlen($name) < 5) {
        $result[] = $name;
    }
}

な感じになるところです。
もう一つの例は、オブジェクト。

<?php
ini_set('include_path', ini_get('include_path') . ':./Classes/');
require_once 'PHPLinq/LinqToObjects.php';

class Employee {
    public $Name;
    public $Email;
    public $Age;

    public function __construct($name, $email, $age) {
        $this->Name     = $name;
        $this->Email     = $email;
        $this->Age        = $age;
    }
}

$employees = array(
    new Employee('Maarten', 'maarten@example.com', 24),
    new Employee('Paul', 'paul@example.com', 30),
    new Employee('Bill', 'bill.a@example.com', 29),
    new Employee('Bill', 'bill.g@example.com', 28),
    new Employee('Xavier', 'xavier@example.com', 40)
);

$result = from('$employee')->in($employees)
            ->where('$employee => strlen($employee->Name) == 4')
            ->orderBy('$employee => $employee->Name')
            ->thenByDescending('$employee => $employee->Age')
            ->select('new {
                    "EmailAddress" => $employee->Email,
                    "Domain" => substr($employee->Email, strpos($employee->Email, "@") + 1)
                  }');

var_dump($result);

今度はorderByとかthenByDescendingが出てきてます。SQLっぽく書くと

SELECT $employee 
FROM $employees
WHERE $employee->name == 4
ORDER BY $employee->name, $employee->age DESC

の感じです。LINQの最後の「new」で、新しいオブジェクト(PHPの場合はstdClass)を生成するようです。つまり、

上の疑似SQLを実行して得られた結果から、新しいオブジェクトを作って配列でまとめて返す

といった動きになるようです。
実行結果は次の通りです。

$ php phplinq01_obj.php
array(3) {
  [0]=>
  object(stdClass)#19 (2) {
    ["EmailAddress"]=>
    string(18) "bill.a@example.com"
    ["Domain"]=>
    string(11) "example.com"
  }
  [1]=>
  object(stdClass)#20 (2) {
    ["EmailAddress"]=>
    string(18) "bill.g@example.com"
    ["Domain"]=>
    string(11) "example.com"
  }
  [2]=>
  object(stdClass)#21 (2) {
    ["EmailAddress"]=>
    string(16) "paul@example.com"
    ["Domain"]=>
    string(11) "example.com"
  }
}
$ 

これも素直に書き直してみると。。。面倒なので割愛w


ちなみに、PHPLinqのソースを見てみると、from以外のinやwhereなどはメソッドになっていて、受け取った引数(文字列)から動的に関数を作成(create_function)して呼び出し(call_user_func)たりしているようです。

in()に指定する配列が巨大な場合とかは、パフォーマンス的に目も当てられない感じもしなくもないですが、これ便利だなぁ。