Skip to content

Commit

Permalink
Doctrine/lexer:2.1 allowed
Browse files Browse the repository at this point in the history
Doctrine/lexer:1 no-more allowed
  • Loading branch information
Alexandre-T committed Mar 14, 2024
1 parent a252152 commit 44cb5d4
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 221 deletions.
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
],
"license": "MIT",
"require": {
"php": "^8.1",
"doctrine/lexer": "^1.0"
"php": "^8.1",
"doctrine/lexer": "^2.1"
},
"require-dev": {
"phpunit/phpunit": "~10.0"
"phpunit/phpunit": "~10.0"
},
"autoload": {
"psr-0": {
Expand Down
2 changes: 2 additions & 0 deletions lib/LongitudeOne/Geo/String/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
*
* @author Derek J. Lambert <[email protected]>
* @license http://dlambert.mit-license.org MIT
*
* @extends AbstractLexer<int, int|float|string>
*/
class Lexer extends AbstractLexer
{
Expand Down
90 changes: 39 additions & 51 deletions lib/LongitudeOne/Geo/String/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace LongitudeOne\Geo\String;

use Doctrine\Common\Lexer\Token;
use LongitudeOne\Geo\String\Exception\RangeException;
use LongitudeOne\Geo\String\Exception\UnexpectedValueException;

Expand Down Expand Up @@ -84,22 +85,19 @@ public function parse($input = null)
/**
* Match cardinal direction and return sign.
*
* @param int|float $value
*
* @return int|float
*
* @throws RangeException
*/
private function cardinal($value)
private function cardinal(int|float $value): int|float
{
// If cardinal direction was not on previous coordinate it can be anything
// If the cardinal direction was not on the previous coordinate, it can be anything
if (null === $this->nextCardinal) {
$this->nextCardinal = Lexer::T_CARDINAL_LON === $this->lexer->lookahead['type'] ? Lexer::T_CARDINAL_LON : Lexer::T_CARDINAL_LAT;
$this->nextCardinal = Lexer::T_CARDINAL_LON === $this->lexer->lookahead?->type ? Lexer::T_CARDINAL_LON : Lexer::T_CARDINAL_LAT;
}

// Match cardinal direction
// Match the cardinal direction
/** @var string $cardinal N, W, S, E, or n, w, s, e */
$cardinal = $this->match($this->nextCardinal);
// By default don't change sign
// By default, don't change sign
$sign = 1;
// Define value range
$range = 0;
Expand Down Expand Up @@ -138,15 +136,13 @@ private function cardinal($value)

/**
* Match and return single coordinate value.
*
* @return float|int
*/
private function coordinate()
private function coordinate(): float|int
{
// By default don't change sign
// By default, don't change sign
$sign = false;

// Match sign if cardinal direction has not been seen
// Match sign if the cardinal direction has not been seen
if (!($this->nextCardinal > 0) && $this->lexer->isNextTokenAny([Lexer::T_PLUS, Lexer::T_MINUS])) {
$sign = $this->sign();
}
Expand All @@ -168,38 +164,37 @@ private function coordinate()
}

/**
* Match and return degrees value.
*
* @return float|int
* Match and return degree value.
*/
private function degrees()
private function degrees(): float|int
{
// Reset symbol requirement
if (Lexer::T_APOSTROPHE === $this->nextSymbol || Lexer::T_QUOTE === $this->nextSymbol) {
$this->nextSymbol = Lexer::T_DEGREE;
}

// If degrees is a float there will be no minutes or seconds
// If degrees is a float, there will be no minutes or seconds
if ($this->lexer->isNextToken(Lexer::T_FLOAT)) {
// Get degree value
/** @var float $degrees */
$degrees = $this->match(Lexer::T_FLOAT);

// Degree float values may be followed by degree symbol
// Degree symbol may follow degree float values
if ($this->lexer->isNextToken(Lexer::T_DEGREE)) {
$this->match(Lexer::T_DEGREE);

// Set symbol requirement for next value in pair
$this->nextSymbol = Lexer::T_DEGREE;
}

// Return value
// Return the float value
return $degrees;
}

// If degrees isn't a float it must be an integer
// If degrees isn't a float, it must be an integer
$degrees = $this->number();

// If integer is not followed by a symbol this value is complete
// If a symbol does not follow integer, this value is complete
if (!$this->symbol()) {
return $degrees;
}
Expand All @@ -221,45 +216,43 @@ private function degrees()

/**
* Match token and return value.
*
* @param int $token
*
* @throws UnexpectedValueException
*/
private function match($token)
private function match(int $token): string|float|int
{
// If next token isn't type specified throw error
// If the next token isn't type specified throw error
if (!$this->lexer->isNextToken($token)) {
throw $this->syntaxError($this->lexer->getLiteral($token));
}

// Move lexer to next token
// Move lexer to the next token
$this->lexer->moveNext();

// Return the token value
return $this->lexer->token['value'];
/** @var Token<int, string|int|float> $nextToken nextToken cannot be null, because of the above test. */
$nextToken = $this->lexer->token;

// FIXME We currently return string|int|float and this is not good. We should return only string|int.
return $nextToken->value;
}

/**
* Match and return minutes value.
*
* @return float|int
*
* @throws RangeException
*/
private function minutes()
private function minutes(): float|int
{
// If using colon or minutes is an integer parse value
if (Lexer::T_COLON === $this->nextSymbol || $this->lexer->isNextToken(Lexer::T_INTEGER)) {
$minutes = $this->match(Lexer::T_INTEGER);
/** @var int $readMinutes */
$readMinutes = $this->match(Lexer::T_INTEGER);

// Throw exception if minutes are greater than 60
if ($minutes > 60) {
if ($readMinutes > 60) {
throw $this->rangeError('Minutes', 60);
}

// Get fractional minutes
$minutes = $minutes / 60;
$minutes = $readMinutes / 60;

// If using colon and one doesn't follow value is done
if (Lexer::T_COLON === $this->nextSymbol && !$this->lexer->isNextToken(Lexer::T_COLON)) {
Expand All @@ -278,6 +271,7 @@ private function minutes()

// If minutes is a float there will be no seconds
if ($this->lexer->isNextToken(Lexer::T_FLOAT)) {
/** @var float $minutes */
$minutes = $this->match(Lexer::T_FLOAT);

// Throw exception if minutes are greater than 60
Expand All @@ -302,18 +296,16 @@ private function minutes()
/**
* Match integer or float token and return value.
*
* @return int|float
*
* @throws UnexpectedValueException
*/
private function number()
private function number(): int|float
{
// If next token is a float match and return it
// If the next token is a float match, then return it
if ($this->lexer->isNextToken(Lexer::T_FLOAT)) {
return $this->match(Lexer::T_FLOAT);
}

// If next token is an integer match and return it
// If the next token is an integer match, then return it
if ($this->lexer->isNextToken(Lexer::T_INTEGER)) {
return $this->match(Lexer::T_INTEGER);
}
Expand Down Expand Up @@ -477,21 +469,17 @@ private function symbol()
}

/**
* Create exception with descriptive error message.
*
* @param string $expected
*
* @return UnexpectedValueException
* Create exception with a descriptive error message.
*/
private function syntaxError($expected)
private function syntaxError(string $expected): UnexpectedValueException
{
$expected = sprintf('Expected %s, got', $expected);
$token = $this->lexer->lookahead;
$found = null === $this->lexer->lookahead ? 'end of string.' : sprintf('"%s"', $token['value']);
$found = null === $token ? 'end of string.' : sprintf('"%s"', $token->value);

$message = sprintf(
'[Syntax Error] line 0, col %d: Error: %s %s in value "%s"',
isset($token['position']) ? $token['position'] : '-1',
$token->position ?? -1,
$expected,
$found,
$this->input
Expand Down
23 changes: 9 additions & 14 deletions quality/php-stan/phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,42 +1,37 @@
parameters:
ignoreErrors:
-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:match\\(\\) has no return type specified\\.$#"
message: "#^Type float\\|int\\|string in generic type Doctrine\\\\Common\\\\Lexer\\\\AbstractLexer\\<int, float\\|int\\|string\\> in PHPDoc tag @extends is not subtype of template type V of int\\|string of class Doctrine\\\\Common\\\\Lexer\\\\AbstractLexer\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php
path: ../../lib/LongitudeOne/Geo/String/Lexer.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:number\\(\\) should return float\\|int but returns float\\|int\\|string\\.$#"
count: 2
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:point\\(\\) return type has no value type specified in iterable type array\\.$#"
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Offset 'type' does not exist on array\\{value\\: int\\|string, type\\: int\\|string\\|null, position\\: int\\}\\|null\\.$#"
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:point\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Offset 'value' does not exist on array\\{value\\: int\\|string, type\\: int\\|string\\|null, position\\: int\\}\\|null\\.$#"
count: 2
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Parameter \\#1 \\$expected of method LongitudeOne\\\\Geo\\\\String\\\\Parser\\:\\:syntaxError\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Tests\\\\LexerTest\\:\\:testLexer\\(\\) has parameter \\$expectedTokens with no value type specified in iterable type array\\.$#"
message: "#^Type float\\|int\\|string in generic type Doctrine\\\\Common\\\\Lexer\\\\Token\\<int, float\\|int\\|string\\> in PHPDoc tag @var for variable \\$nextToken is not subtype of template type V of int\\|string of class Doctrine\\\\Common\\\\Lexer\\\\Token\\.$#"
count: 1
path: ../../tests/LongitudeOne/Geo/String/Tests/LexerTest.php
path: ../../lib/LongitudeOne/Geo/String/Parser.php

-
message: "#^Parameter \\#1 \\$input of method Doctrine\\\\Common\\\\Lexer\\\\AbstractLexer\\:\\:setInput\\(\\) expects string, array\\<int, array\\<string, float\\|int\\|string\\>\\>\\|string given\\.$#"
message: "#^Method LongitudeOne\\\\Geo\\\\String\\\\Tests\\\\LexerTest\\:\\:dataProvider\\(\\) return type with generic class Doctrine\\\\Common\\\\Lexer\\\\Token does not specify its types\\: T, V$#"
count: 1
path: ../../tests/LongitudeOne/Geo/String/Tests/LexerTest.php

Expand Down
Loading

0 comments on commit 44cb5d4

Please sign in to comment.