• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

longitude-one / doctrine-spatial / 16351073931

17 Jul 2025 04:50PM UTC coverage: 95.775% (+0.2%) from 95.599%
16351073931

push

github

Alexandre-T
Fix/issue 123 and upgrade quality tools (#132)

* Add platform specification for PostGIS and MySQL services in docker-compose.yaml
* Fix README to use correct container name for PHP 8.3
* Fix installation commands in README to use 'composer install' instead of 'composer update'
* Update dependencies for PHP Code Sniffer, PHP CS Fixer, PHP Mess Detector, and PHPStan

- PHP Code Sniffer updated from version 3.11.2 to 3.13.2
- PHP CS Fixer updated from version v3.68.1 to v3.84.0
- PHP Mess Detector updated Symfony components to versions 6.4.22 and 6.4.23
- PHPStan updated from version 1.12.15 to 1.12.27 and phpstan-symfony from 1.4.13 to 1.4.15
- Various Symfony components updated to their latest versions, including deprecation contracts and polyfills
* Order of a few methods have changed because of phpcsfixer upgrades

1428 of 1491 relevant lines covered (95.77%)

35.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/lib/LongitudeOne/Spatial/ORM/Query/AST/Functions/AbstractSpatialDQLFunction.php
1
<?php
2
/**
3
 * This file is part of the doctrine spatial extension.
4
 *
5
 * PHP 8.1 | 8.2 | 8.3
6
 * Doctrine ORM 2.19 | 3.1
7
 *
8
 * Copyright Alexandre Tranchant <alexandre.tranchant@gmail.com> 2017-2025
9
 * Copyright Longitude One 2020-2025
10
 * Copyright 2015 Derek J. Lambert
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 *
15
 */
16

17
declare(strict_types=1);
18

19
namespace LongitudeOne\Spatial\ORM\Query\AST\Functions;
20

21
use Doctrine\DBAL\Exception;
22
use Doctrine\DBAL\Platforms\AbstractPlatform;
23
use Doctrine\Deprecations\Deprecation;
24
use Doctrine\ORM\Query\AST\ASTException;
25
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
26
use Doctrine\ORM\Query\AST\Node;
27
use Doctrine\ORM\Query\Parser;
28
use Doctrine\ORM\Query\QueryException;
29
use Doctrine\ORM\Query\SqlWalker;
30
use Doctrine\ORM\Query\TokenType;
31
use LongitudeOne\Spatial\Exception\InvalidValueException;
32
use LongitudeOne\Spatial\Exception\UnsupportedPlatformException;
33

34
/**
35
 * Abstract spatial DQL function.
36
 *
37
 * @author  Derek J. Lambert <dlambert@dereklambert.com>
38
 * @author  Alexandre Tranchant <alexandre.tranchant@gmail.com>
39
 * @license https://dlambert.mit-license.org MIT
40
 *
41
 * This spatial class is updated to avoid non-covered code. A lot of PostgreSQL functions were not tested,
42
 * but that was not displayed by coverage rapport. Some MySQL methods generate bug since MySQL 8.0 because their name
43
 * was updated.
44
 *
45
 * It is not possible to evaluate which function is tested or not with a children containing only protected methods.
46
 * The new pattern consists of create an abstract method for each removed property.
47
 * Then, if tests don't check function, the code coverage tools will report this information.
48
 *
49
 * Thus, if we analyze a platform version, we can implement the getFunctionName method to return geomfromtext for
50
 * MySQL Version 5.7 and return st_geomfromtext for version 8.0
51
 *
52
 * @see https://stackoverflow.com/questions/60377271/why-some-spatial-functions-does-not-exists-on-my-mysql-server
53
 */
54
abstract class AbstractSpatialDQLFunction extends FunctionNode
55
{
56
    /**
57
     * @var Node[]
58
     */
59
    private array $geometryExpression = [];
60

61
    /**
62
     * Get the SQL.
63
     *
64
     * @param SqlWalker $sqlWalker the SQL Walker
65
     *
66
     * @throws UnsupportedPlatformException when platform is unsupported
67
     * @throws Exception                    when an invalid platform was specified for this connection
68
     * @throws ASTException                 when node cannot dispatch SqlWalker
69
     */
70
    public function getSql(SqlWalker $sqlWalker): string
178✔
71
    {
72
        $this->validatePlatform($sqlWalker->getConnection()->getDatabasePlatform());
178✔
73

74
        $arguments = [];
178✔
75
        foreach ($this->getGeometryExpressions() as $expression) {
178✔
76
            $arguments[] = $expression->dispatch($sqlWalker);
178✔
77
        }
78

79
        return sprintf('%s(%s)', $this->getFunctionName(), implode(', ', $arguments));
178✔
80
    }
81

82
    /**
83
     * Parse SQL.
84
     *
85
     * @param Parser $parser parser
86
     *
87
     * @throws QueryException Query exception
88
     */
89
    public function parse(Parser $parser): void
178✔
90
    {
91
        $lexer = $parser->getLexer();
178✔
92

93
        $parser->match(TokenType::T_IDENTIFIER);
178✔
94
        $parser->match(TokenType::T_OPEN_PARENTHESIS);
178✔
95

96
        $this->addGeometryExpression($parser->ArithmeticPrimary());
178✔
97

98
        while (count($this->geometryExpression) < $this->getMinParameter()
178✔
99
            || ((count($this->geometryExpression) < $this->getMaxParameter())
178✔
100
                && TokenType::T_CLOSE_PARENTHESIS != $lexer->lookahead?->type)
178✔
101
        ) {
102
            $parser->match(TokenType::T_COMMA);
111✔
103

104
            $this->addGeometryExpression($parser->ArithmeticPrimary());
111✔
105
        }
106

107
        $parser->match(TokenType::T_CLOSE_PARENTHESIS);
178✔
108
    }
109

110
    /**
111
     * Geometry expressions fluent adder.
112
     *
113
     * @param Node|string $expression the node expression to add to the array of geometry expression
114
     *
115
     * @since 2.0 This function replace the protected property geomExpr which is now private.
116
     *
117
     * @throws InvalidValueException when expression is a string
118
     */
119
    protected function addGeometryExpression(Node|string $expression): self
178✔
120
    {
121
        if (is_string($expression)) {
178✔
122
            throw new InvalidValueException('Expression must be a node.');
×
123
        }
124

125
        $this->geometryExpression[] = $expression;
178✔
126

127
        return $this;
178✔
128
    }
129

130
    /**
131
     * Get the deprecated platforms with this function.
132
     *
133
     * If a function is deprecated on a platform, it is possible to add this platform in this array.
134
     *
135
     * Example of implementation:
136
     * return [
137
     *      PostGreSQLPlatform::class => [
138
     *          'link' => 'http://github.com/longitude-one/doctrine-spatial/issues/42',
139
     *          'message' => 'The StSrid function is deprecated with PostGreSQL since longitude-one/doctrine-spatial. Use ScSrid instead.',
140
     *      ],
141
     * ];
142
     *
143
     * @see https://github.com/doctrine/deprecations?tab=readme-ov-file#usage-from-a-libraryproducer-perspective
144
     *
145
     * @return array<class-string<AbstractPlatform>, array{link: string, message: string}> an array where key is the platform and value an array of the arguments provided to the trigger method
146
     */
147
    protected function getDeprecatedPlatforms(): array
174✔
148
    {
149
        return [];
174✔
150
    }
151

152
    /**
153
     * Geometry expressions getter.
154
     *
155
     * @since 2.0 This function replace the protected property geomExpr which is now private.
156
     *
157
     * @return Node[]
158
     */
159
    final protected function getGeometryExpressions(): array
178✔
160
    {
161
        return $this->geometryExpression;
178✔
162
    }
163

164
    /**
165
     * Check that the current platform supports current spatial function.
166
     *
167
     * TODO when support for 8.1 will be dropped, this method will only return true.
168
     *
169
     * @param AbstractPlatform $platform database spatial
170
     *
171
     * @return true if the current platform is supported
172
     *
173
     * @throws UnsupportedPlatformException when platform is unsupported
174
     */
175
    protected function validatePlatform(AbstractPlatform $platform): bool
178✔
176
    {
177
        foreach ($this->getDeprecatedPlatforms() as $deprecatedPlatform => $arguments) {
178✔
178
            if ($platform instanceof $deprecatedPlatform) {
10✔
179
                Deprecation::trigger(
3✔
180
                    'longitude-one/doctrine-spatial',
3✔
181
                    $arguments['link'],
3✔
182
                    $arguments['message']
3✔
183
                );
3✔
184
            }
185
        }
186

187
        foreach ($this->getPlatforms() as $acceptedPlatform) {
178✔
188
            if ($platform instanceof $acceptedPlatform) {
178✔
189
                return true;
178✔
190
            }
191
        }
192

193
        throw new UnsupportedPlatformException(
×
194
            sprintf('DBAL platform "%s" is not currently supported.', $platform::class)
×
195
        );
×
196
    }
197

198
    /**
199
     * Function SQL name getter.
200
     *
201
     * @since 2.0 This function replace the protected property functionName.
202
     */
203
    abstract protected function getFunctionName(): string;
204

205
    /**
206
     * Maximum number of parameters for the spatial function.
207
     *
208
     * @since 2.0 This function replace the protected property maxGeomExpr.
209
     *
210
     * @return int the inherited methods shall NOT return a null, but 0 when the function has no parameter
211
     */
212
    abstract protected function getMaxParameter(): int;
213

214
    /**
215
     * Minimum number of parameters for the spatial function.
216
     *
217
     * @since 2.0 This function replace the protected property minGeomExpr.
218
     *
219
     * @return int the inherited methods shall NOT return a null, but 0 when the function has no parameter
220
     */
221
    abstract protected function getMinParameter(): int;
222

223
    /**
224
     * Get the platforms accepted.
225
     *
226
     * The AbstractPlatform::getName() method is now deprecated in the doctrine/dbal component.
227
     * We now use the class name to identify the platform.
228
     *
229
     * @see https://github.com/doctrine/dbal/issues/4749
230
     * @see https://github.com/longitude-one/doctrine-spatial/issues/40
231
     *
232
     * @return class-string<AbstractPlatform>[] a non-empty array of accepted platforms
233
     */
234
    abstract protected function getPlatforms(): array;
235
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc