Download the PHP package zahran/mapper without Composer

On this page you can find all versions of the php package zahran/mapper. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package mapper

zahran/mapper

Compile a JSON mapping template once, map many payloads with it.

A mapping template is data, not code: it says where each output field comes from, how to narrow and order lists, and how to transform values on the way out. Templates are validated when they compile, so a broken template fails once, up front, with the path to the problem — never silently, one payload at a time.

Installation

Requires PHP 8.0 or newer and ext-json. Tested on 8.0 through 8.5.

A first mapping

Payloads and templates may be given as JSON strings or as already-decoded PHP arrays.

The template

A template is an attribute. Most templates declare attributes and yield an object, but a template that declares a path or a type of its own yields whatever that describes.

Each attribute takes:

Key Meaning
name the key it writes in the output — required, non-empty
path where to read it from, as a list of segments
paths several sources gathered into one value, instead of path
default what to use when the payload holds nothing for it
required reject the payload when it holds nothing for it — see validating
conditions rewrite the value when it matches
mutators transform the value
cast convert the value
type "array" — see lists
attributes the shape of each item, on an "array" attribute
where, sort, distinct, offset, limit narrow a list

Anything else is rejected when the template compiles, naming the key and where it sat:

Paths

A path is a list of segments walked one at a time.

Keys and indexes

A negative index counts from the end of a list, but only after a literal lookup misses — a payload that really does hold the key -1 still reads the way it always did.

* — every value one level down

Wildcards nest: ["orders", "*", "lines", "*", "sku"] reads every sku of every line of every order. An element that lacks the key contributes nothing rather than a null.

** — every descendant

** matches the scope it stands on and everything nested anywhere beneath it, so ["kept", "**", "id"] searches only under kept.

{"where": …} — every child that matches

A clause reads path (optional — without it the element itself is tested), condition_type (any registered condition) and value. Give where a list of clauses and every one must hold:

A key the element lacks tests as null, so notnull says what you would expect about a key that is not there at all.

@ — the element itself

Inside a list of scalars there is no key to reach the element by:

Collection-shaped paths

A path holding *, ** or a filter resolves to the list of every match — and to an empty list when nothing matched, rather than to null:

Picking positions

A trailing list of indexes reads fixed positions out of whatever the path resolved to. $-prefixed entries are hard-coded values rather than positions:

A position the payload has nothing at reads as null, so the result always has as many entries as the template asked for. To take the first three items of a list and have the result shorten when there are fewer, use limit instead.

When the payload holds nothing

An explicit null is a value; only a key that is not there at all falls back on the default. An attribute with no path at all is a hard-coded value:

Gathering several paths

paths reads several sources into one list, so a mutator can combine them:

Without a mutator the gathered list is the value: {"name": "Both", "paths": [["a"], ["b"]]} yields [1, 2]. A $-prefixed entry is a hard-coded value sitting between the paths.

A path the payload has no value for contributes null and keeps the others in place (["Ada", null]). Only when every path is missing does the attribute fall back on its default.

Values gathered this way are piped whole rather than one element at a time, which is what lets implode and array_sum see the lot.

Lists

An attribute of "type": "array" maps each element of the array its path resolves to, using its own attributes as the shape of each item:

Lists nest to any depth, and each level's paths are relative to its own element:

A list whose source is absent, or is not an array, yields [] — unless the attribute is required, or the mapper is strict. A list attribute may omit its path to map the scope it is already standing on. default, cast, conditions and mutators belong on the nested attributes, not on the list itself.

Narrowing a list

where, sort, distinct, offset and limit decide which elements are mapped and in what order. They run in that order, and they run before anything is mapped, so a limit caps the work as well as the output.

Given {"items": [{"sku": "A", "price": 30, "active": true}, {"sku": "B", "price": 10, "active": false}, {"sku": "C", "price": 20, "active": true}]}:

All five together read as you would expect — keep the active ones, dearest first, take one:

where takes the same clauses as a filter segment: one clause, or a list of clauses that must all hold.

sort takes one sort key or a list of them, weighed in turn so the second only speaks where the first ties. direction is "asc" (the default) or "desc", and path is optional — without it the element itself is compared. Elements the payload has no value for gather at one end rather than scattering, and elements that tie on every key keep the order the payload gave them.

distinct keeps the first of each group. Give it the path — or paths — that identify an element, or true to compare whole elements. Identity is exact, so 1 and "1" stay apart.

Because distinct runs after sort, sorting first decides which duplicate survives.

offset and limit take a window of what is left. offset may be negative to count from the end; limit must be zero or more.

Narrowing works on any list, including one reached by a wildcard:

The pipeline

conditions, mutators and cast transform a value on its way out. They always run in that order — conditions, then mutators, then the cast — however the template is written, and each step feeds the next.

When the value is an array, each step applies to every element of it:

Values gathered with paths are the exception: they are piped whole, so that array_sum and implode can combine them.

The pipeline is skipped entirely when the payload holds nothing for the path — the default stands as written, rather than being cast or mutated.

Conditions

A condition rewrites the value to then when it matches, and to otherwise when it does not. Without otherwise a value that does not match is left alone.

Conditions run in declaration order and feed each other, so a later one sees what an earlier one wrote:

condition_type Matches when
eq the value loosely equals value ("1" equals 1)
neq the value is not identical to value
gt, gte, lt, lte the value compares that way against value
in the value is in value — a list, or a comma-separated string
not_in it is not
contains every needle in value appears in the value, case-insensitively
missing the negation of contains
null, notnull the value is, or is not, null
is_numeric, is_string, is_boolean, is_float (is_double) the value is of that type

Mutators

A mutator transforms the value. arguments is optional; when it is omitted the value is passed as the sole argument, and when it is given the literal "__value__" marks where the value goes.

Mutators run in declaration order:

Registered mutators: add, subtract, multiply, divide, modulo, power. Each takes one argument and coerces both sides to numbers; anything PHP would refuse to do arithmetic on yields null rather than raising.

Beyond those, a mutator may name a PHP function — but only one on the allow list, because a template is data and data must never reach arbitrary code. Every listed function is side-effect free and takes no callable, so none can be used to invoke another:

Anything else is refused when the template compiles:

Add your own with withMutator() or withFunctions().

Casts

type Result
integer "40"40, "40.9"40, null0
float "40.5"40.5, 4040.0
string 40"40", true"1", false and null""
boolean PHP truthiness: "0", "", null, []false
date reformatted with the required format; an empty value stays null

format is required for date, and the template is rejected when it is absent. These conversions are deliberately forgiving; strict mapping makes them refuse values they would have to invent an answer for.

The shape of the result

The root is an attribute like any other, so a mapping is not forced to end in an object.

A root list narrows with where, sort and the rest like any other, and a root value is piped like any other. map() therefore returns mixed.

Validating the payload

Mapping is forgiving by default: a missing path falls back on its default, and a list whose source is not an array yields []. Two things make it strict where you want it.

required

An explicit null satisfies required — only absence does not. On a list attribute, required also rejects a source that is not an array, but accepts an empty one. A failure names the attribute by its full dotted path in the output:

required cannot be combined with default, which already stands in for a missing value; the template is rejected when it compiles.

Strict mapping

A strict mapper refuses to invent values rather than coercing them:

The bar is faithfulness, not convertibility — (bool) "false" is true in PHP, and that is exactly the silent answer strictness is there to spare you. A strict integer takes an int, a bool, a whole float or a whole numeric string; a strict boolean takes a bool, 0/1 or "0"/"1", and refuses "false".

Null is carried through casts untouched rather than becoming 0, "" or false, and a path the payload has no value for still falls back on its default: absence is not an error unless the attribute says required. A list attribute whose source is present but is not a list fails instead of yielding []:

strict() returns a new mapper and survives registering more conditions, casts, mutators or functions. Custom casts stay trusted unless they implement Cast\Validating.

Errors

Everything the library throws implements Exception\MappingException, so one catch covers the lot.

Exception Extends Thrown
InvalidTemplateException InvalidArgumentException compiling — the template is wrong
InvalidJsonException InvalidArgumentException decoding — the JSON is wrong
InvalidPayloadException RuntimeException mapping — the payload is wrong

Whatever a cast or a mutator throws is carried out as an InvalidPayloadException naming the attribute, with the original kept as previous:

Extending the mapper

Every with*() method returns a new mapper and leaves the original alone.

Custom conditions work anywhere a condition does, including where clauses.

To widen the allow list instead of writing a mutator:

Reusing a compiled mapping

Compiling validates the template and builds the nodes; mapping walks them. When the same template serves many payloads, compile once:

mapMany() maps lazily and preserves keys — nothing is read until the generator is consumed:

Mapper::map($data, $template) compiles and maps in one call, which is convenient for a one-off and wasteful in a loop.

What this library does not do

Testing

Or against every supported PHP version, with Docker:

License

MIT.


All versions of mapper with dependencies

PHP Build Version
Package Version
Requires php Version >=8.0
ext-json Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package zahran/mapper contains the following files

Loading the files please wait ...