├── .gitignore ├── phpstan.neon ├── cs-fixer.sh ├── phpstan.sh ├── docker-compose.yaml ├── phpunit.xml ├── src ├── exceptions │ ├── SettingPointException.php │ ├── NotEnoughPointsException.php │ └── FirstAndLastPointNotEqualException.php ├── Line.php ├── Point.php └── Polygon.php ├── .php-cs-fixer.dist.php ├── composer.json ├── Dockerfile ├── LICENSE ├── CONTRIBUTING.md ├── README.md ├── tests ├── LineTest.php ├── PointTest.php └── PolygonTest.php └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | .phpunit.result.cache 4 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 9 3 | paths: 4 | - src 5 | - tests 6 | phpVersion: 80000 -------------------------------------------------------------------------------- /cs-fixer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo docker run --rm --name geophp8-cs-fixer -v $(pwd):/data ghcr.io/php-cs-fixer/php-cs-fixer:3-php8.1 fix /data -------------------------------------------------------------------------------- /phpstan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exec sudo docker run --rm -v "$(pwd)":/app ghcr.io/phpstan/phpstan:latest-php8.3 analyse --configuration=/app/phpstan.neon 3 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | geophp: 5 | build: 6 | context: . 7 | image: custom-php 8 | volumes: 9 | - ./:/home/devuser/geophp/ 10 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/ 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/exceptions/SettingPointException.php: -------------------------------------------------------------------------------- 1 | in(__DIR__."/src") 5 | ->in(__DIR__."/tests"); 6 | 7 | return (new PhpCsFixer\Config()) 8 | ->setRules([ 9 | '@PHP81Migration' => true, 10 | '@PhpCsFixer' => true, 11 | 'array_syntax' => [ 12 | 'syntax' => 'short', 13 | ], 14 | ]) 15 | ->setFinder($finder); 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loveyu/geophp8", 3 | "description": "A little geo spatial library for PHP based on Geo Rust and Turf.js", 4 | "type": "library", 5 | "license": "MIT", 6 | "version": "2.0.0", 7 | "authors": [ 8 | { 9 | "name": "JWare", 10 | "email": "jware.organization@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=8.1" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "JWare\\GeoPHP\\": "src/", 19 | "JWare\\GeoPHP\\Exceptions\\": "src/exceptions/" 20 | } 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "^10.5.39" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-bookworm 2 | 3 | # Installs dependences 4 | RUN apt update \ 5 | && apt install -y git 6 | 7 | # Create non root user 8 | RUN useradd -ms /bin/bash devuser 9 | 10 | USER devuser 11 | WORKDIR /home/devuser 12 | 13 | # Installs Composer 14 | RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ 15 | && php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \ 16 | && php composer-setup.php \ 17 | && php -r "unlink('composer-setup.php');" 18 | 19 | # Allows to run 'source' command 20 | SHELL ["/bin/bash", "-c"] 21 | 22 | # Adds composer to .bashrc 23 | RUN echo 'export PATH=$PATH:/home/devuser/' >> ~/.bashrc \ 24 | && echo 'alias composer="composer.phar" ' >> ~/.bashrc \ 25 | && source ~/.bashrc \ 26 | && chown -R devuser ~/.composer/ 27 | 28 | # Is positioned in the main folder 29 | WORKDIR /home/devuser/geophp 30 | 31 | CMD [ "bash" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 JWare Solutions 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contibuting 2 | 3 | It's nice to have people like you interested in this project. In this document we specify all the things to be considered for developing in this library. 4 | 5 | ## Preparing the environment 6 | 7 | ### With Docker 8 | 9 | You have available a **Docker Compose** file with a custom image with all installed if It's of your preference. Just run inside the project's root folder: 10 | 11 | `docker-compose run geophp` 12 | 13 | The image specified in `Dockerfile` file will be compiled and run. 14 | 15 | ### Without Docker 16 | 17 | You need the following tools installed: 18 | 19 | - PHP 8.x 20 | - [Composer](https://getcomposer.org/) 21 | 22 | 23 | ## Developing 24 | 25 | All the code and test are at `src` and `test` folders respectively. 26 | 27 | There is no standard convention about code and comments, but It would really appreciate that you respect the convention used until now, specifying docs for all the methods indicating types and description for their parameters and returned values. 28 | 29 | All the public method (except some getters and setters) are covered with unit test. Once you have added some code and want to run the tests you can execute [PHPUnit](https://phpunit.de/) with the following command: 30 | 31 | `./vendor/bin/phpunit` 32 | 33 | To check that all is OK. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoPHP 2 | 3 | GeoPHP is a library of geospatial functions based on GeoRust and Turf.js. 4 | 5 | ## Installation 6 | 7 | Install the library running: 8 | 9 | `composer require loveyu/geophp8` 10 | 11 | ## Usage 12 | 13 | ``` 14 | use \JWare\GeoPHP\Polygon; 15 | use \JWare\GeoPHP\Point; 16 | 17 | $polygon = new Polygon([ 18 | new Point(1, -1), 19 | new Point(2, 1), 20 | new Point(3, -1), 21 | new Point(2, -2), 22 | new Point(1, -1) 23 | ]); 24 | 25 | $point = new Point(-1, 2); 26 | $point2 = new Point(2, 2); 27 | 28 | $polygon->containsPoint($point); // False 29 | $polygon->containsPoint($point2); // True 30 | ``` 31 | 32 | Please read the [documentation][documentation_site] to see the full method list available. 33 | 34 | ## Why another Geospatial library? 35 | 36 | GeoPHP was born due to the lack of a modern geospatial library for PHP. The tools available today are not published in the Composer repositories and are not easy to use (some requires DB drivers or constructs the Geometries from complex Strings). GeoPHP offers: 37 | 38 | - 🚀 Modern and fast: made in PHP 7 with fast algorithms 39 | - 🥳 Friendly and simple interface: implemented with the Object Oriented Paradigm, allows its easy and fast use by the developer. 40 | - 👨🏼‍💻 Easy access: the library is published in Composer for quick import into your projects. 41 | - 🛠 Tested: all methods are covered by unit tests. 42 | - 🌟 A lot of examples! 43 | 44 | ## Contributing 45 | 46 | First of all, thanks for consider contributing to the project! Please, read [CONTRIBUTING.md](/CONTRIBUTING.md) for more. 47 | 48 | [documentation_site]: https://jware-solutions.github.io/geophp-documentation/ "Full documentation" -------------------------------------------------------------------------------- /tests/LineTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('\JWare\GeoPHP\Line', new Line( 18 | new Point(0, 0), 19 | new Point(1, 1) 20 | )); 21 | } 22 | 23 | /** 24 | * Tests clone method. 25 | */ 26 | public function testClone(): void 27 | { 28 | $line = new Line( 29 | new Point(0, 0), 30 | new Point(1, 1) 31 | ); 32 | $clone = $line->clone(); 33 | $this->assertEquals($clone->getPoints(), $line->getPoints()); 34 | } 35 | 36 | /** 37 | * Tests the determinant of the line. 38 | */ 39 | public function testDeterminant(): void 40 | { 41 | $line1 = new Line( 42 | new Point(1.34, 2.87), 43 | new Point(3.23, 4.5) 44 | ); 45 | 46 | $line2 = new Line( 47 | new Point(3.23, 4.5), 48 | new Point(1.34, 2.87) 49 | ); 50 | 51 | $this->assertEquals('-3.2401', (string) $line1->determinant()); 52 | 53 | // NOTE: Line(a, b).determinant() == -Line(b, a).determinant() 54 | $this->assertEquals($line1->determinant(), -$line2->determinant()); 55 | } 56 | 57 | /** 58 | * Tests the difference in 'x' components (Δx). 59 | */ 60 | public function testDx(): void 61 | { 62 | $line1 = new Line( 63 | new Point(2.77, 4.23), 64 | new Point(2.77, 7.23) 65 | ); 66 | 67 | $line2 = new Line( 68 | new Point(2.77, 4.23), 69 | new Point(3.77, 7.23) 70 | ); 71 | 72 | $this->assertEquals(0, $line1->dx()); 73 | 74 | $this->assertEquals(1, $line2->dx()); 75 | } 76 | 77 | /** 78 | * Tests the difference in 'y' components (Δy). 79 | */ 80 | public function testDy(): void 81 | { 82 | $line1 = new Line( 83 | new Point(2.77, 4.23), 84 | new Point(2.77, 7.23) 85 | ); 86 | 87 | $line2 = new Line( 88 | new Point(2.77, 14.32), 89 | new Point(3.77, 9.55) 90 | ); 91 | 92 | $this->assertEquals(3, $line1->dy()); 93 | 94 | $this->assertEquals(-4.77, $line2->dy()); 95 | } 96 | 97 | /** 98 | * Tests the slope of a line. 99 | */ 100 | public function testSlope(): void 101 | { 102 | $line1 = new Line( 103 | new Point(2.87, 4.23), 104 | new Point(2.77, 7) 105 | ); 106 | 107 | $line2 = new Line( 108 | new Point(3.45, 11.3), 109 | new Point(0.87, 34.243) 110 | ); 111 | 112 | $line3 = new Line( 113 | new Point(0.87, 34.243), 114 | new Point(3.45, 11.3) 115 | ); 116 | 117 | $line4 = new Line( 118 | new Point(2, 34), 119 | new Point(2, 11) 120 | ); 121 | 122 | $this->assertEquals('-27.7', (string) $line1->slope()); 123 | $this->assertEquals('-8.8926356589147', (string) $line2->slope()); 124 | 125 | // NOTE: Line(a, b).slope() == Line(b, a).slope() 126 | $this->assertEquals($line2->slope(), $line3->slope()); 127 | $this->assertEquals(INF, $line4->slope()); 128 | } 129 | 130 | /** 131 | * Test intersection method. 132 | */ 133 | public function testIntersects(): void 134 | { 135 | // Lines 136 | $line1 = new Line( 137 | new Point(1, 1), 138 | new Point(5, 5) 139 | ); 140 | 141 | $line2 = new Line( 142 | new Point(3, 3), 143 | new Point(5, 1) 144 | ); 145 | 146 | $line3 = new Line( 147 | new Point(-1, 1), 148 | new Point(-5, -10) 149 | ); 150 | 151 | // Points 152 | $point1 = new Point(3, 3); 153 | $point2 = new Point(-1, 1); 154 | $point3 = new Point(-5, -10); 155 | 156 | // Polygons 157 | $polygon1 = new Polygon([ 158 | new Point(2, 4), 159 | new Point(4, 4), 160 | new Point(4, 2), 161 | new Point(3, 1), 162 | new Point(2.5, 3), 163 | new Point(2, 4), 164 | ]); 165 | 166 | $polygon2 = new Polygon([ 167 | new Point(-3, -4), 168 | new Point(-1, -5), 169 | new Point(-2, -6), 170 | new Point(-3, -4), 171 | ]); 172 | 173 | // With Point 174 | $this->assertTrue($line1->intersectsPoint($point1)); 175 | $this->assertTrue($line2->intersectsPoint($point1)); 176 | $this->assertFalse($line3->intersectsPoint($point1)); 177 | $this->assertTrue($line3->intersectsPoint($point2)); 178 | $this->assertTrue($line3->intersectsPoint($point3)); 179 | 180 | // With Line 181 | $this->assertTrue($line1->intersectsLine($line2)); 182 | $this->assertTrue($line2->intersectsLine($line1)); 183 | $this->assertTrue($line1->intersectsLine($line1)); 184 | $this->assertFalse($line1->intersectsLine($line3)); 185 | $this->assertFalse($line2->intersectsLine($line3)); 186 | $this->assertFalse($line3->intersectsLine($line2)); 187 | 188 | // With Polygon 189 | $this->assertTrue($line1->intersectsPolygon($polygon1)); 190 | $this->assertTrue($line2->intersectsPolygon($polygon1)); 191 | $this->assertTrue($line3->intersectsPolygon($polygon2)); 192 | $this->assertFalse($line1->intersectsPolygon($polygon2)); 193 | $this->assertFalse($line2->intersectsPolygon($polygon2)); 194 | $this->assertFalse($line3->intersectsPolygon($polygon1)); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/Line.php: -------------------------------------------------------------------------------- 1 | start = $start; 16 | $this->end = $end; 17 | } 18 | 19 | /** 20 | * Clone method. 21 | * 22 | * @return Line New instance 23 | */ 24 | public function clone(): Line 25 | { 26 | return new Line($this->start, $this->end); 27 | } 28 | 29 | /** 30 | * Getter of the starting point of the line. 31 | * 32 | * @return Point starting point of the line 33 | */ 34 | public function getStart(): Point 35 | { 36 | return $this->start; 37 | } 38 | 39 | /** 40 | * Setter of the starting point of the line. 41 | * 42 | * @param Point $start starting point of the line 43 | * 44 | * @return Line Current instance 45 | */ 46 | public function setStart(Point $start): Line 47 | { 48 | $this->start = $start; 49 | 50 | return $this; 51 | } 52 | 53 | /** 54 | * Getter of the ending point of the line. 55 | * 56 | * @return Point ending point of the line 57 | */ 58 | public function getEnd(): Point 59 | { 60 | return $this->end; 61 | } 62 | 63 | /** 64 | * Setter of the ending point of the line. 65 | * 66 | * @param Point $end ending point of the line 67 | * 68 | * @return Line Instance 69 | */ 70 | public function setEnd(Point $end): Line 71 | { 72 | $this->end = $end; 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * Computes the determinant of the line: 79 | * line.start.x * line.end.y - line.start.y * line.end.x. 80 | * 81 | * @return float The determinant of the line 82 | */ 83 | public function determinant(): float 84 | { 85 | return $this->start->getX() * $this->end->getY() - $this->start->getY() * $this->end->getX(); 86 | } 87 | 88 | /** 89 | * Computes the difference in 'x' components (Δx): 90 | * line.end.x - line.start.x. 91 | * 92 | * @return float The difference in 'x' components (Δx) 93 | */ 94 | public function dx(): float 95 | { 96 | return $this->end->getX() - $this->start->getX(); 97 | } 98 | 99 | /** 100 | * Computes the difference in 'y' components (Δy): 101 | * line.end.y - line.start.y. 102 | * 103 | * @return float The difference in 'y' components (Δy) 104 | */ 105 | public function dy(): float 106 | { 107 | return $this->end->getY() - $this->start->getY(); 108 | } 109 | 110 | /** 111 | * Computes the slope of a line: 112 | * line.dy() / line.dx(). 113 | * 114 | * @return float The slope of a line 115 | */ 116 | public function slope(): float 117 | { 118 | $dx = $this->dx(); 119 | 120 | return (0 != $dx) ? $this->dy() / $dx : INF; 121 | } 122 | 123 | /** 124 | * Getter for starting and ending points. 125 | * 126 | * @return Point[] Array with the starting point (0) and the ending point (1) 127 | */ 128 | public function getPoints(): array 129 | { 130 | return [$this->start, $this->end]; 131 | } 132 | 133 | /** 134 | * Checks whether the line intersects a point. 135 | * 136 | * @param Point $point Point to check 137 | * 138 | * @return bool True if the line intersects with the point, false otherwise 139 | */ 140 | public function intersectsPoint(Point $point): bool 141 | { 142 | $dx = $this->dx(); 143 | $tx = (0 != $dx) ? ($point->getX() - $this->start->getX()) / $this->dx() : null; 144 | 145 | $dy = $this->dy(); 146 | $ty = (0 != $dy) ? ($point->getY() - $this->start->getY()) / $this->dy() : null; 147 | 148 | $txIsNull = is_null($tx); 149 | $tyIsNull = is_null($ty); 150 | 151 | // If I don't have $tx nor $ty 152 | if ($txIsNull && $tyIsNull) { 153 | return $point->isEqual($this->start); 154 | } 155 | 156 | // If I have $tx 157 | if (!$txIsNull && $tyIsNull) { 158 | return $point->getY() == $this->start->getY() && 0 <= $tx && $tx <= 1; 159 | } 160 | 161 | // If I have $ty 162 | if ($txIsNull && !$tyIsNull) { 163 | return $point->getX() == $this->start->getX() && 0 <= $ty && $ty <= 1; 164 | } 165 | 166 | // If I have $tx and $ty... 167 | return abs($tx - $ty) <= 0.000001 && 0 <= $tx && $tx <= 1; 168 | } 169 | 170 | /** 171 | * Checks whether the line intersects another line. 172 | * 173 | * @param Line $line Line to check 174 | * 175 | * @return bool True if the line intersects with the point, false otherwise 176 | */ 177 | public function intersectsLine(Line $line): bool 178 | { 179 | // Using Cramer's Rule: 180 | // https://en.wikipedia.org/wiki/Intersection_%28Euclidean_geometry%29#Two_line_segments 181 | $a1 = $this->dx(); 182 | $a2 = $this->dy(); 183 | $b1 = -$line->dx(); 184 | $b2 = -$line->dy(); 185 | $c1 = $line->start->getX() - $this->start->getX(); 186 | $c2 = $line->start->getY() - $this->start->getY(); 187 | 188 | $d = $a1 * $b2 - $a2 * $b1; 189 | if (!$d) { 190 | $thisPoints = $this->getPoints(); 191 | $selfStartPoint = $thisPoints[0]; 192 | $selfEndPoint = $thisPoints[1]; 193 | $otherPoints = $line->getPoints(); 194 | $otherStartPoint = $otherPoints[0]; 195 | $otherEndPoint = $otherPoints[1]; 196 | 197 | // Lines are parallel 198 | // Return true if at least one end point intersects the other line 199 | return $selfStartPoint->intersectsLine($line) 200 | || $selfEndPoint->intersectsLine($line) 201 | || $otherStartPoint->intersectsLine($this) 202 | || $otherEndPoint->intersectsLine($this); 203 | } 204 | 205 | $s = ($c1 * $b2 - $c2 * $b1) / $d; 206 | $t = ($a1 * $c2 - $a2 * $c1) / $d; 207 | 208 | return (0 <= $s) && ($s <= 1) && (0 <= $t) && ($t <= 1); 209 | } 210 | 211 | /** 212 | * Checks whether the line intersects a polygon. 213 | * 214 | * @param Polygon $polygon Polygon to check 215 | * 216 | * @return bool True if the line intersects with the polygon, false otherwise 217 | */ 218 | public function intersectsPolygon(Polygon $polygon): bool 219 | { 220 | return $polygon->intersectsLine($this); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /tests/PointTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('\JWare\GeoPHP\Point', new Point(0, 0)); 18 | } 19 | 20 | /** 21 | * Tests clone method. 22 | */ 23 | public function testClone(): void 24 | { 25 | $point = new Point(3, 4); 26 | $clone = $point->clone(); 27 | $this->assertEquals($clone->getXAndY(), $point->getXandY()); 28 | } 29 | 30 | /** 31 | * Tests X and Y getter. 32 | */ 33 | public function testGetXandY(): void 34 | { 35 | $point = new Point(3, 4); 36 | $this->assertEquals([3, 4], $point->getXandY()); 37 | } 38 | 39 | /** 40 | * Tests whether two points are the same. 41 | */ 42 | public function testIsEqual(): void 43 | { 44 | $point1 = new Point(1, 2); 45 | $point2 = new Point(1, 2); 46 | $point3 = new Point(3, 23.3); 47 | 48 | $this->assertTrue($point1->isEqual($point1)); 49 | $this->assertTrue($point1->isEqual($point2)); 50 | $this->assertTrue($point2->isEqual($point1)); 51 | $this->assertFalse($point2->isEqual($point3)); 52 | } 53 | 54 | /** 55 | * Test magnitude computation. 56 | */ 57 | public function testGetMagnitude(): void 58 | { 59 | $magnitude1 = (new Point(0, 0))->getMagnitude(); 60 | $this->assertEquals(0, $magnitude1); 61 | $magnitude2 = (new Point(1, 2))->getMagnitude(); 62 | $this->assertEquals(2.236067977, round($magnitude2, $precision = 9)); 63 | } 64 | 65 | /** 66 | * Test conversion from radians to degrees. 67 | */ 68 | public function fromRadiansToDegrees(): void 69 | { 70 | $point1 = new Point(1, 0); 71 | $point2 = new Point(2, 3); 72 | $point1Converted = $point1->fromRadiansToDegrees(); 73 | $point2Converted = $point2->fromRadiansToDegrees(); 74 | 75 | // Converted instances are also Points 76 | $this->assertInstanceOf('\JWare\GeoPHP\Point', $point1Converted); 77 | $this->assertInstanceOf('\JWare\GeoPHP\Point', $point2Converted); 78 | 79 | // Checks converted values 80 | $this->assertEquals(57.29578, $point1Converted->getX()); 81 | $this->assertEquals(0, $point1Converted->getY()); 82 | $this->assertEquals(114.5916, $point2Converted->getX()); 83 | $this->assertEquals(171.8873, $point2Converted->getY()); 84 | } 85 | 86 | /** 87 | * Test conversion from degrees to radians. 88 | */ 89 | public function fromDegreesToRadians(): void 90 | { 91 | $point1 = new Point(57.29578, 0); 92 | $point2 = new Point(114.5916, 171.8873); 93 | $point1Converted = $point1->fromDegreesToRadians(); 94 | $point2Converted = $point2->fromDegreesToRadians(); 95 | 96 | // Converted instances are also Points 97 | $this->assertInstanceOf('\JWare\GeoPHP\Point', $point1Converted); 98 | $this->assertInstanceOf('\JWare\GeoPHP\Point', $point2Converted); 99 | 100 | // Checks converted values 101 | $this->assertEquals(1, $point1Converted->getX()); 102 | $this->assertEquals(0, $point1Converted->getY()); 103 | $this->assertEquals(2, $point2Converted->getX()); 104 | $this->assertEquals(3, $point2Converted->getY()); 105 | } 106 | 107 | /** 108 | * Tests getting the angle between two points. 109 | */ 110 | public function testGetAngle(): void 111 | { 112 | $point1 = new Point(2, 0); 113 | $point2 = new Point(2, 2); 114 | $point3 = new Point(0, 2); 115 | 116 | $this->assertEquals('45', (string) $point2->getAngle($point3)); 117 | $this->assertEquals('45', (string) $point1->getAngle($point2)); 118 | $this->assertEquals('90', (string) $point1->getAngle($point3)); 119 | } 120 | 121 | /** 122 | * Tests the dot product of the two points: 123 | * dot = x1 * x2 + y1 * y2. 124 | */ 125 | public function testDotProduct(): void 126 | { 127 | $point1 = new Point(2, 4.5); 128 | $dot = $point1->dotProduct(new Point(1.5, 0.5)); 129 | $this->assertEquals(5.25, $dot); 130 | } 131 | 132 | /** 133 | * Tests the cross product of three point. 134 | */ 135 | public function testCrossProduct(): void 136 | { 137 | $pointA = new Point(1, 2); 138 | $pointB = new Point(3, 5); 139 | $pointC = new Point(7, 12); 140 | 141 | $cross = $pointA->crossProduct($pointB, $pointC); 142 | $this->assertEquals(2, $cross); 143 | } 144 | 145 | /** 146 | * Tests the euclidean distance between two points. 147 | */ 148 | public function testEuclideanDistance(): void 149 | { 150 | $point1 = new Point(1, 2); 151 | $this->assertEquals(4.243, round($point1->euclideanDistance(new Point(4, 5)), $precision = 3)); 152 | 153 | $point2 = new Point(1, 4); 154 | $this->assertEquals(1.414, round($point2->euclideanDistance(new Point(2, 5)), $precision = 3)); 155 | } 156 | 157 | /** 158 | * Test intersection method. 159 | */ 160 | public function testIntersects(): void 161 | { 162 | // Lines 163 | $line1 = new Line( 164 | new Point(1, 1), 165 | new Point(5, 5) 166 | ); 167 | 168 | // Points 169 | $point1 = new Point(3, 3); 170 | $point2 = new Point(3, 3); 171 | $point3 = new Point(3, 4); 172 | $point4 = new Point(5, 5); 173 | 174 | // Polygons 175 | $polygon = new Polygon([ 176 | new Point(1, 1), 177 | new Point(3, 3), 178 | new Point(2, 7), 179 | new Point(4, 5), 180 | new Point(7, 5), 181 | new Point(1, 1), 182 | ]); 183 | 184 | // With Point 185 | $this->assertTrue($point1->intersectsPoint($point2)); 186 | $this->assertTrue($point2->intersectsPoint($point1)); 187 | $this->assertFalse($point1->intersectsPoint($point3)); 188 | 189 | // With Line 190 | $this->assertTrue($point1->intersectsLine($line1)); 191 | $this->assertTrue($point4->intersectsLine($line1)); // Extreme 192 | $this->assertFalse($point3->intersectsLine($line1)); 193 | 194 | // With Polygon 195 | $this->assertTrue($point1->intersectsPolygon($polygon)); 196 | $this->assertTrue($point4->intersectsPolygon($polygon)); 197 | $this->assertFalse($point3->intersectsPolygon($polygon)); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/Point.php: -------------------------------------------------------------------------------- 1 | x = $x; 22 | $this->y = $y; 23 | } 24 | 25 | /** 26 | * Clone method. 27 | * 28 | * @return Point New instance 29 | */ 30 | public function clone(): Point 31 | { 32 | return new Point($this->x, $this->y); 33 | } 34 | 35 | /** 36 | * Getter of x. 37 | * 38 | * @return float Value of x 39 | */ 40 | public function getX(): float 41 | { 42 | return $this->x; 43 | } 44 | 45 | /** 46 | * Setter of x. 47 | * 48 | * @param float $newX New value for x 49 | * 50 | * @return Point Instance 51 | */ 52 | public function setX(float $newX): Point 53 | { 54 | $this->x = $newX; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * Getter of y. 61 | * 62 | * @return float Value of y 63 | */ 64 | public function getY(): float 65 | { 66 | return $this->y; 67 | } 68 | 69 | /** 70 | * Setter of y. 71 | * 72 | * @param float $newY New value for y 73 | * 74 | * @return Point Instance 75 | */ 76 | public function setY(float $newY): Point 77 | { 78 | $this->y = $newY; 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * Getter for x and y. 85 | * 86 | * @return float[] Array with two values: X (0) and Y (1) 87 | */ 88 | public function getXandY(): array 89 | { 90 | return [$this->x, $this->y]; 91 | } 92 | 93 | /** 94 | * Checks whether two points are equals 95 | * (i.e. have the same coordinates). 96 | * 97 | * @param Point $otherPoint The second point to compare 98 | * 99 | * @return bool True if are equals, false otherwise 100 | */ 101 | public function isEqual(Point $otherPoint): bool 102 | { 103 | return $this->x === $otherPoint->getX() 104 | && $this->y === $otherPoint->getY(); 105 | } 106 | 107 | /** 108 | * Computes the magnitude of the point's vector. 109 | * 110 | * @return float Magnitude of the point's vector 111 | */ 112 | public function getMagnitude(): float 113 | { 114 | return sqrt($this->x ** 2 + $this->y ** 2); 115 | } 116 | 117 | /** 118 | * Converts x and y components of the Point from radians to degrees. 119 | * 120 | * @return Point Point with its components in degrees 121 | */ 122 | public function fromRadiansToDegrees(): Point 123 | { 124 | return new Point( 125 | rad2deg($this->x), 126 | rad2deg($this->y) 127 | ); 128 | } 129 | 130 | /** 131 | * Converts x and y components of the Point from degrees to radians. 132 | * 133 | * @return Point Point with its components in radians 134 | */ 135 | public function fromDegreesToRadians(): Point 136 | { 137 | return new Point( 138 | rad2deg($this->x), 139 | rad2deg($this->y) 140 | ); 141 | } 142 | 143 | /** 144 | * Computes the angle between two points. 145 | * 146 | * @param Point $otherPoint Other point to get the angle 147 | * @param bool $inDegrees If true the result is returned in degrees. In radians otherwise 148 | * 149 | * @return float The angle between the two points 150 | */ 151 | public function getAngle(Point $otherPoint, bool $inDegrees = true): float 152 | { 153 | // Get magnitudes 154 | $magnitudeThis = $this->getMagnitude(); 155 | $magnitudeOtherPoint = $otherPoint->getMagnitude(); 156 | 157 | // Computes angle 158 | $angleInRadians = acos(($this->x * $otherPoint->getX() + $this->y * $otherPoint->getY()) / ($magnitudeThis * $magnitudeOtherPoint)); 159 | 160 | return $inDegrees ? rad2deg($angleInRadians) : $angleInRadians; 161 | } 162 | 163 | /** 164 | * Computes the dot product of the two points. 165 | * 166 | * @param Point $otherPoint Other point to compute the dot product 167 | * 168 | * @return float Dot product = x1 * x2 + y1 * y2 169 | */ 170 | public function dotProduct(Point $otherPoint): float 171 | { 172 | return $this->x * $otherPoint->getX() + $this->y * $otherPoint->getY(); 173 | } 174 | 175 | /** 176 | * Computes the cross product of the three points. 177 | * 178 | * @param Point $point2 second point to compare 179 | * @param Point $point3 third point to compare 180 | * 181 | * @return float a positive value implies $this → #point2 → $point3 is counter-clockwise, negative implies clockwise 182 | */ 183 | public function crossProduct(Point $point2, Point $point3): float 184 | { 185 | return ($point2->getX() - $this->x) * ($point3->getY() - $this->y) 186 | - ($point2->getY() - $this->y) * ($point3->getX() - $this->x); 187 | } 188 | 189 | /** 190 | * Computes the euclidean distance between two points. 191 | * 192 | * @param Point $otherPoint Other point to compute the euclidean distance 193 | * 194 | * @return float Euclidean distance between the two points 195 | */ 196 | public function euclideanDistance(Point $otherPoint): float 197 | { 198 | return sqrt( 199 | (($otherPoint->getX() - $this->x) ** 2) 200 | + (($otherPoint->getY() - $this->y) ** 2) 201 | ); 202 | } 203 | 204 | /** 205 | * Checks whether the point intersects a line. 206 | * 207 | * @param Line $line Line to check 208 | * 209 | * @return bool true if the point intersects with the line, false otherwise 210 | */ 211 | public function intersectsLine(Line $line): bool 212 | { 213 | return $line->intersectsPoint($this); 214 | } 215 | 216 | /** 217 | * Checks whether the point intersects with another point. 218 | * 219 | * @param Point $point Point to check 220 | * 221 | * @return bool true if the point intersects with the other point, false otherwise 222 | */ 223 | public function intersectsPoint(Point $point): bool 224 | { 225 | return $this->isEqual($point); 226 | } 227 | 228 | /** 229 | * Checks whether the point intersects with a polygon. 230 | * 231 | * @param Polygon $polygon Polygon to check 232 | * 233 | * @return bool true if the point intersects with the polygon, false otherwise 234 | */ 235 | public function intersectsPolygon(Polygon $polygon): bool 236 | { 237 | return $polygon->intersectsPoint($this); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /tests/PolygonTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('\JWare\GeoPHP\Polygon', new Polygon([ 26 | $somePoint, 27 | $otherPoint, 28 | $otherPoint2, 29 | $somePoint, 30 | ])); 31 | 32 | // Checks invalid Polygons 33 | // Has only 2 different points 34 | $this->expectException(NotEnoughPointsException::class); 35 | $invalidPolygon2 = new Polygon([ 36 | new Point(0, 1), 37 | new Point(3, 1), 38 | new Point(1, 1), 39 | ]); 40 | 41 | $this->expectException(FirstAndLastPointNotEqualException::class); 42 | $invalidPolygon = new Polygon([ 43 | new Point(0, 1), 44 | new Point(3, 1), 45 | new Point(4, 2), 46 | new Point(1, 1), // Not equals to first point 47 | ]); 48 | } 49 | 50 | /** 51 | * Tests clone method. 52 | */ 53 | public function testClone(): void 54 | { 55 | $polygon = new Polygon([ 56 | new Point(0, 0), 57 | new Point(4, 0), 58 | new Point(4, 4), 59 | new Point(0, 4), 60 | new Point(0, 0), 61 | ]); 62 | $clone = $polygon->clone(); 63 | $this->assertEquals($clone->getPoints(), $polygon->getPoints()); 64 | } 65 | 66 | /** 67 | * Tests setPoint method. 68 | */ 69 | public function testSetPoint(): void 70 | { 71 | $polygon = new Polygon([ 72 | new Point(0, 0), 73 | new Point(4, 0), 74 | new Point(4, 4), 75 | new Point(0, 4), 76 | new Point(0, 0), 77 | ]); 78 | $newPoint = new Point(4, 1); 79 | $polygon->setPoint(1, $newPoint); 80 | $this->assertEquals($polygon->getPoints()[1], $newPoint); 81 | 82 | // Checks invalid Polygon's point setting: It's not allowed to set 83 | // the first or last Point. You must create a new Polygon 84 | $this->expectException(SettingPointException::class); 85 | $polygon->setPoint(0, $newPoint); 86 | $polygon->setPoint(4, $newPoint); 87 | } 88 | 89 | /** 90 | * Tests getCentroid method. 91 | */ 92 | public function testGetCentroid(): void 93 | { 94 | $polygon = new Polygon([ 95 | new Point(-81, 41), 96 | new Point(-88, 36), 97 | new Point(-84, 31), 98 | new Point(-80, 33), 99 | new Point(-77, 39), 100 | new Point(-81, 41), 101 | ]); 102 | $centroid = $polygon->getCentroid(); 103 | $this->assertEquals(new Point(-82, 36), $centroid); 104 | } 105 | 106 | /** 107 | * Test the area computation. 108 | */ 109 | public function testArea(): void 110 | { 111 | $polygon1 = new Polygon([ 112 | new Point(0, 0), 113 | new Point(4, 0), 114 | new Point(4, 4), 115 | new Point(0, 4), 116 | new Point(0, 0), 117 | ]); 118 | 119 | $polygon2 = new Polygon([ 120 | new Point(0, 0), 121 | new Point(4, 0), 122 | new Point(2, 4), 123 | new Point(0, 0), 124 | ]); 125 | 126 | $this->assertEquals(16, $polygon1->area()); 127 | $this->assertEquals(8, $polygon2->area()); 128 | } 129 | 130 | /** 131 | * Test contains method. 132 | */ 133 | public function testContains(): void 134 | { 135 | $polygon1 = new Polygon([ 136 | new Point(0, 0), 137 | new Point(4, 0), 138 | new Point(4, 4), 139 | new Point(0, 4), 140 | new Point(0, 0), 141 | ]); 142 | 143 | $polygon2 = new Polygon([ 144 | new Point(4, 0), 145 | new Point(8, 0), 146 | new Point(8, 4), 147 | new Point(4, 4), 148 | new Point(4, 0), 149 | ]); 150 | 151 | $polygon3 = new Polygon([ 152 | new Point(1, 2), 153 | new Point(2, 2), 154 | new Point(2, 3), 155 | new Point(1, 2), 156 | ]); 157 | 158 | $polygon4 = new Polygon([ 159 | new Point(3, 4), 160 | new Point(4, 5), 161 | new Point(3, 5), 162 | new Point(3, 4), 163 | ]); 164 | 165 | $polygon5 = new Polygon([ 166 | new Point(1, -1), 167 | new Point(2, 1), 168 | new Point(3, -1), 169 | new Point(2, -2), 170 | new Point(1, -1), 171 | ]); 172 | 173 | $line1 = new Line( 174 | new Point(2, 2), 175 | new Point(3, 3) 176 | ); 177 | 178 | $line2 = new Line( 179 | new Point(-1, -1), 180 | new Point(2, 3) 181 | ); 182 | 183 | // With Point 184 | $this->assertTrue($polygon1->containsPoint(new Point(2, 2))); 185 | $this->assertTrue($polygon1->containsPoint(new Point(4, 4))); 186 | $this->assertTrue($polygon1->containsPoint(new Point(0, 2))); 187 | $this->assertFalse($polygon1->containsPoint(new Point(10, 12))); 188 | $this->assertFalse($polygon1->containsPoint(new Point(-1, 2))); 189 | $this->assertFalse($polygon1->containsPoint(new Point(5, -2))); 190 | 191 | // With Line 192 | $this->assertTrue($polygon1->containsLine($line1)); 193 | $this->assertFalse($polygon1->containsLine($line2)); 194 | 195 | // With Polygon 196 | $this->assertTrue($polygon1->containsPolygon($polygon3)); 197 | $this->assertFalse($polygon1->containsPolygon($polygon2)); 198 | $this->assertFalse($polygon1->containsPolygon($polygon4)); 199 | $this->assertFalse($polygon1->containsPolygon($polygon5)); 200 | } 201 | 202 | /** 203 | * Test intersection method. 204 | */ 205 | public function testIntersects(): void 206 | { 207 | // Polygons 208 | $polygon1 = new Polygon([ 209 | new Point(2, 4), 210 | new Point(4, 4), 211 | new Point(5, 2), 212 | new Point(3, 1), 213 | new Point(2.5, 3), 214 | new Point(2, 4), 215 | ]); 216 | 217 | $polygon2 = new Polygon([ 218 | new Point(-3, -4), 219 | new Point(-1, -5), 220 | new Point(-2, -6), 221 | new Point(-3, -4), 222 | ]); 223 | 224 | $polygon3 = new Polygon([ 225 | new Point(4, 3), 226 | new Point(6, 2.5), 227 | new Point(6, 1.5), 228 | new Point(4.5, 1.25), 229 | new Point(4, 3), 230 | ]); 231 | 232 | $polygon4 = new Polygon([ 233 | new Point(-1.5, -5.5), 234 | new Point(-0.5, -6.5), 235 | new Point(-2, -7), 236 | new Point(-1.5, -5.5), 237 | ]); 238 | 239 | // With Point 240 | $this->assertTrue($polygon1->intersectsPoint(new Point(3, 4))); 241 | $this->assertFalse($polygon1->intersectsPoint(new Point(5, 4))); 242 | 243 | // With lines is already tested in LineTest 244 | 245 | // With Polygon 246 | $this->assertTrue($polygon1->intersectsPolygon($polygon3)); 247 | $this->assertTrue($polygon4->intersectsPolygon($polygon2)); 248 | $this->assertFalse($polygon1->intersectsPolygon($polygon2)); 249 | $this->assertFalse($polygon3->intersectsPolygon($polygon2)); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/Polygon.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | private array $points; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param array $points Array of points that make up the polygon 23 | * 24 | * @throws FirstAndLastPointNotEqualException 25 | * @throws NotEnoughPointsException 26 | */ 27 | public function __construct(array $points) 28 | { 29 | // A polygon has at least three points 30 | $pointsCount = count($points); 31 | // '< 4' because the last point has to be the same as first point 32 | if ($pointsCount < 4) { 33 | throw new NotEnoughPointsException('The polygon has to have at least three diferent points', 1); 34 | } 35 | 36 | // Checks first/last equality 37 | $lastPosition = $pointsCount - 1; 38 | if (!$points[0]->isEqual($points[$lastPosition])) { 39 | throw new FirstAndLastPointNotEqualException('First and last point must have the same values', 1); 40 | } 41 | 42 | $this->points = $points; 43 | } 44 | 45 | /** 46 | * Clone method. 47 | * 48 | * @return Polygon New instance 49 | */ 50 | public function clone(): Polygon 51 | { 52 | return new Polygon($this->points); 53 | } 54 | 55 | /** 56 | * Getter of the points of the polygon. 57 | * 58 | * @return Point[] Array of points that make up the polygon 59 | */ 60 | public function getPoints(): array 61 | { 62 | return $this->points; 63 | } 64 | 65 | /** 66 | * Setter for one point of the polygon. 67 | * 68 | * @param int $idx Index in the array of the polygon to replace 69 | * @param Point $point Point to put in the specified position 70 | * 71 | * @return Polygon Current instance 72 | */ 73 | public function setPoint(int $idx, Point $point): Polygon 74 | { 75 | if (0 == $idx || $idx == count($this->getPoints())) { 76 | throw new SettingPointException('First or last Point cannot be changed, you should create a new Polygon', 1); 77 | } 78 | 79 | $this->points[$idx] = $point; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Computes the polygon's centroid. 86 | * 87 | * @return Point Polygon's centroid point 88 | */ 89 | public function getCentroid(): Point 90 | { 91 | $x = 0.0; 92 | $y = 0.0; 93 | $pointsCount = 0; 94 | $elemCount = count($this->points); 95 | // We don't need the last element 96 | for (; $pointsCount < $elemCount - 1; ++$pointsCount) { 97 | $point = $this->points[$pointsCount]; 98 | $x += $point->getX(); 99 | $y += $point->getY(); 100 | } 101 | 102 | return new Point( 103 | $x / $pointsCount, 104 | $y / $pointsCount 105 | ); 106 | } 107 | 108 | /** 109 | * Get the Polygon's area 110 | * Uses the Shoelace Formula: 111 | * https://en.wikipedia.org/wiki/Shoelace_formula. 112 | */ 113 | public function area(): float 114 | { 115 | // Initialze area 116 | $area = 0.0; 117 | 118 | // Sorted vertices are required 119 | $sortedVertices = $this->getSortedVerticies(); 120 | 121 | // Calculate value of Shoelace formula 122 | $j = $n = count($sortedVertices) - 1; 123 | for ($i = 0; $i <= $n; ++$i) { 124 | $point1 = $sortedVertices[$i]; 125 | $point2 = $sortedVertices[$j]; 126 | $area += ($point2->getX() + $point1->getX()) * ($point2->getY() - $point1->getY()); 127 | // j is previous vertex to i 128 | $j = $i; 129 | } 130 | 131 | // Return absolute value 132 | return abs($area / 2.0); 133 | } 134 | 135 | /** 136 | * Checks whether the polygon intersects a point. A Polygon intersects a Point if 137 | * at least one of its lines intersects the Point. 138 | * 139 | * @param Point $point Point to check 140 | * 141 | * @return bool True if the polygon intersects with the point, false otherwise 142 | */ 143 | public function intersectsPoint(Point $point): bool 144 | { 145 | $n = sizeof($this->points); 146 | $polygonPoints = $this->points; 147 | 148 | $i = 0; 149 | do { 150 | $next = ($i + 1) % $n; 151 | 152 | // Check if the line segment from 'p' to 'extreme' intersects 153 | // with the line segment from 'polygonPoints[i]' to 'polygonPoints[next]' 154 | $lineIToNext = new Line($polygonPoints[$i], $polygonPoints[$next]); 155 | if ($lineIToNext->intersectsPoint($point)) { 156 | return true; 157 | } 158 | $i = $next; 159 | } while (0 != $i); 160 | 161 | return false; 162 | } 163 | 164 | /** 165 | * Checks whether the polygon intersects a line. 166 | * 167 | * @param Line $line Line to check 168 | * 169 | * @return bool True if the polygon intersects with the line, false otherwise 170 | */ 171 | public function intersectsLine(Line $line): bool 172 | { 173 | $n = sizeof($this->points); 174 | $polygonPoints = $this->points; 175 | 176 | $i = 0; 177 | do { 178 | $next = ($i + 1) % $n; 179 | 180 | // Check if the line segment from 'p' to 'extreme' intersects 181 | // with the line segment from 'polygonPoints[i]' to 'polygonPoints[next]' 182 | $lineIToNext = new Line($polygonPoints[$i], $polygonPoints[$next]); 183 | if ($line->intersectsLine($lineIToNext)) { 184 | return true; 185 | } 186 | $i = $next; 187 | } while (0 != $i); 188 | 189 | return false; 190 | } 191 | 192 | /** 193 | * Checks whether the polygon intersects another polygon. 194 | * 195 | * @param Polygon $polygon Polygon to check 196 | * 197 | * @return bool True if the polygon intersects with the polygon, false otherwise 198 | */ 199 | public function intersectsPolygon(Polygon $polygon): bool 200 | { 201 | $n = sizeof($this->points); 202 | $polygonPoints = $this->points; 203 | 204 | $i = 0; 205 | do { 206 | $next = ($i + 1) % $n; 207 | 208 | // Check if the line segment from 'p' to 'extreme' intersects 209 | // with the line segment from 'polygonPoints[i]' to 'polygonPoints[next]' 210 | $lineIToNext = new Line($polygonPoints[$i], $polygonPoints[$next]); 211 | if ($polygon->intersectsLine($lineIToNext)) { 212 | return true; 213 | } 214 | $i = $next; 215 | } while (0 != $i); 216 | 217 | return false; 218 | } 219 | 220 | /** 221 | * Checks whether a point is inside a polygon. 222 | * 223 | * @param Point $point Point to check 224 | * 225 | * @return bool True if the point is inside the polygon, false otherwise 226 | */ 227 | public function containsPoint(Point $point): bool 228 | { 229 | $n = sizeof($this->points); 230 | $polygonPoints = $this->points; 231 | 232 | // Create a line segment from p to infinite 233 | $extremePoint = new Point(PHP_INT_MAX, $point->getY()); 234 | $linePointToExtreme = new Line($point, $extremePoint); 235 | 236 | // Count intersections of the above line with sides of polygon 237 | $count = 0; 238 | $i = 0; 239 | do { 240 | $next = ($i + 1) % $n; 241 | 242 | // Check if the line segment from 'p' to 'extreme' intersects 243 | // with the line segment from 'polygonPoints[i]' to 'polygonPoints[next]' 244 | $lineIToNext = new Line($polygonPoints[$i], $polygonPoints[$next]); 245 | if ($linePointToExtreme->intersectsLine($lineIToNext)) { 246 | // If the $point is colinear with line segment 'i-next', 247 | // then check if it lies on segment. If it lies, return true, 248 | // otherwise false 249 | if (0 == $this->orientation($polygonPoints[$i], $point, $polygonPoints[$next])) { 250 | return $lineIToNext->intersectsPoint($point); 251 | } 252 | 253 | ++$count; 254 | } 255 | $i = $next; 256 | } while (0 != $i); 257 | 258 | // Return true if count is odd, false otherwise 259 | return 1 == $count % 2; 260 | } 261 | 262 | /** 263 | * Checks whether the polygon contains a line. 264 | * 265 | * @param Line $line Line to check 266 | * 267 | * @return bool True if the line is inside the polygon, false otherwise 268 | */ 269 | public function containsLine(Line $line): bool 270 | { 271 | return $this->containsPoint($line->getStart()) 272 | && $this->containsPoint($line->getEnd()) 273 | && !$this->intersectsLine($line); 274 | } 275 | 276 | /** 277 | * Checks whether the polygon contains another polygon. 278 | * 279 | * @param Polygon $polygon Polygon to check 280 | * 281 | * @return bool True if the Polygon is inside the polygon, false otherwise 282 | */ 283 | public function containsPolygon(Polygon $polygon): bool 284 | { 285 | $polygonPoints = $polygon->getPoints(); 286 | $n = sizeof($polygonPoints); 287 | 288 | $i = 0; 289 | do { 290 | $next = ($i + 1) % $n; 291 | 292 | // Check if the line segment from 'p' to 'extreme' intersects 293 | // with the line segment from 'polygonPoints[i]' to 'polygonPoints[next]' 294 | $lineIToNext = new Line($polygonPoints[$i], $polygonPoints[$next]); 295 | if (!$this->containsLine($lineIToNext)) { 296 | return false; 297 | } 298 | $i = $next; 299 | } while (0 != $i); 300 | 301 | return true; 302 | } 303 | 304 | /** 305 | * Sorts the polygon's vertices in clockwise order. 306 | * 307 | * @return Point[] Array with the sorted vertices 308 | */ 309 | private function getSortedVerticies(): array 310 | { 311 | // Gets polygon centroid 312 | $center = $this->getCentroid(); 313 | $subArray = array_slice($this->points, 0, count($this->points) - 1); 314 | usort($subArray, function ($point1, $point2) use ($center) { 315 | $a1 = (rad2deg(atan2($point1->getX() - $center->getX(), $point1->getY() - $center->getY())) + 360) % 360; 316 | $a2 = (rad2deg(atan2($point2->getX() - $center->getX(), $point2->getY() - $center->getY())) + 360) % 360; 317 | 318 | return $a2 - $a1; 319 | }); 320 | $subArray[] = $subArray[0]; 321 | 322 | return $subArray; 323 | } 324 | 325 | /** 326 | * To find orientation of ordered triplet (p, q, r). 327 | * The function returns following values. 328 | * 329 | * @param Point $p Point 1 330 | * @param Point $q Point 2 331 | * @param Point $r Point 3 332 | * 333 | * @return int 0 --> p, q and r are colinear, 1 --> Clockwise, 2 --> Counterclockwise 334 | */ 335 | private function orientation(Point $p, Point $q, Point $r): int 336 | { 337 | $val = ($q->getY() - $p->getY()) * ($r->getX() - $q->getX()) - ($q->getX() - $p->getX()) * ($r->getY() - $q->getY()); 338 | if (0 == $val) { 339 | return 0; 340 | } // colinear 341 | 342 | return ($val > 0) ? 1 : 2; // clock or counterclock wise 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "d80d88ba265846e3b375286340984afc", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "myclabs/deep-copy", 12 | "version": "1.12.1", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/myclabs/DeepCopy.git", 16 | "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", 21 | "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.1 || ^8.0" 26 | }, 27 | "conflict": { 28 | "doctrine/collections": "<1.6.8", 29 | "doctrine/common": "<2.13.3 || >=3 <3.2.2" 30 | }, 31 | "require-dev": { 32 | "doctrine/collections": "^1.6.8", 33 | "doctrine/common": "^2.13.3 || ^3.2.2", 34 | "phpspec/prophecy": "^1.10", 35 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "files": [ 40 | "src/DeepCopy/deep_copy.php" 41 | ], 42 | "psr-4": { 43 | "DeepCopy\\": "src/DeepCopy/" 44 | } 45 | }, 46 | "notification-url": "https://packagist.org/downloads/", 47 | "license": [ 48 | "MIT" 49 | ], 50 | "description": "Create deep copies (clones) of your objects", 51 | "keywords": [ 52 | "clone", 53 | "copy", 54 | "duplicate", 55 | "object", 56 | "object graph" 57 | ], 58 | "support": { 59 | "issues": "https://github.com/myclabs/DeepCopy/issues", 60 | "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" 61 | }, 62 | "funding": [ 63 | { 64 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 65 | "type": "tidelift" 66 | } 67 | ], 68 | "time": "2024-11-08T17:47:46+00:00" 69 | }, 70 | { 71 | "name": "nikic/php-parser", 72 | "version": "v5.3.1", 73 | "source": { 74 | "type": "git", 75 | "url": "https://github.com/nikic/PHP-Parser.git", 76 | "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" 77 | }, 78 | "dist": { 79 | "type": "zip", 80 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", 81 | "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", 82 | "shasum": "" 83 | }, 84 | "require": { 85 | "ext-ctype": "*", 86 | "ext-json": "*", 87 | "ext-tokenizer": "*", 88 | "php": ">=7.4" 89 | }, 90 | "require-dev": { 91 | "ircmaxell/php-yacc": "^0.0.7", 92 | "phpunit/phpunit": "^9.0" 93 | }, 94 | "bin": [ 95 | "bin/php-parse" 96 | ], 97 | "type": "library", 98 | "extra": { 99 | "branch-alias": { 100 | "dev-master": "5.0-dev" 101 | } 102 | }, 103 | "autoload": { 104 | "psr-4": { 105 | "PhpParser\\": "lib/PhpParser" 106 | } 107 | }, 108 | "notification-url": "https://packagist.org/downloads/", 109 | "license": [ 110 | "BSD-3-Clause" 111 | ], 112 | "authors": [ 113 | { 114 | "name": "Nikita Popov" 115 | } 116 | ], 117 | "description": "A PHP parser written in PHP", 118 | "keywords": [ 119 | "parser", 120 | "php" 121 | ], 122 | "support": { 123 | "issues": "https://github.com/nikic/PHP-Parser/issues", 124 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" 125 | }, 126 | "time": "2024-10-08T18:51:32+00:00" 127 | }, 128 | { 129 | "name": "phar-io/manifest", 130 | "version": "2.0.4", 131 | "source": { 132 | "type": "git", 133 | "url": "https://github.com/phar-io/manifest.git", 134 | "reference": "54750ef60c58e43759730615a392c31c80e23176" 135 | }, 136 | "dist": { 137 | "type": "zip", 138 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", 139 | "reference": "54750ef60c58e43759730615a392c31c80e23176", 140 | "shasum": "" 141 | }, 142 | "require": { 143 | "ext-dom": "*", 144 | "ext-libxml": "*", 145 | "ext-phar": "*", 146 | "ext-xmlwriter": "*", 147 | "phar-io/version": "^3.0.1", 148 | "php": "^7.2 || ^8.0" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "2.0.x-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "classmap": [ 158 | "src/" 159 | ] 160 | }, 161 | "notification-url": "https://packagist.org/downloads/", 162 | "license": [ 163 | "BSD-3-Clause" 164 | ], 165 | "authors": [ 166 | { 167 | "name": "Arne Blankerts", 168 | "email": "arne@blankerts.de", 169 | "role": "Developer" 170 | }, 171 | { 172 | "name": "Sebastian Heuer", 173 | "email": "sebastian@phpeople.de", 174 | "role": "Developer" 175 | }, 176 | { 177 | "name": "Sebastian Bergmann", 178 | "email": "sebastian@phpunit.de", 179 | "role": "Developer" 180 | } 181 | ], 182 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 183 | "support": { 184 | "issues": "https://github.com/phar-io/manifest/issues", 185 | "source": "https://github.com/phar-io/manifest/tree/2.0.4" 186 | }, 187 | "funding": [ 188 | { 189 | "url": "https://github.com/theseer", 190 | "type": "github" 191 | } 192 | ], 193 | "time": "2024-03-03T12:33:53+00:00" 194 | }, 195 | { 196 | "name": "phar-io/version", 197 | "version": "3.2.1", 198 | "source": { 199 | "type": "git", 200 | "url": "https://github.com/phar-io/version.git", 201 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 202 | }, 203 | "dist": { 204 | "type": "zip", 205 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 206 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 207 | "shasum": "" 208 | }, 209 | "require": { 210 | "php": "^7.2 || ^8.0" 211 | }, 212 | "type": "library", 213 | "autoload": { 214 | "classmap": [ 215 | "src/" 216 | ] 217 | }, 218 | "notification-url": "https://packagist.org/downloads/", 219 | "license": [ 220 | "BSD-3-Clause" 221 | ], 222 | "authors": [ 223 | { 224 | "name": "Arne Blankerts", 225 | "email": "arne@blankerts.de", 226 | "role": "Developer" 227 | }, 228 | { 229 | "name": "Sebastian Heuer", 230 | "email": "sebastian@phpeople.de", 231 | "role": "Developer" 232 | }, 233 | { 234 | "name": "Sebastian Bergmann", 235 | "email": "sebastian@phpunit.de", 236 | "role": "Developer" 237 | } 238 | ], 239 | "description": "Library for handling version information and constraints", 240 | "support": { 241 | "issues": "https://github.com/phar-io/version/issues", 242 | "source": "https://github.com/phar-io/version/tree/3.2.1" 243 | }, 244 | "time": "2022-02-21T01:04:05+00:00" 245 | }, 246 | { 247 | "name": "phpunit/php-code-coverage", 248 | "version": "10.1.16", 249 | "source": { 250 | "type": "git", 251 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 252 | "reference": "7e308268858ed6baedc8704a304727d20bc07c77" 253 | }, 254 | "dist": { 255 | "type": "zip", 256 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", 257 | "reference": "7e308268858ed6baedc8704a304727d20bc07c77", 258 | "shasum": "" 259 | }, 260 | "require": { 261 | "ext-dom": "*", 262 | "ext-libxml": "*", 263 | "ext-xmlwriter": "*", 264 | "nikic/php-parser": "^4.19.1 || ^5.1.0", 265 | "php": ">=8.1", 266 | "phpunit/php-file-iterator": "^4.1.0", 267 | "phpunit/php-text-template": "^3.0.1", 268 | "sebastian/code-unit-reverse-lookup": "^3.0.0", 269 | "sebastian/complexity": "^3.2.0", 270 | "sebastian/environment": "^6.1.0", 271 | "sebastian/lines-of-code": "^2.0.2", 272 | "sebastian/version": "^4.0.1", 273 | "theseer/tokenizer": "^1.2.3" 274 | }, 275 | "require-dev": { 276 | "phpunit/phpunit": "^10.1" 277 | }, 278 | "suggest": { 279 | "ext-pcov": "PHP extension that provides line coverage", 280 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 281 | }, 282 | "type": "library", 283 | "extra": { 284 | "branch-alias": { 285 | "dev-main": "10.1.x-dev" 286 | } 287 | }, 288 | "autoload": { 289 | "classmap": [ 290 | "src/" 291 | ] 292 | }, 293 | "notification-url": "https://packagist.org/downloads/", 294 | "license": [ 295 | "BSD-3-Clause" 296 | ], 297 | "authors": [ 298 | { 299 | "name": "Sebastian Bergmann", 300 | "email": "sebastian@phpunit.de", 301 | "role": "lead" 302 | } 303 | ], 304 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 305 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 306 | "keywords": [ 307 | "coverage", 308 | "testing", 309 | "xunit" 310 | ], 311 | "support": { 312 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 313 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 314 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" 315 | }, 316 | "funding": [ 317 | { 318 | "url": "https://github.com/sebastianbergmann", 319 | "type": "github" 320 | } 321 | ], 322 | "time": "2024-08-22T04:31:57+00:00" 323 | }, 324 | { 325 | "name": "phpunit/php-file-iterator", 326 | "version": "4.1.0", 327 | "source": { 328 | "type": "git", 329 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 330 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" 331 | }, 332 | "dist": { 333 | "type": "zip", 334 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", 335 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", 336 | "shasum": "" 337 | }, 338 | "require": { 339 | "php": ">=8.1" 340 | }, 341 | "require-dev": { 342 | "phpunit/phpunit": "^10.0" 343 | }, 344 | "type": "library", 345 | "extra": { 346 | "branch-alias": { 347 | "dev-main": "4.0-dev" 348 | } 349 | }, 350 | "autoload": { 351 | "classmap": [ 352 | "src/" 353 | ] 354 | }, 355 | "notification-url": "https://packagist.org/downloads/", 356 | "license": [ 357 | "BSD-3-Clause" 358 | ], 359 | "authors": [ 360 | { 361 | "name": "Sebastian Bergmann", 362 | "email": "sebastian@phpunit.de", 363 | "role": "lead" 364 | } 365 | ], 366 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 367 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 368 | "keywords": [ 369 | "filesystem", 370 | "iterator" 371 | ], 372 | "support": { 373 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 374 | "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", 375 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" 376 | }, 377 | "funding": [ 378 | { 379 | "url": "https://github.com/sebastianbergmann", 380 | "type": "github" 381 | } 382 | ], 383 | "time": "2023-08-31T06:24:48+00:00" 384 | }, 385 | { 386 | "name": "phpunit/php-invoker", 387 | "version": "4.0.0", 388 | "source": { 389 | "type": "git", 390 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 391 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" 392 | }, 393 | "dist": { 394 | "type": "zip", 395 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 396 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", 397 | "shasum": "" 398 | }, 399 | "require": { 400 | "php": ">=8.1" 401 | }, 402 | "require-dev": { 403 | "ext-pcntl": "*", 404 | "phpunit/phpunit": "^10.0" 405 | }, 406 | "suggest": { 407 | "ext-pcntl": "*" 408 | }, 409 | "type": "library", 410 | "extra": { 411 | "branch-alias": { 412 | "dev-main": "4.0-dev" 413 | } 414 | }, 415 | "autoload": { 416 | "classmap": [ 417 | "src/" 418 | ] 419 | }, 420 | "notification-url": "https://packagist.org/downloads/", 421 | "license": [ 422 | "BSD-3-Clause" 423 | ], 424 | "authors": [ 425 | { 426 | "name": "Sebastian Bergmann", 427 | "email": "sebastian@phpunit.de", 428 | "role": "lead" 429 | } 430 | ], 431 | "description": "Invoke callables with a timeout", 432 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 433 | "keywords": [ 434 | "process" 435 | ], 436 | "support": { 437 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 438 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" 439 | }, 440 | "funding": [ 441 | { 442 | "url": "https://github.com/sebastianbergmann", 443 | "type": "github" 444 | } 445 | ], 446 | "time": "2023-02-03T06:56:09+00:00" 447 | }, 448 | { 449 | "name": "phpunit/php-text-template", 450 | "version": "3.0.1", 451 | "source": { 452 | "type": "git", 453 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 454 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" 455 | }, 456 | "dist": { 457 | "type": "zip", 458 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", 459 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", 460 | "shasum": "" 461 | }, 462 | "require": { 463 | "php": ">=8.1" 464 | }, 465 | "require-dev": { 466 | "phpunit/phpunit": "^10.0" 467 | }, 468 | "type": "library", 469 | "extra": { 470 | "branch-alias": { 471 | "dev-main": "3.0-dev" 472 | } 473 | }, 474 | "autoload": { 475 | "classmap": [ 476 | "src/" 477 | ] 478 | }, 479 | "notification-url": "https://packagist.org/downloads/", 480 | "license": [ 481 | "BSD-3-Clause" 482 | ], 483 | "authors": [ 484 | { 485 | "name": "Sebastian Bergmann", 486 | "email": "sebastian@phpunit.de", 487 | "role": "lead" 488 | } 489 | ], 490 | "description": "Simple template engine.", 491 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 492 | "keywords": [ 493 | "template" 494 | ], 495 | "support": { 496 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 497 | "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", 498 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" 499 | }, 500 | "funding": [ 501 | { 502 | "url": "https://github.com/sebastianbergmann", 503 | "type": "github" 504 | } 505 | ], 506 | "time": "2023-08-31T14:07:24+00:00" 507 | }, 508 | { 509 | "name": "phpunit/php-timer", 510 | "version": "6.0.0", 511 | "source": { 512 | "type": "git", 513 | "url": "https://github.com/sebastianbergmann/php-timer.git", 514 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" 515 | }, 516 | "dist": { 517 | "type": "zip", 518 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", 519 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", 520 | "shasum": "" 521 | }, 522 | "require": { 523 | "php": ">=8.1" 524 | }, 525 | "require-dev": { 526 | "phpunit/phpunit": "^10.0" 527 | }, 528 | "type": "library", 529 | "extra": { 530 | "branch-alias": { 531 | "dev-main": "6.0-dev" 532 | } 533 | }, 534 | "autoload": { 535 | "classmap": [ 536 | "src/" 537 | ] 538 | }, 539 | "notification-url": "https://packagist.org/downloads/", 540 | "license": [ 541 | "BSD-3-Clause" 542 | ], 543 | "authors": [ 544 | { 545 | "name": "Sebastian Bergmann", 546 | "email": "sebastian@phpunit.de", 547 | "role": "lead" 548 | } 549 | ], 550 | "description": "Utility class for timing", 551 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 552 | "keywords": [ 553 | "timer" 554 | ], 555 | "support": { 556 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 557 | "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" 558 | }, 559 | "funding": [ 560 | { 561 | "url": "https://github.com/sebastianbergmann", 562 | "type": "github" 563 | } 564 | ], 565 | "time": "2023-02-03T06:57:52+00:00" 566 | }, 567 | { 568 | "name": "phpunit/phpunit", 569 | "version": "10.5.39", 570 | "source": { 571 | "type": "git", 572 | "url": "https://github.com/sebastianbergmann/phpunit.git", 573 | "reference": "4e89eff200b801db58f3d580ad7426431949eaa9" 574 | }, 575 | "dist": { 576 | "type": "zip", 577 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4e89eff200b801db58f3d580ad7426431949eaa9", 578 | "reference": "4e89eff200b801db58f3d580ad7426431949eaa9", 579 | "shasum": "" 580 | }, 581 | "require": { 582 | "ext-dom": "*", 583 | "ext-json": "*", 584 | "ext-libxml": "*", 585 | "ext-mbstring": "*", 586 | "ext-xml": "*", 587 | "ext-xmlwriter": "*", 588 | "myclabs/deep-copy": "^1.12.1", 589 | "phar-io/manifest": "^2.0.4", 590 | "phar-io/version": "^3.2.1", 591 | "php": ">=8.1", 592 | "phpunit/php-code-coverage": "^10.1.16", 593 | "phpunit/php-file-iterator": "^4.1.0", 594 | "phpunit/php-invoker": "^4.0.0", 595 | "phpunit/php-text-template": "^3.0.1", 596 | "phpunit/php-timer": "^6.0.0", 597 | "sebastian/cli-parser": "^2.0.1", 598 | "sebastian/code-unit": "^2.0.0", 599 | "sebastian/comparator": "^5.0.3", 600 | "sebastian/diff": "^5.1.1", 601 | "sebastian/environment": "^6.1.0", 602 | "sebastian/exporter": "^5.1.2", 603 | "sebastian/global-state": "^6.0.2", 604 | "sebastian/object-enumerator": "^5.0.0", 605 | "sebastian/recursion-context": "^5.0.0", 606 | "sebastian/type": "^4.0.0", 607 | "sebastian/version": "^4.0.1" 608 | }, 609 | "suggest": { 610 | "ext-soap": "To be able to generate mocks based on WSDL files" 611 | }, 612 | "bin": [ 613 | "phpunit" 614 | ], 615 | "type": "library", 616 | "extra": { 617 | "branch-alias": { 618 | "dev-main": "10.5-dev" 619 | } 620 | }, 621 | "autoload": { 622 | "files": [ 623 | "src/Framework/Assert/Functions.php" 624 | ], 625 | "classmap": [ 626 | "src/" 627 | ] 628 | }, 629 | "notification-url": "https://packagist.org/downloads/", 630 | "license": [ 631 | "BSD-3-Clause" 632 | ], 633 | "authors": [ 634 | { 635 | "name": "Sebastian Bergmann", 636 | "email": "sebastian@phpunit.de", 637 | "role": "lead" 638 | } 639 | ], 640 | "description": "The PHP Unit Testing framework.", 641 | "homepage": "https://phpunit.de/", 642 | "keywords": [ 643 | "phpunit", 644 | "testing", 645 | "xunit" 646 | ], 647 | "support": { 648 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 649 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 650 | "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.39" 651 | }, 652 | "funding": [ 653 | { 654 | "url": "https://phpunit.de/sponsors.html", 655 | "type": "custom" 656 | }, 657 | { 658 | "url": "https://github.com/sebastianbergmann", 659 | "type": "github" 660 | }, 661 | { 662 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 663 | "type": "tidelift" 664 | } 665 | ], 666 | "time": "2024-12-11T10:51:07+00:00" 667 | }, 668 | { 669 | "name": "sebastian/cli-parser", 670 | "version": "2.0.1", 671 | "source": { 672 | "type": "git", 673 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 674 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" 675 | }, 676 | "dist": { 677 | "type": "zip", 678 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", 679 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", 680 | "shasum": "" 681 | }, 682 | "require": { 683 | "php": ">=8.1" 684 | }, 685 | "require-dev": { 686 | "phpunit/phpunit": "^10.0" 687 | }, 688 | "type": "library", 689 | "extra": { 690 | "branch-alias": { 691 | "dev-main": "2.0-dev" 692 | } 693 | }, 694 | "autoload": { 695 | "classmap": [ 696 | "src/" 697 | ] 698 | }, 699 | "notification-url": "https://packagist.org/downloads/", 700 | "license": [ 701 | "BSD-3-Clause" 702 | ], 703 | "authors": [ 704 | { 705 | "name": "Sebastian Bergmann", 706 | "email": "sebastian@phpunit.de", 707 | "role": "lead" 708 | } 709 | ], 710 | "description": "Library for parsing CLI options", 711 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 712 | "support": { 713 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 714 | "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", 715 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" 716 | }, 717 | "funding": [ 718 | { 719 | "url": "https://github.com/sebastianbergmann", 720 | "type": "github" 721 | } 722 | ], 723 | "time": "2024-03-02T07:12:49+00:00" 724 | }, 725 | { 726 | "name": "sebastian/code-unit", 727 | "version": "2.0.0", 728 | "source": { 729 | "type": "git", 730 | "url": "https://github.com/sebastianbergmann/code-unit.git", 731 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" 732 | }, 733 | "dist": { 734 | "type": "zip", 735 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", 736 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", 737 | "shasum": "" 738 | }, 739 | "require": { 740 | "php": ">=8.1" 741 | }, 742 | "require-dev": { 743 | "phpunit/phpunit": "^10.0" 744 | }, 745 | "type": "library", 746 | "extra": { 747 | "branch-alias": { 748 | "dev-main": "2.0-dev" 749 | } 750 | }, 751 | "autoload": { 752 | "classmap": [ 753 | "src/" 754 | ] 755 | }, 756 | "notification-url": "https://packagist.org/downloads/", 757 | "license": [ 758 | "BSD-3-Clause" 759 | ], 760 | "authors": [ 761 | { 762 | "name": "Sebastian Bergmann", 763 | "email": "sebastian@phpunit.de", 764 | "role": "lead" 765 | } 766 | ], 767 | "description": "Collection of value objects that represent the PHP code units", 768 | "homepage": "https://github.com/sebastianbergmann/code-unit", 769 | "support": { 770 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 771 | "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" 772 | }, 773 | "funding": [ 774 | { 775 | "url": "https://github.com/sebastianbergmann", 776 | "type": "github" 777 | } 778 | ], 779 | "time": "2023-02-03T06:58:43+00:00" 780 | }, 781 | { 782 | "name": "sebastian/code-unit-reverse-lookup", 783 | "version": "3.0.0", 784 | "source": { 785 | "type": "git", 786 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 787 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" 788 | }, 789 | "dist": { 790 | "type": "zip", 791 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 792 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", 793 | "shasum": "" 794 | }, 795 | "require": { 796 | "php": ">=8.1" 797 | }, 798 | "require-dev": { 799 | "phpunit/phpunit": "^10.0" 800 | }, 801 | "type": "library", 802 | "extra": { 803 | "branch-alias": { 804 | "dev-main": "3.0-dev" 805 | } 806 | }, 807 | "autoload": { 808 | "classmap": [ 809 | "src/" 810 | ] 811 | }, 812 | "notification-url": "https://packagist.org/downloads/", 813 | "license": [ 814 | "BSD-3-Clause" 815 | ], 816 | "authors": [ 817 | { 818 | "name": "Sebastian Bergmann", 819 | "email": "sebastian@phpunit.de" 820 | } 821 | ], 822 | "description": "Looks up which function or method a line of code belongs to", 823 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 824 | "support": { 825 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 826 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" 827 | }, 828 | "funding": [ 829 | { 830 | "url": "https://github.com/sebastianbergmann", 831 | "type": "github" 832 | } 833 | ], 834 | "time": "2023-02-03T06:59:15+00:00" 835 | }, 836 | { 837 | "name": "sebastian/comparator", 838 | "version": "5.0.3", 839 | "source": { 840 | "type": "git", 841 | "url": "https://github.com/sebastianbergmann/comparator.git", 842 | "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" 843 | }, 844 | "dist": { 845 | "type": "zip", 846 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", 847 | "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", 848 | "shasum": "" 849 | }, 850 | "require": { 851 | "ext-dom": "*", 852 | "ext-mbstring": "*", 853 | "php": ">=8.1", 854 | "sebastian/diff": "^5.0", 855 | "sebastian/exporter": "^5.0" 856 | }, 857 | "require-dev": { 858 | "phpunit/phpunit": "^10.5" 859 | }, 860 | "type": "library", 861 | "extra": { 862 | "branch-alias": { 863 | "dev-main": "5.0-dev" 864 | } 865 | }, 866 | "autoload": { 867 | "classmap": [ 868 | "src/" 869 | ] 870 | }, 871 | "notification-url": "https://packagist.org/downloads/", 872 | "license": [ 873 | "BSD-3-Clause" 874 | ], 875 | "authors": [ 876 | { 877 | "name": "Sebastian Bergmann", 878 | "email": "sebastian@phpunit.de" 879 | }, 880 | { 881 | "name": "Jeff Welch", 882 | "email": "whatthejeff@gmail.com" 883 | }, 884 | { 885 | "name": "Volker Dusch", 886 | "email": "github@wallbash.com" 887 | }, 888 | { 889 | "name": "Bernhard Schussek", 890 | "email": "bschussek@2bepublished.at" 891 | } 892 | ], 893 | "description": "Provides the functionality to compare PHP values for equality", 894 | "homepage": "https://github.com/sebastianbergmann/comparator", 895 | "keywords": [ 896 | "comparator", 897 | "compare", 898 | "equality" 899 | ], 900 | "support": { 901 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 902 | "security": "https://github.com/sebastianbergmann/comparator/security/policy", 903 | "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" 904 | }, 905 | "funding": [ 906 | { 907 | "url": "https://github.com/sebastianbergmann", 908 | "type": "github" 909 | } 910 | ], 911 | "time": "2024-10-18T14:56:07+00:00" 912 | }, 913 | { 914 | "name": "sebastian/complexity", 915 | "version": "3.2.0", 916 | "source": { 917 | "type": "git", 918 | "url": "https://github.com/sebastianbergmann/complexity.git", 919 | "reference": "68ff824baeae169ec9f2137158ee529584553799" 920 | }, 921 | "dist": { 922 | "type": "zip", 923 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", 924 | "reference": "68ff824baeae169ec9f2137158ee529584553799", 925 | "shasum": "" 926 | }, 927 | "require": { 928 | "nikic/php-parser": "^4.18 || ^5.0", 929 | "php": ">=8.1" 930 | }, 931 | "require-dev": { 932 | "phpunit/phpunit": "^10.0" 933 | }, 934 | "type": "library", 935 | "extra": { 936 | "branch-alias": { 937 | "dev-main": "3.2-dev" 938 | } 939 | }, 940 | "autoload": { 941 | "classmap": [ 942 | "src/" 943 | ] 944 | }, 945 | "notification-url": "https://packagist.org/downloads/", 946 | "license": [ 947 | "BSD-3-Clause" 948 | ], 949 | "authors": [ 950 | { 951 | "name": "Sebastian Bergmann", 952 | "email": "sebastian@phpunit.de", 953 | "role": "lead" 954 | } 955 | ], 956 | "description": "Library for calculating the complexity of PHP code units", 957 | "homepage": "https://github.com/sebastianbergmann/complexity", 958 | "support": { 959 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 960 | "security": "https://github.com/sebastianbergmann/complexity/security/policy", 961 | "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" 962 | }, 963 | "funding": [ 964 | { 965 | "url": "https://github.com/sebastianbergmann", 966 | "type": "github" 967 | } 968 | ], 969 | "time": "2023-12-21T08:37:17+00:00" 970 | }, 971 | { 972 | "name": "sebastian/diff", 973 | "version": "5.1.1", 974 | "source": { 975 | "type": "git", 976 | "url": "https://github.com/sebastianbergmann/diff.git", 977 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" 978 | }, 979 | "dist": { 980 | "type": "zip", 981 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", 982 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", 983 | "shasum": "" 984 | }, 985 | "require": { 986 | "php": ">=8.1" 987 | }, 988 | "require-dev": { 989 | "phpunit/phpunit": "^10.0", 990 | "symfony/process": "^6.4" 991 | }, 992 | "type": "library", 993 | "extra": { 994 | "branch-alias": { 995 | "dev-main": "5.1-dev" 996 | } 997 | }, 998 | "autoload": { 999 | "classmap": [ 1000 | "src/" 1001 | ] 1002 | }, 1003 | "notification-url": "https://packagist.org/downloads/", 1004 | "license": [ 1005 | "BSD-3-Clause" 1006 | ], 1007 | "authors": [ 1008 | { 1009 | "name": "Sebastian Bergmann", 1010 | "email": "sebastian@phpunit.de" 1011 | }, 1012 | { 1013 | "name": "Kore Nordmann", 1014 | "email": "mail@kore-nordmann.de" 1015 | } 1016 | ], 1017 | "description": "Diff implementation", 1018 | "homepage": "https://github.com/sebastianbergmann/diff", 1019 | "keywords": [ 1020 | "diff", 1021 | "udiff", 1022 | "unidiff", 1023 | "unified diff" 1024 | ], 1025 | "support": { 1026 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1027 | "security": "https://github.com/sebastianbergmann/diff/security/policy", 1028 | "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" 1029 | }, 1030 | "funding": [ 1031 | { 1032 | "url": "https://github.com/sebastianbergmann", 1033 | "type": "github" 1034 | } 1035 | ], 1036 | "time": "2024-03-02T07:15:17+00:00" 1037 | }, 1038 | { 1039 | "name": "sebastian/environment", 1040 | "version": "6.1.0", 1041 | "source": { 1042 | "type": "git", 1043 | "url": "https://github.com/sebastianbergmann/environment.git", 1044 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" 1045 | }, 1046 | "dist": { 1047 | "type": "zip", 1048 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", 1049 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", 1050 | "shasum": "" 1051 | }, 1052 | "require": { 1053 | "php": ">=8.1" 1054 | }, 1055 | "require-dev": { 1056 | "phpunit/phpunit": "^10.0" 1057 | }, 1058 | "suggest": { 1059 | "ext-posix": "*" 1060 | }, 1061 | "type": "library", 1062 | "extra": { 1063 | "branch-alias": { 1064 | "dev-main": "6.1-dev" 1065 | } 1066 | }, 1067 | "autoload": { 1068 | "classmap": [ 1069 | "src/" 1070 | ] 1071 | }, 1072 | "notification-url": "https://packagist.org/downloads/", 1073 | "license": [ 1074 | "BSD-3-Clause" 1075 | ], 1076 | "authors": [ 1077 | { 1078 | "name": "Sebastian Bergmann", 1079 | "email": "sebastian@phpunit.de" 1080 | } 1081 | ], 1082 | "description": "Provides functionality to handle HHVM/PHP environments", 1083 | "homepage": "https://github.com/sebastianbergmann/environment", 1084 | "keywords": [ 1085 | "Xdebug", 1086 | "environment", 1087 | "hhvm" 1088 | ], 1089 | "support": { 1090 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1091 | "security": "https://github.com/sebastianbergmann/environment/security/policy", 1092 | "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" 1093 | }, 1094 | "funding": [ 1095 | { 1096 | "url": "https://github.com/sebastianbergmann", 1097 | "type": "github" 1098 | } 1099 | ], 1100 | "time": "2024-03-23T08:47:14+00:00" 1101 | }, 1102 | { 1103 | "name": "sebastian/exporter", 1104 | "version": "5.1.2", 1105 | "source": { 1106 | "type": "git", 1107 | "url": "https://github.com/sebastianbergmann/exporter.git", 1108 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf" 1109 | }, 1110 | "dist": { 1111 | "type": "zip", 1112 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", 1113 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf", 1114 | "shasum": "" 1115 | }, 1116 | "require": { 1117 | "ext-mbstring": "*", 1118 | "php": ">=8.1", 1119 | "sebastian/recursion-context": "^5.0" 1120 | }, 1121 | "require-dev": { 1122 | "phpunit/phpunit": "^10.0" 1123 | }, 1124 | "type": "library", 1125 | "extra": { 1126 | "branch-alias": { 1127 | "dev-main": "5.1-dev" 1128 | } 1129 | }, 1130 | "autoload": { 1131 | "classmap": [ 1132 | "src/" 1133 | ] 1134 | }, 1135 | "notification-url": "https://packagist.org/downloads/", 1136 | "license": [ 1137 | "BSD-3-Clause" 1138 | ], 1139 | "authors": [ 1140 | { 1141 | "name": "Sebastian Bergmann", 1142 | "email": "sebastian@phpunit.de" 1143 | }, 1144 | { 1145 | "name": "Jeff Welch", 1146 | "email": "whatthejeff@gmail.com" 1147 | }, 1148 | { 1149 | "name": "Volker Dusch", 1150 | "email": "github@wallbash.com" 1151 | }, 1152 | { 1153 | "name": "Adam Harvey", 1154 | "email": "aharvey@php.net" 1155 | }, 1156 | { 1157 | "name": "Bernhard Schussek", 1158 | "email": "bschussek@gmail.com" 1159 | } 1160 | ], 1161 | "description": "Provides the functionality to export PHP variables for visualization", 1162 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1163 | "keywords": [ 1164 | "export", 1165 | "exporter" 1166 | ], 1167 | "support": { 1168 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1169 | "security": "https://github.com/sebastianbergmann/exporter/security/policy", 1170 | "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" 1171 | }, 1172 | "funding": [ 1173 | { 1174 | "url": "https://github.com/sebastianbergmann", 1175 | "type": "github" 1176 | } 1177 | ], 1178 | "time": "2024-03-02T07:17:12+00:00" 1179 | }, 1180 | { 1181 | "name": "sebastian/global-state", 1182 | "version": "6.0.2", 1183 | "source": { 1184 | "type": "git", 1185 | "url": "https://github.com/sebastianbergmann/global-state.git", 1186 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" 1187 | }, 1188 | "dist": { 1189 | "type": "zip", 1190 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", 1191 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", 1192 | "shasum": "" 1193 | }, 1194 | "require": { 1195 | "php": ">=8.1", 1196 | "sebastian/object-reflector": "^3.0", 1197 | "sebastian/recursion-context": "^5.0" 1198 | }, 1199 | "require-dev": { 1200 | "ext-dom": "*", 1201 | "phpunit/phpunit": "^10.0" 1202 | }, 1203 | "type": "library", 1204 | "extra": { 1205 | "branch-alias": { 1206 | "dev-main": "6.0-dev" 1207 | } 1208 | }, 1209 | "autoload": { 1210 | "classmap": [ 1211 | "src/" 1212 | ] 1213 | }, 1214 | "notification-url": "https://packagist.org/downloads/", 1215 | "license": [ 1216 | "BSD-3-Clause" 1217 | ], 1218 | "authors": [ 1219 | { 1220 | "name": "Sebastian Bergmann", 1221 | "email": "sebastian@phpunit.de" 1222 | } 1223 | ], 1224 | "description": "Snapshotting of global state", 1225 | "homepage": "https://www.github.com/sebastianbergmann/global-state", 1226 | "keywords": [ 1227 | "global state" 1228 | ], 1229 | "support": { 1230 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1231 | "security": "https://github.com/sebastianbergmann/global-state/security/policy", 1232 | "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" 1233 | }, 1234 | "funding": [ 1235 | { 1236 | "url": "https://github.com/sebastianbergmann", 1237 | "type": "github" 1238 | } 1239 | ], 1240 | "time": "2024-03-02T07:19:19+00:00" 1241 | }, 1242 | { 1243 | "name": "sebastian/lines-of-code", 1244 | "version": "2.0.2", 1245 | "source": { 1246 | "type": "git", 1247 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1248 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" 1249 | }, 1250 | "dist": { 1251 | "type": "zip", 1252 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", 1253 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", 1254 | "shasum": "" 1255 | }, 1256 | "require": { 1257 | "nikic/php-parser": "^4.18 || ^5.0", 1258 | "php": ">=8.1" 1259 | }, 1260 | "require-dev": { 1261 | "phpunit/phpunit": "^10.0" 1262 | }, 1263 | "type": "library", 1264 | "extra": { 1265 | "branch-alias": { 1266 | "dev-main": "2.0-dev" 1267 | } 1268 | }, 1269 | "autoload": { 1270 | "classmap": [ 1271 | "src/" 1272 | ] 1273 | }, 1274 | "notification-url": "https://packagist.org/downloads/", 1275 | "license": [ 1276 | "BSD-3-Clause" 1277 | ], 1278 | "authors": [ 1279 | { 1280 | "name": "Sebastian Bergmann", 1281 | "email": "sebastian@phpunit.de", 1282 | "role": "lead" 1283 | } 1284 | ], 1285 | "description": "Library for counting the lines of code in PHP source code", 1286 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1287 | "support": { 1288 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1289 | "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", 1290 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" 1291 | }, 1292 | "funding": [ 1293 | { 1294 | "url": "https://github.com/sebastianbergmann", 1295 | "type": "github" 1296 | } 1297 | ], 1298 | "time": "2023-12-21T08:38:20+00:00" 1299 | }, 1300 | { 1301 | "name": "sebastian/object-enumerator", 1302 | "version": "5.0.0", 1303 | "source": { 1304 | "type": "git", 1305 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1306 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" 1307 | }, 1308 | "dist": { 1309 | "type": "zip", 1310 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", 1311 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", 1312 | "shasum": "" 1313 | }, 1314 | "require": { 1315 | "php": ">=8.1", 1316 | "sebastian/object-reflector": "^3.0", 1317 | "sebastian/recursion-context": "^5.0" 1318 | }, 1319 | "require-dev": { 1320 | "phpunit/phpunit": "^10.0" 1321 | }, 1322 | "type": "library", 1323 | "extra": { 1324 | "branch-alias": { 1325 | "dev-main": "5.0-dev" 1326 | } 1327 | }, 1328 | "autoload": { 1329 | "classmap": [ 1330 | "src/" 1331 | ] 1332 | }, 1333 | "notification-url": "https://packagist.org/downloads/", 1334 | "license": [ 1335 | "BSD-3-Clause" 1336 | ], 1337 | "authors": [ 1338 | { 1339 | "name": "Sebastian Bergmann", 1340 | "email": "sebastian@phpunit.de" 1341 | } 1342 | ], 1343 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1344 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1345 | "support": { 1346 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1347 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" 1348 | }, 1349 | "funding": [ 1350 | { 1351 | "url": "https://github.com/sebastianbergmann", 1352 | "type": "github" 1353 | } 1354 | ], 1355 | "time": "2023-02-03T07:08:32+00:00" 1356 | }, 1357 | { 1358 | "name": "sebastian/object-reflector", 1359 | "version": "3.0.0", 1360 | "source": { 1361 | "type": "git", 1362 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1363 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" 1364 | }, 1365 | "dist": { 1366 | "type": "zip", 1367 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", 1368 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", 1369 | "shasum": "" 1370 | }, 1371 | "require": { 1372 | "php": ">=8.1" 1373 | }, 1374 | "require-dev": { 1375 | "phpunit/phpunit": "^10.0" 1376 | }, 1377 | "type": "library", 1378 | "extra": { 1379 | "branch-alias": { 1380 | "dev-main": "3.0-dev" 1381 | } 1382 | }, 1383 | "autoload": { 1384 | "classmap": [ 1385 | "src/" 1386 | ] 1387 | }, 1388 | "notification-url": "https://packagist.org/downloads/", 1389 | "license": [ 1390 | "BSD-3-Clause" 1391 | ], 1392 | "authors": [ 1393 | { 1394 | "name": "Sebastian Bergmann", 1395 | "email": "sebastian@phpunit.de" 1396 | } 1397 | ], 1398 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1399 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1400 | "support": { 1401 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1402 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" 1403 | }, 1404 | "funding": [ 1405 | { 1406 | "url": "https://github.com/sebastianbergmann", 1407 | "type": "github" 1408 | } 1409 | ], 1410 | "time": "2023-02-03T07:06:18+00:00" 1411 | }, 1412 | { 1413 | "name": "sebastian/recursion-context", 1414 | "version": "5.0.0", 1415 | "source": { 1416 | "type": "git", 1417 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1418 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712" 1419 | }, 1420 | "dist": { 1421 | "type": "zip", 1422 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", 1423 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712", 1424 | "shasum": "" 1425 | }, 1426 | "require": { 1427 | "php": ">=8.1" 1428 | }, 1429 | "require-dev": { 1430 | "phpunit/phpunit": "^10.0" 1431 | }, 1432 | "type": "library", 1433 | "extra": { 1434 | "branch-alias": { 1435 | "dev-main": "5.0-dev" 1436 | } 1437 | }, 1438 | "autoload": { 1439 | "classmap": [ 1440 | "src/" 1441 | ] 1442 | }, 1443 | "notification-url": "https://packagist.org/downloads/", 1444 | "license": [ 1445 | "BSD-3-Clause" 1446 | ], 1447 | "authors": [ 1448 | { 1449 | "name": "Sebastian Bergmann", 1450 | "email": "sebastian@phpunit.de" 1451 | }, 1452 | { 1453 | "name": "Jeff Welch", 1454 | "email": "whatthejeff@gmail.com" 1455 | }, 1456 | { 1457 | "name": "Adam Harvey", 1458 | "email": "aharvey@php.net" 1459 | } 1460 | ], 1461 | "description": "Provides functionality to recursively process PHP variables", 1462 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1463 | "support": { 1464 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1465 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" 1466 | }, 1467 | "funding": [ 1468 | { 1469 | "url": "https://github.com/sebastianbergmann", 1470 | "type": "github" 1471 | } 1472 | ], 1473 | "time": "2023-02-03T07:05:40+00:00" 1474 | }, 1475 | { 1476 | "name": "sebastian/type", 1477 | "version": "4.0.0", 1478 | "source": { 1479 | "type": "git", 1480 | "url": "https://github.com/sebastianbergmann/type.git", 1481 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" 1482 | }, 1483 | "dist": { 1484 | "type": "zip", 1485 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", 1486 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", 1487 | "shasum": "" 1488 | }, 1489 | "require": { 1490 | "php": ">=8.1" 1491 | }, 1492 | "require-dev": { 1493 | "phpunit/phpunit": "^10.0" 1494 | }, 1495 | "type": "library", 1496 | "extra": { 1497 | "branch-alias": { 1498 | "dev-main": "4.0-dev" 1499 | } 1500 | }, 1501 | "autoload": { 1502 | "classmap": [ 1503 | "src/" 1504 | ] 1505 | }, 1506 | "notification-url": "https://packagist.org/downloads/", 1507 | "license": [ 1508 | "BSD-3-Clause" 1509 | ], 1510 | "authors": [ 1511 | { 1512 | "name": "Sebastian Bergmann", 1513 | "email": "sebastian@phpunit.de", 1514 | "role": "lead" 1515 | } 1516 | ], 1517 | "description": "Collection of value objects that represent the types of the PHP type system", 1518 | "homepage": "https://github.com/sebastianbergmann/type", 1519 | "support": { 1520 | "issues": "https://github.com/sebastianbergmann/type/issues", 1521 | "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" 1522 | }, 1523 | "funding": [ 1524 | { 1525 | "url": "https://github.com/sebastianbergmann", 1526 | "type": "github" 1527 | } 1528 | ], 1529 | "time": "2023-02-03T07:10:45+00:00" 1530 | }, 1531 | { 1532 | "name": "sebastian/version", 1533 | "version": "4.0.1", 1534 | "source": { 1535 | "type": "git", 1536 | "url": "https://github.com/sebastianbergmann/version.git", 1537 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" 1538 | }, 1539 | "dist": { 1540 | "type": "zip", 1541 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", 1542 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", 1543 | "shasum": "" 1544 | }, 1545 | "require": { 1546 | "php": ">=8.1" 1547 | }, 1548 | "type": "library", 1549 | "extra": { 1550 | "branch-alias": { 1551 | "dev-main": "4.0-dev" 1552 | } 1553 | }, 1554 | "autoload": { 1555 | "classmap": [ 1556 | "src/" 1557 | ] 1558 | }, 1559 | "notification-url": "https://packagist.org/downloads/", 1560 | "license": [ 1561 | "BSD-3-Clause" 1562 | ], 1563 | "authors": [ 1564 | { 1565 | "name": "Sebastian Bergmann", 1566 | "email": "sebastian@phpunit.de", 1567 | "role": "lead" 1568 | } 1569 | ], 1570 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1571 | "homepage": "https://github.com/sebastianbergmann/version", 1572 | "support": { 1573 | "issues": "https://github.com/sebastianbergmann/version/issues", 1574 | "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" 1575 | }, 1576 | "funding": [ 1577 | { 1578 | "url": "https://github.com/sebastianbergmann", 1579 | "type": "github" 1580 | } 1581 | ], 1582 | "time": "2023-02-07T11:34:05+00:00" 1583 | }, 1584 | { 1585 | "name": "theseer/tokenizer", 1586 | "version": "1.2.3", 1587 | "source": { 1588 | "type": "git", 1589 | "url": "https://github.com/theseer/tokenizer.git", 1590 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" 1591 | }, 1592 | "dist": { 1593 | "type": "zip", 1594 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1595 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", 1596 | "shasum": "" 1597 | }, 1598 | "require": { 1599 | "ext-dom": "*", 1600 | "ext-tokenizer": "*", 1601 | "ext-xmlwriter": "*", 1602 | "php": "^7.2 || ^8.0" 1603 | }, 1604 | "type": "library", 1605 | "autoload": { 1606 | "classmap": [ 1607 | "src/" 1608 | ] 1609 | }, 1610 | "notification-url": "https://packagist.org/downloads/", 1611 | "license": [ 1612 | "BSD-3-Clause" 1613 | ], 1614 | "authors": [ 1615 | { 1616 | "name": "Arne Blankerts", 1617 | "email": "arne@blankerts.de", 1618 | "role": "Developer" 1619 | } 1620 | ], 1621 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1622 | "support": { 1623 | "issues": "https://github.com/theseer/tokenizer/issues", 1624 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3" 1625 | }, 1626 | "funding": [ 1627 | { 1628 | "url": "https://github.com/theseer", 1629 | "type": "github" 1630 | } 1631 | ], 1632 | "time": "2024-03-03T12:36:25+00:00" 1633 | } 1634 | ], 1635 | "aliases": [], 1636 | "minimum-stability": "stable", 1637 | "stability-flags": {}, 1638 | "prefer-stable": false, 1639 | "prefer-lowest": false, 1640 | "platform": { 1641 | "php": ">=8.1" 1642 | }, 1643 | "platform-dev": {}, 1644 | "plugin-api-version": "2.6.0" 1645 | } 1646 | --------------------------------------------------------------------------------