0

Using PHP, I'd like to bind a value to a JSON string similar to how it is done when preparing an SQL statement for a database using a question mark. For example, I would like do something like the following:

$v1 = 'v1';
$k2 = 'k2';
$result = json_bind('[{"k1": ?}, {?: "v2"}]', $v1, $k2);
echo $result; // [{"k1": "v1"}, {"k2": "v2"}]
  1. I don't want to just do a pure string replace as this technique doesn't escape/quote the values properly and it can't format values like arrays and objects properly.
  2. I don't want to just create an array (or an object) first, assign my params, and use json_encode because this gets messy since PHP's syntax differs from JSON's syntax and I need something more dynamic because I may not always know the structure of the JSON.

Is there already a library or function that can do this very thing?

Ziminji
  • 1,286
  • 1
  • 14
  • 18
  • What is `json_bind()` – RiggsFolly Sep 01 '18 at 18:56
  • That is a "made-up" function to demonstrate what I want to do. – Ziminji Sep 01 '18 at 18:58
  • preg_replace_callback – splash58 Sep 01 '18 at 18:59
  • `preg_replace_callback` can't necessarily distinguish between a question mark in a literal and elsewhere. Looking for something that takes a more lexical analyzer approach. – Ziminji Sep 01 '18 at 19:02
  • "I may not always know the structure of the JSON". Wow would like see how you test it and are you sure the result is always OK. I think the library not exist because it would be mega magic. – daremachine Sep 01 '18 at 19:03
  • 2
    what works for me is to put everything in an associative array en then json_encode() it. Keys and values are separate, and the function takes care of correctly writting the json string. – Juan Sep 01 '18 at 19:05
  • You would have to write your own lexer then, if you can't ensure there's no literal `?` in your template. It seems there would be little use for such a thing outside of trivial structures. Enumerated placeholders wouldn't make sense for deeply nested structures IMO. Though I would say a regex is [feasible](https://stackoverflow.com/questions/2583472/regex-to-validate-json); it's more tricky to use it for search and replace here. – mario Sep 01 '18 at 19:18

3 Answers3

1

Is there already a library or function that can do this very thing?

No.

Bart Friederichs
  • 33,050
  • 15
  • 95
  • 195
0

It's sort of doable with a simple regex "tokenizer" - by skipping strings mostly.

#-- Replace `?` placeholders
function json_bind($template, ...$vars) {
    return preg_replace_callback(
        '/
            [{}[\],:] \K |             # meta chars
            (true|false|null) \K |     # literals
            [-+\d.e]+ \K |             # numbers
            "([^"\\\\]+|\\\\.)*" \K |  # strings
            ([?])        # placeholder (do not skip)
         /x',
         function ($m) use (& $vars) {
             if ($m[0] == "?") {
                 return json_encode(array_shift($vars));
             }
         },
         $template
    );
}

While that's not a very complete/clean regex, it should skip over most standard JSON structures and most strings (see Regex to validate JSON for better regexps). Which is how you can find literal ? placeholders.

Notably that doesn't take care of semantically invalid templates, like array params for JSON keys e.g.

mario
  • 144,265
  • 20
  • 237
  • 291
  • This is much better than what is mentioned here: https://stackoverflow.com/questions/8464335/question-mark-placeholder/8464422 – Ziminji Sep 01 '18 at 20:02
  • Although a nifty solution, the OP didn't want to use json_encode – C. van Dorsten Sep 01 '18 at 20:17
  • 1
    @C.vanDorsten OP didn't want to use json_encode *as part of* manual array construction. Didn't imply zero usage at all. – mario Sep 01 '18 at 20:39
0

How can you not know the JSON structure? What than is the use of passing values to a function in order to bind values to a string, if you can not distinguish a question mark being a key or a value?

In a sence you are saying I do know what I want to bind and I pass it in an arbitrary way to a function, although I have no clue as to the structure of the source JSON string. Thus I have no clue as to the outcome being correct or not.

Why would anyone want to have such a thing?

You simply have to go with the answer of @Bart Friederichs. This is the only proper answer to your question.

  • It seems people are getting hung up on the fact that I said that "I may not always know the structure of the JSON". So let me clarify....I may have two or three JSON templates in a closed environment. Each has a similar structure, but are slightly different. For example, `{"order": {"customer": {"id": ?}}}` and `{"customer": {"id": ?}}`. I would like to create a simple method that has two parameters: `addCustomerId($json, $id)`. – Ziminji Sep 01 '18 at 20:43
  • If that is the actual layout, just stick with a global search on " : ?" and replace it with (": ".$id) (roughtly speaking that is) and you have what you want, although you at first said you don't want to. – C. van Dorsten Sep 02 '18 at 19:14