lossik / replacer
Replaces '$object->property' strings with values of registered objects and evaluates simple functions.
Requires
- php: ^8.2
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- squizlabs/php_codesniffer: ^3.13
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-22 01:51:15 UTC
README
Replaces '$object->property' strings with values of registered objects and evaluates simple functions written as
arrays, e.g. ['eqval()', '$user->age', 30]. Useful for configuration-driven conditions and values.
Installation
composer require lossik/replacer
Supported PHP
| branch | minimum patch version |
|---|---|
| 8.2 | 8.2.35 |
| 8.3 | 8.3.36 |
| 8.4 | 8.4.27 |
| 8.5 | 8.5.12 |
Older patch versions are vulnerable to PHP2Shell (CVE-2026-109618, CVE-2026-109619) and are not supported.
composer.json requires only php: ^8.2, because distributions backport security fixes without bumping the
version number; the CI enforces the patched minimums above. Projects on older PHP stay on 1.0.x.
Quick start
<?php require 'vendor/autoload.php'; use Lossik\Replacer\Functions; use Lossik\Replacer\Replacer; $replacer = (new Replacer()) ->addObject((object) ['name' => 'Alice', 'age' => 30], 'user') ->addObject(['city' => 'Prague'], 'address'); echo json_encode($replacer->replace('$user->name')), "\n"; // "Alice" echo json_encode($replacer->replace('$address->city')), "\n"; // "Prague" echo json_encode($replacer->replace('$nobody', false, '-')), "\n"; // "-" $functions = new Functions($replacer); echo json_encode($functions->callFunction(['eqval()', '$user->age', 30])), "\n"; // true echo json_encode($functions->callFunction(['concat()', '$user->name', ', ', '$address->city'])), "\n"; // "Alice, Prague" echo json_encode($functions->callFunction(['and()', ['is()', '$user->name'], ['no()', false]])), "\n"; // true
API
Lossik\Replacer\Replacer
An expression is a string $name (the whole registered value) or $name->property (an object property or an array
key). Only one -> is allowed.
| method | description |
|---|---|
addObject($obj, $name) |
registers an object, array, scalar or null under $name (without $); fluent |
removeObject($name) |
removes it again; the name as passed to addObject(), optionally with a leading $ ('row' or '$row'; an object added as addObject($o, '$x') is stored as $$x); fluent |
isReplaceable($string, $syntaxOnly = true) |
checks the syntax; with $syntaxOnly = false also that the object is registered and has the property / key |
replace($string, $need = true, $default = null) |
returns the value; when the expression cannot be replaced it throws LogicException, or returns $default if $need is false |
Replacer::replaceable($string) |
static syntax check, same as isReplaceable($string) |
<?php require 'vendor/autoload.php'; use Lossik\Replacer\Replacer; $replacer = (new Replacer())->addObject(['0' => 'zero', '' => 'empty', 'x' => 1], 'row'); echo json_encode(Replacer::replaceable('$row->x')), "\n"; // true echo json_encode(Replacer::replaceable('$a->b->c')), "\n"; // false echo json_encode($replacer->isReplaceable('$row->y', false)), "\n"; // false echo json_encode($replacer->replace('$row->0')), "\n"; // "zero" echo json_encode($replacer->replace('$row->')), "\n"; // "empty" $replacer->removeObject('row'); echo json_encode($replacer->replace('$row', false, 'GONE')), "\n"; // "GONE"
Lossik\Replacer\Functions
A call is an array whose first element is the function name with (), followed by the arguments:
['name()', arg1, arg2, …]. Before the call, each argument that is itself a call is evaluated, and each argument
that is an expression is replaced (replace($arg, false), so a missing variable becomes null, see below).
Other arguments are passed as they are.
| method | description |
|---|---|
__construct(Replacer $replacer) |
registers the built-in functions; expressions are resolved by $replacer |
callFunction($function) |
evaluates the call; throws LogicException when $function is not a call or the name is not registered |
isFunction($function) |
checks the syntax of a call (not whether the name is registered) |
addFunction($callable, $funcName) |
registers a callable under $funcName (without ()); fluent |
Functions::isFunctionS($string) |
static syntax check, same as isFunction() |
Built-in functions:
| function | result |
|---|---|
is(value) |
(bool) value |
no(value) |
!value |
eqval(a, b) |
a === b |
no_eqval(a, b) |
a !== b |
strpos_is(haystack, needle, position) |
strpos(haystack, needle) === position |
in(value, array) |
in_array(value, array) (loose comparison) |
group_concat(array, delimiter) |
implode(delimiter, array) |
column(rows, key) |
list of unique non-empty values of key in the rows (arrays or objects) |
explode(delimiter, string) |
explode(delimiter, string) |
get_value(arrayOrObject, key) |
the array key or object property, null when missing |
remove(string, search) |
string without all occurrences of search |
concat(a, …) |
all arguments concatenated |
and(…) |
true when all arguments are truthy |
or(…) |
true when any argument is truthy |
<?php require 'vendor/autoload.php'; use Lossik\Replacer\Functions; use Lossik\Replacer\Replacer; $functions = (new Functions(new Replacer()))->addFunction(fn ($a, $b) => $a + $b, 'sum'); echo json_encode($functions->callFunction(['sum()', 1, 2])), "\n"; // 3 echo json_encode($functions->callFunction(['is()', ['no()', false]])), "\n"; // true echo json_encode($functions->callFunction(['strpos_is()', 'abc', 'b', 1])), "\n"; // true echo json_encode($functions->callFunction(['column()', [['id' => 1], ['id' => 1], (object) ['id' => 3]], 'id'])), "\n"; // [1,3] echo json_encode(Functions::isFunctionS(['nope()'])), "\n"; // true
Missing variables in function arguments
An expression whose object (or property) is not registered is passed to the function as null. The built-in
functions accept such a null without a warning; any other wrong type fails as before.
| function | missing argument | result |
|---|---|---|
is, no, eqval, no_eqval, concat, and, or |
any | null is used as the value |
strpos_is |
haystack or needle | treated as '', so a missing needle with expected position 0 gives true |
in |
array | false |
in |
value | loose comparison (in_array): matches any element loosely equal to null — '', 0, 0.0, false, null, [] |
group_concat |
array | '' |
group_concat |
delimiter | treated as '': the values joined without a separator |
column |
rows / key | [] / lookup of the key '' |
explode |
delimiter | [] (a literal '' delimiter still throws ValueError) |
explode |
string | [''] |
get_value |
array | null |
get_value |
key | lookup of the key '' |
remove |
string | '' |
remove |
search | treated as '': the string is returned unchanged |
<?php require 'vendor/autoload.php'; use Lossik\Replacer\Functions; use Lossik\Replacer\Replacer; $functions = new Functions(new Replacer()); echo json_encode($functions->callFunction(['in()', '$missing', ['', 'a']])), "\n"; // true echo json_encode($functions->callFunction(['in()', 'a', '$missing'])), "\n"; // false echo json_encode($functions->callFunction(['strpos_is()', 'abc', '$missing', 0])), "\n"; // true echo json_encode($functions->callFunction(['explode()', '$missing', 'a,b'])), "\n"; // [] echo json_encode($functions->callFunction(['explode()', ',', '$missing'])), "\n"; // [""] echo json_encode($functions->callFunction(['group_concat()', ['a', 'b'], '$missing'])), "\n"; // "ab" echo json_encode($functions->callFunction(['remove()', 'abc', '$missing'])), "\n"; // "abc"
Lossik\Replacer\TReplacer
A trait for classes that resolve configuration values: initReplacer() creates $this->replacer and
$this->functions; getValue($item, $need = true) returns the replaced expression, the result of a call, or $item
unchanged.
<?php require 'vendor/autoload.php'; use Lossik\Replacer\TReplacer; final class Condition { use TReplacer; public function __construct(array $row) { $this->initReplacer(); $this->replacer->addObject($row, 'row'); } public function evaluate(mixed $item): mixed { return $this->getValue($item); } } $condition = new Condition(['state' => 'active']); echo json_encode($condition->evaluate('$row->state')), "\n"; // "active" echo json_encode($condition->evaluate(['eqval()', '$row->state', 'active'])), "\n"; // true echo json_encode($condition->evaluate('plain')), "\n"; // "plain"
Lossik\Utils\Arrays
Eight static helpers: assoc, getValue, difference, leftMerge, column, unsetNull, first, unsetEach.
Their behaviour is characterized by tests/ArraysTest.php; rely only on what the tests hold. column() returns the
unique non-empty values of a column (falsy values such as 0 and '' are dropped).
<?php require 'vendor/autoload.php'; use Lossik\Utils\Arrays; $rows = [['id' => 1], ['id' => 2], ['id' => 1], ['id' => 0], (object) ['id' => 3]]; echo json_encode(array_values(Arrays::column($rows, 'id'))), "\n"; // [1,2,3] echo json_encode(Arrays::getValue(['k' => 'v'], 'k')), "\n"; // "v" echo json_encode(Arrays::first(['b' => 2, 'c' => 3], ['a', 'b', 'c'])), "\n"; // 2
Known issues
concat()without any argument (Functions::_concat()) throwsArgumentCountError.- Properties are looked up with
property_exists(): only real (declared or dynamic) properties and array keys are visible. Magic properties served by__get()are not, so$row->nameon such an object cannot be replaced andget_value()/column()returnnull/[];ArrayAccessobjects are not read as arrays.
License
MIT, see LICENSE.