{"id":21107245,"url":"https://github.com/glaivepro/php-sf","last_synced_at":"2025-07-08T16:31:33.268Z","repository":{"id":39754285,"uuid":"499670355","full_name":"GlaivePro/php-sf","owner":"GlaivePro","description":null,"archived":false,"fork":false,"pushed_at":"2024-05-28T21:06:10.000Z","size":185,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-15T18:39:51.210Z","etag":null,"topics":["geospatial","gis","hacktoberfest","php","postgresql","simple-features","spatial"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GlaivePro.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-06-03T22:44:14.000Z","updated_at":"2024-05-28T21:06:14.000Z","dependencies_parsed_at":"2022-07-31T23:38:09.806Z","dependency_job_id":"42f03512-2c33-487c-89c6-7bea202a7562","html_url":"https://github.com/GlaivePro/php-sf","commit_stats":{"total_commits":84,"total_committers":2,"mean_commits":42.0,"dds":"0.023809523809523836","last_synced_commit":"5ebf1875fba925b2da264d6f9a6822920629a68c"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlaivePro%2Fphp-sf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlaivePro%2Fphp-sf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlaivePro%2Fphp-sf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GlaivePro%2Fphp-sf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GlaivePro","download_url":"https://codeload.github.com/GlaivePro/php-sf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225449686,"owners_count":17476094,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["geospatial","gis","hacktoberfest","php","postgresql","simple-features","spatial"],"created_at":"2024-11-20T00:37:16.057Z","updated_at":"2024-11-20T00:37:17.921Z","avatar_url":"https://github.com/GlaivePro.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP Simple Features SQL\n\nBuild SFA-SQL (SQL/MM) expressions using SFA-CA syntax.\n\n\u003e **Note**\n\u003e Currently this doc contains not only description of this package and\n\u003e documentation sketch, but plans and research as well. WIP. Unreleased.\n\n[Simple Features](https://en.wikipedia.org/wiki/Simple_Features) is a standard\n(multiple standards actually) by Open Geospatial Consortium defining models for\ngeospatial data. The standard defines object oriented and SQL access (known as\nSFA-SQL, SQL/MM and ArcSDE among others) to these models. This package\nimplements the object oriented SFA API in PHP and produces SFA-SQL expressions\nfrom it.\n\nIt can be used to build SQL statements for systems such as PostGIS,\nSpatiaLite, MySQL spatial and others implementing SFA-SQL or SQL-MM.\n\n## Feature examples\n\nThe objects in this lib allow chaining SFA to create SQL expressions:\n\n```php\n$geometry = new Geometry('ST_MakePoint(?, ?)', [3, 5]);\n$another = new Geometry(\"'LINESTRING ( 2 0, 0 2 )'::geometry\");\n\n$envelope = $geometry-\u003eenvelope();\n\n(string) $envelope; // ST_Envelope(ST_MakePoint(?, ?))\n$envelope-\u003ebindings; // [3, 5]\n\n$expression = $geometry\n\t-\u003eunion($another)\n\t-\u003ebuffer($geometry-\u003edistance($another))\n\t-\u003ebuffer(5.2)\n\t-\u003econvexHull();\n\n(string) $expression; // ST_ConvexHull(ST_Buffer(ST_Buffer(ST_Union(ST_MakePoint(?, ?), 'LINESTRING ( 2 0, 0 2 )'::geometry), ST_Distance(ST_MakePoint(?, ?), ST_MakePoint(1, 1))), ?))\n$expression-\u003ebindings; // [3, 5, 3, 5, 5.2]\n```\n\nYou can also specify columns:\n\n```php\n$geom = new Geometry('the_geom');\n$srid = $geom-\u003esrid();\n(string) $srid; // ST_SRID(the_geom);\n\n// alternate syntaxes to retrieve the SQL expression:\n$srid-\u003esql; // ST_SRID(the_geom);\n$srid-\u003e__toString(); // ST_SRID(the_geom);\n```\n\nRaw expressions can be prevented from going to bindings by wrapping them in an\nExpression object:\n\n```php\n$geom = new Geometry('geom');\n\n// Works as expected\n$bufferSize = new Expression('3 + 5');\n$buf1 = $geom-\u003ebuffer($bufferSize);\n(string) $buf1; // ST_Buffer(geom, 3 + 5)\n\n// Expressions can also have bindings\n$dynamicBuffer = new Expression('bufpad + ?', [3]);\n$buf2 = $geom-\u003ebuffer($dynamicBuffer);\n(string) $buf2; // ST_Buffer(geom, bufpad + ?)\n$buf2-\u003ebindings; // [3]\n```\n\nInitiate objects using constructors\n\n```php\nSfc::pointFromText('POINT(-71.064544 42.28787)')-\u003eX();\n// Produces ST_X(ST_PointFromText(?)) with binding 'POINT(-71.064544 42.28787)'\n```\n\nUse grammar-specific constructors and methods:\n\n```php\nPostGIS\\Sfc::makePoint(1, 3)-\u003esetSRID(3059);\n// Produces ST_SetSRID(ST_MakePoint(?, ?), ?) with bindings [1, 3, 3059]\n\nSpatiaLite\\Sfc::makePoint(1, 3)-\u003esetSRID(3059);\n// Produces SetSRID(MakePoint(?, ?), ?) with bindings [1, 3, 3059]\n```\n\nUse factories with drivers set on them:\n\n```php\n// simple feature classes\n$sf = new Sf('PostGIS');\n$sf-\u003epoint('mycol'); // a PostGIS/Point wrapping the `mycol`\n\n// simple feature constructors\n$sfc = new Sfc('PostGIS');\n$sfc-\u003emakePoint(1, 3); // a PostGIS/Point with ST_MakePoint(?, ?) and [1, 3] bindings\n```\n\nIf you can't use bindings, you can use the quoting mode:\n\n```php\n// specify a quoter\nExpression::setQuoter($myPdoInstance-\u003equote(...));\n\n// use as normally, bindings will always be empty\nSfc::makePoint(1, 3)-\u003esetSRID(3059); // ST_SetSRID(ST_MakePoint(1, 3), 3059)\n```\n\n## Usage examples\n\nThis section presents some plain examples in plain PDO.\n\nQuery a relation between columns `yard` and `road`:\n\n```php\n$yard = new Geometry('yard');\n$road = new Geometry('road');\n\n$expr = $yard-\u003eintersects($road);\n\n$statement = $pdo-\u003eprepare(\"\n\tSELECT *\n\tFROM features\n\tWHERE $expr\n\");\n\n$statement-\u003eexecute($expr-\u003ebindings);\n```\n\nQuery a column against a raw statement:\n\n```php\n$box = Geometry::fromMethod('ST_MakeEnvelope', 0, 0, 1, 3);\n\n// You may skip trivial `new Geometry('geom')` and just supply the column name\n// or any other SQL string.\n$contains = $box-\u003econtains('geom');\n// produces ST_Contains(ST_MakeEnvelope(?, ?, ?, ?), geom)\n\n$statement = $pdo-\u003eprepare(\"\n\tSELECT *\n\tFROM features\n\tWHERE\n\t\tcreated_at \u003e ? \n\t\tAND $contains\n\");\n\n// merge bindings with other bindings\n$statement-\u003eexecute([$createdFrom, ...$contains-\u003ebindings]);\n```\n\nSet a value:\n\n```php\n$point = new Point('ST_MakePoint(?, ?)', [2, 7]);\n// Equivalent to:\n// $point = Point::fromMethod('ST_MakePoint', [2, 7]);\n\n$statement = $pdo-\u003eprepare(\"\n\tUPDATE features\n\tSET location = $point\n\tWHERE id = ?\n\");\n\n// merge bindings with other bindings\n$statement-\u003eexecute([...$point-\u003ebindings, $id]);\n```\n\n## Scope and goals\n\nThe main goal is to support creation of PostGIS expressions which follows the\nArcSDE implementation. If method names are different, we should have aliases\nto support PostGIS naming. If arguments are different, we should strive to\nsupport all cases.\n\nMySQL, MariaDB and SpatiaLite support would also be nice.\n\nHowever, at this point in time, this is not supposed to be a compatibility\nlayer between the databases. If the same function does different things on\ndifferent databases, we are not homogenizing the behaviour. We just let you\ncall the function as is.\n\nA homogenization layer might be useful, but that's another step, after the raw\nbuilder.\n\n### Roadmap\n\n- Document internals (class, trait \u0026 contract modelling; callFromMethod, wrap etc)\n- All the SFA model\n- All the other SFA-SQL functions\n- Internal support for return and arg types (e.g. `IntExpression`, `AreaExpression`)\n- PostGIS specific stuff like constructors, additional args, geography stuff and so on\n- Testing support, including a SpatiaLite driver and ability to replace \"drivers\"\n- Laravel query builder and Eloquent (casts, setting, queries...) integration\n- MariaDB support\n\n## Architecture\n\nSome implementation details to help organizing the stuff.\n\n### Expressions\n\nThe main building block is the `Expression` class that represents an SQL\nexpression along with bindings. All the geometry classes are subclasses of\n`Expression`. All expressions have two public properties and they are stringable:\n\n```php\n$expression-\u003esql; // string\n$expression-\u003ebindings; // array\n(string) $expression; // same as $expression-\u003esql\n```\n\nThere are three ways to create any expression object:\n\n```php\n// the constructor allows specifying sql and optionally bindings\nnew Expression('mycolumn');\nnew Expression('count(mycolumn)');\nnew Expression('? + ?', [1, 3]);\n// You can also instantiate any subclass of expression\n// here's an expression that refers to your location column and knows that\n// it represents a point-typed value\nnew Point('the_location');\n\n// the make factory can instantiate an expression as well\nExpression::make('mycolumn');\n// it can wrap an existing expression to cast it to another type:\nGeometry::make($someExpression); // return a Geometry with the same $sql and $bindings as $someExpression has\n// the goal is to enforce type — it only accepts strings or subclasses\n// i.e. the argument of Expression::make is covariant and violates Liskov substitution principle\nGeometry::make(new Geometry('the_location')); // ok\nGeometry::make(new Point('the_location')); // ok, point can be treated as geometry\nPoint::make(new Geometry('the_location')); // throws InvalidExpressionType — arbitrary geometry can't be treated as a point\nGeometry::make('mygeom'); // ok, we trust you know what you're doing\n\n// the fromMethod factory constructs an SQL statement calling a function\nExpression::fromMethod('VERSION'); // makes an expression with $sql equal to VERSION()\n// it puts raw args as bindings\nPoint::fromMethod('ST_MakePoint', 23, 56); // $sql is ST_MakeLine(?, ?) and $bindings is [23, 56]\n// expression args are inserted as args\nLineString::fromMethod('ST_MakeLine', new Point('start_point_col'), new Point('end_point_col')); // $sql is ST_MakeLine(start_point_col, end_point_col)\n// expression arg bindings are merged\n$point1 = Point::fromMethod('POINT', 23, 56);\n$point2 = Point::fromMethod('POINT', 23, 57);\nLineString::fromMethod('ST_MakeLine', $point1, $point2); // $sql is ST_MakeLine(POINT(?, ?), POINT(?, ?)) and $bindings is [23, 56, 23, 57]\n```\n\n### Expression subclasses\n\nThe goal of expression subclasses is to know what type of object we are dealing\nwith and what methods are available on it.\n\n```php\n$point = new Point('some_col');\n$point-\u003ex(); // an Expression with $sql equal to ST_X(some_col)\n\n$line = new LineString('line_column');\n$endPoint = $line-\u003epointN(-1); // a Point with $sql = ST_PointN(line_column, ?) and $bindings [-1]\n$endPoint-\u003ex(); // and expression with $sql = ST_X(ST_PointN(line_column, ?)) and $bindings = [-1]\n$endPoint-\u003epointN(-1); // throws exception as you cant ST_PointN on a point\n```\n\nCurrently we only have expression subclasses for geometry, but we might have\nother types (integer valued expression, area valued expression and so on) later.\n\n### Geometry class\n\nIn addition to base Expression tools, `Geometry` (and all the GIS subclasses) has these helpers:\n\n```php\n// Wrap the expression in a function call\n$myGeometry = new Geometry('col');\n$myGeometry-\u003ewrap('COUNT'); // COUNT(col)\n\n// Query the geometry against another geometry\n$myGeometry = new Point('start');\n$myGeometry-\u003equery('ST_Distance', new Point('end')); // Expression holding ST_Distance(start, end)\n// This also autowraps the second arg as a geometry if you pass a string\n$myGeometry-\u003equery('ST_Distance', 'end'); // Expression holding ST_Distance(start, end)\n\n// Combine the geometry with another\n$point = new Point('start');\n$point-\u003ecombine('ST_Union', 'end'); // Geometry holding ST_Union(start, end)\n// The only difference is that -\u003equery() returns a base Expression, but -\u003ecombine() returns a Geometry\n```\n\n### Hierarchy and composition\n\nThe geometry class hierarchy as defined in the OGC spec along with the required\nmethods is defined in interfaces in `OGC\\Contracts`.\n\nThe default mapping from OOP methods to SQL as described in the OGC spec is\nimplemented in `OGC\\Traits`.\n\nThe classes are built somewhat like this (pseudocode):\n\n```php\nclass PostGIS\\Line extends PostGIS\\LineString implements OGC\\Contracts\\Line\n{\n\t// default implementation\n\tuse OGC\\Traits\\Line;\n\n\t// PostGIS-specific overrides and additions\n\tpublic function someLineMethod($anArg)\n\t{\n\t\t//\n\t}\n}\n```\n\nWe also include classes in the `OGC` namespace that implement everything as\ndefined in the spec. Seemingly there are no DBMSs that implement everything\nexactly like that, but let it be for now.\n\nBtw if an OGC-defined method is not implemented in the particular DBMS, you\nwill get a MethodNotImplemented exception thrown.\n\n### Constructors\n\nConstructors are implemented in classes named `Sfc` (simple feature constructor).\n\nYou have the defaults in `OGC\\Sfc`:\n\n```php\nSfc::pointFromText('POINT(23 56)'); // returns a Point with $sql = ST_PointFromText(?) and $bindings = ['POINT(23 56)']\n```\n\nAnd the more specific stuff in the specific `Sfc`s:\n\n```php\nPostGIS\\Sfc::makePointM(23, 56, 5); // returns a Point with $sql = ST_MakePointM(?, ?, ?) and $bindings = [23, 56, 5]\n```\n\nThe `Sfc` classes also have the `[type]FromMethod` magic methods, e.g.\n```php\nPostGIS\\Sfc::pointFromMethod(...$args); // same as PostGIS\\Point::fromMethod(...$args)\nMySQL\\Sfc::pointFromMethod(...$args); // same as MySQL\\Point::fromMethod(...$args)\n```\n\nWhich is useful when you want to share functionality across Sfc classes but\nneed to return a geometry class belonging to the particular DBMS.\n\n\n## Terminology and references\n\n- **SF** — [Simple Features](https://en.wikipedia.org/wiki/Simple_Features),\nthe geometries and the interface to interact with them.\n- **SFA** — Simple Feature Access, same thing as Simple Features.\n- **OGC** — Open Geospatial Consortium\n- **SQL/MM** — SQL multimedia schema defined by ISO/IEC 13249, including SQL/MM\nspatial defined in ISO/IEC 13249-3.\n- **SFA-CA** — Geometry model defined by OGC in [SFA part 1](https://www.ogc.org/standards/sfa)\n- **SFA-SQL** — SQL schema for simple features defined by OGC in [SFA part 2](https://www.ogc.org/standards/sfs)\n- **ArcSDE** — the thing in ArcGIS suite that communicates with a relational\ndatabase. Some RDBMS treat it as a de facto standard and sometimes follows it\nover SFA (dropping SFA methods that ArcSDE is not using https://trac.osgeo.org/postgis/changeset/8680)\nor SQL/MM (implemented for ArcSDE not SQL/MM https://postgis.net/docs/ST_OrderingEquals.html).\n\nOur API mainly tries to support the \n[OpenGIS SFA standard](https://www.ogc.org/standards/sfa), but it might also\ninclude some common aliases from ArcSDE and others.\n\nThe supportable SQL is described in multiple standards and other resources:\n\n- [OpenGIS SFA part 2](https://www.ogc.org/standards/sfs)\n- SQL/MM (ISO/IEC 13249-3), no free access\n- [ArcSDE](https://desktop.arcgis.com/en/arcmap/latest/manage-data/using-sql-with-gdbs/a-quick-tour-of-sql-functions-used-with-st-geometry.htm)\n- [PostGIS](https://postgis.net/docs/reference.html)\n\nSome might also be interested to see ISO 19125 (SFA), ISO/IEC 13249-3 (SQL/MM spatial)\nand ISO 19107 (spatial schema).\n\n## Similar projects\n\n### brick/geo\n\nhttps://github.com/brick/geo\n\nAllows doing GIS stuff inside PHP, using an underlying engine like GEOS ext,\nPostGIS and others. Includes SF query engines for various databases.\n\n### brick/geo-doctrine\n\nhttps://github.com/brick/geo-doctrine\n\nGIS mappings for Doctrine and functions for DQL.\n\n### GeoPHP\n\nhttps://geophp.net/\n\nDoes GIS stuff inside PHP and allows communicating with DB using WK and other\nformats.\n\n### vincjo/spatialite\n\nhttps://github.com/vincjo/spatialite\n\nPHP interface to SpatiaLite.\n\n### elevenlab/php-ogc\n\nhttps://github.com/eleven-lab/php-ogc\n\nImplementation of SFA types in PHP.\n\n### elevenlab/laravel-geo\n\nhttps://github.com/eleven-lab/laravel-geo\n\nExtending Laravel query builder and Eloquent with some of the SFA-SQL methods.\n\n### mstaack/laravel-postgis\n\nhttps://github.com/mstaack/laravel-postgis\n\nImplementation of SFA types with custom interface and support for those objects\nin Eloquent. Also adds support for these types in migrations.\n\n### geo-io/geometry\n\nhttps://github.com/geo-io/geometry\n\nImplementation of SFA object oriented features in plain PHP. Part of project\n[Geo I/O](https://github.com/geo-io) that includes other Geo PHP tools as well.\n\n### jsor/doctrine-postgis\n\nhttps://github.com/jsor/doctrine-postgis\n\nPostGIS support for Doctrine, see the [supported functions](https://github.com/jsor/doctrine-postgis/blob/main/docs/function-index.md).\nWe should strive to support the same.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglaivepro%2Fphp-sf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglaivepro%2Fphp-sf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglaivepro%2Fphp-sf/lists"}