LDAP検索式の構築(式ツリーを拡張)

前のロジックは「全ての関係式は2項式」って前提で書いたけど、一般的な数式のBNFみたいに「ANDが連続するならterm,ORが連続するならexpression」って思えば各ノードに2個以上の条件式を追加することができる。
括弧を単体の構造で表すのは面倒(beginParenthesisさせたらendParenthesisを忘れたりする)だからComplex構造を使う。言わばCompositeのノード。
前のロジックではComplexFilterとComplexFilterNodeに分けてたけど、今回はComplexFilterのみ。ノード用クラスは使わない。そのかわりComplexのルートノードになるオブジェクトは 必ず(OR式 -* AND式)になり、ANDのみからなる条件式はツリーのネストが1段階多い。じゃないとa OR B and C みたいな式でCを追加できない。

それではコードを。

class Expression{
    public function compose(Composer $composer){}    
}
class ComplexFilter extends Expression{
    var $modifier;
    var $operator;
    var $filters;
    
    public function __construct($operator = "OR"){
        $this->modifier = "";
        $this->operator = $operator;
        $this->filters = array();
    }
    public function add($filter,$operator = "OR"){
        $childCount = count($this->filters);
        $last = $this->lastChild();
        if($last == null){
            $this->filters[] = $filter; 
        }
        else{
            if($this->operator == $operator){
                $this->filters[] = $filter; 
            }
            else{
                $this->addToLast($filter,$operator);
            }
        }
        
        return $this;        
    }
    public function addAnd($filter){
        return $this->add($filter,"AND");
    }
    public function addOr($filter){
       return $this->add($filter,"OR");
    }
    public function setModifier($mod){
        $this->modifier = $mod;
    }
    
    public function compose(Composer $composer){
        $composer->composeComplex($this);
    }    
    function lastChild(){
        $childCount = count($this->filters);
        if($childCount == 0){
            return null;
        }
        return $this->filters[$childCount -1];
    }
    function addToLast($filter,$operator){
        $childCount = count($this->filters);
        $last = $this->lastChild();
        if($last instanceof ComplexFilter){
            if($last->operator == $operator){
                $last->add($filter,$operator);
            }
            else{
                $this->filters[$childCount -1] = $this->createComplex($last,$filter,$operator);
            }
        }
        else{
            $this->filters[$childCount -1] = $this->createComplex($last,$filter,$operator);
        }               
        
    }
    function createComplex($filter1,$filter2,$operator){
        $complex = new ComplexFilter($operator);
        $complex->add($filter1,$operator);
        $complex->add($filter2,$operator);
        return $complex;
    }
}
class TextFilter extends Expression{
    var $text;
    public function __construct($text){
        $this->text = $text;
    }
    public function compose(Composer $composer){
        $composer->composeText($this);
    }    
}
class ExpressionFactory{
    public function text($text){
        return new TextFilter($text);
    }
    public function complex(){
        return new ComplexFilter();
    }
    
}
class Composer{
    var $text;
    public function composeText(TextFilter $filter){
        $this->text = $filter->text;
    }
    public function composeComplex(ComplexFilter $filter){
        $operator = $filter->operator;
        $this->text = "";
        
        $text = "";
        foreach($filter->filters as $node){
            if($text != ""){
                $text .= " " . $filter->operator . " ";
            }
            $node->compose($this);
            $text .= $this->text;
        }
        if(count($filter->filters)>1){
            $this->text = "(".$text.")";
        }
        else{
            $this->text = $text;
        }
        
    }
}
class LdapComposer extends Composer{
    var $text;
    public function composeText(TextFilter $filter){
        $this->text = "(". $filter->text .")";
    }
    public function composeComplex(ComplexFilter $filter){
        $operator = $filter->operator == "AND" ? "&" : "|"; 
        $this->text = "";
        
        $text = "";
        foreach($filter->filters as $node){
            $node->compose($this);
            $text .= $this->text;
        }
        if(count($filter->filters)>1){
            
            $this->text = "(".$operator .$text.")";
        }
        else{
            $this->text = $text;
        }
        
    }
}

$composer = new Composer();
$expr = new ExpressionFactory();
//1 . A=1
$filter = $expr->text("A=1");
$filter->compose($composer);
echo "A=1 : ";
echo $composer->text. "<br />";

//2 . A=1 
$filter = $expr->complex()
        ->addAnd($expr->text("A=1"))
        ;
$filter->compose($composer);
echo "A=1 : ";
echo $composer->text. "<br />";

//3 . A=1 and B=1 
$filter = $expr->complex()
        ->addAnd($expr->text("A=1"))
        ->addAnd($expr->text("B=1"))
        ;
$filter->compose($composer);
echo "A=1 and B=1 ";
echo $composer->text. "<br />";

//4 . A=1 and B=1 and C=2 
$filter = $expr->complex()
        ->add($expr->text("A=1"))
        ->addAnd($expr->text("B=1"))
        ->addAnd($expr->text("C=2"))
        ;
$filter->compose($composer);
echo "A=1 and B=1 and C=2 :";
echo $composer->text. "<br />";

//5 . A=1 and B=1 or C=2 
$filter = $expr->complex()
        ->addAnd($expr->text("A=1"))
        ->addAnd($expr->text("B=1"))
        ->addOr($expr->text("C=2"))
        ;
$filter->compose($composer);
echo $composer->text. "<br />";

//6 . A=1 or B=1 and C=2 
$filter = $expr->complex()
        ->addAnd($expr->text("A=1"))
        ->addOr($expr->text("B=1"))
        ->addAnd($expr->text("C=2"))
        ;
$filter->compose($composer);
echo $composer->text. "<br />";

//7 . A=1 and B=1 or C=2 and D=3 
$filter = $expr->complex()
        ->addAnd($expr->text("A=1"))
        ->addAnd($expr->text("B=1"))
        ->addOr($expr->text("C=2"))
        ->addAnd($expr->text("D=3"))
        ;
$filter->compose($composer);
echo $composer->text. "<br />";

//8 . (A=1 or B=1) 
$filter = $expr->complex()
        ->add(
            $expr->complex()
                ->add($expr->text("A=1"))
                ->addOr($expr->text("B=1"))
            )
        ;
$filter->compose($composer);
echo $composer->text. "<br />";
echo "<br />";

//9 . (A=1 or B=1) and (C=2 or D=3) 
$filter = $expr->complex()
        ->add(
            $expr->complex()
                ->add($expr->text("A=1"))
                ->addOr($expr->text("B=1"))
                ->addOr($expr->text("B=2"))
                )
        ->addAnd(
            $expr->complex()
                ->add($expr->text("C=2"))
                ->addOr($expr->text("D=3"))
        )
        ;
$filter->compose($composer);
echo $composer->text. "<br />";
echo "<br />";

//10 . (A=1 or B=1) or (C=2 or D=3) 
$filter = $expr->complex()
        ->add(
            $expr->complex()
                ->add($expr->text("A=1"))
                ->addOr($expr->text("B=1"))
            )
        ->addOr(
            $expr->complex()
                ->add($expr->text("C=2"))
                ->addOr($expr->text("D=3"))
        )
        ;
$filter->compose($composer);
echo $composer->text. "<br />";
echo "<br />";

//10 . (A=1 or B=1) and  (C=2 or D=3 and ( E=1 or F=2) ) 
$filter = $expr->complex()
        ->add(
            $expr->complex()
                ->add($expr->text("A=1"))
                ->addOr($expr->text("B=1"))
            )
        ->addOr(
            $expr->complex()
                ->addAnd($expr->complex()->add($expr->text("E=1"))->addOr($expr->text("F=2")))
                ->addAnd($expr->text("C=2"))
                ->addOr($expr->text("D=3"))
        )
        ;
$filter->compose($composer);
echo $composer->text. "<br />";
echo "<br />";

Composerクラスは本当はabstractになるべきで、DatabaseExpressionComposerとLdapExpressionComposerなどに分かれるべき。なぜかといえば「せっかく継承するのに親クラスのコードを一切使わないなら継承はしてはならない」から。シグネーチャをあわせるならインターフェースか抽象クラスを使うべき。
ところでComposerクラスの存在は微妙(デザインパターン的な意味で)なんだが、これはVisitorと思っていいんだろうか?