├── .github
└── workflows
│ └── continuous-integration.yml
├── .gitignore
├── README.md
├── composer.json
├── database.png
├── doc
├── discard_unused_parameters.md
├── images
│ ├── schema1.png
│ ├── shortest_path.png
│ └── shortest_path.pptx
├── magic_join.md
└── magic_twig.md
├── phpstan-baseline.neon
├── phpstan.neon
├── phpunit.xml.dist
├── src
├── Mouf
│ └── Database
│ │ ├── MagicQuery.php
│ │ ├── MagicQuery
│ │ └── Twig
│ │ │ ├── ForbiddenTwigParameterInSqlException.php
│ │ │ ├── SqlTwigEnvironmentFactory.php
│ │ │ └── StringLoader.php
│ │ ├── MagicQueryException.php
│ │ ├── MagicQueryMissingConnectionException.php
│ │ ├── MagicQueryParserException.php
│ │ └── QueryWriter
│ │ ├── Condition
│ │ ├── ParamAvailableCondition.php
│ │ ├── ParamEqualsCondition.php
│ │ └── ParamNotAvailableCondition.php
│ │ ├── CountNbResult.php
│ │ ├── QueryResult.php
│ │ ├── ResultSet.php
│ │ └── Utils
│ │ ├── DbHelper.php
│ │ └── FindParametersService.php
└── SQLParser
│ ├── Node
│ ├── AbstractInListOperator.php
│ ├── AbstractManyInstancesOperator.php
│ ├── AbstractTwoOperandsOperator.php
│ ├── AggregateFunction.php
│ ├── AndOp.php
│ ├── Between.php
│ ├── BitwiseAnd.php
│ ├── BitwiseOr.php
│ ├── BitwiseXor.php
│ ├── BypassableInterface.php
│ ├── CaseOperation.php
│ ├── ColRef.php
│ ├── ConstNode.php
│ ├── Different.php
│ ├── Div.php
│ ├── Divide.php
│ ├── ElseOperation.php
│ ├── Equal.php
│ ├── Expression.php
│ ├── Greater.php
│ ├── GreaterOrEqual.php
│ ├── Hint.php
│ ├── In.php
│ ├── Is.php
│ ├── IsNot.php
│ ├── Less.php
│ ├── LessOrEqual.php
│ ├── Like.php
│ ├── LimitNode.php
│ ├── Minus.php
│ ├── Modulo.php
│ ├── Multiply.php
│ ├── NodeFactory.php
│ ├── NodeInterface.php
│ ├── NotIn.php
│ ├── NotLike.php
│ ├── NullCompatibleEqual.php
│ ├── Operation.php
│ ├── Operator.php
│ ├── OrOp.php
│ ├── Parameter.php
│ ├── Plus.php
│ ├── Regexp.php
│ ├── Reserved.php
│ ├── ShiftLeft.php
│ ├── ShiftRight.php
│ ├── SimpleFunction.php
│ ├── SubQuery.php
│ ├── Table.php
│ ├── Then.php
│ ├── Traverser
│ │ ├── CompositeVisitor.php
│ │ ├── DetectMagicJoinSelectVisitor.php
│ │ ├── DetectTablesVisitor.php
│ │ ├── MagicJoinSelect.php
│ │ ├── NodeTraverser.php
│ │ ├── TraverserException.php
│ │ └── VisitorInterface.php
│ ├── UnquotedParameter.php
│ ├── WhenConditions.php
│ └── XorOp.php
│ ├── Query
│ ├── Select.php
│ ├── StatementFactory.php
│ ├── StatementInterface.php
│ └── Union.php
│ └── SqlRenderInterface.php
└── tests
├── Mouf
└── Database
│ ├── MagicQuery
│ └── Twig
│ │ └── SqlTwigEnvironmentFactoryTest.php
│ ├── MagicQueryTest.php
│ └── StubSchemaManager.php
├── SQLParser
└── Node
│ └── Traverser
│ ├── DetectTableVisitorTest.php
│ └── NodeTraverserTest.php
├── bootstrap.php
└── phpunit-mysql8.sh
/.github/workflows/continuous-integration.yml:
--------------------------------------------------------------------------------
1 | name: "Continuous Integration"
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - "1.4"
8 |
9 | jobs:
10 | coding-quality:
11 | name: "Code Quality"
12 | runs-on: "ubuntu-latest"
13 |
14 | strategy:
15 | matrix:
16 | php-version:
17 | - "7.4"
18 |
19 | steps:
20 | - name: "Checkout"
21 | uses: "actions/checkout@v2"
22 |
23 | - name: "Install PHP"
24 | uses: "shivammathur/setup-php@v2"
25 | with:
26 | coverage: "none"
27 | php-version: "${{ matrix.php-version }}"
28 | tools: "cs2pr"
29 |
30 | - name: "Cache dependencies installed with composer"
31 | uses: "actions/cache@v1"
32 | with:
33 | path: "~/.composer/cache"
34 | key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
35 | restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
36 |
37 | - name: "Install dependencies with composer"
38 | run: "composer install --no-interaction --no-progress --no-suggest"
39 |
40 | - name: "Run a static analysis with phpstan/phpstan"
41 | run: "composer phpstan -- --error-format=checkstyle | cs2pr"
42 |
43 | phpunit-prefer-lowest:
44 | name: "PHPUnit with prefer-lowest"
45 | runs-on: "ubuntu-latest"
46 |
47 | strategy:
48 | matrix:
49 | php-version:
50 | - "7.4"
51 |
52 | services:
53 | mysql:
54 | image: "mysql:8"
55 | env:
56 | MYSQL_ALLOW_EMPTY_PASSWORD: true
57 | MYSQL_ROOT_PASSWORD:
58 | ports:
59 | - "3306:3306"
60 |
61 | steps:
62 | - name: "Checkout"
63 | uses: "actions/checkout@v2"
64 | with:
65 | fetch-depth: 2
66 |
67 | - name: "Install PHP"
68 | uses: "shivammathur/setup-php@v2"
69 | with:
70 | php-version: "${{ matrix.php-version }}"
71 | extensions: ""
72 |
73 | - name: "Cache dependencies installed with composer"
74 | uses: "actions/cache@v1"
75 | with:
76 | path: "~/.composer/cache"
77 | key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
78 | restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
79 |
80 | - name: "Install dependencies with composer"
81 | run: "composer update --no-interaction --no-progress --no-suggest --prefer-lowest"
82 |
83 | - name: Wait for MySQL
84 | run: |
85 | while ! mysqladmin ping --host=127.0.0.1 --silent; do
86 | sleep 1
87 | done
88 |
89 | - name: "Run PHPUnit"
90 | run: "vendor/bin/phpunit --no-coverage"
91 |
92 | phpunit-code-coverage:
93 | name: "PHPUnit with Code Coverage"
94 | runs-on: "ubuntu-latest"
95 |
96 | strategy:
97 | matrix:
98 | php-version:
99 | - "7.4"
100 |
101 | services:
102 | mysql:
103 | image: "mysql:8"
104 | env:
105 | MYSQL_ALLOW_EMPTY_PASSWORD: true
106 | MYSQL_ROOT_PASSWORD:
107 | ports:
108 | - "3306:3306"
109 |
110 | steps:
111 | - name: "Checkout"
112 | uses: "actions/checkout@v2"
113 | with:
114 | fetch-depth: 2
115 |
116 | - name: "Install PHP"
117 | uses: "shivammathur/setup-php@v2"
118 | with:
119 | php-version: "${{ matrix.php-version }}"
120 | extensions: ""
121 | coverage: "pcov"
122 |
123 | - name: "Cache dependencies installed with composer"
124 | uses: "actions/cache@v1"
125 | with:
126 | path: "~/.composer/cache"
127 | key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
128 | restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
129 |
130 | - name: "Install dependencies with composer"
131 | run: "composer install --no-interaction --no-progress --no-suggest"
132 |
133 | - name: Wait for MySQL
134 | run: |
135 | while ! mysqladmin ping --host=127.0.0.1 --silent; do
136 | sleep 1
137 | done
138 |
139 | - name: "Run PHPUnit"
140 | run: "vendor/bin/phpunit"
141 |
142 | - name: "Run Coveralls"
143 | run: "vendor/bin/php-coveralls -v"
144 | env:
145 | COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
146 |
147 | phpunit:
148 | name: "PHPUnit"
149 | runs-on: "ubuntu-latest"
150 |
151 | strategy:
152 | matrix:
153 | php-version:
154 | - "8.0"
155 |
156 | services:
157 | mysql:
158 | image: "mysql:8"
159 | env:
160 | MYSQL_ALLOW_EMPTY_PASSWORD: true
161 | MYSQL_ROOT_PASSWORD:
162 | ports:
163 | - "3306:3306"
164 |
165 | steps:
166 | - name: "Checkout"
167 | uses: "actions/checkout@v2"
168 | with:
169 | fetch-depth: 2
170 |
171 | - name: "Install PHP"
172 | uses: "shivammathur/setup-php@v2"
173 | with:
174 | php-version: "${{ matrix.php-version }}"
175 | extensions: ""
176 |
177 | - name: "Cache dependencies installed with composer"
178 | uses: "actions/cache@v1"
179 | with:
180 | path: "~/.composer/cache"
181 | key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
182 | restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
183 |
184 | - name: "Install dependencies with composer"
185 | run: "composer install --no-interaction --no-progress --no-suggest"
186 |
187 | - name: Wait for MySQL
188 | run: |
189 | while ! mysqladmin ping --host=127.0.0.1 --silent; do
190 | sleep 1
191 | done
192 |
193 | - name: "Run PHPUnit"
194 | run: "vendor/bin/phpunit --no-coverage"
195 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 | phpunit.xml
4 | build/
5 | .phpunit.result.cache
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://packagist.org/packages/mouf/magic-query)
2 | [](https://packagist.org/packages/mouf/magic-query)
3 | [](https://packagist.org/packages/mouf/magic-query)
4 | [](https://scrutinizer-ci.com/g/thecodingmachine/magic-query/?branch=1.2)
5 | [](https://travis-ci.org/thecodingmachine/magic-query)
6 | [](https://coveralls.io/r/thecodingmachine/magic-query?branch=1.2)
7 |
8 | What is Magic-query?
9 | ====================
10 |
11 | Magic-query is a PHP library that helps you work with complex SQL queries.
12 |
13 | It comes with 3 great features:
14 |
15 | - [**MagicParameters**: it helps you work with SQL queries that require a variable number of parameters.](#parameters)
16 | - [**MagicJoin**: it writes JOINs for you!](#joins)
17 | - [**MagicTwig**: use Twig templating in your SQL queries](#twig)
18 |
19 | Installation
20 | ------------
21 |
22 | Simply use the composer package:
23 |
24 | ```json
25 | {
26 | "require": {
27 | "mouf/magic-query": "^1.2"
28 | },
29 | "minimum-stability": "dev",
30 | "prefer-stable": true
31 | }
32 | ```
33 |
34 |
35 | Automatically discard unused parameters with MagicParameters
36 | ------------------------------------------------------------
37 |
38 | Just write the query with all possible parameters.
39 |
40 | ```php
41 | use Mouf\Database\MagicQuery;
42 |
43 | $sql = "SELECT * FROM users WHERE name LIKE :name AND country LIKE :country";
44 |
45 | // Get a MagicQuery object.
46 | $magicQuery = new MagicQuery();
47 |
48 | // Let's pass only the "name" parameter
49 | $result = $magicQuery->build($sql, [ "name" => "%John%" ]);
50 | // $result = SELECT * FROM users WHERE name LIKE '%John%'
51 | // Did you notice how the bit about the country simply vanished?
52 |
53 | // Let's pass no parameter at all!
54 | $result2 = $magicQuery->build($sql, []);
55 | // $result2 = SELECT * FROM users
56 | // The whole WHERE condition disappeared because it is not needed anymore!
57 | ```
58 |
59 | Curious to know how this work? Check out the parameters guide!
60 |
61 |
62 | Automatically guess JOINs with MagicJoin!
63 | -----------------------------------------
64 |
65 | Fed up of writing joins in SQL? Let MagicQuery do the work for you!
66 |
67 | Seriously? Yes! All you have to do is:
68 |
69 | - Pass a **[Doctrine DBAL connection](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/)** to MagicQuery's constructor. MagicQuery will analyze your schema.
70 | - In your SQL query, replace the tables with `magicjoin(start_table)`
71 | - For each column of your query, use the complete name ([table_name].[column_name] instead of [column_name] alone)
72 |
73 | Let's assume your database schema is:
74 |
75 | 
76 |
77 | Using MagicJoin, you can write this SQL query:
78 |
79 | ```sql
80 | SELECT users.* FROM MAGICJOIN(users) WHERE groups.name = 'Admins' AND country.name='France';
81 | ```
82 |
83 | and it will automatically be transformed into this:
84 |
85 | ```sql
86 | SELECT users.* FROM users
87 | LEFT JOIN users_groups ON users.user_id = users_groups.user_id
88 | LEFT JOIN groups ON groups.group_id = users_groups.group_id
89 | LEFT JOIN country ON country.country_id = users.country_id
90 | WHERE groups.name = 'Admins' AND country.name='France';
91 | ```
92 |
93 | And the code is so simple!
94 |
95 | ```php
96 | use Mouf\Database\MagicQuery;
97 |
98 | $sql = "SELECT users.* FROM MAGICJOIN(users) WHERE groups.name = 'Admins' AND country.name='France'";
99 |
100 | // Get a MagicQuery object.
101 | // $conn is a Doctrine DBAL connection.
102 | $magicQuery = new MagicQuery($conn);
103 |
104 | $completeSql = $magicQuery->build($sql);
105 | // $completeSql contains the complete SQL request, with all joins.
106 | ```
107 |
108 | Want to know more? Check out the MagicJoin guide!
109 |
110 |
111 | Use Twig templating in your SQL queries!
112 | ----------------------------------------
113 |
114 | Discarding unused parameters and auto-joining keys is not enough? You have very specific needs? Say hello to
115 | Twig integration!
116 |
117 | Using Twig integration, you can directly add Twig conditions right into your SQL.
118 |
119 | ```php
120 | use Mouf\Database\MagicQuery;
121 |
122 | $sql = "SELECT users.* FROM users {% if isAdmin %} WHERE users.admin = 1 {% endif %}";
123 |
124 | $magicQuery = new MagicQuery();
125 | // By default, Twig integration is disabled. You need to enable it.
126 | $magicQuery->setEnableTwig(true);
127 |
128 | $completeSql = $magicQuery->build($sql, ['isAdmin' => true]);
129 | // Parameters are passed to the Twig SQL query, and the SQL query is returned.
130 | ```
131 |
132 |
Heads up! The Twig integration cannot be used to insert parameters
133 | into the SQL query. You should use classic SQL parameters for this. This means that instead if writing
134 | {{ id }}, you should write :id.
135 |
136 | Want to know more? Check out the MagicTwig guide!
137 |
138 | Is it a MySQL only tool?
139 | ------------------------
140 |
141 | No. By default, your SQL is parsed and then rewritten using the MySQL dialect, but you use any kind of dialect
142 | known by [Doctrine DBAL](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/). Magic-query optionally uses Doctrine DBAL. You can pass a `Connection` object
143 | as the first parameter of the `MagicQuery` constructor. Magic-query will then use the matching dialect.
144 |
145 | For instance:
146 |
147 | ```php
148 | $config = new \Doctrine\DBAL\Configuration();
149 | $connectionParams = array(
150 | 'url' => 'sqlite:///somedb.sqlite',
151 | );
152 | $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config);
153 |
154 | $magicQuery = new \Mouf\Database\MagicQuery($conn);
155 | ```
156 |
157 | Also, if you have no connection to your database configured but you want to generate SQL in some specific dialect, you can
158 | instead set the DBAL database platform used:
159 |
160 | ```php
161 | $magicQuery->setOutputDialect(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform());
162 | $magicQuery = new \Mouf\Database\MagicQuery();
163 | ```
164 |
165 |
166 | What about performances?
167 | ------------------------
168 |
169 | MagicQuery does a lot to your query. It will parse it, render it internally as a tree of SQL nodes, etc...
170 | This processing is time consuming. So you should definitely consider using a cache system. MagicQuery is compatible
171 | with Doctrine Cache. You simply have to pass a Doctrine Cache instance has the second parameter of the constructor.
172 |
173 | ```php
174 | use Mouf\Database\MagicQuery;
175 | use Doctrine\Common\Cache\ApcCache;
176 |
177 | // $conn is a Doctrine connection
178 | $magicQuery = new MagicQuery($conn, new ApcCache());
179 | ```
180 |
181 | Any problem?
182 | ------------
183 |
184 | With MagicQuery, a lot happens to your SQL query. In particular, it is parsed using a modified version
185 | of the php-sql-parser library. If you face any issues with a complex query, it is likely there is a bug
186 | in the parser. Please open [an issue on Github](https://github.com/thecodingmachine/magic-query/issues) and we'll try to fix it.
187 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mouf/magic-query",
3 | "description": "A very clever library to help you with SQL: generate prepared statements with a variable number of parameters, automatically writes joins... and much more!",
4 | "keywords": ["database", "query", "mouf"],
5 | "homepage": "http://mouf-php.com/packages/mouf/magic-query",
6 | "type": "library",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "David Négrier",
11 | "email": "d.negrier@thecodingmachine.com",
12 | "homepage": "http://mouf-php.com"
13 | }
14 | ],
15 | "require": {
16 | "php": "^7.4 || ^8.0",
17 | "mouf/utils.common.conditioninterface": "~2.0",
18 | "mouf/utils.value.value-interface": "~1.0",
19 | "mouf/utils.common.paginable-interface": "~1.0",
20 | "mouf/utils.common.sortable-interface": "~1.0",
21 | "mouf/schema-analyzer": "^2.0",
22 | "twig/twig": "^2.11 || ^3",
23 | "greenlion/php-sql-parser": "^4.3",
24 | "doctrine/cache": "^1.5"
25 | },
26 | "require-dev": {
27 | "phpunit/phpunit": "^9.5",
28 | "php-coveralls/php-coveralls": "^2.0.0",
29 | "doctrine/dbal": "^3.0",
30 | "phpstan/phpstan": "^0.12.82"
31 | },
32 | "suggest": {
33 | "doctrine/dbal": "To support more databases than just MySQL and to use MagicJoin feature",
34 | "mouf/database.querywriter": "To get a nice user interface to edit your SQL queries",
35 | "mouf/mouf": "To get a nice user interface to edit your SQL queries"
36 | },
37 | "conflict": {
38 | "mouf/database.querywriter": "< 4.0"
39 | },
40 | "autoload": {
41 | "psr-4": {
42 | "Mouf\\Database\\": "src/Mouf/Database/",
43 | "SQLParser\\": "src/SQLParser/"
44 | }
45 | },
46 | "autoload-dev": {
47 | "psr-4": {
48 | "Mouf\\Database\\": "tests/Mouf/Database/",
49 | "SQLParser\\": "tests/SQLParser/"
50 | }
51 | },
52 | "extra": {
53 | "mouf": {
54 | "logo": "database.png",
55 | "doc": [
56 | {
57 | "title": "MagicParameters: automatically discard unused parameters",
58 | "url": "doc/discard_unused_parameters.md"
59 | },
60 | {
61 | "title": "MagicJoin: let MagicQuery write JOINs for you",
62 | "url": "doc/magic_join.md"
63 | }
64 | ]
65 | }
66 | },
67 | "minimum-stability": "dev",
68 | "prefer-stable": true,
69 | "scripts": {
70 | "phpstan": "vendor/bin/phpstan analyse -c phpstan.neon"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodingmachine/magic-query/b813bec1534e96d4ffe97a1a8d633df874d721a5/database.png
--------------------------------------------------------------------------------
/doc/discard_unused_parameters.md:
--------------------------------------------------------------------------------
1 | Automatically discard unused parameters
2 | ---------------------------------------
3 |
4 | ###How does it work?
5 |
6 | Just write the query with all possible parameters.
7 |
8 | ```php
9 | use Mouf\Database\MagicQuery;
10 |
11 | $sql = "SELECT * FROM users WHERE name LIKE :name AND country LIKE :country";
12 |
13 | // Get a MagicQuery object.
14 | $magicQuery = new MagicQuery();
15 |
16 | // Let's pass only the "name" parameter
17 | $result = $magicQuery->build($sql, [ "name" => "%John%" ]);
18 | // $result = SELECT * FROM users WHERE name LIKE '%John%'
19 | // Did you notice how the bit about the country simply vanished?
20 |
21 | // Let's pass no parameter at all!
22 | $result2 = $magicQuery->build($sql, []);
23 | // $result2 = SELECT * FROM users
24 | // The whole WHERE condition disappeared because it is not needed anymore!
25 | ```
26 |
27 | Magic-parameters can also collapse a BETWEEN filter into a simple `>=` or `<=` filter:
28 |
29 | ```php
30 | $sql = "SELECT * FROM products WHERE status BETWEEN :lowerStatus AND :upperStatus";
31 |
32 | // Let's pass only the "lowerStatus" parameter
33 | $result = $magicQuery->build($sql, [ "lowerStatus" => 2 ]);
34 | // $result = SELECT * FROM products WHERE status >= 2
35 | // See? The BETWEEN filter was transformed in a >= filter because we did not provide a higher limit.
36 | ```
37 |
38 | ###Why should I care?
39 |
40 | Because it is **the most efficient way to deal with queries that can have a variable number of parameters**!
41 | Think about a typical datagrid with a bunch of filter (for instance a list of products filtered by name, company, price, ...).
42 | If you have the very common idea to generate the SQL query using no PHP library, your code will look like this:
43 |
44 | ####Without Magic-query
45 |
You should not do this!
46 |
47 | ```php
48 | // People usually write queries like this:
49 | $sql = "SELECT * FROM products p JOIN companies c ON p.company_id = c.id WHERE 1=1 ";
50 | // They keep testing for parameters, and concatenating strings....
51 | if (isset($params['name'])) {
52 | $sql .= "AND (p.name LIKE '".addslashes($params['name'])."%' OR p.altname LIKE '".addslashes($params['name'])."%')";
53 | }
54 | if (isset($params['company'])) {
55 | $sql .= "AND c.name LIKE '".addslashes($params['company'])."%'";
56 | }
57 | if (isset($params['country'])) {
58 | $sql .= "AND c.country LIKE '".addslashes($params['country'])."%'";
59 | }
60 | // And so on... for each parameter, we have a "if" statement
61 | ```
62 |
63 | Concatenating SQL queries is **dangerous** (especially if you forget to protect parameters).
64 | You can always use parametrized SQL queries, but you will still have to concatenate the filters.
65 |
66 | ####With Magic-Query
67 |
68 | ```php
69 | // One query with all parameters
70 | $sql = "SELECT * FROM products p JOIN companies c ON p.company_id = c.id WHERE
71 | (p.name LIKE :name OR p.altname LIKE :name)
72 | AND c.name LIKE :company
73 | AND c.country LIKE :country";
74 |
75 | $magicQuery = new MagicQuery();
76 | $sql = $magicQuery->build($sql, $params);
77 | ```
78 |
79 | ####Forcing some parameters to be null
80 |
81 | MagicQuery assumes that if a parameter is null, you want to completely remove the code related to this
82 | parameter from the SQL.
83 |
84 | But sometimes, you actually want to test parameters against the NULL value.
85 | In those cases, you need to disable the MagicParameter feature of MagicQuery for those parameters.
86 |
87 |
To disable MagicParameter for a given parameter, simply add an exclamation
88 | mark (!) after the parameter name.
89 |
90 | ```php
91 | // This query uses twice the "status" parameter. Once with ! and once without
92 | $sql = "SELECT * FROM products p WHERE status1 = :status AND status2 = :status!";
93 |
94 | $magicQuery = new MagicQuery();
95 | // We don't pass the "status" parameter
96 | $sql = $magicQuery->build($sql, []);
97 | // The part "status1 = :status" is discarded, but the part "status2 = :status!" is kept
98 | // $sql == "SELECT * FROM products p WHERE status2 = null"
99 | ```
100 |
101 | ####Working with prepared statements
102 |
103 | The `MagicQuery::build` method will remove unused parameters AND replace placeholders with the values.
104 | If you are looking for the best possible performance, you can use the alternative `MagicQuery::buildPreparedStatement` method.
105 |
106 | The "buildPreparedStatement" method is removing unused parameters BUT it does not replace placeholders.
107 | As a result, MagicQuery can perform some more internal caching and if you have many similar requests,
108 | you will get some performance gains.
109 |
110 | ###Other alternatives
111 |
112 | To avoid concatenating strings, frameworks and libraries have used different strategies. Using a full ORM (like
113 | Doctrine or Propel) is a good idea, but it makes writing complex queries even more complex. Other frameworks like
114 | Zend are building queries using function calls. These are valid strategies, but you are no more typing SQL queries
115 | directly, and let's face it, it is always useful to use a query directly.
116 |
117 | How does it work under the hood?
118 | --------------------------------
119 |
120 | A lot happens to your SQL query. It is actually parsed (thanks to a modified
121 | version of the php-sql-parser library) and then changed into a tree.
122 | The magic happens on the tree where the node containing unused parameters
123 | are simply discarded. When it's done, the tree is changed back to SQL and
124 | "shazam!", your SQL query is purged of useless parameters!
--------------------------------------------------------------------------------
/doc/images/schema1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodingmachine/magic-query/b813bec1534e96d4ffe97a1a8d633df874d721a5/doc/images/schema1.png
--------------------------------------------------------------------------------
/doc/images/shortest_path.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodingmachine/magic-query/b813bec1534e96d4ffe97a1a8d633df874d721a5/doc/images/shortest_path.png
--------------------------------------------------------------------------------
/doc/images/shortest_path.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodingmachine/magic-query/b813bec1534e96d4ffe97a1a8d633df874d721a5/doc/images/shortest_path.pptx
--------------------------------------------------------------------------------
/doc/magic_join.md:
--------------------------------------------------------------------------------
1 | Automatically guess JOINs with MagicJoin!
2 | -----------------------------------------
3 |
4 | Fed up of writing joins in SQL? Let MagicQuery do the work for you!
5 |
6 | Seriously? Yes! All you have to do is:
7 |
8 | - Pass a **Doctrine DBAL connection** to MagicQuery's constructor. MagicQuery will analyze your schema.
9 | - In your SQL query, replace the tables with `magicjoin(start_table)`
10 | - For each column of your query, use the complete name ([table_name].[column_name] instead of [column_name] alone)
11 |
12 | Let's assume your database schema is:
13 |
14 | 
15 |
16 | Using MagicJoin, you can write this SQL query:
17 |
18 | ```sql
19 | SELECT users.* FROM MAGICJOIN(users) WHERE groups.name = 'Admins' AND country.name='France';
20 | ```
21 |
22 | and it will automatically be transformed into this:
23 |
24 | ```sql
25 | SELECT users.* FROM users
26 | LEFT JOIN users_groups ON users.user_id = users_groups.user_id
27 | LEFT JOIN groups ON groups.group_id = users_groups.group_id
28 | LEFT JOIN country ON country.country_id = users.country_id
29 | WHERE groups.name = 'Admins' AND country.name='France';
30 | ```
31 |
32 | And the code is so simple!
33 |
34 | ```php
35 | use Mouf\Database\MagicQuery;
36 |
37 | $sql = "SELECT users.* FROM MAGICJOIN(users) WHERE groups.name = 'Admins' AND country.name='France'";
38 |
39 | // Get a MagicQuery object.
40 | // $conn is a Doctrine DBAL connection.
41 | $magicQuery = new MagicQuery($conn);
42 |
43 | $completeSql = $magicQuery->build($sql);
44 | // $completeSql contains the complete SQL request, with all joins.
45 | ```
46 |
47 | ###How does it work?
48 |
49 | When you reference a column, you have to give its full name in the form **[table_name].[column_name]**.
50 | Because each column is referenced by its full name, MagicJoin is able to build a list of tables that
51 | need to be joined.
52 |
53 | Then, MagicJoin scans your data model. Using **foreign keys**, it discovers the relationship between
54 | tables by itself. Then, it finds the **shortest path** between you "main" table and all the other tables that are
55 | needed.
56 |
57 | From this shortest path, it can build the correct JOINs.
58 |
59 | Now that you understand the concept, you should understand the power and the limits of MagicJoin.
60 |
61 | - MagicJoin **cannot generate queries with recursive relationships** (like a parent/child relationship). This is
62 | because parent/child relationships are represented by loops in the dependency graph, and a loop is never the
63 | shortest path.
64 | - MagicJoin assumes you are looking for the shortest path between 2 tables
65 | - This is 80% of the case true
66 | - If you are in the remaining 20%, do not use MagicJoin
67 |
68 |
MagicJoin is meant to be used on the 80% of the cases where writing joins is trivial
69 | and boring. If you have complex joins, do not try to use MagicJoin. Go back to pure SQL instead.
70 |
71 | If you are looking for details on how this shortest path is computed, have a look at
72 | [mouf/schema-analyzer](http://mouf-php.com/packages/mouf/schema-analyzer/README.md) which is the package in charge
73 | of computing that shortest path.
74 |
75 | ###Ambiguity exceptions and fine-tuning
76 |
77 | Sometimes, there can be 2 possible paths that link 2 tables and that are equally short.
78 | In this case, rather than choosing a path at random, MagicQuery will let you know there is a problem by issuing
79 | a `ShortestPathAmbiguityException`.
80 |
81 | 
82 |
83 | Let's have a look at the example above. We have *products* that are part of a *store*. Stores are part of a *district*.
84 | Both *districts* and *products* have a *status*.
85 |
86 | Now, let's have a look at this query:
87 |
88 | ```sql
89 | SELECT products.* FROM MAGICJOIN(products) WHERE district.name = 'NY';
90 | ```
91 |
92 | Very obviously, we want all the products from the district. Bu for MagicJoin, this is far from obvious.
93 | There are really 2 paths from products to district. One is going through the "store" table and one through the "status"
94 | table. Those paths are equally short.
95 |
96 | We need to *hint* MagicJoin into understanding that the "status" table is irrelevant.
97 |
98 | To do this, we must first provide to MagicJoin an instance of `SchemaAnalyzer`. [`SchemaAnalyzer` is the underlying
99 | package that is in charge of computing the shortest path.](http://mouf-php.com/packages/mouf/schema-analyzer/README.md)
100 |
101 | ```php
102 | use Mouf\Database\MagicQuery;
103 | use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
104 |
105 | // $conn is a Doctrine DBAL connection.
106 | // $cache is a Doctrine Cache
107 |
108 | // Get a SchemaAnalyzer object.
109 | $schemaAnalyzer = new SchemaAnalyzer($conn->getSchemaManager(), $cache, "some_unique_key");
110 |
111 | // On SchemaAnalyzer, let's modify the cost of the "status" table to make it unlikely to be used:
112 | $schemaAnalyzer->setTableCostModifier("status", SchemaAnalyzer::WEIGHT_IRRELEVANT);
113 |
114 | // Get a MagicQuery object.
115 | $magicQuery = new MagicQuery($conn, $cache, $schemaAnalyzer);
116 |
117 | $completeSql = $magicQuery->build("SELECT products.* FROM MAGICJOIN(products) WHERE district.name = 'NY'");
118 | ```
119 |
120 | The call to `setTableCostModifier` above will explain to MagicJoin that the *status* table is mostly irrelevant,
121 | and that it should most of the time be avoided.
122 |
123 | You can also set a *cost* on a single foreign key rather than on a table. For instance:
124 |
125 | ```php
126 | // Avoid using the "status_id" foreign_key from the "products" table.
127 | $schemaAnalyzer->setForeignKeyCost("products", "status_id", SchemaAnalyzer::WEIGHT_IRRELEVANT);
128 | ```
129 |
130 | ###With great power comes great responsibility
131 |
132 | MagicJoin is a powerful feature because it can **guess** what you want to do. Please be wise while using it.
133 | A change in your model could modify the shortest path computed by MagicJoin and therefore the returned SQL query.
134 |
135 | If you database model is complex enough and is rapidly changing, generated queries could suddenly change in
136 | your application.
137 |
138 | Please consider one of these 2 options:
139 |
140 | - write unit tests (you have unit tests, don't you?)
141 | - use MagicJoin in development mode only. While developing, dump the SQL generated by MagicJoin and put it back
142 | in your code.
143 |
--------------------------------------------------------------------------------
/doc/magic_twig.md:
--------------------------------------------------------------------------------
1 | Use Twig templating in your SQL queries!
2 | ----------------------------------------
3 |
4 | Using Twig integration, you can directly add Twig conditions right into your SQL.
5 |
6 | ```php
7 | use Mouf\Database\MagicQuery;
8 |
9 | $sql = "SELECT users.* FROM users {% if isAdmin %} WHERE users.admin = 1 {% endif %}";
10 |
11 | $magicQuery = new MagicQuery();
12 | // By default, Twig integration is disabled. You need to enable it.
13 | $magicQuery->setEnableTwig(true);
14 |
15 | $completeSql = $magicQuery->build($sql, ['isAdmin' => true]);
16 | // Parameters are passed to the Twig SQL query, and the SQL query is returned.
17 | ```
18 |
19 | Limitations
20 | -----------
21 |
22 |
Heads up! The Twig integration cannot be used to insert parameters
23 | into the SQL query. You should use classic SQL parameters for this. This means that instead if writing
24 | {{ id }}, you should write :id.
25 |
26 | You cannot directly use Twig parameters because Twig transformation is applied before SQL parsing. If parameters
27 | where replaced by Twig before SQL is parsed, the caching of the transformation *SQL => parsed SQL* would become
28 | inefficient.
29 |
30 | For this reason, if you try to use `{{ parameter }}` instead of `:parameter` in your SQL query, an exception will
31 | be thrown.
32 |
33 | Usage
34 | -----
35 |
36 | For most queries, we found out that MagicJoin combined to MagicParameters is enough.
37 | For this reason, MagicTwig is disabled by default. If you want to enable it, you can simply call
38 | `$magicQuery->setEnableTwig(true)` before calling the `build` method.
39 |
40 | MagicTwig can typically be useful when you have parts of a query that needs to be added conditionally, depending
41 | on provided parameters.
42 |
--------------------------------------------------------------------------------
/phpstan-baseline.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | ignoreErrors:
3 | -
4 | message: "#^Method Mouf\\\\Database\\\\QueryWriter\\\\QueryResult\\:\\:val\\(\\) should return array but returns Mouf\\\\Database\\\\QueryWriter\\\\ResultSet\\.$#"
5 | count: 1
6 | path: src/Mouf/Database/QueryWriter/QueryResult.php
7 |
8 | -
9 | message: "#^Method Mouf\\\\Database\\\\QueryWriter\\\\Utils\\\\DbHelper\\:\\:getAll\\(\\) has no return typehint specified\\.$#"
10 | count: 1
11 | path: src/Mouf/Database/QueryWriter/Utils/DbHelper.php
12 |
13 | -
14 | message: "#^Parameter \\#1 \\$rightOperand of method SQLParser\\\\Node\\\\AbstractInListOperator\\:\\:refactorParameterToExpression\\(\\) expects SQLParser\\\\Node\\\\NodeInterface, array\\\\|SQLParser\\\\Node\\\\NodeInterface\\|string given\\.$#"
15 | count: 1
16 | path: src/SQLParser/Node/AbstractInListOperator.php
17 |
18 | -
19 | message: "#^Cannot access offset 0 on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\.$#"
20 | count: 1
21 | path: src/SQLParser/Node/AbstractInListOperator.php
22 |
23 | -
24 | message: "#^Method SQLParser\\\\Node\\\\AbstractTwoOperandsOperator\\:\\:setLeftOperand\\(\\) has no return typehint specified\\.$#"
25 | count: 1
26 | path: src/SQLParser/Node/AbstractTwoOperandsOperator.php
27 |
28 | -
29 | message: "#^Method SQLParser\\\\Node\\\\AbstractTwoOperandsOperator\\:\\:setRightOperand\\(\\) has no return typehint specified\\.$#"
30 | count: 1
31 | path: src/SQLParser/Node/AbstractTwoOperandsOperator.php
32 |
33 | -
34 | message: "#^Cannot call method walk\\(\\) on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\|string\\.$#"
35 | count: 2
36 | path: src/SQLParser/Node/AbstractTwoOperandsOperator.php
37 |
38 | -
39 | message: "#^Method SQLParser\\\\Node\\\\Between\\:\\:setLeftOperand\\(\\) has no return typehint specified\\.$#"
40 | count: 1
41 | path: src/SQLParser/Node/Between.php
42 |
43 | -
44 | message: "#^Method SQLParser\\\\Node\\\\Between\\:\\:setMinValueOperand\\(\\) has no return typehint specified\\.$#"
45 | count: 1
46 | path: src/SQLParser/Node/Between.php
47 |
48 | -
49 | message: "#^Cannot call method walk\\(\\) on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\|string\\.$#"
50 | count: 1
51 | path: src/SQLParser/Node/Between.php
52 |
53 | -
54 | message: "#^Cannot call method walk\\(\\) on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\|string\\.$#"
55 | count: 1
56 | path: src/SQLParser/Node/CaseOperation.php
57 |
58 | -
59 | message: "#^Cannot access offset mixed on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\.$#"
60 | count: 2
61 | path: src/SQLParser/Node/Expression.php
62 |
63 | -
64 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:toObject\\(\\) has no return typehint specified\\.$#"
65 | count: 1
66 | path: src/SQLParser/Node/NodeFactory.php
67 |
68 | -
69 | message: "#^Parameter \\#1 \\$refClause of method SQLParser\\\\Node\\\\SubQuery\\:\\:setRefClause\\(\\) expects array\\, array\\\\|SQLParser\\\\Node\\\\NodeInterface given\\.$#"
70 | count: 1
71 | path: src/SQLParser/Node/NodeFactory.php
72 |
73 | -
74 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:buildFromSubtree\\(\\) has no return typehint specified\\.$#"
75 | count: 1
76 | path: src/SQLParser/Node/NodeFactory.php
77 |
78 | -
79 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:buildFromSubtree\\(\\) has parameter \\$subTree with no typehint specified\\.$#"
80 | count: 1
81 | path: src/SQLParser/Node/NodeFactory.php
82 |
83 | -
84 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:mapArrayToNodeObjectList\\(\\) has no return typehint specified\\.$#"
85 | count: 1
86 | path: src/SQLParser/Node/NodeFactory.php
87 |
88 | -
89 | message: "#^Property SQLParser\\\\Node\\\\NodeFactory\\:\\:\\$PRECEDENCE has no typehint specified\\.$#"
90 | count: 1
91 | path: src/SQLParser/Node/NodeFactory.php
92 |
93 | -
94 | message: "#^Property SQLParser\\\\Node\\\\NodeFactory\\:\\:\\$OPERATOR_TO_CLASS has no typehint specified\\.$#"
95 | count: 1
96 | path: src/SQLParser/Node/NodeFactory.php
97 |
98 | -
99 | message: "#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, array\\\\|SQLParser\\\\Node\\\\NodeInterface given\\.$#"
100 | count: 1
101 | path: src/SQLParser/Node/NodeFactory.php
102 |
103 | -
104 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:array_map_deep\\(\\) has no return typehint specified\\.$#"
105 | count: 1
106 | path: src/SQLParser/Node/NodeFactory.php
107 |
108 | -
109 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:array_map_deep\\(\\) has parameter \\$array with no typehint specified\\.$#"
110 | count: 1
111 | path: src/SQLParser/Node/NodeFactory.php
112 |
113 | -
114 | message: "#^Method SQLParser\\\\Node\\\\NodeFactory\\:\\:array_map_deep\\(\\) has parameter \\$callback with no typehint specified\\.$#"
115 | count: 1
116 | path: src/SQLParser/Node/NodeFactory.php
117 |
118 | -
119 | message: "#^Property SQLParser\\\\Node\\\\Operator\\:\\:\\$value has no typehint specified\\.$#"
120 | count: 1
121 | path: src/SQLParser/Node/Operator.php
122 |
123 | -
124 | message: "#^Method SQLParser\\\\Node\\\\Operator\\:\\:getValue\\(\\) has no return typehint specified\\.$#"
125 | count: 1
126 | path: src/SQLParser/Node/Operator.php
127 |
128 | -
129 | message: "#^Method SQLParser\\\\Node\\\\Operator\\:\\:setValue\\(\\) has no return typehint specified\\.$#"
130 | count: 1
131 | path: src/SQLParser/Node/Operator.php
132 |
133 | -
134 | message: "#^Strict comparison using \\=\\=\\= between mixed and null will always evaluate to false\\.$#"
135 | count: 1
136 | path: src/SQLParser/Node/Parameter.php
137 |
138 | -
139 | message: "#^Argument of an invalid type array\\\\|SQLParser\\\\Node\\\\NodeInterface supplied for foreach, only iterables are supported\\.$#"
140 | count: 1
141 | path: src/SQLParser/Node/SimpleFunction.php
142 |
143 | -
144 | message: "#^Cannot access offset mixed on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\.$#"
145 | count: 2
146 | path: src/SQLParser/Node/SimpleFunction.php
147 |
148 | -
149 | message: "#^Cannot access offset mixed on array\\\\|SQLParser\\\\Node\\\\NodeInterface\\.$#"
150 | count: 2
151 | path: src/SQLParser/Node/Table.php
152 |
153 | -
154 | message: "#^Elseif condition is always true\\.$#"
155 | count: 1
156 | path: src/SQLParser/Node/Table.php
157 |
158 | -
159 | message: "#^Property SQLParser\\\\Node\\\\Table\\:\\:\\$refClause \\(array\\\\|SQLParser\\\\Node\\\\NodeInterface\\) does not accept null\\.$#"
160 | count: 1
161 | path: src/SQLParser/Node/Table.php
162 |
163 | -
164 | message: "#^Method SQLParser\\\\Query\\\\Select\\:\\:overwriteInstanceDescriptor\\(\\) has parameter \\$name with no typehint specified\\.$#"
165 | count: 1
166 | path: src/SQLParser/Query/Select.php
167 |
168 | -
169 | message: "#^Method SQLParser\\\\Query\\\\Select\\:\\:walkChildren\\(\\) has parameter \\$children with no typehint specified\\.$#"
170 | count: 1
171 | path: src/SQLParser/Query/Select.php
172 |
173 | -
174 | message: "#^Argument of an invalid type array\\\\|SQLParser\\\\Node\\\\NodeInterface supplied for foreach, only iterables are supported\\.$#"
175 | count: 1
176 | path: src/SQLParser/Query/StatementFactory.php
177 |
178 | -
179 | message: "#^Cannot access offset mixed on \\(array\\&nonEmpty\\)\\|SQLParser\\\\Node\\\\NodeInterface\\.$#"
180 | count: 1
181 | path: src/SQLParser/Query/StatementFactory.php
182 |
183 | -
184 | message: "#^Parameter \\#1 \\$columns of method SQLParser\\\\Query\\\\Select\\:\\:setColumns\\(\\) expects array\\, array\\\\|SQLParser\\\\Node\\\\NodeInterface given\\.$#"
185 | count: 1
186 | path: src/SQLParser/Query/StatementFactory.php
187 |
188 | -
189 | message: "#^Method SQLParser\\\\Query\\\\Union\\:\\:overwriteInstanceDescriptor\\(\\) has parameter \\$name with no typehint specified\\.$#"
190 | count: 1
191 | path: src/SQLParser/Query/Union.php
192 |
193 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - phpstan-baseline.neon
3 |
4 | parameters:
5 | level: 7
6 | paths:
7 | - src
8 | inferPrivatePropertyTypeFromConstructor: true
9 | checkMissingIterableValueType: false
10 | checkGenericClassInNonGenericObjectType: false
11 | reportUnmatchedIgnoredErrors: false
12 | ignoreErrors:
13 | - "#Mouf\\\\MoufManager#"
14 | - "#Mouf\\\\MoufInstanceDescriptor#"
15 | - '#Method Mouf\\Database\\QueryWriter\\Utils\\FindParametersService::findParameters\(\) should return array but returns array#'
16 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | src/
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ./tests/
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Mouf/Database/MagicQuery/Twig/ForbiddenTwigParameterInSqlException.php:
--------------------------------------------------------------------------------
1 | self::getCacheDirectory(),
29 | 'strict_variables' => true,
30 | 'autoescape' => 'sql' // Default autoescape mode: sql
31 | );
32 |
33 | $twig = new Environment($stringLoader, $options);
34 |
35 | // Default escaper will throw an exception. This is because we want to use SQL parameters instead of Twig.
36 | // This has a number of advantages, especially in terms of caching.
37 |
38 | /** @var EscaperExtension $twigExtensionEscaper */
39 | $twigExtensionEscaper = $twig->getExtension(EscaperExtension::class);
40 | $twigExtensionEscaper->setEscaper('sql', function () {
41 | throw new ForbiddenTwigParameterInSqlException('You cannot use Twig expressions (like "{{ id }}"). Instead, you should use SQL parameters (like ":id"). Twig integration is limited to Twig statements (like "{% for .... %}"');
42 | });
43 |
44 | self::$twig = $twig;
45 |
46 | return $twig;
47 | }
48 |
49 | private static function getCacheDirectory(): string
50 | {
51 | // If we are running on a Unix environment, let's prepend the cache with the user id of the PHP process.
52 | // This way, we can avoid rights conflicts.
53 |
54 | // @codeCoverageIgnoreStart
55 | if (function_exists('posix_geteuid')) {
56 | $posixGetuid = '_'.posix_geteuid();
57 | } else {
58 | $posixGetuid = '';
59 | }
60 | // @codeCoverageIgnoreEnd
61 | $cacheDirectory = rtrim(sys_get_temp_dir(), '/\\').'/magicquerysqltwigtemplate'.$posixGetuid.str_replace(':', '', __DIR__);
62 |
63 | return $cacheDirectory;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Mouf/Database/MagicQuery/Twig/StringLoader.php:
--------------------------------------------------------------------------------
1 | parameterName = $parameterName;
24 | }
25 |
26 | public function isOk($parameters = null)
27 | {
28 | return isset($parameters[$this->parameterName]) && !empty($parameters[$this->parameterName]);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/Condition/ParamEqualsCondition.php:
--------------------------------------------------------------------------------
1 | parameterName = $parameterName;
28 | $this->value = $value;
29 | }
30 |
31 | public function isOk($parameters = null)
32 | {
33 | return isset($parameters[$this->parameterName]) && $parameters[$this->parameterName] == ValueUtils::val($this->value);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/Condition/ParamNotAvailableCondition.php:
--------------------------------------------------------------------------------
1 | parameterName = $parameterName;
24 | }
25 |
26 | public function isOk($parameters = null)
27 | {
28 | return !isset($parameters[$this->parameterName]) || empty($parameters[$this->parameterName]);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/CountNbResult.php:
--------------------------------------------------------------------------------
1 | queryResult = $queryResult;
41 | $this->connection = $connection;
42 | }
43 |
44 | /**
45 | * (non-PHPdoc).
46 | *
47 | * @see \Mouf\Utils\Value\ArrayValueInterface::val()
48 | */
49 | public function val()
50 | {
51 | $sql = 'SELECT count(*) as cnt FROM ('.$this->queryResult->toSql().') tmp';
52 |
53 | return $this->connection->fetchOne($sql);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/QueryResult.php:
--------------------------------------------------------------------------------
1 | |array|ArrayValueInterface
44 | */
45 | private $parameters = array();
46 |
47 | /** @var int|null */
48 | private $offset;
49 | /** @var int|null */
50 | private $limit;
51 |
52 | /**
53 | * @param Select $select
54 | * @param Connection $connection
55 | */
56 | public function __construct(Select $select, Connection $connection)
57 | {
58 | $this->select = $select;
59 | $this->connection = $connection;
60 | }
61 |
62 | /**
63 | * The list of parameters to apply to the SQL request.
64 | *
65 | * @param array|array|ArrayValueInterface $parameters
66 | */
67 | public function setParameters($parameters): void
68 | {
69 | $this->parameters = $parameters;
70 | }
71 |
72 | /**
73 | * (non-PHPdoc).
74 | *
75 | * @see \Mouf\Utils\Value\ArrayValueInterface::val()
76 | */
77 | public function val()
78 | {
79 | $parameters = ValueUtils::val($this->parameters);
80 | $pdoStatement = $this->connection->prepare($this->select->toSql($parameters, $this->connection->getDatabasePlatform()).DbHelper::getFromLimitString($this->offset, $this->limit));
81 |
82 | return new ResultSet($pdoStatement->getWrappedStatement());
83 | }
84 |
85 | /**
86 | * Returns the SQL for this query-result (without pagination, but with parameters accounted for).
87 | *
88 | * @return string|null
89 | */
90 | public function toSql()
91 | {
92 | $parameters = ValueUtils::val($this->parameters);
93 |
94 | return $this->select->toSql($parameters, $this->connection->getDatabasePlatform());
95 | }
96 |
97 | /**
98 | * Paginates the result set.
99 | *
100 | * @param int $limit
101 | * @param int $offset
102 | */
103 | public function paginate($limit, $offset = 0): void
104 | {
105 | $this->limit = $limit;
106 | $this->offset = $offset;
107 | }
108 |
109 | /* (non-PHPdoc)
110 | * @see \Mouf\Utils\Common\SortableInterface::sort()
111 | */
112 | public function sort($key, $direction = SortableInterface::ASC): void
113 | {
114 | $result = $this->findColumnByKey($key);
115 | if ($result != null) {
116 | $columnObj = clone($result);
117 | if (method_exists($columnObj, 'setAlias')) {
118 | $columnObj->setAlias(null);
119 | }
120 | if (method_exists($columnObj, 'setDirection')) {
121 | $columnObj->setDirection($direction);
122 | }
123 | } else {
124 | $columnObj = new ColRef();
125 | $columnObj->setColumn($key);
126 | $columnObj->setDirection($direction);
127 | }
128 | $this->select->setOrder(array($columnObj));
129 | }
130 |
131 | /**
132 | * Returns the object representing a column from the key passed in parameter.
133 | * It will first scan the column aliases to find if an alias match the key, then the column names, etc...
134 | * It will throw an exception if the column cannot be found.
135 | *
136 | * @param string $key
137 | *
138 | * @return NodeInterface|null
139 | */
140 | private function findColumnByKey($key): ?NodeInterface
141 | {
142 | $columns = $this->select->getColumns();
143 | foreach ($columns as $column) {
144 | if (method_exists($column, 'getAlias')) {
145 | $alias = $column->getAlias();
146 | if ($alias === $key) {
147 | return $column;
148 | }
149 | }
150 | if ($column instanceof ColRef) {
151 | if ($column->getColumn() === $key) {
152 | return $column;
153 | }
154 | }
155 | }
156 |
157 | return null;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/ResultSet.php:
--------------------------------------------------------------------------------
1 | statement = $statement;
28 | }
29 |
30 | public function rewind(): void
31 | {
32 | ++$this->rewindCalls;
33 | if ($this->rewindCalls == 2) {
34 | throw new \Exception("Error: rewind is not possible in a database rowset. You can call 'foreach' on the rowset only once. Use CachedResultSet to be able to call the result several times. TODO: develop CachedResultSet");
35 | }
36 | }
37 |
38 | public function current(): array
39 | {
40 | if (!$this->fetched) {
41 | $this->fetch();
42 | }
43 |
44 | return $this->result;
45 | }
46 |
47 | public function key(): int
48 | {
49 | return $this->key;
50 | }
51 |
52 | public function next(): void
53 | {
54 | ++$this->key;
55 | $this->fetched = false;
56 | $this->fetch();
57 | }
58 |
59 | private function fetch(): void
60 | {
61 | $this->result = $this->statement->execute()->fetchAllAssociative();
62 | $this->fetched = true;
63 | }
64 |
65 | public function valid(): bool
66 | {
67 | if (!$this->fetched) {
68 | $this->fetch();
69 | }
70 |
71 | return $this->result !== false;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/Utils/DbHelper.php:
--------------------------------------------------------------------------------
1 | get('dbalConnection');
14 | $sql .= self::getFromLimitString($offset, $limit);
15 | $statement = $dbalConnection->executeQuery($sql);
16 | $results = $statement->fetchAll();
17 |
18 | $array = [];
19 |
20 | foreach ($results as $result) {
21 | $array[] = $result;
22 | }
23 |
24 | return $array;
25 | }
26 |
27 | public static function getFromLimitString(?int $from = null, ?int $limit = null): string
28 | {
29 | if ($limit !== null) {
30 | $queryStr = ' LIMIT '.$limit;
31 |
32 | if ($from !== null) {
33 | $queryStr .= ' OFFSET '.$from;
34 | }
35 |
36 | return $queryStr;
37 | } else {
38 | return '';
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Mouf/Database/QueryWriter/Utils/FindParametersService.php:
--------------------------------------------------------------------------------
1 | $visitedInstances The list of names of visited instances.
27 | */
28 | private static function recursiveFindParameters(MoufInstanceDescriptor $instanceDescriptor, array $visitedInstances = array(), array $foundParameters = array()): array
29 | {
30 | if (isset($visitedInstances[$instanceDescriptor->getIdentifierName()])) {
31 | return array($visitedInstances, $foundParameters);
32 | }
33 | $visitedInstances[$instanceDescriptor->getIdentifierName()] = true;
34 |
35 | if ($instanceDescriptor->getClassDescriptor()->getName() == 'SQLParser\\Node\\Parameter') {
36 | $foundParameters[$instanceDescriptor->getProperty('name')->getValue()] = true;
37 |
38 | return array($visitedInstances, $foundParameters);
39 | }
40 | if ($instanceDescriptor->getClassDescriptor()->getName() == 'Mouf\\Database\\QueryWriter\\Condition\\ParamAvailableCondition'
41 | || $instanceDescriptor->getClassDescriptor()->getName() == 'Mouf\\Database\\QueryWriter\\Condition\\ParamEqualsCondition'
42 | || $instanceDescriptor->getClassDescriptor()->getName() == 'Mouf\\Database\\QueryWriter\\Condition\\ParamNotAvailableCondition') {
43 | $foundParameters[$instanceDescriptor->getProperty('parameterName')->getValue()] = true;
44 |
45 | return array($visitedInstances, $foundParameters);
46 | }
47 |
48 | $classDescriptor = $instanceDescriptor->getClassDescriptor();
49 | /* @var $classDescriptor MoufReflectionClass */
50 | foreach ($classDescriptor->getInjectablePropertiesByConstructor() as $moufPropertyDescriptor) {
51 | /* @var $moufPropertyDescriptor MoufPropertyDescriptor */
52 | $name = $moufPropertyDescriptor->getName();
53 |
54 | $value = $instanceDescriptor->getConstructorArgumentProperty($name)->getValue();
55 | if ($value instanceof MoufInstanceDescriptor) {
56 | list($visitedInstances, $foundParameters) = self::recursiveFindParameters($value, $visitedInstances, $foundParameters);
57 | } elseif (is_array($value)) {
58 | foreach ($value as $val) {
59 | if ($val instanceof MoufInstanceDescriptor) {
60 | list($visitedInstances, $foundParameters) = self::recursiveFindParameters($val, $visitedInstances, $foundParameters);
61 | }
62 | }
63 | }
64 | }
65 |
66 | foreach ($classDescriptor->getInjectablePropertiesBySetter() as $moufPropertyDescriptor) {
67 | /* @var $moufPropertyDescriptor MoufPropertyDescriptor */
68 | $name = $moufPropertyDescriptor->getName();
69 |
70 | $value = $instanceDescriptor->getSetterProperty($name)->getValue();
71 | if ($value instanceof MoufInstanceDescriptor) {
72 | list($visitedInstances, $foundParameters) = self::recursiveFindParameters($value, $visitedInstances, $foundParameters);
73 | } elseif (is_array($value)) {
74 | foreach ($value as $val) {
75 | if ($val instanceof MoufInstanceDescriptor) {
76 | list($visitedInstances, $foundParameters) = self::recursiveFindParameters($val, $visitedInstances, $foundParameters);
77 | }
78 | }
79 | }
80 | }
81 |
82 | foreach ($classDescriptor->getInjectablePropertiesByPublicProperty() as $moufPropertyDescriptor) {
83 | /* @var $moufPropertyDescriptor MoufPropertyDescriptor */
84 | $name = $moufPropertyDescriptor->getName();
85 |
86 | $value = $instanceDescriptor->getInjectablePropertiesByPublicProperty($name)->getValue();
87 | if ($value instanceof MoufInstanceDescriptor) {
88 | list($visitedInstances, $foundParameters) = self::recursiveFindParameters($value, $visitedInstances, $foundParameters);
89 | } elseif (is_array($value)) {
90 | foreach ($value as $val) {
91 | if ($val instanceof MoufInstanceDescriptor) {
92 | list($visitedInstances, $foundParameters) = self::recursiveFindParameters($val, $visitedInstances, $foundParameters);
93 | }
94 | }
95 | }
96 | }
97 |
98 | return array($visitedInstances, $foundParameters);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/AbstractInListOperator.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | abstract class AbstractInListOperator extends AbstractTwoOperandsOperator
14 | {
15 | protected function getSql(array $parameters, AbstractPlatform $platform, int $indent = 0, int $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): string
16 | {
17 | $rightOperand = $this->getRightOperand();
18 |
19 | $rightOperand = $this->refactorParameterToExpression($rightOperand);
20 |
21 | $this->setRightOperand($rightOperand);
22 |
23 | $parameterNode = $this->getParameter($rightOperand);
24 |
25 | if ($parameterNode !== null) {
26 | if (!isset($parameters[$parameterNode->getName()])) {
27 | throw new MagicQueryException("Missing parameter '" . $parameterNode->getName() . "' for 'IN' operand.");
28 | }
29 | if ($parameters[$parameterNode->getName()] === []) {
30 | return "0 <> 0";
31 | }
32 | }
33 |
34 | return parent::getSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters);
35 | }
36 |
37 | protected function refactorParameterToExpression(NodeInterface $rightOperand): NodeInterface
38 | {
39 | if ($rightOperand instanceof Parameter) {
40 | $expression = new Expression();
41 | $expression->setSubTree([$rightOperand]);
42 | $expression->setBrackets(true);
43 | return $expression;
44 | }
45 | return $rightOperand;
46 | }
47 |
48 | protected function getParameter(NodeInterface $operand): ?Parameter
49 | {
50 | if (!$operand instanceof Expression) {
51 | return null;
52 | }
53 | $subtree = $operand->getSubTree();
54 | if (!isset($subtree[0])) {
55 | return null;
56 | }
57 | if ($subtree[0] instanceof Parameter) {
58 | return $subtree[0];
59 | }
60 | return null;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/AbstractManyInstancesOperator.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | abstract class AbstractManyInstancesOperator implements NodeInterface
17 | {
18 | /** @var array */
19 | private $operands;
20 |
21 | public function getOperands(): array
22 | {
23 | return $this->operands;
24 | }
25 |
26 | /**
27 | * Sets the operands.
28 | *
29 | * @Important
30 | * //@param array> $operands
31 | *
32 | * @param array $operands
33 | */
34 | public function setOperands($operands): void
35 | {
36 | if (!is_array($operands)) {
37 | $operands = array($operands);
38 | }
39 | $this->operands = $operands;
40 | }
41 |
42 | /**
43 | * Returns a Mouf instance descriptor describing this object.
44 | *
45 | * @param MoufManager $moufManager
46 | *
47 | * @return MoufInstanceDescriptor
48 | */
49 | public function toInstanceDescriptor(MoufManager $moufManager)
50 | {
51 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
52 | $instanceDescriptor->getProperty('operands')->setValue(NodeFactory::nodeToInstanceDescriptor($this->operands, $moufManager));
53 |
54 | return $instanceDescriptor;
55 | }
56 |
57 | /**
58 | * Renders the object as a SQL string.
59 | *
60 | * @param array $parameters
61 | * @param AbstractPlatform $platform
62 | * @param int $indent
63 | * @param int $conditionsMode
64 | *
65 | * @param bool $extrapolateParameters
66 | * @return string
67 | */
68 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
69 | {
70 | $sqlOperands = array();
71 | foreach ($this->operands as $operand) {
72 | $sql = NodeFactory::toSql($operand, $platform, $parameters, ' ', true, $indent, $conditionsMode, $extrapolateParameters);
73 | if ($sql != null) {
74 | $sqlOperands[] = $sql;
75 | }
76 | }
77 |
78 | return implode("\n".str_repeat(' ', $indent).$this->getOperatorSymbol().' ', $sqlOperands);
79 | }
80 |
81 | /**
82 | * Walks the tree of nodes, calling the visitor passed in parameter.
83 | *
84 | * @param VisitorInterface $visitor
85 | */
86 | public function walk(VisitorInterface $visitor)
87 | {
88 | $node = $this;
89 | $result = $visitor->enterNode($node);
90 | if ($result instanceof NodeInterface) {
91 | $node = $result;
92 | }
93 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
94 | foreach ($this->operands as $key => $operand) {
95 | $result2 = $operand->walk($visitor);
96 | if ($result2 === NodeTraverser::REMOVE_NODE) {
97 | unset($this->operands[$key]);
98 | } elseif ($result2 instanceof NodeInterface) {
99 | $this->operands[$key] = $result2;
100 | }
101 | }
102 | }
103 |
104 | return $visitor->leaveNode($node);
105 | }
106 |
107 | /**
108 | * Returns the symbol for this operator.
109 | *
110 | * @return string
111 | */
112 | abstract protected function getOperatorSymbol(): string;
113 | }
114 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/AbstractTwoOperandsOperator.php:
--------------------------------------------------------------------------------
1 | , etc...) in an SQL expression.
14 | *
15 | * @author David Négrier
16 | */
17 | abstract class AbstractTwoOperandsOperator implements NodeInterface
18 | {
19 | use ConditionTrait;
20 |
21 | /** @var NodeInterface|NodeInterface[]|string */
22 | private $leftOperand;
23 |
24 | /**
25 | * @return NodeInterface|NodeInterface[]|string
26 | */
27 | public function getLeftOperand()
28 | {
29 | return $this->leftOperand;
30 | }
31 |
32 | /**
33 | * Sets the leftOperand.
34 | *
35 | * @Important
36 | *
37 | * @param NodeInterface|NodeInterface[]|string $leftOperand
38 | */
39 | public function setLeftOperand($leftOperand)
40 | {
41 | $this->leftOperand = $leftOperand;
42 | }
43 |
44 | /** @var NodeInterface|NodeInterface[]|string */
45 | private $rightOperand;
46 |
47 | /**
48 | * @return NodeInterface|NodeInterface[]|string
49 | */
50 | public function getRightOperand()
51 | {
52 | return $this->rightOperand;
53 | }
54 |
55 | /**
56 | * Sets the rightOperand.
57 | *
58 | * @Important
59 | *
60 | * @param string|NodeInterface|NodeInterface[] $rightOperand
61 | */
62 | public function setRightOperand($rightOperand)
63 | {
64 | $this->rightOperand = $rightOperand;
65 | }
66 |
67 | /**
68 | * Returns a Mouf instance descriptor describing this object.
69 | *
70 | * @param MoufManager $moufManager
71 | *
72 | * @return MoufInstanceDescriptor
73 | */
74 | public function toInstanceDescriptor(MoufManager $moufManager)
75 | {
76 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
77 | $instanceDescriptor->getProperty('leftOperand')->setValue(NodeFactory::nodeToInstanceDescriptor($this->leftOperand, $moufManager));
78 | $instanceDescriptor->getProperty('rightOperand')->setValue(NodeFactory::nodeToInstanceDescriptor($this->rightOperand, $moufManager));
79 |
80 | if ($this->leftOperand instanceof Parameter) {
81 | // Let's add a condition on the parameter.
82 | $conditionDescriptor = $moufManager->createInstance('Mouf\\Database\\QueryWriter\\Condition\\ParamAvailableCondition');
83 | $conditionDescriptor->getProperty('parameterName')->setValue($this->leftOperand->getName());
84 | $instanceDescriptor->getProperty('condition')->setValue($conditionDescriptor);
85 | }
86 | // TODO: manage cases where both leftOperand and rightOperand are parameters.
87 | if ($this->rightOperand instanceof Parameter) {
88 | // Let's add a condition on the parameter.
89 | $conditionDescriptor = $moufManager->createInstance('Mouf\\Database\\QueryWriter\\Condition\\ParamAvailableCondition');
90 | $conditionDescriptor->getProperty('parameterName')->setValue($this->rightOperand->getName());
91 | $instanceDescriptor->getProperty('condition')->setValue($conditionDescriptor);
92 | }
93 |
94 | return $instanceDescriptor;
95 | }
96 |
97 | /**
98 | * Renders the object as a SQL string.
99 | *
100 | * @param array $parameters
101 | * @param AbstractPlatform $platform
102 | * @param int $indent
103 | * @param int $conditionsMode
104 | *
105 | * @param bool $extrapolateParameters
106 | * @return string
107 | */
108 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
109 | {
110 | if ($conditionsMode == self::CONDITION_GUESS) {
111 | $bypass = false;
112 | if ($this->leftOperand instanceof BypassableInterface && $this->leftOperand->canBeBypassed($parameters)) {
113 | $bypass = true;
114 | }
115 | if ($this->rightOperand instanceof BypassableInterface && $this->rightOperand->canBeBypassed($parameters)) {
116 | $bypass = true;
117 | }
118 | if ($bypass === true) {
119 | return null;
120 | } else {
121 | $conditionsMode = self::CONDITION_IGNORE;
122 | }
123 | }
124 | if ($conditionsMode == self::CONDITION_IGNORE || !$this->condition || $this->condition->isOk($parameters)) {
125 | $sql = $this->getSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters);
126 | } else {
127 | $sql = null;
128 | }
129 |
130 | return $sql;
131 | }
132 |
133 | protected function getSql(array $parameters, AbstractPlatform $platform, int $indent = 0, int $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): string
134 | {
135 | $sql = NodeFactory::toSql($this->leftOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
136 | $sql .= ' '.$this->getOperatorSymbol().' ';
137 | $sql .= NodeFactory::toSql($this->rightOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
138 |
139 | return $sql;
140 | }
141 |
142 | /**
143 | * Walks the tree of nodes, calling the visitor passed in parameter.
144 | *
145 | * @param VisitorInterface $visitor
146 | */
147 | public function walk(VisitorInterface $visitor)
148 | {
149 | $node = $this;
150 | $result = $visitor->enterNode($node);
151 | if ($result instanceof NodeInterface) {
152 | $node = $result;
153 | }
154 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
155 | $result2 = $this->leftOperand->walk($visitor);
156 | if ($result2 === NodeTraverser::REMOVE_NODE) {
157 | return NodeTraverser::REMOVE_NODE;
158 | } elseif ($result2 instanceof NodeInterface) {
159 | $this->leftOperand = $result2;
160 | }
161 |
162 | $result2 = $this->rightOperand->walk($visitor);
163 | if ($result2 === NodeTraverser::REMOVE_NODE) {
164 | return NodeTraverser::REMOVE_NODE;
165 | } elseif ($result2 instanceof NodeInterface) {
166 | $this->rightOperand = $result2;
167 | }
168 | }
169 |
170 | return $visitor->leaveNode($node);
171 | }
172 |
173 | /**
174 | * Returns the symbol for this operator.
175 | */
176 | abstract protected function getOperatorSymbol(): string;
177 | }
178 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/AggregateFunction.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufManager;
37 | use Mouf\MoufInstanceDescriptor;
38 | use SQLParser\Node\Traverser\NodeTraverser;
39 | use SQLParser\Node\Traverser\VisitorInterface;
40 |
41 | /**
42 | * This class represents an aggregation expression (like COUNT, SUM...) that is an SQL expression.
43 | *
44 | * @author David Négrier
45 | */
46 | class AggregateFunction implements NodeInterface
47 | {
48 | /** @var string */
49 | private $functionName;
50 |
51 | /**
52 | * Returns the base expression (the string that generated this expression).
53 | *
54 | * @return string
55 | */
56 | public function getFunctionName()
57 | {
58 | return $this->functionName;
59 | }
60 |
61 | /**
62 | * Sets the base expression (the string that generated this expression).
63 | *
64 | * @Important
65 | *
66 | * @param string $functionName
67 | */
68 | public function setFunctionName($functionName): void
69 | {
70 | $this->functionName = $functionName;
71 | }
72 |
73 | /** @var NodeInterface[] */
74 | private $subTree;
75 |
76 | /**
77 | * @return NodeInterface[]
78 | */
79 | public function getSubTree()
80 | {
81 | return $this->subTree;
82 | }
83 |
84 | /**
85 | * Sets the subtree.
86 | *
87 | * @Important
88 | *
89 | * @param array $subTree
90 | */
91 | public function setSubTree($subTree): void
92 | {
93 | $this->subTree = $subTree;
94 | }
95 |
96 | /** @var string */
97 | private $alias;
98 |
99 | /**
100 | * @return string
101 | */
102 | public function getAlias()
103 | {
104 | return $this->alias;
105 | }
106 |
107 | /**
108 | * Sets the alias.
109 | *
110 | * @param string|array $alias
111 | */
112 | public function setAlias($alias): void
113 | {
114 | $this->alias = is_array($alias) ? $alias['name'] : $alias;
115 | }
116 |
117 | /** @var string */
118 | private $direction;
119 |
120 | /**
121 | * @return string
122 | */
123 | public function getDirection()
124 | {
125 | return $this->direction;
126 | }
127 |
128 | /**
129 | * Sets the direction.
130 | *
131 | * @Important
132 | *
133 | * @param string $direction
134 | */
135 | public function setDirection($direction): void
136 | {
137 | $this->direction = $direction;
138 | }
139 |
140 | /**
141 | * Returns a Mouf instance descriptor describing this object.
142 | *
143 | * @param MoufManager $moufManager
144 | *
145 | * @return MoufInstanceDescriptor
146 | */
147 | public function toInstanceDescriptor(MoufManager $moufManager)
148 | {
149 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
150 | $instanceDescriptor->getProperty('functionName')->setValue($this->functionName, $moufManager);
151 | $instanceDescriptor->getProperty('subTree')->setValue(NodeFactory::nodeToInstanceDescriptor($this->subTree, $moufManager));
152 | $instanceDescriptor->getProperty('alias')->setValue($this->alias, $moufManager);
153 |
154 | return $instanceDescriptor;
155 | }
156 |
157 | /**
158 | * Renders the object as a SQL string.
159 | *
160 | * @param array $parameters
161 | * @param AbstractPlatform $platform
162 | * @param int $indent
163 | * @param int $conditionsMode
164 | *
165 | * @param bool $extrapolateParameters
166 | * @return string
167 | */
168 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
169 | {
170 | $subTreeSql = NodeFactory::toSql($this->subTree, $platform, $parameters, ', ', false, $indent, $conditionsMode, $extrapolateParameters);
171 | if ($subTreeSql !== null) {
172 | $sql = $this->functionName.'(';
173 | $sql .= $subTreeSql;
174 | $sql .= ')';
175 | if ($this->alias) {
176 | $sql .= ' AS '.$this->alias;
177 | }
178 | if ($this->direction) {
179 | $sql .= ' '.$this->direction;
180 | }
181 | } else {
182 | $sql = null;
183 | }
184 |
185 | return $sql;
186 | }
187 |
188 | /**
189 | * Walks the tree of nodes, calling the visitor passed in parameter.
190 | *
191 | * @param VisitorInterface $visitor
192 | */
193 | public function walk(VisitorInterface $visitor)
194 | {
195 | $node = $this;
196 | $result = $visitor->enterNode($node);
197 | if ($result instanceof NodeInterface) {
198 | $node = $result;
199 | }
200 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
201 | foreach ($this->subTree as $key => $operand) {
202 | $result2 = $operand->walk($visitor);
203 | if ($result2 === NodeTraverser::REMOVE_NODE) {
204 | unset($this->subTree[$key]);
205 | } elseif ($result2 instanceof NodeInterface) {
206 | $this->subTree[$key] = $result2;
207 | }
208 | }
209 | }
210 |
211 | return $visitor->leaveNode($node);
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/AndOp.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | /**
36 | * This class represents an AND in an SQL expression.
37 | *
38 | * @author David Négrier
39 | */
40 | class AndOp extends AbstractManyInstancesOperator
41 | {
42 | /**
43 | * Returns the symbol for this operator.
44 | *
45 | * @return string
46 | */
47 | protected function getOperatorSymbol(): string
48 | {
49 | return 'AND';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Between.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Between implements NodeInterface
19 | {
20 | /** @var NodeInterface|NodeInterface[]|string */
21 | private $leftOperand;
22 |
23 | /**
24 | * @return NodeInterface|NodeInterface[]|string
25 | */
26 | public function getLeftOperand()
27 | {
28 | return $this->leftOperand;
29 | }
30 |
31 | /**
32 | * Sets the leftOperand.
33 | *
34 | * @Important
35 | *
36 | * @param NodeInterface|NodeInterface[]|string $leftOperand
37 | */
38 | public function setLeftOperand($leftOperand)
39 | {
40 | $this->leftOperand = $leftOperand;
41 | }
42 |
43 | /**
44 | * @var NodeInterface
45 | */
46 | private $minValueOperand;
47 |
48 | /**
49 | * @var NodeInterface
50 | */
51 | private $maxValueOperand;
52 |
53 | /**
54 | * @return NodeInterface
55 | */
56 | public function getMinValueOperand()
57 | {
58 | return $this->minValueOperand;
59 | }
60 |
61 | /**
62 | * @param NodeInterface $minValueOperand
63 | */
64 | public function setMinValueOperand(NodeInterface $minValueOperand)
65 | {
66 | $this->minValueOperand = $minValueOperand;
67 | }
68 |
69 | /**
70 | * @return NodeInterface
71 | */
72 | public function getMaxValueOperand()
73 | {
74 | return $this->maxValueOperand;
75 | }
76 |
77 | /**
78 | * @param NodeInterface $maxValueOperand
79 | */
80 | public function setMaxValueOperand(NodeInterface $maxValueOperand): void
81 | {
82 | $this->maxValueOperand = $maxValueOperand;
83 | }
84 |
85 | /**
86 | * @var ConditionInterface|null
87 | */
88 | protected $minValueCondition;
89 |
90 | /**
91 | * Sets the condition.
92 | *
93 | * @Important IfSet
94 | *
95 | * @param ConditionInterface|null $minValueCondition
96 | */
97 | public function setMinValueCondition(ConditionInterface $minValueCondition = null): void
98 | {
99 | $this->minValueCondition = $minValueCondition;
100 | }
101 |
102 | /**
103 | * @var ConditionInterface|null
104 | */
105 | protected $maxValueCondition;
106 |
107 | /**
108 | * Sets the condition.
109 | *
110 | * @Important IfSet
111 | *
112 | * @param ConditionInterface|null $maxValueCondition
113 | */
114 | public function setMaxValueCondition(ConditionInterface $maxValueCondition = null): void
115 | {
116 | $this->maxValueCondition = $maxValueCondition;
117 | }
118 |
119 | /**
120 | * Returns a Mouf instance descriptor describing this object.
121 | *
122 | * @param MoufManager $moufManager
123 | *
124 | * @return MoufInstanceDescriptor
125 | */
126 | public function toInstanceDescriptor(MoufManager $moufManager)
127 | {
128 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
129 | $instanceDescriptor->getProperty('leftOperand')->setValue(NodeFactory::nodeToInstanceDescriptor($this->leftOperand, $moufManager));
130 | $instanceDescriptor->getProperty('minValueOperand')->setValue(NodeFactory::nodeToInstanceDescriptor($this->minValueOperand, $moufManager));
131 | $instanceDescriptor->getProperty('maxValueOperand')->setValue(NodeFactory::nodeToInstanceDescriptor($this->maxValueOperand, $moufManager));
132 |
133 | if ($this->minValueOperand instanceof Parameter) {
134 | // Let's add a condition on the parameter.
135 | $conditionDescriptor = $moufManager->createInstance('Mouf\\Database\\QueryWriter\\Condition\\ParamAvailableCondition');
136 | $conditionDescriptor->getProperty('parameterName')->setValue($this->minValueOperand->getName());
137 | $instanceDescriptor->getProperty('minValueCondition')->setValue($conditionDescriptor);
138 | }
139 |
140 | if ($this->maxValueOperand instanceof Parameter) {
141 | // Let's add a condition on the parameter.
142 | $conditionDescriptor = $moufManager->createInstance('Mouf\\Database\\QueryWriter\\Condition\\ParamAvailableCondition');
143 | $conditionDescriptor->getProperty('parameterName')->setValue($this->maxValueOperand->getName());
144 | $instanceDescriptor->getProperty('maxValueCondition')->setValue($conditionDescriptor);
145 | }
146 |
147 | return $instanceDescriptor;
148 | }
149 |
150 | /**
151 | * Renders the object as an SQL string.
152 | *
153 | * @param array $parameters
154 | * @param AbstractPlatform $platform
155 | * @param int $indent
156 | * @param int $conditionsMode
157 | *
158 | * @param bool $extrapolateParameters
159 | * @return string
160 | */
161 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
162 | {
163 | switch ($conditionsMode) {
164 | case self::CONDITION_APPLY:
165 | $minBypass = $this->minValueCondition && !$this->minValueCondition->isOk($parameters);
166 | $maxBypass = $this->maxValueCondition && !$this->maxValueCondition->isOk($parameters);
167 | break;
168 | case self::CONDITION_GUESS:
169 | $minBypass = $this->minValueOperand instanceof Parameter && $this->minValueOperand->isDiscardedOnNull() && !isset($parameters[$this->minValueOperand->getName()]);
170 | $maxBypass = $this->maxValueOperand instanceof Parameter && $this->maxValueOperand->isDiscardedOnNull() && !isset($parameters[$this->maxValueOperand->getName()]);
171 | break;
172 | case self::CONDITION_IGNORE:
173 | $minBypass = false;
174 | $maxBypass = false;
175 | break;
176 | default:
177 | throw new \InvalidArgumentException('Invalid `$conditionsMode`: "' . $conditionsMode. '"');
178 | }
179 |
180 | if ($maxBypass && $minBypass) {
181 | return null;
182 | }
183 |
184 | if ($minBypass) {
185 | return sprintf('%s <= %s',
186 | NodeFactory::toSql($this->leftOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters),
187 | NodeFactory::toSql($this->maxValueOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters)
188 | );
189 | }
190 |
191 | if ($maxBypass) {
192 | return sprintf('%s >= %s',
193 | NodeFactory::toSql($this->leftOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters),
194 | NodeFactory::toSql($this->minValueOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters));
195 | }
196 |
197 | return sprintf('%s BETWEEN %s AND %s',
198 | NodeFactory::toSql($this->leftOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters),
199 | NodeFactory::toSql($this->minValueOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters),
200 | NodeFactory::toSql($this->maxValueOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters)
201 | );
202 | }
203 |
204 | /**
205 | * Walks the tree of nodes, calling the visitor passed in parameter.
206 | *
207 | * @param VisitorInterface $visitor
208 | */
209 | public function walk(VisitorInterface $visitor)
210 | {
211 | $node = $this;
212 | $result = $visitor->enterNode($node);
213 | if ($result instanceof NodeInterface) {
214 | $node = $result;
215 | }
216 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
217 | $result2 = $this->leftOperand->walk($visitor);
218 | if ($result2 === NodeTraverser::REMOVE_NODE) {
219 | return NodeTraverser::REMOVE_NODE;
220 | } elseif ($result2 instanceof NodeInterface) {
221 | $this->leftOperand = $result2;
222 | }
223 |
224 | $result2 = $this->minValueOperand->walk($visitor);
225 | if ($result2 === NodeTraverser::REMOVE_NODE) {
226 | return NodeTraverser::REMOVE_NODE;
227 | } elseif ($result2 instanceof NodeInterface) {
228 | $this->minValueOperand = $result2;
229 | }
230 |
231 | $result2 = $this->maxValueOperand->walk($visitor);
232 | if ($result2 === NodeTraverser::REMOVE_NODE) {
233 | return NodeTraverser::REMOVE_NODE;
234 | } elseif ($result2 instanceof NodeInterface) {
235 | $this->maxValueOperand = $result2;
236 | }
237 | }
238 |
239 | return $visitor->leaveNode($node);
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/BitwiseAnd.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class BitwiseAnd extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '&';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/BitwiseOr.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class BitwiseOr extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '|';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/BitwiseXor.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class BitwiseXor extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '^';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/BypassableInterface.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class CaseOperation implements NodeInterface
17 | {
18 | /** @var NodeInterface|NodeInterface[]|string */
19 | private $operation;
20 |
21 | /**
22 | * @return NodeInterface|NodeInterface[]|string
23 | */
24 | public function getOperation()
25 | {
26 | return $this->operation;
27 | }
28 |
29 | /**
30 | * Sets the operation.
31 | *
32 | * @Important
33 | *
34 | * @param NodeInterface|NodeInterface[]|string $operation
35 | */
36 | public function setOperation($operation): void
37 | {
38 | $this->operation = $operation;
39 | }
40 |
41 | /**
42 | * Returns a Mouf instance descriptor describing this object.
43 | *
44 | * @param MoufManager $moufManager
45 | *
46 | * @return MoufInstanceDescriptor
47 | */
48 | public function toInstanceDescriptor(MoufManager $moufManager)
49 | {
50 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
51 | $instanceDescriptor->getProperty('operation')->setValue(NodeFactory::nodeToInstanceDescriptor($this->operation, $moufManager));
52 |
53 | return $instanceDescriptor;
54 | }
55 |
56 | /**
57 | * Renders the object as a SQL string.
58 | *
59 | * @param array $parameters
60 | * @param AbstractPlatform $platform
61 | * @param int $indent
62 | * @param int $conditionsMode
63 | *
64 | * @param bool $extrapolateParameters
65 | * @return string
66 | */
67 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
68 | {
69 | $sql = 'CASE '.NodeFactory::toSql($this->operation, $platform, $parameters, ' ', false, $indent, $conditionsMode).' END';
70 |
71 | return $sql;
72 | }
73 |
74 | /**
75 | * Walks the tree of nodes, calling the visitor passed in parameter.
76 | *
77 | * @param VisitorInterface $visitor
78 | */
79 | public function walk(VisitorInterface $visitor)
80 | {
81 | $node = $this;
82 | $result = $visitor->enterNode($node);
83 | if ($result instanceof NodeInterface) {
84 | $node = $result;
85 | }
86 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
87 | $result2 = $this->operation->walk($visitor);
88 | if ($result2 === NodeTraverser::REMOVE_NODE) {
89 | return NodeTraverser::REMOVE_NODE;
90 | } elseif ($result2 instanceof NodeInterface) {
91 | $this->operation = $result2;
92 | }
93 | }
94 |
95 | return $visitor->leaveNode($node);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/ColRef.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufManager;
37 | use Mouf\MoufInstanceDescriptor;
38 | use SQLParser\Node\Traverser\VisitorInterface;
39 |
40 | /**
41 | * This class represents an column in an SQL expression.
42 | *
43 | * @author David Négrier
44 | */
45 | class ColRef implements NodeInterface
46 | {
47 | /** @var mixed */
48 | private $database;
49 |
50 | /**
51 | * Returns the name of the database.
52 | *
53 | * @return mixed
54 | */
55 | public function getDatabase()
56 | {
57 | return $this->database;
58 | }
59 |
60 | /**
61 | * Sets the name of the database
62 | *
63 | * @param mixed $database
64 | */
65 | public function setDatabase($database): void
66 | {
67 | $this->database = $database;
68 | }
69 |
70 | /** @var string */
71 | private $table;
72 |
73 | /**
74 | * Returns the table name.
75 | *
76 | * @return string
77 | */
78 | public function getTable()
79 | {
80 | return $this->table;
81 | }
82 |
83 | /**
84 | * Sets the table name.
85 | *
86 | * @Important
87 | *
88 | * @param string $table
89 | */
90 | public function setTable($table): void
91 | {
92 | $this->table = $table;
93 | }
94 |
95 | /** @var string */
96 | private $column;
97 |
98 | /**
99 | * Returns the column name.
100 | *
101 | * @return string
102 | */
103 | public function getColumn()
104 | {
105 | return $this->column;
106 | }
107 |
108 | /**
109 | * Sets the column name.
110 | *
111 | * @Important
112 | *
113 | * @param string $column
114 | */
115 | public function setColumn($column): void
116 | {
117 | $this->column = $column;
118 | }
119 |
120 | /** @var string */
121 | private $alias;
122 |
123 | /**
124 | * Returns the alias.
125 | *
126 | * @return string
127 | */
128 | public function getAlias()
129 | {
130 | return $this->alias;
131 | }
132 |
133 | /**
134 | * Sets the alias.
135 | *
136 | * @Important
137 | *
138 | * @param string $alias
139 | */
140 | public function setAlias($alias): void
141 | {
142 | $this->alias = $alias;
143 | }
144 |
145 | /** @var string */
146 | private $direction;
147 |
148 | /**
149 | * Returns the direction.
150 | *
151 | * @return string
152 | */
153 | public function getDirection()
154 | {
155 | return $this->direction;
156 | }
157 |
158 | /**
159 | * Sets the direction.
160 | *
161 | * @Important
162 | *
163 | * @param string $direction
164 | */
165 | public function setDirection($direction): void
166 | {
167 | $this->direction = $direction;
168 | }
169 |
170 | /**
171 | * Returns a Mouf instance descriptor describing this object.
172 | *
173 | * @param MoufManager $moufManager
174 | *
175 | * @return MoufInstanceDescriptor
176 | */
177 | public function toInstanceDescriptor(MoufManager $moufManager)
178 | {
179 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
180 | $instanceDescriptor->getProperty('database')->setValue($this->database);
181 | $instanceDescriptor->getProperty('table')->setValue($this->table);
182 | $instanceDescriptor->getProperty('column')->setValue($this->column);
183 | $instanceDescriptor->getProperty('alias')->setValue($this->alias);
184 | $instanceDescriptor->getProperty('direction')->setValue($this->direction);
185 |
186 | return $instanceDescriptor;
187 | }
188 |
189 | /**
190 | * Renders the object as a SQL string.
191 | *
192 | * @param array $parameters
193 | * @param AbstractPlatform $platform
194 | * @param int $indent
195 | * @param int $conditionsMode
196 | *
197 | * @param bool $extrapolateParameters
198 | * @return string
199 | */
200 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
201 | {
202 | $sql = '';
203 | if ($this->database) {
204 | $sql .= $platform->quoteSingleIdentifier($this->database).'.';
205 | }
206 | if ($this->table) {
207 | $sql .= $platform->quoteSingleIdentifier($this->table).'.';
208 | }
209 | if ($this->column !== '*') {
210 | $sql .= $platform->quoteSingleIdentifier($this->column);
211 | } else {
212 | $sql .= '*';
213 | }
214 | if ($this->alias) {
215 | $sql .= ' AS '.$this->alias;
216 | }
217 | if ($this->direction) {
218 | $sql .= ' '.$this->direction;
219 | }
220 |
221 | return $sql;
222 | }
223 |
224 | /**
225 | * Walks the tree of nodes, calling the visitor passed in parameter.
226 | *
227 | * @param VisitorInterface $visitor
228 | */
229 | public function walk(VisitorInterface $visitor)
230 | {
231 | $node = $this;
232 | $result = $visitor->enterNode($node);
233 | if ($result instanceof NodeInterface) {
234 | $node = $result;
235 | }
236 |
237 | return $visitor->leaveNode($node);
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/ConstNode.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufManager;
37 | use Mouf\MoufInstanceDescriptor;
38 | use SQLParser\Node\Traverser\VisitorInterface;
39 |
40 | /**
41 | * This class represents a constant in an SQL expression.
42 | *
43 | * @author David Négrier
44 | */
45 | class ConstNode implements NodeInterface
46 | {
47 | /** @var string */
48 | private $value;
49 | /** @var bool */
50 | private $isString = true;
51 |
52 | /**
53 | * @return string
54 | */
55 | public function getValue()
56 | {
57 | return $this->value;
58 | }
59 |
60 | /**
61 | * Sets the value.
62 | *
63 | * @Important
64 | *
65 | * @param string $value
66 | */
67 | public function setValue($value): void
68 | {
69 | $this->value = $value;
70 | }
71 |
72 | /**
73 | * @return bool
74 | */
75 | public function getIsString()
76 | {
77 | return $this->isString;
78 | }
79 |
80 | /**
81 | * @param bool $isString
82 | */
83 | public function setIsString($isString): void
84 | {
85 | $this->isString = $isString;
86 | }
87 |
88 | /**
89 | * Returns a Mouf instance descriptor describing this object.
90 | *
91 | * @param MoufManager $moufManager
92 | *
93 | * @return MoufInstanceDescriptor
94 | */
95 | public function toInstanceDescriptor(MoufManager $moufManager)
96 | {
97 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
98 | $instanceDescriptor->getProperty('value')->setValue(NodeFactory::nodeToInstanceDescriptor($this->value, $moufManager));
99 |
100 | return $instanceDescriptor;
101 | }
102 |
103 | /**
104 | * Renders the object as a SQL string.
105 | *
106 | * @param array $parameters
107 | * @param AbstractPlatform $platform
108 | * @param int $indent
109 | * @param int $conditionsMode
110 | *
111 | * @param bool $extrapolateParameters
112 | * @return string
113 | */
114 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
115 | {
116 | if ($this->value === null) {
117 | return 'NULL';
118 | } elseif (!$this->isString) {
119 | return $this->value;
120 | } else {
121 | return $platform->quoteStringLiteral($this->value);
122 | }
123 | }
124 |
125 | /**
126 | * Walks the tree of nodes, calling the visitor passed in parameter.
127 | *
128 | * @param VisitorInterface $visitor
129 | *
130 | * @return NodeInterface|null|string Can return null if nothing is to be done or a node that should replace this node, or NodeTraverser::REMOVE_NODE to remove the node
131 | */
132 | public function walk(VisitorInterface $visitor)
133 | {
134 | $node = $this;
135 | $result = $visitor->enterNode($node);
136 | if ($result instanceof NodeInterface) {
137 | $node = $result;
138 | }
139 |
140 | return $visitor->leaveNode($node);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Different.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Different extends AbstractTwoOperandsOperator
13 | {
14 | /**
15 | * Returns the symbol for this operator.
16 | *
17 | * @return string
18 | */
19 | protected function getOperatorSymbol(): string
20 | {
21 | return '<>';
22 | }
23 |
24 | protected function getSql(array $parameters, AbstractPlatform $platform, int $indent = 0, int $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): string
25 | {
26 | $rightOperand = $this->getRightOperand();
27 | if ($rightOperand instanceof Parameter && !isset($parameters[$rightOperand->getName()])) {
28 | $isNull = true;
29 | } else {
30 | $isNull = false;
31 | }
32 |
33 | $sql = NodeFactory::toSql($this->getLeftOperand(), $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
34 | if ($isNull) {
35 | $sql .= ' IS NOT null';
36 | } else {
37 | $sql .= ' '.$this->getOperatorSymbol().' ';
38 | $sql .= NodeFactory::toSql($rightOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
39 | }
40 |
41 | return $sql;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Div.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Div extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'DIV';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Divide.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Divide extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '/';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/ElseOperation.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class ElseOperation extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'ELSE';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Equal.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class Equal extends AbstractTwoOperandsOperator
13 | {
14 | /**
15 | * Returns the symbol for this operator.
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '=';
20 | }
21 |
22 | protected function getSql(array $parameters, AbstractPlatform $platform, int $indent = 0, int $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): string
23 | {
24 | $rightOperand = $this->getRightOperand();
25 | if ($rightOperand instanceof Parameter && !isset($parameters[$rightOperand->getName()])) {
26 | $isNull = true;
27 | } else {
28 | $isNull = false;
29 | }
30 |
31 | $sql = NodeFactory::toSql($this->getLeftOperand(), $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
32 | if ($isNull) {
33 | $sql .= ' IS null';
34 | } else {
35 | $sql .= ' '.$this->getOperatorSymbol().' ';
36 | $sql .= NodeFactory::toSql($rightOperand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
37 | }
38 |
39 | return $sql;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Greater.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Greater extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '>';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/GreaterOrEqual.php:
--------------------------------------------------------------------------------
1 | = operation in an SQL expression.
7 | *
8 | * @author David Négrier
9 | */
10 | class GreaterOrEqual extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '>=';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Hint.php:
--------------------------------------------------------------------------------
1 | type = $type;
24 | $this->list = $list;
25 | }
26 |
27 | public function getType(): string
28 | {
29 | return $this->type;
30 | }
31 |
32 | public function getList(): string
33 | {
34 | return $this->list;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/In.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class In extends AbstractInListOperator
13 | {
14 | /**
15 | * Returns the symbol for this operator.
16 | *
17 | * @return string
18 | */
19 | protected function getOperatorSymbol(): string
20 | {
21 | return 'IN';
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Is.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Is extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'IS';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/IsNot.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class IsNot extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'IS NOT';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Less.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Less extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '<';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/LessOrEqual.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class LessOrEqual extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '<=';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Like.php:
--------------------------------------------------------------------------------
1 | operation in an SQL expression.
7 | *
8 | * @author David Négrier
9 | */
10 | class Like extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'LIKE';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/LimitNode.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufManager;
37 | use Mouf\MoufInstanceDescriptor;
38 | use SQLParser\Node\Traverser\VisitorInterface;
39 |
40 | /**
41 | * This class represents a constant of a LIMIT in an SQL expression (so either a limit or an offset)
42 | *
43 | * @author David MAECHLER
44 | */
45 | class LimitNode implements NodeInterface
46 | {
47 | /** @var string */
48 | private $value;
49 |
50 | /**
51 | * @return string
52 | */
53 | public function getValue()
54 | {
55 | return $this->value;
56 | }
57 |
58 | /**
59 | * Sets the value.
60 | *
61 | * @Important
62 | *
63 | * @param string $value
64 | */
65 | public function setValue($value): void
66 | {
67 | $this->value = $value;
68 | }
69 |
70 | /**
71 | * Returns a Mouf instance descriptor describing this object.
72 | *
73 | * @param MoufManager $moufManager
74 | *
75 | * @return MoufInstanceDescriptor
76 | */
77 | public function toInstanceDescriptor(MoufManager $moufManager)
78 | {
79 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
80 | $instanceDescriptor->getProperty('value')->setValue(NodeFactory::nodeToInstanceDescriptor($this->value, $moufManager));
81 |
82 | return $instanceDescriptor;
83 | }
84 |
85 | /**
86 | * Renders the object as a SQL string.
87 | *
88 | * @param array $parameters
89 | * @param AbstractPlatform $platform
90 | * @param int $indent
91 | * @param int $conditionsMode
92 | *
93 | * @param bool $extrapolateParameters
94 | * @return string
95 | *
96 | * @throws \Exception
97 | */
98 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
99 | {
100 | if ($this->value === null) {
101 | throw new \Exception('A limit parameter must be an integer');
102 | }
103 |
104 | if (is_numeric($this->value)) {
105 | return (string) ((int) $this->value);
106 | } elseif (empty($this->value)) {
107 | return null;
108 | } else {
109 | return $platform->quoteStringLiteral($this->value);
110 | }
111 | }
112 |
113 | /**
114 | * Walks the tree of nodes, calling the visitor passed in parameter.
115 | *
116 | * @param VisitorInterface $visitor
117 | *
118 | * @return NodeInterface|null|string Can return null if nothing is to be done or a node that should replace this node, or NodeTraverser::REMOVE_NODE to remove the node
119 | */
120 | public function walk(VisitorInterface $visitor)
121 | {
122 | $node = $this;
123 | $result = $visitor->enterNode($node);
124 | if ($result instanceof NodeInterface) {
125 | $node = $result;
126 | }
127 |
128 | return $visitor->leaveNode($node);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Minus.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Minus extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '-';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Modulo.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Modulo extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '%';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Multiply.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Multiply extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '*';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/NodeInterface.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | interface NodeInterface extends SqlRenderInterface
16 | {
17 | /**
18 | * Returns a Mouf instance descriptor describing this object.
19 | *
20 | * @param MoufManager $moufManager
21 | *
22 | * @return MoufInstanceDescriptor
23 | */
24 | public function toInstanceDescriptor(MoufManager $moufManager);
25 |
26 | /**
27 | * Walks the tree of nodes, calling the visitor passed in parameter.
28 | *
29 | * @param VisitorInterface $visitor
30 | *
31 | * @return NodeInterface|null|string Can return null if nothing is to be done or a node that should replace this node, or NodeTraverser::REMOVE_NODE to remove the node
32 | */
33 | public function walk(VisitorInterface $visitor);
34 | }
35 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/NotIn.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class NotIn extends AbstractInListOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'NOT IN';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/NotLike.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class NotLike extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'NOT LIKE';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/NullCompatibleEqual.php:
--------------------------------------------------------------------------------
1 | ' operation in an SQL expression.
7 | *
8 | * @author David Négrier
9 | */
10 | class NullCompatibleEqual extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '<=>';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Operation.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | /**
36 | * This class represents an operation (AND, OR, ...) in an SQL expression.
37 | *
38 | * @author David Négrier
39 | */
40 | class Operation extends AbstractManyInstancesOperator
41 | {
42 | /** @var string */
43 | private $operator;
44 |
45 | /**
46 | * @return string
47 | */
48 | public function getOperatorSymbol(): string
49 | {
50 | return $this->operator;
51 | }
52 |
53 | /**
54 | * Sets the operator.
55 | *
56 | * @param string $operator
57 | */
58 | public function setOperatorSymbol($operator): void
59 | {
60 | $this->operator = $operator;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Operator.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufInstanceDescriptor;
37 | use Mouf\MoufManager;
38 | use SQLParser\Node\Traverser\VisitorInterface;
39 |
40 | /**
41 | * This class represents an operator (=, <, AND, OR, ...) in an SQL expression.
42 | *
43 | * @author David Négrier
44 | */
45 | class Operator implements NodeInterface
46 | {
47 | private $value;
48 |
49 | public function getValue()
50 | {
51 | return $this->value;
52 | }
53 |
54 | /**
55 | * Sets the value.
56 | *
57 | * @Important
58 | *
59 | * @param string $value
60 | */
61 | public function setValue($value)
62 | {
63 | $this->value = $value;
64 | }
65 |
66 | /**
67 | * Returns a Mouf instance descriptor describing this object.
68 | *
69 | * @param MoufManager $moufManager
70 | *
71 | * @return MoufInstanceDescriptor
72 | */
73 | public function toInstanceDescriptor(MoufManager $moufManager)
74 | {
75 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
76 | $instanceDescriptor->getProperty('value')->setValue(NodeFactory::nodeToInstanceDescriptor($this->value, $moufManager));
77 |
78 | return $instanceDescriptor;
79 | }
80 |
81 | /**
82 | * Renders the object as a SQL string.
83 | *
84 | * @param array $parameters
85 | * @param AbstractPlatform $platform
86 | * @param int $indent
87 | * @param int $conditionsMode
88 | *
89 | * @param bool $extrapolateParameters
90 | * @return string
91 | */
92 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
93 | {
94 | return $this->value;
95 | }
96 |
97 | /**
98 | * Walks the tree of nodes, calling the visitor passed in parameter.
99 | *
100 | * @param VisitorInterface $visitor
101 | *
102 | * @return NodeInterface|null|string Can return null if nothing is to be done or a node that should replace this node, or NodeTraverser::REMOVE_NODE to remove the node
103 | */
104 | public function walk(VisitorInterface $visitor)
105 | {
106 | $node = $this;
107 | $result = $visitor->enterNode($node);
108 | if ($result instanceof NodeInterface) {
109 | $node = $result;
110 | }
111 |
112 | return $visitor->leaveNode($node);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/OrOp.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | /**
36 | * This class represents an OR in an SQL expression.
37 | *
38 | * @author David Négrier
39 | */
40 | class OrOp extends AbstractManyInstancesOperator
41 | {
42 | /**
43 | * Returns the symbol for this operator.
44 | *
45 | * @return string
46 | */
47 | protected function getOperatorSymbol(): string
48 | {
49 | return 'OR';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Parameter.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufInstanceDescriptor;
37 | use Mouf\MoufManager;
38 | use SQLParser\Node\Traverser\VisitorInterface;
39 |
40 | /**
41 | * This class represents a parameter (as in parameterized query).
42 | *
43 | * @author David Négrier
44 | */
45 | class Parameter implements NodeInterface, BypassableInterface
46 | {
47 | /** @var string */
48 | protected $name;
49 | /** @var bool */
50 | protected $discardedOnNull = true;
51 |
52 | /**
53 | * Returns the name.
54 | *
55 | * @return string
56 | */
57 | public function getName()
58 | {
59 | return $this->name;
60 | }
61 |
62 | /**
63 | * Sets the name.
64 | * If the name ends with !, the parameter will be considered not nullable (regarding magicparameter settings).
65 | *
66 | * @Important
67 | *
68 | * @param string $name
69 | */
70 | public function setName($name): void
71 | {
72 | if (strrpos($name, '!') === strlen($name) - 1) {
73 | $this->name = substr($name, 0, strlen($name) - 1);
74 | $this->discardedOnNull = false;
75 | } else {
76 | $this->name = $name;
77 | $this->discardedOnNull = true;
78 | }
79 | }
80 |
81 | /**
82 | * @var string
83 | */
84 | protected $autoPrepend;
85 |
86 | /**
87 | * @var string
88 | */
89 | protected $autoAppend;
90 |
91 | /**
92 | * @return string
93 | */
94 | public function getAutoPrepend()
95 | {
96 | return $this->autoPrepend;
97 | }
98 |
99 | /**
100 | * Sets a string that will automatically be appended to the parameter, if the parameter is available.
101 | * Very useful to automatically add "%" to a parameter used in a LIKE.
102 | *
103 | * @Important IfSet
104 | *
105 | * @param string $autoPrepend
106 | */
107 | public function setAutoPrepend($autoPrepend): self
108 | {
109 | $this->autoPrepend = $autoPrepend;
110 |
111 | return $this;
112 | }
113 |
114 | /**
115 | * @return string
116 | */
117 | public function getAutoAppend()
118 | {
119 | return $this->autoAppend;
120 | }
121 |
122 | /**
123 | * Sets a string that will automatically be preprended to the parameter, if the parameter is available.
124 | * Very useful to automatically add "%" to a parameter used in a LIKE.
125 | *
126 | * @Important IfSet
127 | *
128 | * @param string $autoAppend
129 | */
130 | public function setAutoAppend($autoAppend): self
131 | {
132 | $this->autoAppend = $autoAppend;
133 |
134 | return $this;
135 | }
136 |
137 | /**
138 | * Returns a Mouf instance descriptor describing this object.
139 | *
140 | * @param MoufManager $moufManager
141 | *
142 | * @return MoufInstanceDescriptor
143 | */
144 | public function toInstanceDescriptor(MoufManager $moufManager)
145 | {
146 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
147 | $instanceDescriptor->getProperty('name')->setValue($this->name);
148 | $instanceDescriptor->getProperty('autoPrepend')->setValue($this->autoPrepend);
149 | $instanceDescriptor->getProperty('autoAppend')->setValue($this->autoAppend);
150 |
151 | return $instanceDescriptor;
152 | }
153 |
154 | /**
155 | * Renders the object as a SQL string.
156 | *
157 | * @param array $parameters
158 | * @param AbstractPlatform $platform
159 | * @param int $indent
160 | * @param int $conditionsMode
161 | *
162 | * @param bool $extrapolateParameters
163 | * @return string
164 | */
165 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
166 | {
167 | if ($extrapolateParameters && isset($parameters[$this->name])) {
168 | if ($parameters[$this->name] === null) {
169 | return 'null';
170 | } elseif (is_array($parameters[$this->name])) {
171 | return implode(',', array_map(function ($item) use ($platform) {
172 | return $platform->quoteStringLiteral($this->autoPrepend.$item.$this->autoAppend);
173 | }, $parameters[$this->name]));
174 | } else {
175 | return $platform->quoteStringLiteral($this->autoPrepend.$parameters[$this->name].$this->autoAppend);
176 | }
177 | } elseif ($extrapolateParameters && !$this->isDiscardedOnNull()) {
178 | return 'null';
179 | } else {
180 | return ':'.$this->name;
181 | }
182 | }
183 |
184 | /**
185 | * Walks the tree of nodes, calling the visitor passed in parameter.
186 | *
187 | * @param VisitorInterface $visitor
188 | *
189 | * @return NodeInterface|null|string Can return null if nothing is to be done or a node that should replace this node, or NodeTraverser::REMOVE_NODE to remove the node
190 | */
191 | public function walk(VisitorInterface $visitor)
192 | {
193 | $node = $this;
194 | $result = $visitor->enterNode($node);
195 | if ($result instanceof NodeInterface) {
196 | $node = $result;
197 | }
198 |
199 | return $visitor->leaveNode($node);
200 | }
201 |
202 | /**
203 | * Returns whether the parameter can be discarded if provided value is null.
204 | *
205 | * @return bool
206 | */
207 | public function isDiscardedOnNull()
208 | {
209 | return $this->discardedOnNull;
210 | }
211 |
212 | /**
213 | * Returns if this node should be removed from the tree.
214 | */
215 | public function canBeBypassed(array $parameters): bool
216 | {
217 | return $this->isDiscardedOnNull() && !isset($parameters[$this->getName()]);
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Plus.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Plus extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '+';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Regexp.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Regexp extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'REGEXP';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Reserved.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufInstanceDescriptor;
37 | use Mouf\MoufManager;
38 | use SQLParser\Node\Traverser\VisitorInterface;
39 |
40 | /**
41 | * This class represents a node that is an SQL expression.
42 | *
43 | * @author David Négrier
44 | */
45 | class Reserved implements NodeInterface
46 | {
47 | /** @var string */
48 | private $baseExpression;
49 |
50 | /**
51 | * Returns the base expression (the string that generated this expression).
52 | *
53 | * @return string
54 | */
55 | public function getBaseExpression()
56 | {
57 | return $this->baseExpression;
58 | }
59 |
60 | /**
61 | * Sets the base expression (the string that generated this expression).
62 | *
63 | * @param string $baseExpression
64 | */
65 | public function setBaseExpression($baseExpression): void
66 | {
67 | $this->baseExpression = $baseExpression;
68 | }
69 |
70 | /** @var bool */
71 | private $brackets = false;
72 |
73 | /**
74 | * Returns true if the expression is between brackets.
75 | *
76 | * @return bool
77 | */
78 | public function hasBrackets()
79 | {
80 | return $this->brackets;
81 | }
82 |
83 | /**
84 | * Sets to true if the expression is between brackets.
85 | *
86 | * @Important
87 | *
88 | * @param bool $brackets
89 | */
90 | public function setBrackets($brackets): void
91 | {
92 | $this->brackets = $brackets;
93 | }
94 |
95 | /**
96 | * Returns a Mouf instance descriptor describing this object.
97 | *
98 | * @param MoufManager $moufManager
99 | *
100 | * @return MoufInstanceDescriptor
101 | */
102 | public function toInstanceDescriptor(MoufManager $moufManager)
103 | {
104 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
105 | $instanceDescriptor->getProperty('baseExpression')->setValue(NodeFactory::nodeToInstanceDescriptor($this->baseExpression, $moufManager));
106 | $instanceDescriptor->getProperty('brackets')->setValue(NodeFactory::nodeToInstanceDescriptor($this->brackets, $moufManager));
107 |
108 | return $instanceDescriptor;
109 | }
110 |
111 | /**
112 | * Renders the object as a SQL string.
113 | *
114 | * @param array $parameters
115 | * @param AbstractPlatform $platform
116 | * @param int $indent
117 | * @param int $conditionsMode
118 | *
119 | * @param bool $extrapolateParameters
120 | * @return string
121 | */
122 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
123 | {
124 | $sql = '';
125 |
126 | if ($this->baseExpression) {
127 | $sql .= ' '.$this->baseExpression.' ';
128 | }
129 |
130 | if ($this->brackets) {
131 | $sql = '('.$sql.')';
132 | }
133 |
134 | return $sql;
135 | }
136 |
137 | /**
138 | * Walks the tree of nodes, calling the visitor passed in parameter.
139 | *
140 | * @param VisitorInterface $visitor
141 | */
142 | public function walk(VisitorInterface $visitor)
143 | {
144 | $node = $this;
145 | $result = $visitor->enterNode($node);
146 | if ($result instanceof NodeInterface) {
147 | $node = $result;
148 | }
149 |
150 | return $visitor->leaveNode($node);
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/ShiftLeft.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class ShiftLeft extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '<<';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/ShiftRight.php:
--------------------------------------------------------------------------------
1 | > operation in an SQL expression.
7 | *
8 | * @author David Négrier
9 | */
10 | class ShiftRight extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return '>>';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/SimpleFunction.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufInstanceDescriptor;
37 | use Mouf\MoufManager;
38 | use SQLParser\Node\Traverser\NodeTraverser;
39 | use SQLParser\Node\Traverser\VisitorInterface;
40 |
41 | /**
42 | * This class represents a node that is an SQL function call.
43 | *
44 | * @author David Négrier
45 | */
46 | class SimpleFunction implements NodeInterface
47 | {
48 | /** @var string */
49 | private $baseExpression;
50 |
51 | /**
52 | * Returns the base expression (the string that generated this expression).
53 | *
54 | * @return string
55 | */
56 | public function getBaseExpression()
57 | {
58 | return $this->baseExpression;
59 | }
60 |
61 | /**
62 | * Sets the base expression (the string that generated this expression).
63 | *
64 | * @param string $baseExpression
65 | */
66 | public function setBaseExpression($baseExpression): void
67 | {
68 | $this->baseExpression = $baseExpression;
69 | }
70 |
71 | /** @var NodeInterface[]|NodeInterface */
72 | private $subTree;
73 |
74 | /**
75 | * @return NodeInterface|NodeInterface[]
76 | */
77 | public function getSubTree()
78 | {
79 | return $this->subTree;
80 | }
81 |
82 | /**
83 | * Sets the subtree.
84 | *
85 | * @Important
86 | *
87 | * @param array|NodeInterface $subTree
88 | */
89 | public function setSubTree($subTree): void
90 | {
91 | $this->subTree = NodeFactory::simplify($subTree);
92 | }
93 |
94 | /** @var string */
95 | private $alias;
96 |
97 | /**
98 | * @return string
99 | */
100 | public function getAlias()
101 | {
102 | return $this->alias;
103 | }
104 |
105 | /**
106 | * Sets the alias.
107 | *
108 | * @Important
109 | *
110 | * @param string $alias
111 | */
112 | public function setAlias($alias): void
113 | {
114 | $this->alias = $alias;
115 | }
116 |
117 | /** @var string */
118 | private $direction;
119 |
120 | /**
121 | * @return string
122 | */
123 | public function getDiretion()
124 | {
125 | return $this->direction;
126 | }
127 |
128 | /**
129 | * Sets the direction.
130 | *
131 | * @Important
132 | *
133 | * @param string $direction
134 | */
135 | public function setDirection($direction): void
136 | {
137 | $this->direction = $direction;
138 | }
139 |
140 | /** @var bool */
141 | private $brackets = false;
142 |
143 | /**
144 | * Returns true if the expression is between brackets.
145 | *
146 | * @return bool
147 | */
148 | public function hasBrackets()
149 | {
150 | return $this->brackets;
151 | }
152 |
153 | /**
154 | * Returns a Mouf instance descriptor describing this object.
155 | *
156 | * @param MoufManager $moufManager
157 | *
158 | * @return MoufInstanceDescriptor
159 | */
160 | public function toInstanceDescriptor(MoufManager $moufManager)
161 | {
162 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
163 | $instanceDescriptor->getProperty('baseExpression')->setValue(NodeFactory::nodeToInstanceDescriptor($this->baseExpression, $moufManager));
164 | $instanceDescriptor->getProperty('subTree')->setValue(NodeFactory::nodeToInstanceDescriptor($this->subTree, $moufManager));
165 | $instanceDescriptor->getProperty('alias')->setValue(NodeFactory::nodeToInstanceDescriptor($this->alias, $moufManager));
166 | $instanceDescriptor->getProperty('direction')->setValue(NodeFactory::nodeToInstanceDescriptor($this->direction, $moufManager));
167 |
168 | return $instanceDescriptor;
169 | }
170 |
171 | /**
172 | * Renders the object as a SQL string.
173 | *
174 | * @param array $parameters
175 | * @param AbstractPlatform $platform
176 | * @param int $indent
177 | * @param int $conditionsMode
178 | *
179 | * @param bool $extrapolateParameters
180 | * @return string
181 | */
182 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
183 | {
184 | $sql = '';
185 | if (!empty($this->baseExpression)) {
186 | $sql .= $this->baseExpression.'(';
187 | }
188 | $sql .= NodeFactory::toSql($this->subTree, $platform, $parameters, ',', false, $indent, $conditionsMode, $extrapolateParameters);
189 | if (!empty($this->baseExpression)) {
190 | $sql .= ')';
191 | }
192 |
193 | if ($this->alias) {
194 | $sql .= ' AS '.$this->alias;
195 | }
196 | if ($this->direction) {
197 | $sql .= ' '.$this->direction;
198 | }
199 |
200 | return $sql;
201 | }
202 |
203 | /**
204 | * Walks the tree of nodes, calling the visitor passed in parameter.
205 | *
206 | * @param VisitorInterface $visitor
207 | */
208 | public function walk(VisitorInterface $visitor)
209 | {
210 | $node = $this;
211 | $result = $visitor->enterNode($node);
212 | if ($result instanceof NodeInterface) {
213 | $node = $result;
214 | }
215 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
216 | foreach ($this->subTree as $key => $operand) {
217 | $result2 = $operand->walk($visitor);
218 | if ($result2 === NodeTraverser::REMOVE_NODE) {
219 | unset($this->subTree[$key]);
220 | } elseif ($result2 instanceof NodeInterface) {
221 | $this->subTree[$key] = $result2;
222 | }
223 | }
224 | }
225 |
226 | return $visitor->leaveNode($node);
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/SubQuery.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use SQLParser\Node\Traverser\NodeTraverser;
37 | use SQLParser\Node\Traverser\VisitorInterface;
38 | use SQLParser\Query\Select;
39 | use Mouf\MoufInstanceDescriptor;
40 | use Mouf\MoufManager;
41 | use SQLParser\Query\Union;
42 |
43 | /**
44 | * This class represents a subquery (and optionally a JOIN .. ON expression in an SQL expression.
45 | *
46 | * @author David Négrier
47 | */
48 | class SubQuery implements NodeInterface
49 | {
50 | /** @var Select|Union|NodeInterface */
51 | private $subQuery;
52 |
53 | /**
54 | * Returns the list of subQuery statements.
55 | *
56 | * @return Select|Union|NodeInterface
57 | */
58 | public function getSubQuery()
59 | {
60 | return $this->subQuery;
61 | }
62 |
63 | /**
64 | * Sets the list of subQuery statements.
65 | *
66 | * @param Select|Union $subQuery
67 | */
68 | public function setSubQuery($subQuery): void
69 | {
70 | $this->subQuery = $subQuery;
71 | }
72 |
73 | /** @var string */
74 | private $alias;
75 |
76 | /**
77 | * Returns the alias.
78 | *
79 | * @return string
80 | */
81 | public function getAlias()
82 | {
83 | return $this->alias;
84 | }
85 |
86 | /**
87 | * Sets the alias.
88 | *
89 | * @param string $alias
90 | */
91 | public function setAlias($alias): void
92 | {
93 | $this->alias = $alias;
94 | }
95 |
96 | /** @var string */
97 | private $joinType;
98 |
99 | /**
100 | * Returns the join type.
101 | *
102 | * @return string
103 | */
104 | public function getJoinType()
105 | {
106 | return $this->joinType;
107 | }
108 |
109 | /**
110 | * Sets the join type (JOIN, LEFT JOIN, RIGHT JOIN, etc...).
111 | *
112 | * @param string $joinType
113 | */
114 | public function setJoinType($joinType): void
115 | {
116 | $this->joinType = $joinType;
117 | }
118 |
119 | /** @var NodeInterface[] */
120 | private $refClause;
121 |
122 | /**
123 | * Returns the list of refClause statements.
124 | *
125 | * @return NodeInterface[]
126 | */
127 | public function getRefClause()
128 | {
129 | return $this->refClause;
130 | }
131 |
132 | /**
133 | * Sets the list of refClause statements.
134 | *
135 | * @param NodeInterface[] $refClause
136 | */
137 | public function setRefClause($refClause): void
138 | {
139 | $this->refClause = $refClause;
140 | }
141 |
142 | /**
143 | * Returns a Mouf instance descriptor describing this object.
144 | *
145 | * @param MoufManager $moufManager
146 | *
147 | * @return MoufInstanceDescriptor
148 | */
149 | public function toInstanceDescriptor(MoufManager $moufManager)
150 | {
151 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
152 | $instanceDescriptor->getProperty('subQuery')->setValue($this->subQuery->toInstanceDescriptor($moufManager));
153 | $instanceDescriptor->getProperty('alias')->setValue($this->alias);
154 | $instanceDescriptor->getProperty('joinType')->setValue($this->joinType);
155 | $instanceDescriptor->getProperty('refClause')->setValue(NodeFactory::nodeToInstanceDescriptor($this->refClause, $moufManager));
156 |
157 | return $instanceDescriptor;
158 | }
159 |
160 | /**
161 | * Walks the tree of nodes, calling the visitor passed in parameter.
162 | *
163 | * @param VisitorInterface $visitor
164 | */
165 | public function walk(VisitorInterface $visitor)
166 | {
167 | $node = $this;
168 | $result = $visitor->enterNode($node);
169 | if ($result instanceof NodeInterface) {
170 | $node = $result;
171 | }
172 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
173 | $result2 = $this->subQuery->walk($visitor);
174 | if ($result2 === NodeTraverser::REMOVE_NODE) {
175 | return NodeTraverser::REMOVE_NODE;
176 | } elseif ($result2 instanceof NodeInterface) {
177 | $this->subQuery = $result2;
178 | }
179 | }
180 |
181 | return $visitor->leaveNode($node);
182 | }
183 |
184 | /**
185 | * Renders the object as a SQL string.
186 | *
187 | * @param array $parameters
188 | * @param AbstractPlatform $platform
189 | * @param int $indent
190 | * @param int $conditionsMode
191 | *
192 | * @param bool $extrapolateParameters
193 | * @return string
194 | */
195 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
196 | {
197 | $sql = '';
198 | if ($this->refClause) {
199 | $sql .= "\n ".$this->joinType.' ';
200 | }
201 | $sql .= '('.$this->subQuery->toSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters).')';
202 | if ($this->alias) {
203 | $sql .= ' '.$platform->quoteSingleIdentifier($this->alias);
204 | }
205 | if ($this->refClause) {
206 | $sql .= ' ON ';
207 | $sql .= NodeFactory::toSql($this->refClause, $platform, $parameters, ' ', true, $indent, $conditionsMode, $extrapolateParameters);
208 | }
209 |
210 | return $sql;
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Table.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 | use Mouf\MoufInstanceDescriptor;
37 | use Mouf\MoufManager;
38 | use SQLParser\Node\Traverser\NodeTraverser;
39 | use SQLParser\Node\Traverser\VisitorInterface;
40 |
41 | /**
42 | * This class represents a table (and optionally a JOIN .. ON expression) in a SQL expression.
43 | *
44 | * @author David Négrier
45 | */
46 | class Table implements NodeInterface
47 | {
48 | /** @var mixed */
49 | private $database;
50 |
51 | /**
52 | * Returns the name of the database.
53 | *
54 | * @return mixed
55 | */
56 | public function getDatabase()
57 | {
58 | return $this->database;
59 | }
60 |
61 | /**
62 | * Sets the name of the database
63 | *
64 | * @param mixed $database
65 | */
66 | public function setDatabase($database): void
67 | {
68 | $this->database = $database;
69 | }
70 |
71 | /** @var string */
72 | private $table;
73 |
74 | /**
75 | * Returns the table name.
76 | *
77 | * @return string
78 | */
79 | public function getTable()
80 | {
81 | return $this->table;
82 | }
83 |
84 | /**
85 | * Sets the table name.
86 | *
87 | * @Important
88 | *
89 | * @param string $table
90 | */
91 | public function setTable($table): void
92 | {
93 | $this->table = $table;
94 | }
95 |
96 | /** @var string */
97 | private $alias;
98 |
99 | /**
100 | * Returns the alias.
101 | *
102 | * @return string
103 | */
104 | public function getAlias()
105 | {
106 | return $this->alias;
107 | }
108 |
109 | /**
110 | * Sets the alias.
111 | *
112 | * @Important
113 | *
114 | * @param string $alias
115 | */
116 | public function setAlias($alias): void
117 | {
118 | $this->alias = $alias;
119 | }
120 |
121 | /** @var string */
122 | private $joinType;
123 |
124 | /**
125 | * Returns the join type.
126 | *
127 | * @return string
128 | */
129 | public function getJoinType()
130 | {
131 | return $this->joinType;
132 | }
133 |
134 | /**
135 | * Sets the join type (JOIN, LEFT JOIN, RIGHT JOIN, etc...).
136 | *
137 | * @Important
138 | *
139 | * @param string $joinType
140 | */
141 | public function setJoinType($joinType): void
142 | {
143 | $this->joinType = $joinType;
144 | }
145 |
146 | /** @var NodeInterface[]|NodeInterface */
147 | private $refClause;
148 |
149 | /**
150 | * Returns the list of refClause statements.
151 | *
152 | * @return NodeInterface[]|NodeInterface
153 | */
154 | public function getRefClause()
155 | {
156 | return $this->refClause;
157 | }
158 |
159 | /**
160 | * Sets the list of refClause statements.
161 | *
162 | * @Important
163 | *
164 | * @param NodeInterface[]|NodeInterface $refClause
165 | */
166 | public function setRefClause($refClause): void
167 | {
168 | $this->refClause = $refClause;
169 | }
170 |
171 | /**
172 | * @var Hint[]
173 | */
174 | private $hints;
175 |
176 | /**
177 | * Return the list of table hints
178 | *
179 | * @return Hint[]
180 | */
181 | public function getHints(): array
182 | {
183 | return $this->hints;
184 | }
185 |
186 | /**
187 | * Set a list of table hints
188 | *
189 | * @param Hint[] $hints
190 | */
191 | public function setHints(array $hints): void
192 | {
193 | $this->hints = $hints;
194 | }
195 |
196 | /**
197 | * Returns a Mouf instance descriptor describing this object.
198 | *
199 | * @param MoufManager $moufManager
200 | *
201 | * @return MoufInstanceDescriptor
202 | */
203 | public function toInstanceDescriptor(MoufManager $moufManager)
204 | {
205 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
206 | $instanceDescriptor->getProperty('database')->setValue($this->database);
207 | $instanceDescriptor->getProperty('table')->setValue($this->table);
208 | $instanceDescriptor->getProperty('alias')->setValue($this->alias);
209 | $instanceDescriptor->getProperty('joinType')->setValue($this->joinType);
210 | $instanceDescriptor->getProperty('refClause')->setValue(NodeFactory::nodeToInstanceDescriptor($this->refClause, $moufManager));
211 |
212 | return $instanceDescriptor;
213 | }
214 |
215 | /**
216 | * Renders the object as a SQL string.
217 | *
218 | * @param array $parameters
219 | * @param AbstractPlatform $platform
220 | * @param int $indent
221 | * @param int $conditionsMode
222 | *
223 | * @param bool $extrapolateParameters
224 | * @return string
225 | */
226 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
227 | {
228 | $sql = '';
229 | if ($this->refClause || $this->joinType === 'CROSS JOIN') {
230 | $sql .= "\n ".$this->joinType.' ';
231 | }
232 | if ($this->database) {
233 | $sql .= $platform->quoteSingleIdentifier($this->database).'.';
234 | }
235 | $sql .= $platform->quoteSingleIdentifier($this->table);
236 | if ($this->alias) {
237 | $sql .= ' '.$platform->quoteSingleIdentifier($this->alias);
238 | }
239 | if ($this->hints) {
240 | foreach ($this->hints as $hint) {
241 | $sql .= ' ' . $hint->getType() . ' ' . $hint->getList();
242 | }
243 | }
244 | if ($this->refClause) {
245 | $sql .= ' ON ';
246 | $sql .= NodeFactory::toSql($this->refClause, $platform, $parameters, ' ', true, $indent, $conditionsMode, $extrapolateParameters);
247 | }
248 |
249 | return $sql;
250 | }
251 |
252 | /**
253 | * Walks the tree of nodes, calling the visitor passed in parameter.
254 | *
255 | * @param VisitorInterface $visitor
256 | */
257 | public function walk(VisitorInterface $visitor)
258 | {
259 | $node = $this;
260 | $result = $visitor->enterNode($node);
261 | if ($result instanceof NodeInterface) {
262 | $node = $result;
263 | }
264 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
265 | if (is_array($this->refClause)) {
266 | foreach ($this->refClause as $key => $operand) {
267 | $result2 = $operand->walk($visitor);
268 | if ($result2 === NodeTraverser::REMOVE_NODE) {
269 | unset($this->refClause[$key]);
270 | } elseif ($result2 instanceof NodeInterface) {
271 | $this->refClause[$key] = $result2;
272 | }
273 | }
274 | } elseif ($this->refClause) {
275 | $result2 = $this->refClause->walk($visitor);
276 | if ($result2 === NodeTraverser::REMOVE_NODE) {
277 | $this->refClause = null;
278 | } elseif ($result2 instanceof NodeInterface) {
279 | $this->refClause = $result2;
280 | }
281 | }
282 | }
283 |
284 | return $visitor->leaveNode($node);
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Then.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Then extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'THEN';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Traverser/CompositeVisitor.php:
--------------------------------------------------------------------------------
1 | visitors[] = $visitor;
30 | array_unshift($this->reverseVisitors, $visitor);
31 | }
32 |
33 | /**
34 | * Called on every node when the traverser enters the node.
35 | * The enterNode() method can return a changed node, or null if nothing is changed.
36 | * The enterNode() method can also return the value NodeTraverser::DONT_TRAVERSE_CHILDREN,
37 | * which instructs the traverser to skip all children of the current node.
38 | *
39 | * @param NodeInterface $node
40 | *
41 | * @return NodeInterface|string|null
42 | */
43 | public function enterNode(NodeInterface $node)
44 | {
45 | foreach ($this->reverseVisitors as $visitor) {
46 | $result = $visitor->enterNode($node);
47 | if ($result instanceof NodeInterface) {
48 | $node = $result;
49 | } elseif ($result === NodeTraverser::DONT_TRAVERSE_CHILDREN) {
50 | return NodeTraverser::DONT_TRAVERSE_CHILDREN;
51 | } elseif ($result !== null) {
52 | throw new TraverserException('Unexpected return value for enterNode. Return value should be a NodeInterface instance or the NodeTraverser::DONT_TRAVERSE_CHILDREN constant or null.');
53 | }
54 | }
55 |
56 | return $node;
57 | }
58 |
59 | /**
60 | * {@inheritDoc}
61 | */
62 | public function leaveNode(NodeInterface $node)
63 | {
64 | foreach ($this->visitors as $visitor) {
65 | $result = $visitor->leaveNode($node);
66 | if ($result instanceof NodeInterface) {
67 | $node = $result;
68 | } elseif ($result === NodeTraverser::REMOVE_NODE) {
69 | return NodeTraverser::REMOVE_NODE;
70 | } elseif ($result !== null) {
71 | throw new TraverserException('Unexpected return value for leaveNode. Return value should be a NodeInterface instance or the NodeTraverser::REMOVE_NODE constant or null.');
72 | }
73 | }
74 |
75 | return $node;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Traverser/DetectMagicJoinSelectVisitor.php:
--------------------------------------------------------------------------------
1 | */
18 | private $magicJoinSelects = array();
19 |
20 | /**
21 | * Removes all detected magic join selects.
22 | * Useful for reusing the visitor instance on another node traversal.
23 | */
24 | public function resetVisitor(): void
25 | {
26 | $this->magicJoinSelects = array();
27 | $this->lastVisitedSelect = null;
28 | }
29 |
30 | /**
31 | * Return the list of all Select object that have a MagicJoin table.
32 | *
33 | * @return MagicJoinSelect[]
34 | */
35 | public function getMagicJoinSelects()
36 | {
37 | // TODO: throw an exception if the magicjoin table is not the only one
38 | return $this->magicJoinSelects;
39 | }
40 |
41 | /**
42 | * Called on every node when the traverser enters the node.
43 | * The enterNode() method can return a changed node, or null if nothing is changed.
44 | * The enterNode() method can also return the value NodeTraverser::DONT_TRAVERSE_CHILDREN,
45 | * which instructs the traverser to skip all children of the current node.
46 | *
47 | * @param NodeInterface $node
48 | *
49 | * @return NodeInterface|string|null
50 | */
51 | public function enterNode(NodeInterface $node)
52 | {
53 | if ($node instanceof Select) {
54 | $this->lastVisitedSelect = $node;
55 | } elseif ($node instanceof Table) {
56 | if (strtolower($node->getTable()) == 'magicjoin') {
57 | $mainTable = trim($node->getAlias(), '()');
58 | $this->magicJoinSelects[] = new MagicJoinSelect($this->lastVisitedSelect, $mainTable);
59 | }
60 | }
61 |
62 | return null;
63 | }
64 |
65 | /**
66 | * {@inheritDoc}
67 | */
68 | public function leaveNode(NodeInterface $node)
69 | {
70 | return null;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Traverser/DetectTablesVisitor.php:
--------------------------------------------------------------------------------
1 | */
19 | private $tables = array();
20 |
21 | private $defaultTable;
22 |
23 | /**
24 | * Removes all detected magic join selects.
25 | * Useful for reusing the visitor instance on another node traversal.
26 | */
27 | public function resetVisitor(): void
28 | {
29 | $this->tables = array();
30 | $this->isSelectVisited = false;
31 | }
32 |
33 | /**
34 | * @param string $defaultTable Sets the default table that will be used if no table is specified in a colref.
35 | */
36 | public function __construct($defaultTable)
37 | {
38 | $this->defaultTable = $defaultTable;
39 | }
40 |
41 | /**
42 | * Return the list of tables referenced in the Select.
43 | *
44 | * @return string[] The key and the value are the table name.
45 | */
46 | public function getTables()
47 | {
48 | return $this->tables;
49 | }
50 |
51 | /**
52 | * Called on every node when the traverser enters the node.
53 | * The enterNode() method can return a changed node, or null if nothing is changed.
54 | * The enterNode() method can also return the value NodeTraverser::DONT_TRAVERSE_CHILDREN,
55 | * which instructs the traverser to skip all children of the current node.
56 | *
57 | * @param NodeInterface $node
58 | *
59 | * @return NodeInterface|string|null
60 | */
61 | public function enterNode(NodeInterface $node)
62 | {
63 | if ($node instanceof Select) {
64 | if ($this->isSelectVisited) {
65 | return NodeTraverser::DONT_TRAVERSE_CHILDREN;
66 | } else {
67 | $this->isSelectVisited = true;
68 | }
69 | } elseif ($node instanceof ColRef) {
70 | if (empty($node->getTable())) {
71 | $node->setTable($this->defaultTable);
72 | }
73 | $this->tables[$node->getTable()] = $node->getTable();
74 | }
75 |
76 | return null;
77 | }
78 |
79 | /**
80 | * {@inheritDoc}
81 | */
82 | public function leaveNode(NodeInterface $node)
83 | {
84 | return null;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Traverser/MagicJoinSelect.php:
--------------------------------------------------------------------------------
1 | select = $select;
31 | $this->mainTable = $mainTable;
32 | }
33 |
34 | /**
35 | * @return Select
36 | */
37 | public function getSelect()
38 | {
39 | return $this->select;
40 | }
41 |
42 | /**
43 | * @return string
44 | */
45 | public function getMainTable()
46 | {
47 | return $this->mainTable;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Traverser/NodeTraverser.php:
--------------------------------------------------------------------------------
1 | visitor = new CompositeVisitor();
20 | }
21 |
22 | /**
23 | * Registers a new visitor with this traverser.
24 | *
25 | * @param VisitorInterface $visitor
26 | */
27 | public function addVisitor(VisitorInterface $visitor): void
28 | {
29 | $this->visitor->addVisitor($visitor);
30 | }
31 |
32 | /**
33 | * Starts traversing the tree, calling each visitor on each node.
34 | *
35 | * @param NodeInterface $node
36 | */
37 | public function walk(NodeInterface $node): void
38 | {
39 | $node->walk($this->visitor);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/Traverser/TraverserException.php:
--------------------------------------------------------------------------------
1 |
9 | * and David Négrier
10 | *
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without modification,
14 | * are permitted provided that the following conditions are met:
15 | *
16 | * * Redistributions of source code must retain the above copyright notice,
17 | * this list of conditions and the following disclaimer.
18 | * * Redistributions in binary form must reproduce the above copyright notice,
19 | * this list of conditions and the following disclaimer in the documentation
20 | * and/or other materials provided with the distribution.
21 | *
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 | * DAMAGE.
32 | */
33 | namespace SQLParser\Node;
34 |
35 | use Doctrine\DBAL\Platforms\AbstractPlatform;
36 |
37 | /**
38 | * This class represents a parameter (as in parameterized query).
39 | *
40 | * @author David MAECHLER
41 | */
42 | class UnquotedParameter extends Parameter
43 | {
44 | /**
45 | * Renders the object as a SQL string without quote if its a numeric.
46 | *
47 | * @param array $parameters
48 | * @param AbstractPlatform $platform
49 | * @param int $indent
50 | * @param int $conditionsMode
51 | *
52 | * @param bool $extrapolateParameters
53 | * @return string
54 | */
55 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
56 | {
57 | $name = parent::toSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters);
58 | $name = str_replace("'", '', $name ?? '');
59 |
60 | return $name;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/WhenConditions.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class WhenConditions extends AbstractManyInstancesOperator
13 | {
14 | /** @var NodeInterface|NodeInterface[]|string */
15 | private $value;
16 |
17 | /**
18 | * @return NodeInterface|NodeInterface[]|string
19 | */
20 | public function getValue()
21 | {
22 | return $this->value;
23 | }
24 |
25 | /**
26 | * Sets the value.
27 | *
28 | * @Important
29 | *
30 | * @param NodeInterface|NodeInterface[]|string $value
31 | */
32 | public function setValue($value): void
33 | {
34 | $this->value = $value;
35 | }
36 |
37 | /**
38 | * Renders the object as a SQL string.
39 | *
40 | * @param array $parameters
41 | * @param AbstractPlatform $platform
42 | * @param int $indent
43 | * @param int $conditionsMode
44 | *
45 | * @param bool $extrapolateParameters
46 | * @return string
47 | */
48 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
49 | {
50 | $fullSql = '';
51 |
52 | if ($this->value) {
53 | $fullSql = NodeFactory::toSql($this->value, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
54 | }
55 |
56 | foreach ($this->getOperands() as $operand) {
57 | $sql = NodeFactory::toSql($operand, $platform, $parameters, ' ', false, $indent, $conditionsMode, $extrapolateParameters);
58 | if ($sql != null) {
59 | $fullSql .= "\n".str_repeat(' ', $indent).'WHEN '.$sql;
60 | }
61 | }
62 |
63 | return $fullSql;
64 | }
65 |
66 | /**
67 | * Returns the symbol for this operator.
68 | *
69 | * @return string
70 | */
71 | protected function getOperatorSymbol(): string
72 | {
73 | return 'WHEN';
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/SQLParser/Node/XorOp.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class XorOp extends AbstractTwoOperandsOperator
11 | {
12 | /**
13 | * Returns the symbol for this operator.
14 | *
15 | * @return string
16 | */
17 | protected function getOperatorSymbol(): string
18 | {
19 | return 'XOR';
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/SQLParser/Query/StatementFactory.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class StatementFactory
16 | {
17 | /**
18 | * @return Select|Union
19 | * @throws MagicQueryException
20 | */
21 | public static function toObject(array $desc)
22 | {
23 | if (isset($desc['SELECT'])) {
24 | $select = new Select();
25 |
26 | $columns = array_map(function ($item) {
27 | return NodeFactory::toObject($item);
28 | }, $desc['SELECT']);
29 | $columns = NodeFactory::simplify($columns);
30 |
31 | $options = [];
32 | foreach ($columns as $key => $column) {
33 | if ($column instanceof Reserved) {
34 | if (strtoupper($column->getBaseExpression()) === 'DISTINCT') {
35 | $select->setDistinct(true);
36 | } else {
37 | $options[] = $column->getBaseExpression();
38 | }
39 | unset($columns[$key]);
40 | }
41 | }
42 | $select->setOptions($options);
43 |
44 | $select->setColumns($columns);
45 |
46 | if (isset($desc['FROM'])) {
47 | $from = NodeFactory::mapArrayToNodeObjectList($desc['FROM']);
48 | $select->setFrom($from);
49 | }
50 |
51 | if (isset($desc['WHERE'])) {
52 | $where = NodeFactory::mapArrayToNodeObjectList($desc['WHERE']);
53 | $where = NodeFactory::simplify($where);
54 | $select->setWhere($where);
55 | }
56 |
57 | if (isset($desc['GROUP'])) {
58 | $group = NodeFactory::mapArrayToNodeObjectList($desc['GROUP']);
59 | $group = NodeFactory::simplify($group);
60 | $select->setGroup($group);
61 | }
62 |
63 | if (isset($desc['HAVING'])) {
64 | $having = NodeFactory::mapArrayToNodeObjectList($desc['HAVING']);
65 | $having = NodeFactory::simplify($having);
66 | $select->setHaving($having);
67 | }
68 |
69 | if (isset($desc['ORDER'])) {
70 | $order = NodeFactory::mapArrayToNodeObjectList($desc['ORDER']);
71 | $order = NodeFactory::simplify($order);
72 | $select->setOrder($order);
73 | }
74 |
75 | if (isset($desc['LIMIT'])) {
76 | $descLimit = $desc['LIMIT'];
77 |
78 | //$limit = NodeFactory::toObject($descLimit['limit']);
79 | //$limit = NodeFactory::simplify($limit);
80 | if (isset($descLimit['rowcount'])) {
81 | $select->setLimit(NodeFactory::toLimitNode($descLimit['rowcount']));
82 | }
83 |
84 | if (isset($descLimit['offset'])) {
85 | $select->setOffset(NodeFactory::toLimitNode($descLimit['offset']));
86 | }
87 |
88 |
89 | //$offset = NodeFactory::toObject($descLimit['offset']);
90 | //$offset = NodeFactory::simplify($offset);
91 | //$select->setOffset($offset);
92 | }
93 |
94 | return $select;
95 | }
96 | // UNION and UNION DISTINCT have similar behavior
97 | if (isset($desc['UNION']) || isset($desc['UNION ALL']) || isset($desc['UNION DISTINCT'])) {
98 | $isUnionAll = isset($desc['UNION ALL']);
99 | $unionStatement = $desc['UNION'] ?? ($desc['UNION ALL'] ?? $desc['UNION DISTINCT']);
100 |
101 | /** @var Select[] $selects */
102 | $selects = array_map([self::class, 'toObject'], $unionStatement);
103 |
104 | $union = new Union($selects, $isUnionAll);
105 |
106 | if (isset($desc['0']) && isset($desc['0']['ORDER'])) {
107 | $order = NodeFactory::mapArrayToNodeObjectList($desc['0']['ORDER']);
108 | $order = NodeFactory::simplify($order);
109 | $union->setOrder($order);
110 | }
111 |
112 | return $union;
113 | }
114 |
115 | throw new \BadMethodCallException('Unknown query');
116 | }
117 |
118 | /**
119 | * @param array $descLimit
120 | *
121 | * @return array
122 | *
123 | * @throws \Exception
124 | */
125 | /*private static function checkLimitDesc(array $descLimit)
126 | {
127 | if (count($descLimit) > 2) {
128 | throw new \Exception('The limit returned by the SQLParser contains more than 2 items, something might went wrong.');
129 | }
130 |
131 | return ['offset' => $descLimit['offset'], 'limit' => $descLimit['rowcount']];
132 | }*/
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/src/SQLParser/Query/StatementInterface.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | interface StatementInterface extends SqlRenderInterface
13 | {
14 | }
15 |
--------------------------------------------------------------------------------
/src/SQLParser/Query/Union.php:
--------------------------------------------------------------------------------
1 | UNION query. You can use it to generate a SQL query statement
16 | * using the toSql method.
17 | * You can use the QueryResult class if you want to run the query directly.
18 | *
19 | * @author David Négrier
20 | */
21 | class Union implements StatementInterface, NodeInterface
22 | {
23 | /**
24 | * @var array|Select[]
25 | */
26 | private $selects;
27 |
28 | /**
29 | * @var bool
30 | */
31 | private $isUnionAll;
32 |
33 | /**
34 | * Union constructor.
35 | * @param Select[] $selects
36 | */
37 | public function __construct(array $selects, bool $isUnionAll)
38 | {
39 | $this->selects = $selects;
40 | $this->isUnionAll = $isUnionAll;
41 | }
42 |
43 | /** @var NodeInterface[]|NodeInterface */
44 | private $order;
45 |
46 | /**
47 | * Returns the list of order statements.
48 | *
49 | * @return NodeInterface[]|NodeInterface
50 | */
51 | public function getOrder()
52 | {
53 | return $this->order;
54 | }
55 |
56 | /**
57 | * Sets the list of order statements.
58 | *
59 | * @param NodeInterface[]|NodeInterface $order
60 | */
61 | public function setOrder($order): void
62 | {
63 | $this->order = $order;
64 | }
65 |
66 | /**
67 | * @param MoufManager $moufManager
68 | *
69 | * @return MoufInstanceDescriptor
70 | */
71 | public function toInstanceDescriptor(MoufManager $moufManager)
72 | {
73 | $instanceDescriptor = $moufManager->createInstance(get_called_class());
74 | $instanceDescriptor->getProperty('selects')->setValue(NodeFactory::nodeToInstanceDescriptor($this->selects, $moufManager));
75 | $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
76 |
77 | return $instanceDescriptor;
78 | }
79 |
80 | /**
81 | * Configure the $instanceDescriptor describing this object (it must already exist as a Mouf instance).
82 | *
83 | * @param MoufManager $moufManager
84 | *
85 | * @return MoufInstanceDescriptor
86 | */
87 | public function overwriteInstanceDescriptor($name, MoufManager $moufManager)
88 | {
89 | //$name = $moufManager->findInstanceName($this);
90 | $instanceDescriptor = $moufManager->getInstanceDescriptor($name);
91 | $instanceDescriptor->getProperty('selects')->setValue(NodeFactory::nodeToInstanceDescriptor($this->selects, $moufManager));
92 | $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
93 |
94 | return $instanceDescriptor;
95 | }
96 |
97 | /**
98 | * Renders the object as a SQL string.
99 | *
100 | * @param array $parameters
101 | * @param AbstractPlatform $platform
102 | * @param int $indent
103 | * @param int $conditionsMode
104 | *
105 | * @param bool $extrapolateParameters
106 | * @return string
107 | */
108 | public function toSql(array $parameters, AbstractPlatform $platform, int $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true): ?string
109 | {
110 | $selectsSql = array_map(function(Select $select) use ($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters) {
111 | return $select->toSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters);
112 | }, $this->selects);
113 |
114 | $unionStatement = $this->isUnionAll ? 'UNION ALL' : 'UNION';
115 |
116 | $sql = '(' . implode(') ' . $unionStatement . ' (', $selectsSql) . ')';
117 |
118 | if (!empty($this->order)) {
119 | $order = NodeFactory::toSql($this->order, $platform, $parameters, ',', false, $indent + 2, $conditionsMode, $extrapolateParameters);
120 | if ($order) {
121 | $sql .= "\nORDER BY ".$order;
122 | }
123 | }
124 |
125 | return $sql;
126 | }
127 |
128 | /**
129 | * Walks the tree of nodes, calling the visitor passed in parameter.
130 | *
131 | * @param VisitorInterface $visitor
132 | */
133 | public function walk(VisitorInterface $visitor)
134 | {
135 | $node = $this;
136 | $result = $visitor->enterNode($node);
137 | if ($result instanceof NodeInterface) {
138 | $node = $result;
139 | }
140 | if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
141 | $this->walkChildren($this->selects, $visitor);
142 | $this->walkChildren($this->order, $visitor);
143 | }
144 |
145 | return $visitor->leaveNode($node);
146 | }
147 |
148 | /**
149 | * @param array