├── .gitignore
├── src
└── Pel
│ ├── Exception
│ ├── Exception.php
│ └── RuntimeException.php
│ └── Expression
│ ├── Ast
│ ├── ExpressionInterface.php
│ ├── ParameterExpression.php
│ ├── ConstantExpression.php
│ ├── ArrayExpression.php
│ ├── NotExpression.php
│ ├── AndExpression.php
│ ├── GetPropertyExpression.php
│ ├── OrExpression.php
│ ├── VariableExpression.php
│ ├── FunctionExpression.php
│ ├── GetItemExpression.php
│ ├── IsEqualExpression.php
│ ├── MethodCallExpression.php
│ └── ConcatExpression.php
│ ├── ExpressionHandlerInterface.php
│ ├── Compiler
│ ├── AndExpressionCompiler.php
│ ├── OrExpressionCompiler.php
│ ├── IsEqualExpressionCompiler.php
│ ├── ConcatExpressionCompiler.php
│ ├── TypeCompilerInterface.php
│ ├── Func
│ │ └── FunctionCompilerInterface.php
│ ├── NotExpressionCompiler.php
│ ├── BinaryExprCompiler.php
│ ├── VariableExpressionCompiler.php
│ └── ParameterExpressionCompiler.php
│ ├── Expression.php
│ ├── ExpressionLexer.php
│ ├── ExpressionParser.php
│ └── ExpressionCompiler.php
├── tests
├── bootstrap.php.dist
├── autoload.php.dist
└── Pel
│ └── Tests
│ └── Expression
│ ├── ExpressionLexerTest.php
│ ├── OrExpressionCompilerTest.php
│ ├── AndExpressionCompilerTest.php
│ ├── NotExpressionCompilerTest.php
│ ├── ExpressionCompilerTest.php
│ ├── ParameterExpressionCompilerTest.php
│ └── ExpressionParserTest.php
├── .travis.yml
├── phpunit.xml.dist
├── composer.json
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/src/Pel/Exception/Exception.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ./tests
8 |
9 |
10 |
11 |
12 |
13 | ./
14 |
15 | ./vendor
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/ExpressionLexerTest.php:
--------------------------------------------------------------------------------
1 | lexer->setInput('#contact');
14 |
15 | $this->assertEquals(array(
16 | 'contact',
17 | 0,
18 | ExpressionLexer::T_PARAMETER,
19 | ), $this->lexer->next);
20 | $this->assertFalse($this->lexer->moveNext());
21 | }
22 |
23 | protected function setUp()
24 | {
25 | $this->lexer = new ExpressionLexer();
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/ExpressionInterface.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | interface ExpressionInterface
22 | {
23 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/ParameterExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class ParameterExpression implements ExpressionInterface
22 | {
23 | public $name;
24 |
25 | public function __construct($name)
26 | {
27 | $this->name = $name;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/ConstantExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class ConstantExpression implements ExpressionInterface
22 | {
23 | public $value;
24 |
25 | public function __construct($value)
26 | {
27 | $this->value = $value;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Pel/Expression/ExpressionHandlerInterface.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression;
20 |
21 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
22 |
23 | interface ExpressionHandlerInterface
24 | {
25 | public function createContext(TokenInterface $token, $object);
26 | }
27 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/ArrayExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class ArrayExpression implements ExpressionInterface
22 | {
23 | public $elements;
24 |
25 | public function __construct(array $elements)
26 | {
27 | $this->elements = $elements;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/NotExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class NotExpression implements ExpressionInterface
22 | {
23 | public $expr;
24 |
25 | public function __construct(ExpressionInterface $expr)
26 | {
27 | $this->expr = $expr;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/AndExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | class AndExpressionCompiler extends BinaryExprCompiler
22 | {
23 | public function getType()
24 | {
25 | return 'Pel\Expression\Ast\AndExpression';
26 | }
27 |
28 | protected function getOperator()
29 | {
30 | return '&&';
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/OrExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | class OrExpressionCompiler extends BinaryExprCompiler
22 | {
23 | public function getType()
24 | {
25 | return 'Pel\Expression\Ast\OrExpression';
26 | }
27 |
28 | protected function getOperator()
29 | {
30 | return '||';
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/AndExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class AndExpression implements ExpressionInterface
22 | {
23 | public $left;
24 | public $right;
25 |
26 | public function __construct(ExpressionInterface $left, ExpressionInterface $right)
27 | {
28 | $this->left = $left;
29 | $this->right = $right;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/GetPropertyExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class GetPropertyExpression implements ExpressionInterface
22 | {
23 | public $object;
24 | public $name;
25 |
26 | public function __construct(ExpressionInterface $obj, $name)
27 | {
28 | $this->object = $obj;
29 | $this->name = $name;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/OrExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class OrExpression implements ExpressionInterface
22 | {
23 | public $left;
24 | public $right;
25 |
26 | public function __construct(ExpressionInterface $left, ExpressionInterface $right)
27 | {
28 | $this->left = $left;
29 | $this->right = $right;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/VariableExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class VariableExpression implements ExpressionInterface
22 | {
23 | public $name;
24 | public $allowNull;
25 |
26 | public function __construct($name, $allowNull = false)
27 | {
28 | $this->name = $name;
29 | $this->allowNull = $allowNull;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/FunctionExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class FunctionExpression implements ExpressionInterface
22 | {
23 | /** READ-ONLY */
24 | public $name;
25 | public $args;
26 |
27 | public function __construct($name, array $args)
28 | {
29 | $this->name = $name;
30 | $this->args = $args;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/GetItemExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class GetItemExpression implements ExpressionInterface
22 | {
23 | public $array;
24 | public $key;
25 |
26 | public function __construct(ExpressionInterface $array, ExpressionInterface $key)
27 | {
28 | $this->array = $array;
29 | $this->key = $key;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/IsEqualExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class IsEqualExpression implements ExpressionInterface
22 | {
23 | public $left;
24 | public $right;
25 |
26 | public function __construct(ExpressionInterface $left, ExpressionInterface $right)
27 | {
28 | $this->left = $left;
29 | $this->right = $right;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/IsEqualExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | class IsEqualExpressionCompiler extends BinaryExprCompiler
22 | {
23 | public function getType()
24 | {
25 | return 'Pel\Expression\Ast\IsEqualExpression';
26 | }
27 |
28 | protected function getOperator()
29 | {
30 | return '===';
31 | }
32 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kitano/php-expression",
3 | "description": "PHP Expression language",
4 | "keywords": ["expression language", "el", "expression", "expressions"],
5 | "type": "library",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Benjamin Dulau",
10 | "email": "benjamin.dulau@anonymation.com",
11 | "homepage": "http://www.anonymation.com"
12 | },
13 | {
14 | "name": "Boris Guéry",
15 | "email": "guery.b@gmail.com",
16 | "homepage": "http://borisguery.com"
17 | }
18 | ],
19 | "require": {
20 | "php": ">=5.3.3",
21 | "jms/parser-lib": "1.*"
22 | },
23 | "require-dev": {
24 | "phpunit/phpunit": "3.*",
25 | "jms/cg": "1.*"
26 | },
27 | "config": {
28 | "bin-dir": "bin"
29 | },
30 | "autoload": {
31 | "psr-0": { "Pel": "src/" }
32 | },
33 | "extra": {
34 | "branch-alias": {
35 | "dev-master": "0.1-dev"
36 | }
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/MethodCallExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | class MethodCallExpression implements ExpressionInterface
22 | {
23 | public $object;
24 | public $method;
25 | public $args;
26 |
27 | public function __construct(ExpressionInterface $obj, $method, array $args)
28 | {
29 | $this->object = $obj;
30 | $this->method = $method;
31 | $this->args = $args;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/ConcatExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | /**
22 | * @author Adrien Brault
23 | */
24 | class ConcatExpressionCompiler extends BinaryExprCompiler
25 | {
26 | public function getType()
27 | {
28 | return 'Pel\Expression\Ast\ConcatExpression';
29 | }
30 |
31 | protected function getOperator()
32 | {
33 | return '.';
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Ast/ConcatExpression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Ast;
20 |
21 | /**
22 | * @author Adrien Brault
23 | */
24 | class ConcatExpression implements ExpressionInterface
25 | {
26 | public $left;
27 | public $right;
28 |
29 | public function __construct(ExpressionInterface $left, ExpressionInterface $right)
30 | {
31 | $this->left = $left;
32 | $this->right = $right;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/TypeCompilerInterface.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | use Pel\Expression\Ast\ExpressionInterface;
22 | use Pel\Expression\ExpressionCompiler;
23 |
24 | interface TypeCompilerInterface
25 | {
26 | public function getType();
27 | public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr);
28 | public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr);
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Kitano
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/Func/FunctionCompilerInterface.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler\Func;
20 |
21 | use Pel\Expression\Ast\FunctionExpression;
22 | use Pel\Expression\ExpressionCompiler;
23 |
24 | interface FunctionCompilerInterface
25 | {
26 | public function getName();
27 | public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function);
28 | public function compile(ExpressionCompiler $compiler, FunctionExpression $function);
29 | }
30 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/OrExpressionCompilerTest.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class OrExpressionCompilerTest extends \PHPUnit_Framework_TestCase
12 | {
13 | /**
14 | * @dataProvider getOrExpressions
15 | */
16 | public function testAndExpressionCompiler($expr, $expectedResult)
17 | {
18 | $evaluator = eval(
19 | $source = $this->compiler->compileExpression(
20 | new Expression($expr)
21 | )
22 | );
23 |
24 | $this->assertSame($expectedResult, $evaluator(array()));
25 | }
26 |
27 | public function getOrExpressions()
28 | {
29 | return array(
30 | array('"true" or not "true"', true),
31 | array('"true" or "true"', true),
32 | array('not "true" or not "true"', false),
33 | array('not "true" or "true"', true),
34 | );
35 | }
36 |
37 | protected function setUp()
38 | {
39 | $this->compiler = new ExpressionCompiler();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Expression.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression;
20 |
21 | final class Expression
22 | {
23 | /** READ-ONLY */
24 | public $expression;
25 |
26 | public function __construct($expression)
27 | {
28 | $this->expression = $expression;
29 | }
30 |
31 | public function getHashCode()
32 | {
33 | return sha1($this->expression);
34 | }
35 |
36 | public function __toString()
37 | {
38 | return 'EXPRESSION('.$this->expression.')';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/AndExpressionCompilerTest.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class AndExpressionCompilerTest extends \PHPUnit_Framework_TestCase
12 | {
13 | /**
14 | * @dataProvider getAndExpressions
15 | */
16 | public function testAndExpressionCompiler($expr, $expectedResult)
17 | {
18 | $evaluator = eval(
19 | $source = $this->compiler->compileExpression(
20 | new Expression($expr)
21 | )
22 | );
23 |
24 | $this->assertSame($expectedResult, $evaluator(array()));
25 | }
26 |
27 | public function getAndExpressions()
28 | {
29 | return array(
30 | array('"true" and not "true"', false),
31 | array('"true" and "true"', true),
32 | array('not "true" and not "true"', false),
33 | array('not "true" and "true"', false),
34 | );
35 | }
36 |
37 | protected function setUp()
38 | {
39 | $this->compiler = new ExpressionCompiler();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/NotExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | use Pel\Expression\ExpressionCompiler;
22 | use Pel\Expression\Ast\ExpressionInterface;
23 |
24 | class NotExpressionCompiler implements TypeCompilerInterface
25 | {
26 | public function getType()
27 | {
28 | return 'Pel\Expression\Ast\NotExpression';
29 | }
30 |
31 | public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr)
32 | {
33 | $compiler->compilePreconditions($expr->expr);
34 | }
35 |
36 | public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
37 | {
38 | $compiler
39 | ->write('!(')
40 | ->compileInternal($expr->expr)
41 | ->write(')')
42 | ;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/NotExpressionCompilerTest.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class NotExpressionCompilerTest extends \PHPUnit_Framework_TestCase
12 | {
13 | /**
14 | * @dataProvider getFalseExpressions
15 | */
16 | public function testNotExpressionCompiler($expr, $expectedResult)
17 | {
18 | $evaluator = eval(
19 | $source = $this->compiler->compileExpression(
20 | new Expression($expr)
21 | )
22 | );
23 |
24 | $this->assertSame($expectedResult, $evaluator(array()));
25 | }
26 |
27 | public function getFalseExpressions()
28 | {
29 | return array(
30 | array('not "a string is always true"', false),
31 | // (except if it is empty)
32 | array('not ""', true),
33 | // zero as string is evaluated to false
34 | array('not "0"', true),
35 | // but "1" is true
36 | array('not "1"', false),
37 | // the same expr with another notation
38 | array('! "a string is always true"', false),
39 | // (except if it is empty)
40 | array('! ""', true),
41 | // zero as string is evaluated to false
42 | array('! "0"', true),
43 | // but "1" is true
44 | array('! "1"', false),
45 | );
46 | }
47 |
48 | protected function setUp()
49 | {
50 | $this->compiler = new ExpressionCompiler();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/BinaryExprCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | use Pel\Expression\Ast\ExpressionInterface;
22 | use Pel\Expression\ExpressionCompiler;
23 |
24 | /**
25 | * Base Compiler for Binary Operators.
26 | *
27 | * @author Johannes M. Schmitt
28 | */
29 | abstract class BinaryExprCompiler implements TypeCompilerInterface
30 | {
31 | public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr)
32 | {
33 | $compiler
34 | ->compilePreconditions($expr->left)
35 | ->compilePreconditions($expr->right)
36 | ;
37 | }
38 |
39 | public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
40 | {
41 | $compiler
42 | ->write("(")
43 | ->compileInternal($expr->left)
44 | ->write(") ".$this->getOperator()." (")
45 | ->compileInternal($expr->right)
46 | ->write(")")
47 | ;
48 | }
49 |
50 | abstract protected function getOperator();
51 | }
52 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/VariableExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | use Pel\Expression\Ast\VariableExpression;
22 | use Pel\Expression\Ast\ExpressionInterface;
23 | use Pel\Expression\ExpressionCompiler;
24 |
25 | class VariableExpressionCompiler implements TypeCompilerInterface
26 | {
27 | public function getType()
28 | {
29 | return 'Pel\Expression\Ast\VariableExpression';
30 | }
31 |
32 | public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $expr)
33 | {
34 | if (!$expr->allowNull && !$this->isKnown($expr->name)) {
35 | $compiler->verifyItem($expr->name);
36 | }
37 | }
38 |
39 | public function compile(ExpressionCompiler $compiler, ExpressionInterface $expr)
40 | {
41 | if ('permitAll' === $expr->name) {
42 | $compiler->write('true');
43 |
44 | return;
45 | }
46 |
47 | if ('denyAll' === $expr->name) {
48 | $compiler->write('false');
49 |
50 | return;
51 | }
52 |
53 | if ($expr->allowNull) {
54 | $compiler->write("(isset(\$context['{$expr->name}']) ? ");
55 | }
56 |
57 | $compiler->write("\$context['{$expr->name}']");
58 |
59 | if ($expr->allowNull) {
60 | $compiler->write(" : null)");
61 | }
62 | }
63 |
64 | protected function isKnown($variable)
65 | {
66 | return in_array($variable, array('permitAll', 'denyAll'), true);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/ExpressionCompilerTest.php:
--------------------------------------------------------------------------------
1 | compiler->compileExpression(new Expression('["foo","bar"]')));
19 |
20 | $expected = array('foo', 'bar');
21 | $this->assertEquals($expected, $evaluator(array()));
22 | }
23 |
24 | public function testCompileArrayAccessExpressionInteger()
25 | {
26 | $evaluator = eval($this->compiler->compileExpression(new Expression('foos[0]')));
27 |
28 | $context = array(
29 | 'foos' => array(
30 | 'foo',
31 | 'bar',
32 | ),
33 | );
34 | $expected = 'foo';
35 | $this->assertEquals($expected, $evaluator($context));
36 | }
37 |
38 | public function testCompileConcatExpression()
39 | {
40 | $evaluator = eval($this->compiler->compileExpression(new Expression('"Hello " ~ name ~ "!"')));
41 |
42 | $context = array(
43 | 'name' => 'Adrien',
44 | );
45 | $expected = 'Hello Adrien!';
46 | $this->assertEquals($expected, $evaluator($context));
47 | }
48 |
49 | public function testCompileArrayAccessExpressionString()
50 | {
51 | $evaluator = eval($this->compiler->compileExpression(new Expression('foos["foo"]')));
52 |
53 | $context = array(
54 | 'foos' => array(
55 | 'foo' => 'Adrien',
56 | 'bar' => 'William',
57 | ),
58 | );
59 | $expected = 'Adrien';
60 | $this->assertEquals($expected, $evaluator($context));
61 | }
62 |
63 | public function testComplexExpression()
64 | {
65 | $expression = 'requests[0].attributes.get(name, "default")';
66 | $evaluator = eval($this->compiler->compileExpression(new Expression($expression)));
67 | $context = array(
68 | 'requests' => array(
69 | new Request(),
70 | ),
71 | 'name' => 'Adrien',
72 | );
73 |
74 | $expected = array('Adrien', 'default');
75 | $this->assertEquals($expected, $evaluator($context));
76 | }
77 |
78 | protected function setUp()
79 | {
80 | $this->compiler = new ExpressionCompiler();
81 | }
82 | }
83 |
84 | class Request
85 | {
86 | public $attributes;
87 |
88 | public function __construct()
89 | {
90 | $this->attributes = new ParameterBag();
91 | }
92 |
93 | public function getRequestFormat()
94 | {
95 | return 'html';
96 | }
97 | }
98 |
99 | class ParameterBag
100 | {
101 | public function get($name, $default)
102 | {
103 | return array($name, $default);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Pel/Expression/Compiler/ParameterExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression\Compiler;
20 |
21 | use Pel\Expression\Ast\VariableExpression;
22 | use Pel\Expression\ExpressionCompiler;
23 | use Pel\Expression\Ast\ExpressionInterface;
24 | use Pel\Expression\Compiler\TypeCompilerInterface;
25 |
26 | class ParameterExpressionCompiler implements TypeCompilerInterface
27 | {
28 | public function getType()
29 | {
30 | return 'Pel\Expression\Ast\ParameterExpression';
31 | }
32 |
33 | public function compilePreconditions(ExpressionCompiler $compiler, ExpressionInterface $parameter)
34 | {
35 | $compiler->verifyItem('object', 'CG\Proxy\MethodInvocation');
36 |
37 | if (!isset($compiler->attributes['parameter_mapping_name'])) {
38 | $this->addParameterMapping($compiler);
39 | }
40 |
41 | $compiler
42 | ->writeln("if (!isset(\${$compiler->attributes['parameter_mapping_name']}['{$parameter->name}'])) {")
43 | ->indent()
44 | ->write("throw new RuntimeException(sprintf('There is no parameter with name \"{$parameter->name}\" for method \"%s\".', ")
45 | ->compileInternal(new VariableExpression('object'))
46 | ->writeln("));")
47 | ->outdent()
48 | ->write("}\n\n")
49 | ;
50 | }
51 |
52 | public function compile(ExpressionCompiler $compiler, ExpressionInterface $parameter)
53 | {
54 | $compiler
55 | ->compileInternal(new VariableExpression('object'))
56 | ->write("->arguments[")
57 | ->write("\${$compiler->attributes['parameter_mapping_name']}")
58 | ->write("['{$parameter->name}']]")
59 | ;
60 | }
61 |
62 | private function addParameterMapping(ExpressionCompiler $compiler)
63 | {
64 | $name = $compiler->nextName();
65 | $indexName = $compiler->nextName();
66 | $paramName = $compiler->nextName();
67 |
68 | $compiler
69 | ->setAttribute('parameter_mapping_name', $name)
70 | ->writeln("\$$name = array();")
71 | ->write("foreach (")
72 | ->compileInternal(new VariableExpression('object'))
73 | ->writeln("->reflection->getParameters() as \$$indexName => \$$paramName) {")
74 | ->indent()
75 | ->writeln("\${$name}[\${$paramName}->name] = \$$indexName;")
76 | ->outdent()
77 | ->writeln("}\n")
78 | ;
79 | }
80 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PHP Expression Language
2 | =======================
3 |
4 | The purpose of this library is to provide a common base for an PHP Expression Language.
5 |
6 | This is not really a creative library since it burrows almost all the code from the great
7 | [JMSSecurityExtraBundle](http://jmsyst.com/bundles/JMSSecurityExtraBundle "") which already defines
8 | more or less such a base for a powerful EL (Expression Language).
9 |
10 | The idea is to take this code outside of the Johannes's bundle and to standardize it.
11 |
12 | Simple usage
13 | ------------
14 |
15 | ```php
16 | $compiler = new ExpressionCompiler();
17 | $evaluator = eval($compiler->compileExpression(new Expression("date.format(format)")));
18 |
19 | $context = array(
20 | 'date' => new \DateTime(),
21 | 'format' => 'Y',
22 | );
23 | $result = $evaluator($context);
24 |
25 | echo $result; // 2013
26 | ```
27 |
28 | Adding a custom function compiler
29 | ---------------------------------
30 |
31 | The isNumber() function expression:
32 |
33 | 1. First you need to create a compiler for your function
34 |
35 | ```PHP
36 | args)) {
55 | throw new RuntimeException(sprintf('The isNumber() function expects exactly one argument, but got "%s".', var_export($function->args, true)));
56 | }
57 | }
58 |
59 | public function compile(ExpressionCompiler $compiler, FunctionExpression $function)
60 | {
61 | $compiler
62 | ->write("is_numeric(")
63 | ->compileInternal($function->args[0])
64 | ->write(")")
65 | ;
66 | }
67 | }
68 | ```
69 |
70 | 2. Next, after having instanciated the `ExpressionCompiler`, you just need to register your custom function compiler
71 |
72 | ```PHP
73 | addFunctionCompiler(new IsNumberFunctionCompiler());
77 |
78 | $evaluator = eval($compiler->compileExpression(new Expression("isNumber('1234')")));
79 | var_dump(call_user_func($evaluator, array()));
80 | // bool(true)
81 |
82 | $evaluator = eval($compiler->compileExpression(new Expression("isNumber('1234abc')")));
83 | var_dump(call_user_func($evaluator, array()));
84 | // bool(false)
85 | ```
86 |
87 |
88 | License
89 | -------
90 |
91 | This bundle is under the MIT license. See the complete license in library:
92 |
93 | LICENSE
94 |
--------------------------------------------------------------------------------
/src/Pel/Expression/ExpressionLexer.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression;
20 |
21 | final class ExpressionLexer extends \JMS\Parser\AbstractLexer
22 | {
23 | const T_STRING = 1;
24 | const T_IDENTIFIER = 2;
25 | const T_NONE = 3;
26 | const T_COMMA = 4;
27 | const T_OPEN_PARENTHESIS = 5;
28 | const T_CLOSE_PARENTHESIS = 6;
29 | const T_AND = 7;
30 | const T_OR = 8;
31 | const T_PARAMETER = 9;
32 | const T_OBJECT_OPERATOR = 10;
33 | const T_OPEN_BRACKET = 11;
34 | const T_CLOSE_BRACKET = 12;
35 | const T_OPEN_BRACE = 13;
36 | const T_CLOSE_BRACE = 14;
37 | const T_COLON = 15;
38 | const T_IS_EQUAL = 16;
39 | const T_NOT = 17;
40 | const T_INTEGER = 18;
41 | const T_CONCAT = 19;
42 |
43 | protected function getRegex()
44 | {
45 | return
46 | '/
47 | (
48 | \#?[a-z][a-z0-9]*
49 | |\'(?:
50 | [^\']
51 | |(?<=
52 | \\\\
53 | )\'
54 | )*\'
55 | |"(?:
56 | [^"]
57 | |(?<=
58 | \\\\
59 | )
60 | ")*"
61 | |&&
62 | |\|\|
63 | |==
64 | |~
65 | )
66 | |\s+
67 | |(.)
68 | /ix'
69 | ;
70 | }
71 |
72 | protected function determineTypeAndValue($value)
73 | {
74 | $type = self::T_NONE;
75 |
76 | if ("'" === $value[0] || '"' === $value[0]) {
77 | $type = self::T_STRING;
78 | $value = substr($value, 1, -1);
79 | } elseif (',' === $value) {
80 | $type = self::T_COMMA;
81 | } elseif ('(' === $value) {
82 | $type = self::T_OPEN_PARENTHESIS;
83 | } elseif (')' === $value) {
84 | $type = self::T_CLOSE_PARENTHESIS;
85 | } elseif ('[' === $value) {
86 | $type = self::T_OPEN_BRACKET;
87 | } elseif (']' === $value) {
88 | $type = self::T_CLOSE_BRACKET;
89 | } elseif ('{' === $value) {
90 | $type = self::T_OPEN_BRACE;
91 | } elseif ('}' === $value) {
92 | $type = self::T_CLOSE_BRACE;
93 | } elseif ('&&' === $value || 'and' === strtolower($value)) {
94 | $type = self::T_AND;
95 | } elseif ('||' === $value || 'or' === strtolower($value)) {
96 | $type = self::T_OR;
97 | } elseif ('!' === $value || 'not' === strtolower($value)) {
98 | $type = self::T_NOT;
99 | } elseif (':' === $value) {
100 | $type = self::T_COLON;
101 | } elseif ('.' === $value) {
102 | $type = self::T_OBJECT_OPERATOR;
103 | } elseif ('==' === $value) {
104 | $type = self::T_IS_EQUAL;
105 | } elseif ('~' === $value) {
106 | $type = self::T_CONCAT;
107 | } elseif ('#' === $value[0]) {
108 | $type = self::T_PARAMETER;
109 | $value = substr($value, 1);
110 | } elseif (ctype_alpha($value)) {
111 | $type = self::T_IDENTIFIER;
112 | } elseif (ctype_digit($value)) {
113 | $type = self::T_INTEGER;
114 | $value = (int) $value;
115 | }
116 |
117 | return array($type, $value);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/ParameterExpressionCompilerTest.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Tests\Expression;
20 |
21 | use CG\Proxy\MethodInvocation;
22 | use Pel\Expression\Expression;
23 | use Pel\Expression\Compiler\ParameterExpressionCompiler;
24 | use Pel\Expression\ExpressionCompiler;
25 |
26 | class ParameterExpressionCompilerTest extends \PHPUnit_Framework_TestCase
27 | {
28 | /**
29 | * @var ExpressionCompiler
30 | */
31 | private $compiler;
32 |
33 | public function testCompile()
34 | {
35 | $evaluator = eval($source = $this->compiler->compileExpression(new Expression(
36 | '#foo == "bar"')));
37 |
38 | $object = new ParameterAccessTest;
39 | $reflection = new \ReflectionMethod($object, 'usefulMethod');
40 | $invocation = new MethodInvocation($reflection, $object, array('bar'), array());
41 | $this->assertTrue($evaluator(array('object' => $invocation)));
42 |
43 | $invocation->arguments = array('foo');
44 | $this->assertFalse($evaluator(array('object' => $invocation)));
45 | }
46 |
47 | /**
48 | * @expectedException \RuntimeException
49 | */
50 | public function testCompileThrowsExceptionWhenNoMethodInvocation()
51 | {
52 | $evaluator = eval($this->compiler->compileExpression(new Expression(
53 | '#foo == "fofo"')));
54 |
55 | $evaluator(array('object' => new \stdClass));
56 | }
57 |
58 | public function testCompileArrayWithParameters()
59 | {
60 | $evaluator = eval(
61 | $source = $this->compiler->compileExpression(
62 | new Expression('[#foo,"test"]')
63 | )
64 | );
65 |
66 | $object = new ParameterAccessTest;
67 | $reflection = new \ReflectionMethod($object, 'moreUsefulMethod');
68 | $invocation = new MethodInvocation($reflection, $object, array('foo_value'), array());
69 |
70 | $expected = array('foo_value', 'test');
71 | $this->assertEquals($expected, $evaluator(array('object' => $invocation)));
72 | }
73 |
74 | public function testCompileArrayWithMixedElements()
75 | {
76 | $evaluator = eval(
77 | $source = $this->compiler->compileExpression(
78 | new Expression('[#someObject.foo, #str, #int, #someArray]')
79 | )
80 | );
81 |
82 | $object = new ParameterAccessTest();
83 | $reflection = new \ReflectionMethod($object, 'complexMethod');
84 |
85 | $someObject = new \stdClass();
86 | $someObject->foo = 'bar';
87 |
88 | $invocation = new MethodInvocation(
89 | $reflection,
90 | $object,
91 | array(
92 | $someObject,
93 | 'string',
94 | 1,
95 | array(
96 | 'anArrayElement',
97 | 1 => 'foo',
98 | 'bar',
99 | 'key' => 'value',
100 | )
101 | ),
102 | array()
103 | );
104 |
105 | $expected = array(
106 | 'bar',
107 | 'string',
108 | 1,
109 | array(
110 | 'anArrayElement',
111 | 1 => 'foo',
112 | 'bar',
113 | 'key' => 'value',
114 | )
115 | );
116 |
117 | $this->assertSame($expected, $evaluator(array('object' => $invocation)));
118 |
119 | }
120 |
121 | protected function setUp()
122 | {
123 | $this->compiler = new ExpressionCompiler();
124 | $this->compiler->addTypeCompiler(new ParameterExpressionCompiler());
125 | }
126 | }
127 |
128 | class ParameterAccessTest
129 | {
130 | public function usefulMethod($foo)
131 | {
132 | }
133 |
134 | public function complexMethod(\stdClass $someObject, $str, $int, array $someArray = array())
135 | {
136 |
137 | }
138 |
139 | public function moreUsefulMethod($foo, $bar)
140 | {
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/tests/Pel/Tests/Expression/ExpressionParserTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(new FunctionExpression('myFunction', array()),
27 | $this->parser->parse('myFunction()'));
28 | }
29 |
30 | public function testAnd()
31 | {
32 | $expected = new AndExpression(
33 | new FunctionExpression('isEverythingRight', array()),
34 | new FunctionExpression('isSimple', array()));
35 |
36 | $this->assertEquals($expected, $this->parser->parse('isEverythingRight() && isSimple()'));
37 | $this->assertEquals($expected, $this->parser->parse('isEverythingRight() and isSimple()'));
38 | }
39 |
40 | public function testOr()
41 | {
42 | $expected = new OrExpression(
43 | new FunctionExpression('isEverythingRight', array()),
44 | new FunctionExpression('isSimple', array())
45 | );
46 |
47 | $this->assertEquals($expected, $this->parser->parse('isEverythingRight() || isSimple()'));
48 | $this->assertEquals($expected, $this->parser->parse('isEverythingRight() OR isSimple()'));
49 | }
50 |
51 | public function testNot()
52 | {
53 | $expected = new NotExpression(new FunctionExpression('isTrue', array()));
54 |
55 | $this->assertEquals($expected, $this->parser->parse('!isTrue()'));
56 | $this->assertEquals($expected, $this->parser->parse('not isTrue()'));
57 | }
58 |
59 | public function testGetProperty()
60 | {
61 | $expected = new GetPropertyExpression(new VariableExpression('A'), 'foo');
62 | $this->assertEquals($expected, $this->parser->parse('A.foo'));
63 | }
64 |
65 | public function testMethodCall()
66 | {
67 | $expected = new MethodCallExpression(new VariableExpression('A'), 'foo', array());
68 | $this->assertEquals($expected, $this->parser->parse('A.foo()'));
69 | }
70 |
71 | public function testArray()
72 | {
73 | $expected = new ArrayExpression(array(
74 | 'foo' => new ConstantExpression('bar'),
75 | ));
76 | $this->assertEquals($expected, $this->parser->parse('{"foo":"bar",}'));
77 | $this->assertEquals($expected, $this->parser->parse('{"foo":"bar"}'));
78 |
79 | $expected = new ArrayExpression(array(
80 | new ConstantExpression('foo'),
81 | new ConstantExpression('bar'),
82 | ));
83 | $this->assertEquals($expected, $this->parser->parse('["foo","bar",]'));
84 | $this->assertEquals($expected, $this->parser->parse('["foo","bar"]'));
85 | }
86 |
87 | public function testGetItem()
88 | {
89 | $expected = new GetItemExpression(
90 | new GetPropertyExpression(new VariableExpression('A'), 'foo'),
91 | new ConstantExpression('foo')
92 | );
93 | $this->assertEquals($expected, $this->parser->parse('A.foo["foo"]'));
94 | }
95 |
96 | public function testParameter()
97 | {
98 | $expected = new ParameterExpression('contact');
99 | $this->assertEquals($expected, $this->parser->parse('#contact'));
100 | }
101 |
102 | public function testArrayOfParameters()
103 | {
104 | $expected = new ArrayExpression(array(
105 | new ParameterExpression('foo'),
106 | new ParameterExpression('bar'),
107 | ));
108 | $this->assertEquals($expected, $this->parser->parse('[#foo,#bar]'));
109 | }
110 |
111 | public function testIsEqual()
112 | {
113 | $expected = new IsEqualExpression(new MethodCallExpression(
114 | new VariableExpression('user'), 'getUsername', array()),
115 | new ConstantExpression('Johannes'));
116 | $this->assertEquals($expected, $this->parser->parse('user.getUsername() == "Johannes"'));
117 | }
118 |
119 | /**
120 | * @dataProvider getPrecedenceTests
121 | */
122 | public function testPrecedence($expected, $expr)
123 | {
124 | $this->assertEquals($expected, $this->parser->parse($expr));
125 | }
126 |
127 | public function getPrecedenceTests()
128 | {
129 | $tests = array();
130 |
131 | $expected = new OrExpression(
132 | new AndExpression(new VariableExpression('A'), new VariableExpression('B')),
133 | new VariableExpression('C')
134 | );
135 | $tests[] = array($expected, 'A && B || C');
136 | $tests[] = array($expected, '(A && B) || C');
137 |
138 | $expected = new OrExpression(
139 | new VariableExpression('C'),
140 | new AndExpression(new VariableExpression('A'), new VariableExpression('B'))
141 | );
142 | $tests[] = array($expected, 'C || A && B');
143 | $tests[] = array($expected, 'C || (A && B)');
144 |
145 | $expected = new AndExpression(
146 | new AndExpression(new VariableExpression('A'), new VariableExpression('B')),
147 | new VariableExpression('C')
148 | );
149 | $tests[] = array($expected, 'A && B && C');
150 |
151 | $expected = new AndExpression(
152 | new VariableExpression('A'),
153 | new OrExpression(new VariableExpression('B'), new VariableExpression('C'))
154 | );
155 | $tests[] = array($expected, 'A && (B || C)');
156 |
157 | return $tests;
158 | }
159 |
160 | public function testInteger()
161 | {
162 | $expected = new ConstantExpression(1);
163 | $expression = $this->parser->parse('1');
164 |
165 | $this->assertEquals($expected, $expression);
166 | $this->assertSame($expected->value, $expression->value);
167 | }
168 |
169 | public function testConcat()
170 | {
171 | $expected = new ConcatExpression(
172 | new ConstantExpression('hey'),
173 | new ConstantExpression(' matelo!')
174 | );
175 | $expression = $this->parser->parse('"hey" ~ " matelo!"');
176 |
177 | $this->assertEquals($expected, $expression);
178 | }
179 |
180 | protected function setUp()
181 | {
182 | $this->parser = new ExpressionParser();
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/Pel/Expression/ExpressionParser.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression;
20 |
21 | use Pel\Expression\Ast\ConcatExpression;
22 | use Pel\Expression\Ast\NotExpression;
23 | use Pel\Expression\Ast\IsEqualExpression;
24 | use Pel\Expression\Ast\ParameterExpression;
25 | use Pel\Expression\Ast\VariableExpression;
26 | use Pel\Expression\Ast\ConstantExpression;
27 | use Pel\Expression\Ast\OrExpression;
28 | use Pel\Expression\Ast\AndExpression;
29 | use Pel\Expression\Ast\ArrayExpression;
30 | use Pel\Expression\Ast\GetItemExpression;
31 | use Pel\Expression\Ast\GetPropertyExpression;
32 | use Pel\Expression\Ast\MethodCallExpression;
33 | use Pel\Expression\Ast\ExpressionInterface;
34 | use Pel\Expression\Ast\FunctionExpression;
35 |
36 | final class ExpressionParser extends \JMS\Parser\AbstractParser
37 | {
38 | const PRECEDENCE_CONCAT = 5;
39 | const PRECEDENCE_OR = 10;
40 | const PRECEDENCE_AND = 15;
41 | const PRECEDENCE_IS_EQUAL = 20;
42 | const PRECEDENCE_NOT = 30;
43 |
44 | public function __construct()
45 | {
46 | parent::__construct(new ExpressionLexer());
47 | }
48 |
49 | public function parseInternal()
50 | {
51 | return $this->Expression();
52 | }
53 |
54 | private function Expression($precedence = 0)
55 | {
56 | $expr = $this->Primary();
57 |
58 | while (true) {
59 | if ($this->lexer->isNext(ExpressionLexer::T_CONCAT) && $precedence <= self::PRECEDENCE_CONCAT) {
60 | $this->lexer->moveNext();
61 |
62 | $expr = new ConcatExpression($expr, $this->Expression(
63 | self::PRECEDENCE_AND + 1
64 | ));
65 | continue;
66 | }
67 |
68 | if ($this->lexer->isNext(ExpressionLexer::T_AND) && $precedence <= self::PRECEDENCE_AND) {
69 | $this->lexer->moveNext();
70 |
71 | $expr = new AndExpression($expr, $this->Expression(
72 | self::PRECEDENCE_AND + 1));
73 | continue;
74 | }
75 |
76 | if ($this->lexer->isNext(ExpressionLexer::T_OR) && $precedence <= self::PRECEDENCE_OR) {
77 | $this->lexer->moveNext();
78 |
79 | $expr = new OrExpression($expr, $this->Expression(
80 | self::PRECEDENCE_OR + 1));
81 | continue;
82 | }
83 |
84 | if ($this->lexer->isNext(ExpressionLexer::T_IS_EQUAL) && $precedence <= self::PRECEDENCE_IS_EQUAL) {
85 | $this->lexer->moveNext();
86 |
87 | $expr = new IsEqualExpression($expr, $this->Expression(
88 | self::PRECEDENCE_IS_EQUAL + 1));
89 | continue;
90 | }
91 |
92 | break;
93 | }
94 |
95 | return $expr;
96 | }
97 |
98 | private function Primary()
99 | {
100 | if ($this->lexer->isNext(ExpressionLexer::T_NOT)) {
101 | $this->lexer->moveNext();
102 | $expr = new NotExpression($this->Expression(self::PRECEDENCE_NOT));
103 |
104 | return $this->Suffix($expr);
105 | }
106 |
107 | if ($this->lexer->isNext(ExpressionLexer::T_OPEN_PARENTHESIS)) {
108 | $this->lexer->moveNext();
109 | $expr = $this->Expression();
110 | $this->match(ExpressionLexer::T_CLOSE_PARENTHESIS);
111 |
112 | return $this->Suffix($expr);
113 | }
114 |
115 | if ($this->lexer->isNext(ExpressionLexer::T_STRING)) {
116 | return new ConstantExpression($this->match(ExpressionLexer::T_STRING));
117 | }
118 |
119 | if ($this->lexer->isNext(ExpressionLexer::T_INTEGER)) {
120 | return new ConstantExpression($this->match(ExpressionLexer::T_INTEGER));
121 | }
122 |
123 | if ($this->lexer->isNext(ExpressionLexer::T_OPEN_BRACE)) {
124 | return $this->Suffix($this->MapExpr());
125 | }
126 |
127 | if ($this->lexer->isNext(ExpressionLexer::T_OPEN_BRACKET)) {
128 | return $this->Suffix($this->ListExpr());
129 | }
130 |
131 | if ($this->lexer->isNext(ExpressionLexer::T_IDENTIFIER)) {
132 | $name = $this->match(ExpressionLexer::T_IDENTIFIER);
133 |
134 | if ($this->lexer->isNext(ExpressionLexer::T_OPEN_PARENTHESIS)) {
135 | $args = $this->Arguments();
136 |
137 | return $this->Suffix(new FunctionExpression($name, $args));
138 | }
139 |
140 | return $this->Suffix(new VariableExpression($name));
141 | }
142 |
143 | if ($this->lexer->isNext(ExpressionLexer::T_PARAMETER)) {
144 | return $this->Suffix(new ParameterExpression($this->match(ExpressionLexer::T_PARAMETER)));
145 | }
146 |
147 | $this->syntaxError('primary expression');
148 | }
149 |
150 | private function ListExpr()
151 | {
152 | $this->match(ExpressionLexer::T_OPEN_BRACKET);
153 |
154 | $elements = array();
155 | while ( ! $this->lexer->isNext(ExpressionLexer::T_CLOSE_BRACKET)) {
156 | $elements[] = $this->Expression();
157 |
158 | if ( ! $this->lexer->isNext(ExpressionLexer::T_COMMA)) {
159 | break;
160 | }
161 | $this->lexer->moveNext();
162 | }
163 |
164 | $this->match(ExpressionLexer::T_CLOSE_BRACKET);
165 |
166 | return new ArrayExpression($elements);
167 | }
168 |
169 | private function MapExpr()
170 | {
171 | $this->match(ExpressionLexer::T_OPEN_BRACE);
172 |
173 | $entries = array();
174 | while ( ! $this->lexer->isNext(ExpressionLexer::T_CLOSE_BRACE)) {
175 | $key = $this->match(ExpressionLexer::T_STRING);
176 | $this->match(ExpressionLexer::T_COLON);
177 | $entries[$key] = $this->Expression();
178 |
179 | if ( ! $this->lexer->isNext(ExpressionLexer::T_COMMA)) {
180 | break;
181 | }
182 |
183 | $this->lexer->moveNext();
184 | }
185 |
186 | $this->match(ExpressionLexer::T_CLOSE_BRACE);
187 |
188 | return new ArrayExpression($entries);
189 | }
190 |
191 | private function Suffix(ExpressionInterface $expr)
192 | {
193 | while (true) {
194 | if ($this->lexer->isNext(ExpressionLexer::T_OBJECT_OPERATOR)) {
195 | $this->lexer->moveNext();
196 | $name = $this->match(ExpressionLexer::T_IDENTIFIER);
197 |
198 | if ($this->lexer->isNext(ExpressionLexer::T_OPEN_PARENTHESIS)) {
199 | $args = $this->Arguments();
200 | $expr = new MethodCallExpression($expr, $name, $args);
201 | continue;
202 | }
203 |
204 | $expr = new GetPropertyExpression($expr, $name);
205 | continue;
206 | }
207 |
208 | if ($this->lexer->isNext(ExpressionLexer::T_OPEN_BRACKET)) {
209 | $this->lexer->moveNext();
210 | $key = $this->Expression();
211 | $this->match(ExpressionLexer::T_CLOSE_BRACKET);
212 | $expr = new GetItemExpression($expr, $key);
213 | continue;
214 | }
215 |
216 | break;
217 | }
218 |
219 | return $expr;
220 | }
221 |
222 | private function Arguments()
223 | {
224 | $this->match(ExpressionLexer::T_OPEN_PARENTHESIS);
225 | $args = array();
226 |
227 | while ( ! $this->lexer->isNext(ExpressionLexer::T_CLOSE_PARENTHESIS)) {
228 | $args[] = $this->Expression();
229 |
230 | if ( ! $this->lexer->isNext(ExpressionLexer::T_COMMA)) {
231 | break;
232 | }
233 |
234 | $this->match(ExpressionLexer::T_COMMA);
235 | }
236 | $this->match(ExpressionLexer::T_CLOSE_PARENTHESIS);
237 |
238 | return $args;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/Pel/Expression/ExpressionCompiler.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | namespace Pel\Expression;
20 |
21 | use Pel\Exception\RuntimeException;
22 | use Pel\Expression\Compiler\TypeCompilerInterface;
23 | use Pel\Expression\Compiler\Func\FunctionCompilerInterface;
24 | use Pel\Expression\Ast\ExpressionInterface;
25 | use Pel\Expression\Ast\VariableExpression;
26 | use Pel\Expression\Ast\MethodCallExpression;
27 | use Pel\Expression\Ast\GetPropertyExpression;
28 | use Pel\Expression\Ast\GetItemExpression;
29 | use Pel\Expression\Ast\FunctionExpression;
30 | use Pel\Expression\Ast\ConstantExpression;
31 | use Pel\Expression\Ast\ArrayExpression;
32 |
33 | class ExpressionCompiler
34 | {
35 | public $attributes = array();
36 |
37 | private $indentationLevel = 0;
38 | private $indentationSpaces = 4;
39 |
40 | private $nameCount = 0;
41 | private $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
42 | private $charCount = 52;
43 | private $reservedNames = array('context' => true);
44 |
45 | private $itemExists = array();
46 |
47 | private $code;
48 | private $parser;
49 | private $typeCompilers;
50 | private $functionCompilers;
51 |
52 | public function __construct()
53 | {
54 | $this->addTypeCompiler(new Compiler\AndExpressionCompiler());
55 | $this->addTypeCompiler(new Compiler\IsEqualExpressionCompiler());
56 | $this->addTypeCompiler(new Compiler\OrExpressionCompiler());
57 | $this->addTypeCompiler(new Compiler\VariableExpressionCompiler());
58 | $this->addTypeCompiler(new Compiler\NotExpressionCompiler());
59 | $this->addTypeCompiler(new Compiler\ConcatExpressionCompiler());
60 | }
61 |
62 | public function setAttribute($name, $value)
63 | {
64 | $this->attributes[$name] = $value;
65 |
66 | return $this;
67 | }
68 |
69 | public function addTypeCompiler(TypeCompilerInterface $compiler)
70 | {
71 | $this->typeCompilers[$compiler->getType()] = $compiler;
72 | }
73 |
74 | public function addFunctionCompiler(FunctionCompilerInterface $compiler)
75 | {
76 | $this->functionCompilers[$compiler->getName()] = $compiler;
77 | }
78 |
79 | public function compileExpression(Expression $expr)
80 | {
81 | return $this->compile($this->getParser()->parse($expr->expression),
82 | $expr->expression);
83 | }
84 |
85 | public function compile(ExpressionInterface $expr, $raw = null)
86 | {
87 | $this->nameCount = 0;
88 | $this->code = '';
89 | $this->itemExists = $this->attributes = array();
90 | $this->rolesName = null;
91 |
92 | if ($raw) {
93 | $this->writeln('// Expression: '.$raw);
94 | }
95 |
96 | $this
97 | ->writeln('return function(array $context) {')
98 | ->indent()
99 | ->compilePreconditions($expr)
100 | ->write('return ')
101 | ->compileInternal($expr)
102 | ->writeln(';')
103 | ->outdent()
104 | ->writeln('};')
105 | ;
106 |
107 | return $this->code;
108 | }
109 |
110 | public function indent()
111 | {
112 | $this->indentationLevel += 1;
113 |
114 | return $this;
115 | }
116 |
117 | public function outdent()
118 | {
119 | $this->indentationLevel -= 1;
120 |
121 | if ($this->indentationLevel < 0) {
122 | throw new RuntimeException('The indentation level cannot be less than zero.');
123 | }
124 |
125 | return $this;
126 | }
127 |
128 | public function writeln($content)
129 | {
130 | $this->write($content."\n");
131 |
132 | return $this;
133 | }
134 |
135 | public function verifyItem($key, $expectedType = null)
136 | {
137 | if (!isset($this->itemExists[$key])) {
138 | $this->itemExists[$key] = true;
139 |
140 | $this
141 | ->writeln("if (!isset(\$context['$key'])) {")
142 | ->indent()
143 | ->writeln("throw new RuntimeException('The context contains no item with key \"$key\".');")
144 | ->outdent()
145 | ->write("}\n\n")
146 | ;
147 | }
148 |
149 | if (null !== $expectedType) {
150 | $this
151 | ->writeln("if (!\$context['$key'] instanceof $expectedType) {")
152 | ->indent()
153 | ->writeln("throw new RuntimeException(sprintf('The item \"$key\" is expected to be of type \"$expectedType\", but got \"%s\".', get_class(\$context['$key'])));")
154 | ->outdent()
155 | ->write("}\n\n")
156 | ;
157 | }
158 |
159 | return $this;
160 | }
161 |
162 | public function write($content)
163 | {
164 | $lines = explode("\n", $content);
165 | for ($i=0,$c=count($lines); $i<$c; $i++) {
166 | if ($this->indentationLevel > 0
167 | && !empty($lines[$i])
168 | && (empty($this->code) || "\n" === substr($this->code, -1))) {
169 | $this->code .= str_repeat(' ', $this->indentationLevel * $this->indentationSpaces);
170 | }
171 |
172 | $this->code .= $lines[$i];
173 |
174 | if ($i+1 < $c) {
175 | $this->code .= "\n";
176 | }
177 | }
178 |
179 | return $this;
180 | }
181 |
182 | public function nextName()
183 | {
184 | while (true) {
185 | $name = '';
186 | $i = $this->nameCount;
187 |
188 | $name .= $this->chars[$i % $this->charCount];
189 | $i = intval($i / $this->charCount);
190 |
191 | while ($i > 0) {
192 | $i -= 1;
193 | $name .= $this->chars[$i % $this->charCount];
194 | $i = intval($i / $this->charCount);
195 | }
196 |
197 | $this->nameCount += 1;
198 |
199 | // check that the name is not reserved
200 | if (isset($this->reservedNames[$name])) {
201 | continue;
202 | }
203 |
204 | return $name;
205 | }
206 | }
207 |
208 | public function compilePreconditions(ExpressionInterface $expr)
209 | {
210 | if ($typeCompiler = $this->findTypeCompiler(get_class($expr))) {
211 | $typeCompiler->compilePreconditions($this, $expr);
212 |
213 | return $this;
214 | }
215 |
216 | if ($expr instanceof FunctionExpression) {
217 | foreach ($expr->args as $arg) {
218 | $this->compilePreconditions($arg);
219 | }
220 |
221 | $this->getFunctionCompiler($expr->name)->compilePreconditions($this, $expr);
222 |
223 | return $this;
224 | }
225 |
226 | if ($expr instanceof MethodCallExpression) {
227 | $this->compilePreconditions($expr->object);
228 |
229 | foreach ($expr->args as $arg) {
230 | $this->compilePreconditions($arg);
231 | }
232 |
233 | return $this;
234 | }
235 |
236 | if ($expr instanceof GetPropertyExpression) {
237 | $this->compilePreconditions($expr->object);
238 |
239 | return $this;
240 | }
241 |
242 | if ($expr instanceof ArrayExpression) {
243 | foreach ($expr->elements as $element) {
244 | $this->compilePreconditions($element);
245 | }
246 | }
247 |
248 | return $this;
249 | }
250 |
251 | public function compileInternal(ExpressionInterface $expr)
252 | {
253 | if ($typeCompiler = $this->findTypeCompiler(get_class($expr))) {
254 | $typeCompiler->compile($this, $expr);
255 |
256 | return $this;
257 | }
258 |
259 | if ($expr instanceof ArrayExpression) {
260 | $this->code .= 'array(';
261 | foreach ($expr->elements as $key => $value) {
262 | $this->code .= var_export($key, true).' => ';
263 | $this->compileInternal($value);
264 | $this->code .= ',';
265 | }
266 | $this->code .= ')';
267 |
268 | return $this;
269 | }
270 |
271 | if ($expr instanceof ConstantExpression) {
272 | $this->code .= var_export($expr->value, true);
273 |
274 | return $this;
275 | }
276 |
277 | if ($expr instanceof FunctionExpression) {
278 | $this->getFunctionCompiler($expr->name)->compile($this, $expr);
279 |
280 | return $this;
281 | }
282 |
283 | if ($expr instanceof GetItemExpression) {
284 | $this->compileInternal($expr->array);
285 | $this->code .= '[';
286 | $this->compileInternal($expr->key);
287 | $this->code .= ']';
288 |
289 | return $this;
290 | }
291 |
292 | if ($expr instanceof GetPropertyExpression) {
293 | $this->compileInternal($expr->object);
294 | $this->code .= '->'.$expr->name;
295 |
296 | return $this;
297 | }
298 |
299 | if ($expr instanceof MethodCallExpression) {
300 | $this->compileInternal($expr->object);
301 | $this->code .= '->'.$expr->method.'(';
302 |
303 | $first = true;
304 | foreach ($expr->args as $arg) {
305 | if (!$first) {
306 | $this->code .= ', ';
307 | }
308 | $first = false;
309 |
310 | $this->compileInternal($arg);
311 | }
312 | $this->code .= ')';
313 |
314 | return $this;
315 | }
316 |
317 | throw new RuntimeException(sprintf('Unknown expression "%s".', get_class($expr)));
318 | }
319 |
320 | public function getFunctionCompiler($name)
321 | {
322 | if (!isset($this->functionCompilers[$name])) {
323 | throw new RuntimeException(sprintf('There is no compiler for function "%s".', $name));
324 | }
325 |
326 | return $this->functionCompilers[$name];
327 | }
328 |
329 | private function findTypeCompiler($type)
330 | {
331 | return isset($this->typeCompilers[$type]) ? $this->typeCompilers[$type] : null;
332 | }
333 |
334 | private function getParser()
335 | {
336 | if (null !== $this->parser) {
337 | return $this->parser;
338 | }
339 |
340 | return $this->parser = new ExpressionParser();
341 | }
342 | }
343 |
--------------------------------------------------------------------------------