4

PHPで、DSL(Rails 3ルートに基づく)を取得して正規表現に変換するルーターを作成しました。オプションのセグメントがあります((ネストされた)括弧で示されます)。以下は、現在の字句解析アルゴリズムです。

private function tokenize($pattern)
{
    $rules = array(
        self::OPEN_PAREN_TYPE  => '/^(\()/',
        self::CLOSE_PAREN_TYPE => '/^(\))/',
        self::VARIABLE_TYPE    => '/^:([a-z0-9_]+)/',
        self::TEXT_TYPE        => '/^([^:()]+)/',
    );

    $cursor = 0;
    $tokens = array();
    $buffer = $pattern;
    $buflen = strlen($buffer);

    while ($cursor < $buflen)
    {
        $chunk = substr($buffer, $cursor);

        $matched = false;
        foreach ($rules as $type => $rule)
        {
            if (preg_match($rule, $chunk, $matches))
            {
                $tokens[] = array(
                    'type'  => $type,
                    'value' => $matches[1],
                );

                $matched = true;
                $cursor += strlen($matches[0]);
            }
        }

        if (!$matched)
        {
            throw new \Exception(sprintf('Problem parsing route "%s" at char "%d".', $pattern, $cursor));
        }
    }

    return $tokens;
}

私が見逃している明らかなスピードアップはありますか?preg_ *を完全に放棄する方法、または正規表現を1つのパターンに結合する方法などはありますか?

これがxhprofコールグラフです(ベンチマークはテストに最大2500の一意のルートを使用します):

コールグラフ

最善の解決策は、すべてのリクエストに対してこれを呼び出さないことです(APCでキャッシュすることを計画しています)が、APCを有効にせずにこのライブラリを使用する人々にとって可能な限り効率的にしたいと思います。

編集:

また、パフォーマンスが向上しているように見えるクイックステートマシンバージョンも作成しました。最初のコードの方がエレガントだったと思うので、どちらの面でもまだ提案を受け付けています。

private function tokenize2($pattern)
{
    $buffer = '';
    $invariable = false;

    $tokens = array();
    foreach (str_split($pattern) as $char)
    {
        switch ($char)
        {
            case '(':
                if ($buffer)
                {
                    $tokens[] = array(
                        'type'  => $invariable ? self::VARIABLE_TYPE : self::TEXT_TYPE,
                        'value' => $buffer,
                    );
                    $buffer = '';
                    $invariable = false;
                }

                $tokens[] = array(
                    'type' => self::OPEN_PAREN_TYPE,
                );
                break;
            case ')':
                if ($buffer)
                {
                    $tokens[] = array(
                        'type'  => $invariable ? self::VARIABLE_TYPE : self::TEXT_TYPE,
                        'value' => $buffer,
                    );
                    $buffer = '';
                    $invariable = false;
                }

                $tokens[] = array(
                    'type' => self::CLOSE_PAREN_TYPE,
                );
                break;
            case ':':
                if ($buffer)
                {
                    $tokens[] = array(
                        'type'  => $invariable ? self::VARIABLE_TYPE : self::TEXT_TYPE,
                        'value' => $buffer,
                    );
                    $buffer = '';
                    $invariable = false;
                }

                $invariable = true;
                break;
            default:
                if ($invariable && !(ctype_alnum($char) || '_' == $char ))
                {
                    $invariable = false;
                    $tokens[] = array(
                        'type'  => self::VARIABLE_TYPE,
                        'value' => $buffer,
                    );

                    $buffer = '';
                    $invariable = false;
                }

                $buffer .= $char;
                break;
        }
    }

    if ($buffer)
    {
        $tokens[] = array(
        'type'  => $invariable ? self::VARIABLE_TYPE : self::TEXT_TYPE,
        'value' => $buffer,
        );
        $buffer = '';
    }

    return $tokens;

コールグラフ


パフォーマンス上の理由からステートマシンを使用し、字句解析プロセス全体をAPCでキャッシュすることになりました(理由は...なぜですか)。

誰かが貢献するものがあれば、私は喜んで答えを動かします。

4

1 に答える 1

1

興味深いコード:)。

「字句解析プロセス全体をAPCでキャッシュする」とあなたが何を言っているのかよくわからないので、あなたがすでに行っていることを示唆しているかもしれません。

入力URLと、実際の字句解析プロセスの上の結果をキャッシュできますか?ここでは権限ベースの制限を適用していないように見えるため、キャッシュはグローバルです。非常にホットなスポットがいくつかある大規模なサイトであっても、ルートの数は限られている傾向があります。字句解析を完全にバイパスし、以前に使用したルートのキャッシュをヒットします。

于 2011-10-25T18:53:38.300 に答える