├── .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 | --------------------------------------------------------------------------------