| <?php | 
 | /** | 
 |  * | 
 |  * Licensed to the Apache Software Foundation (ASF) under one | 
 |  * or more contributor license agreements.  See the NOTICE file | 
 |  * distributed with this work for additional information | 
 |  * regarding copyright ownership.  The ASF licenses this file | 
 |  * to you under the Apache License, Version 2.0 (the | 
 |  * "License"); you may not use this file except in compliance | 
 |  * with the License.  You may obtain a copy of the License at | 
 |  *  | 
 |  *   http://www.apache.org/licenses/LICENSE-2.0 | 
 |  *  | 
 |  * Unless required by applicable law or agreed to in writing, | 
 |  * software distributed under the License is distributed on an | 
 |  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 |  * KIND, either express or implied.  See the License for the | 
 |  * specific language governing permissions and limitations | 
 |  * under the License. | 
 |  * | 
 |  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 | 
 |  * @version //autogentag// | 
 |  * @filesource | 
 |  * @package Search | 
 |  * @subpackage Tests | 
 |  */ | 
 |  | 
 | require_once 'testfiles/test-classes.php'; | 
 |  | 
 | /** | 
 |  * Test the query tokenizer and builder | 
 |  * | 
 |  * @package Search | 
 |  * @subpackage Tests | 
 |  */ | 
 | class ezcSearchBuildSearchQueryTest extends ezcTestCase | 
 | { | 
 |     public static function suite() | 
 |     { | 
 |          return new PHPUnit_Framework_TestSuite( "ezcSearchBuildSearchQueryTest" ); | 
 |     } | 
 |  | 
 |     public static function testOneWord() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( 'word' ); | 
 |         $expected = array( new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'word' ) ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testMoreWords() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "I'll be back" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, "I'll" ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'be' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'back' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testAnd1() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "me AND you" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'me' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::LOGICAL_AND, 'AND' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'you' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |      | 
 |     public static function testAnd2() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "me and you" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'me' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::LOGICAL_AND, 'and' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'you' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testOr1() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "me OR you" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'me' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::LOGICAL_OR, 'OR' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'you' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testOr2() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "me or you" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'me' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::LOGICAL_OR, 'or' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'you' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testInternalAnd() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "meANDyou" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'meANDyou' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testInternalOr() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( "meORyou" ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'meORyou' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testWordsInQuotes() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( '"The world is not enough"' ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::QUOTE, '"' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'The' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'world' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'is' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'not' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'enough' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::QUOTE, '"' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testPlusMinus() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( '+plus -minus' ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::PLUS, '+' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'plus' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::MINUS, '-' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'minus' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testBraces() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( 'a OR (b AND c)' ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'a' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::LOGICAL_OR, 'OR' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::BRACE_OPEN, '(' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'b' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::LOGICAL_AND, 'AND' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::SPACE, " " ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'c' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::BRACE_CLOSE, ')' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testColon() | 
 |     { | 
 |         $result = BuildQueryQuery::tokenize( 'site:ezcomponents.org' ); | 
 |         $expected = array( | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'site' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::COLON, ':' ), | 
 |             new ezcSearchQueryToken( ezcSearchQueryToken::STRING, 'ezcomponents.org' ), | 
 |         ); | 
 |         self::assertEquals( $expected, $result ); | 
 |     } | 
 |  | 
 |     public static function testAddOneWordOneField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'goldfinger', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( "fieldOne_t:goldfinger" ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddOneWordTwoField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'thunderball', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         self::assertSame( array( "( fieldOne_t:thunderball OR fieldTwo_t:thunderball )" ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddMoreWordsOneField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'from russia with love', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( "fieldOne_t:from", "fieldOne_t:russia", "fieldOne_t:with", "fieldOne_t:love" ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddMoreWordsTwoField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'you only live twice', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         $expected = array( | 
 |             "( fieldOne_t:you OR fieldTwo_t:you )", | 
 |             "( fieldOne_t:only OR fieldTwo_t:only )", | 
 |             "( fieldOne_t:live OR fieldTwo_t:live )", | 
 |             "( fieldOne_t:twice OR fieldTwo_t:twice )", | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddOnePhraseOneField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"diamonds are forever"', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( 'fieldOne_t:"diamonds are forever"' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddOnePhraseTwoField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"live and let die"', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         self::assertSame( array( '( fieldOne_t:"live and let die" OR fieldTwo_t:"live and let die" )' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddOnePhraseOneFieldMissingQuotes() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         try | 
 |         { | 
 |             $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"for your eyes only', array( 'fieldOne' ) ); | 
 |             self::fail( 'Expected exception not-thrown.' ); | 
 |         } | 
 |         catch ( ezcSearchBuildQueryException $e ) | 
 |         { | 
 |             self::assertSame( "Unterminated quotes in query string.", $e->getMessage() ); | 
 |         } | 
 |     } | 
 |  | 
 |     public static function testAddMorePhrasesTwoField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"the man with the golden gun" "the spy who loved me"', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         $expected = array( | 
 |             '( fieldOne_t:"the man with the golden gun" OR fieldTwo_t:"the man with the golden gun" )', | 
 |             '( fieldOne_t:"the spy who loved me" OR fieldTwo_t:"the spy who loved me" )', | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testOrMoreWordsOneField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'cake OR death', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( '( fieldOne_t:cake OR fieldOne_t:death )' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testOrMoreWordsTwoField() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'cake OR death', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         $expected = array( | 
 |             '( ( fieldOne_t:cake OR fieldTwo_t:cake ) OR ( fieldOne_t:death OR fieldTwo_t:death ) )', | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testMixedAndAndOrWithoutBraces() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         try | 
 |         { | 
 |             $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'god OR does AND not OR play AND with OR dices', array( 'fieldOne' ) ); | 
 |             self::fail( 'Expected exception not-thrown.' ); | 
 |         } | 
 |         catch ( ezcSearchBuildQueryException $e ) | 
 |         { | 
 |             self::assertSame( 'You can not mix OR and AND without using "(" and ")".', $e->getMessage() ); | 
 |         } | 
 |     } | 
 |  | 
 |     public static function testMixedOrAndAndWithoutBraces() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         try | 
 |         { | 
 |             $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'I AND don\'t OR believe AND in OR mathematics', array( 'fieldOne' ) ); | 
 |             self::fail( 'Expected exception not-thrown.' ); | 
 |         } | 
 |         catch ( ezcSearchBuildQueryException $e ) | 
 |         { | 
 |             self::assertSame( 'You can not mix AND and OR without using "(" and ")".', $e->getMessage() ); | 
 |         } | 
 |     } | 
 |  | 
 |     public static function testOrMoreWordsTwoFieldWithBraces() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '(cake OR death)', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         $expected = array( | 
 |             '( ( fieldOne_t:cake OR fieldTwo_t:cake ) OR ( fieldOne_t:death OR fieldTwo_t:death ) )', | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testMixedAndAndOrMoreWordsOneFieldWithBraces() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'cake OR (cookies AND death)', array( 'fieldOne' ) ); | 
 |         $expected = array( | 
 |             '( fieldOne_t:cake OR ( fieldOne_t:cookies AND fieldOne_t:death ) )', | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testMixedAndAndOrMoreWordsTwoFieldWithBraces() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'cake OR (cookies AND death)', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         $expected = array( | 
 |             '( ( fieldOne_t:cake OR fieldTwo_t:cake ) OR ( ( fieldOne_t:cookies OR fieldTwo_t:cookies ) AND ( fieldOne_t:death OR fieldTwo_t:death ) ) )', | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddMoreWordsOneFieldWithPlus() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'from +russia with love', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( "fieldOne_t:from", "+fieldOne_t:russia", "fieldOne_t:with", "fieldOne_t:love" ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testAddMoreWordsTwoFieldWithMinus() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '-you only live -twice', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         $expected = array( | 
 |             "( !fieldOne_t:you AND !fieldTwo_t:you )", | 
 |             "( fieldOne_t:only OR fieldTwo_t:only )", | 
 |             "( fieldOne_t:live OR fieldTwo_t:live )", | 
 |             "( !fieldOne_t:twice AND !fieldTwo_t:twice )", | 
 |         ); | 
 |         self::assertSame( $expected, $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testSpecialCharsInQuotes() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"Testing+more-less is (good):	not! "', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         self::assertSame( array( '( fieldOne_t:"Testing+more-less is (good):	not!" OR fieldTwo_t:"Testing+more-less is (good):	not!" )' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     // test for issue #14159 | 
 |     public static function testWithColon1() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"http://blahblah"', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( 'fieldOne_t:"http://blahblah"' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testWithColon2() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionTwoFields' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, '"http://blahblah"', array( 'fieldOne', 'fieldTwo' ) ); | 
 |         self::assertSame( array( '( fieldOne_t:"http://blahblah" OR fieldTwo_t:"http://blahblah" )' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     // tests for issue #15298 | 
 |     public static function testAndOnlyWithDelimiters() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'cakeANDdeath', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( 'fieldOne_t:cakeANDdeath' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function testOrdOnlyWithDelimiters() | 
 |     { | 
 |         $q = self::setupQuery( 'DefinitionOneField' ); | 
 |         $qb = new ezcSearchQueryBuilder(); | 
 |         $qb->parseSearchQuery( $q, 'INTERSPORT', array( 'fieldOne' ) ); | 
 |         self::assertSame( array( 'fieldOne_t:INTERSPORT' ), $q->whereClauses ); | 
 |     } | 
 |  | 
 |     public static function setupQuery( $definition ) | 
 |     { | 
 |         $h = new TestHandler; | 
 |         $d = new ezcSearchDocumentDefinition( $definition ); | 
 |         $d->fields['fieldOne'] = new ezcSearchDefinitionDocumentField( 'fieldOne' ); | 
 |         if ( $definition == 'DefinitionTwoFields' ) | 
 |         { | 
 |             $d->fields['fieldTwo'] = new ezcSearchDefinitionDocumentField( 'fieldTwo' ); | 
 |         } | 
 |         $d->fields['ezcsearch_type'] = new ezcSearchDefinitionDocumentField( 'type' ); | 
 |         $q = $h->createFindQuery( 'DefinitionOneField', $d ); | 
 |         $q->whereClauses = array(); | 
 |         return $q; | 
 |     } | 
 | } | 
 | ?> |