Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wkb Parser Laravel Package

geo-io/wkb-parser

Parse Well-Known Binary (WKB) geometry data in PHP with geo-io/wkb-parser. This lightweight library decodes binary spatial geometries into usable objects, making it easy to work with WKB data from databases, APIs, and GIS files in PHP applications.

Deep Wiki
Context7

Getting Started

geo-io/wkb-parser is a small, spec-focused reader for Well-Known Binary geometry. It takes a PSR-7 stream containing WKB bytes and returns geometry objects from geo-io/geos (or your own objects, via a pluggable factory). It is not a spatial library — it does not compute areas, intersections, or reprojections. Treat it as the decoding half of a serialization layer.

Install:

composer require geo-io/wkb-parser geo-io/geos

You also need any PSR-7 stream implementation (most Laravel apps already have guzzlehttp/psr7 transitively; nyholm/psr7 works too).

First use case — decode a database geometry column. This is the 90% case. MySQL's ST_AsBinary() and PostGIS's ST_AsBinary() both return raw WKB, so the round trip is:

use GeoIO\WKB\Parser\Parser;
use GeoIO\WKB\Parser\ParserFactory;
use GuzzleHttp\Psr7\Utils;

$row = DB::table('locations')->select('id', DB::raw('ST_AsBinary(geom) as wkb'))->first();

$factory  = new ParserFactory();
$parser   = $factory->createParser();
$geometry = $parser->parse(Utils::streamFor($row->wkb));

// $geometry is a GeoIO\Geometry\Point / LineString / Polygon / ...

Where to look first:

  1. The package README for the exact ParserFactory signature — the factory injection point is the main API surface and the only thing that changes across minor versions.
  2. The geo-io/geos model classes (Point, LineString, Polygon, GeometryCollection, Coordinate) — these are what you'll actually be pushing into your DTOs and API responses.
  3. The geo-io/wkb-writer package (separate) if you need the reverse direction. This package does not serialize.

Do not reach for this library if you only need to move geometries between the database and GeoJSON. Your database can do ST_AsGeoJSON() for free. WKB parsing earns its keep when you (a) need to inspect individual coordinates in PHP, (b) receive WKB from a client that chose it for payload size, or (c) want to normalize third-party spatial data into geo-io/geos objects before re-emitting.

Implementation Patterns

Wrap the parser in a singleton service. Parser construction is cheap but the factory is stateless — resolve one instance from the container rather than new-ing it in every controller or job:

// AppServiceProvider
$this->app->singleton(Parser::class, fn () => (new ParserFactory())->createParser());

Then inject Parser anywhere. This also gives you one place to swap in a custom factory later.

Use a custom factory to skip the intermediate geometry objects. If you don't want geo-io/geos objects leaking into your domain, implement the factory interface and return your own value objects (Coordinate, Polygon, etc.) directly. This is the intended extension point — the parser never touches coordinate values itself, it only calls factory methods. You get WKB → your DTOs with no mapping layer.

Decode-on-read, via a cast or accessor. For Eloquent models with a binary geometry attribute:

protected function geom(): Attribute
{
    return Attribute::get(fn ($value) => $value === null
        ? null
        : app(Parser::class)->parse(Utils::streamFor($value)));
}

Pair it with a select that returns the raw binary (ST_AsBinary(geom) as geom), not the MySQL native GEOMETRY type — MySQL's internal format is not WKB.

Chunk and stream large result sets. Because the parser works on streams, you can parse row-by-row inside chunkById() and never hold more than one geometry payload in memory. This is the pattern for exporting a table of shapes to GeoJSON:

Location::query()->selectRaw('id, ST_AsBinary(geom) as wkb')->chunkById(500, function ($rows) {
    foreach ($rows as $row) {
        $geometry = app(Parser::class)->parse(Utils::streamFor($row->wkb));
        // transform to GeoJSON feature and write to a streamed response
    }
});

Normalize inbound payloads at the edge. If an API accepts a base64 or hex-encoded WKB blob (common from mobile SDKs), decode it in a FormRequest or a route middleware, validate the header byte (0x00/0x01) before handing it to the parser, and store it as a binary/bytea column. Failing fast on malformed input beats catching parser exceptions deep in a job.

Iterate geometry collections explicitly. A GeometryCollection is returned as a top-level object with child geometries; recursive rendering (to GeoJSON, to a map payload, to a diff) is your responsibility. Write one recursive transform per output format rather than sprinkling instanceof checks across controllers.

Keep spatial queries in the database. The parser gives you the geometry in PHP for display and transformation. Do ST_Intersects, ST_Distance, ST_Transform, and SRID handling in MySQL/PostGIS — this library has none of that.

Gotchas and Tips

SRID is not in vanilla WKB. The WKB spec has no room for a spatial reference ID. If you're reading from PostGIS, ST_AsBinary strips the SRID; select ST_SRID(geom) alongside it and carry it in your DTO. MySQL sometimes emits EWKB (with an SRID prefix) — whether this parser tolerates the EWKB variant is version-dependent, so don't assume it will. When in doubt, ST_AsBinary explicitly and keep SRID out of band.

WKB ≠ WKT ≠ GeoJSON. Three different encodings. If your data comes in as WKT (POINT(1 2)) or GeoJSON, this package will fail on it. Use geo-io/geojson-parser for GeoJSON; WKT generally requires a different dependency or a database cast.

Endianness is handled for you — the leading byte order byte is read and respected. You do not need to normalize byte order before parsing. But you do need to know whether the input is big- or little-endian when you're hand-constructing test fixtures.

Streams are cursor-based. The parser consumes the stream from its current position. If you've already read or rewound a stream, rewind it ($stream->rewind()) before parsing, or hand it a fresh Utils::streamFor($bytes). A "parser produced garbage" bug is almost always a cursor-position bug.

Wrap parsing in a try/catch on the package's exception interface, not on a bare \Exception. Malformed WKB throws a parser-specific exception; distinguish it from a DB error so you can return a 422 rather than a 500. Invalid geometry type codes and truncated payloads are the two failures you'll see in practice.

Handle NULL geometries at the boundary. ST_AsBinary(NULL) returns NULL; Utils::streamFor(null) produces an empty stream and the parser will throw on the truncated header. Check $value === null before parsing — the accessor pattern above does this.

Empty geometries are legal. POINT EMPTY, LINESTRING EMPTY etc. are valid WKB and will parse to empty geometry objects. Code that assumes at least one coordinate ($point->getCoordinate()) will blow up. Guard when you build centroids or bounding boxes.

This is a thin, low-activity package. Pin the version in composer.json, and if it's a load-bearing dependency, add a fixture-based test suite that covers every geometry type you actually store. The parser is a spec implementation — the spec is stable, so the risk is maintenance, not churn. Budget a day to absorb it if the package ever goes unmaintained; the byte format is well documented and the parser is a few hundred lines.

Custom factory is the extension point, not subclassing the parser. The factory interface covers every geometry type. If you need a new coordinate representation, extra metadata on Point, or lazily-built geometries, do it there. Subclassing the parser is unnecessary and will break on upgrades.

Measure before you optimize. WKB is compact and parsing is fast; the bottleneck in a real Laravel app is almost always the SELECT that produced the bytes, not the parse. If you're parsing thousands of geometries per request, move the transformation into the database (GeoJSON output) or a queued job before you consider a faster parser.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource