Skip to content

Class designed to handle JSON Patch operations in accordance with the RFC 6902 specification

License

Notifications You must be signed in to change notification settings

blancks/fast-jsonpatch-php

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PHP Fast JSON Patch

Test phpstan codecov Maintainability PHP Version Require Latest Stable Version

This documentation covers the FastJsonPatch PHP class, designed to apply a series of JSON Patch operations as specified in RFC 6902.

Fast JSON Patch is the fastest fully RFC compliant package available in PHP, you can find compliance test and benchmark results here.

Installation via Composer

composer require blancks/fast-jsonpatch-php

Class Overview

The FastJsonPatch class provides a way to modify JSON documents using a structured patch object.
The patch object contains an array of operations (add, remove, replace, move, copy, and test) that describe the changes to be made to the target JSON document.

Usage Example

Below is an example of how to use the FastJsonPatch class to apply a patch to a JSON document:

use blancks\JsonPatch\FastJsonPatch;
use blancks\JsonPatch\exceptions\FastJsonPatchException;

$document = '{
    "contacts":[
        {"name":"John","number":"-"},
        {"name":"Dave","number":"+1 222 333 4444"}
    ]
}';

$patch = '[
    {"op":"add","path":"/contacts/-","value":{"name":"Jane", "number":"+1 353 644 2121"}},
    {"op":"replace","path":"/contacts/0/number","value":"+1 212 555 1212"},
    {"op":"remove","path":"/contacts/1"}
]';

$FastJsonPatch = FastJsonPatch::fromJson($document);

try {

    $FastJsonPatch->apply($patch);
    
} catch (FastJsonPatchException $e) {

    // here if patch cannot be applied for some reason
    echo $e->getMessage(), "\n";
    
}

var_dump($FastJsonPatch->getDocument());

Expected Output:

object(stdClass) (1) {
  ["contacts"]=>
  array(2) {
    [0]=>
    object(stdClass) (2) {
      ["name"]=>
      string(4) "John"
      ["number"]=>
      string(15) "+1 212 555 1212"
    }
    [1]=>
    object(stdClass) (2) {
      ["name"]=>
      string(4) "Jane"
      ["number"]=>
      string(15) "+1 353 644 2121"
    }
  }
}

The expected workflow is that once you got a FastJsonPatch instance you can call the apply method each time a new patch is received and this is particularly handy in long-running context like a websocket client/server.

Patch application is designed to be atomic. If any operation of a given patch fails the original document is restored, ensuring a consistent state of the document.

Constructor

__construct(mixed &$document, ?JsonHandlerInterface $JsonHandler = null, ?JsonPointerHandlerInterface $JsonPointerHandler = null)

  • Description: Initializes a new instance of the FastJsonPatch class.
  • Parameters:
    • mixed &$document: The decoded JSON document.
    • ?JsonHandlerInterface $JsonHandler: An instance of the JSON handler which will be responsible for encoding/decoding and CRUD operations.
      The default handler is blancks\JsonPatch\json\handlers\BasicJsonHandler and decodes json objects as php \stdClass instances. This is the recommended way.
      If you application requires working with associative arrays only, you can pass a blancks\JsonPatch\json\handlers\ArrayJsonHandler instance instead.
    • ?JsonPointerHandlerInterface $JsonPointerHandler: Instance of the object responsible to validate and parse JSON Pointers.
      The default handler is blancks\JsonPatch\json\pointer\JsonPointer6901
  • Returns: Instance of the FastJsonPatch class.
  • Example:
    $document = ['one', 'two', 'three'];
    $FastJsonPatch = new FastJsonPatch($document);

Public Methods

static function fromJson(string $patch, ?JsonHandlerInterface $JsonHandler = null, ?JsonPointerHandlerInterface $JsonPointerHandler = null) : void

  • Description: Returns a new instance of the FastJsonPatch class.
  • Parameters:
    • string $document: A json encoded document to which the patches will be applied
    • ?JsonHandlerInterface $JsonHandler: An instance of the JSON handler which will be responsible for encoding/decoding and CRUD operations.
      The default handler is blancks\JsonPatch\json\handlers\BasicJsonHandler and decodes json objects as php \stdClass instances. This is the recommended way.
      If you application requires working with associative arrays only, you can pass a blancks\JsonPatch\json\handlers\ArrayJsonHandler instance instead.
    • ?JsonPointerHandlerInterface $JsonPointerHandler: Instance of the object responsible to validate and parse JSON Pointers.
      The default handler is blancks\JsonPatch\json\pointer\JsonPointer6901
  • Example:
    $FastJsonPatch = FastJsonPatch::fromJson('{"foo":"bar","baz":["qux","quux"]}');

function apply(string $patch) : void

  • Description: Applies a series of patch operations to the specified JSON document. Ensures atomicity by applying all operations successfully or restoring the original document if any operation fails.
  • Parameters:
    • string $patch: A json-encoded array of patch operations.
  • Exceptions:
    • Throws FastJsonPatchValidationException if a patch operation is invalid or improperly formatted.
    • Throws FastJsonPatchException if any other error occurs while applying the patch
  • Example:
    $patch = '[{"op":"test", "path":"/foo", "value":"bar"}]';
    $FastJsonPatch->apply($patch);

function isValidPatch(string $patch): bool

  • Description: Tells if the $patch is syntactically valid
  • Parameters:
    • string $patch: A json-encoded array of patch operations.
  • Returns: True is the patch is valid, false otherwise
  • Example:
    $patch = '[{"op":"add","path":"/foo"}]'; // invalid because there's no "value" key 
    
    if ($FastJsonPatch->isValidPatch($patch)) {
        $FastJsonPatch->apply($patch);
    } else {
        echo "Invalid patch!";
    }

function read(string $path): mixed

  • Description: Uses a JSON Pointer (RFC-6901) to fetch data from the referenced document
  • Parameters:
    • string $patch: A json pointer
  • Returns: The value located by the provided pointer
  • Example:
    $FastJsonPatch = FastJsonPatch::fromJson('{"foo":"bar","baz":["qux","quux"]}');
    echo $FastJsonPatch->read('/baz/1'); // "quux"

function &getDocument(): mixed

  • Description: Returns the document reference that the instance is holding
  • Returns: The referenced document
  • Example:
    $FastJsonPatch = FastJsonPatch::fromJson('["qux","quux"]');
    var_dump($FastJsonPatch->getDocument()); // array(2) {[0]=> string(3) "qux" [1]=> string(4) "quux"}

function registerOperation(PatchOperationInterface $PatchOperation): void

  • Description: Allows to register new patch operation handlers or to override existing ones.
  • Parameters:
    • PatchOperationInterface $PatchOperation: The handler class for handling the operation.
  • Example:
    $FastJsonPatch->registerOperation(new Add);

Supported Operations

add

  • Description: Adds a value to the specified path in the document.
  • Parameters:
    • path: JSON Pointer to the location where the value should be added.
    • value: The value to add.
  • Example:
    {"op":"add","path":"/new/key","value":"example"}

remove

  • Description: Removes the value at the specified path.
  • Parameters:
    • path: JSON Pointer to the location of the value to remove.
  • Example:
    {"op":"remove","path":"/old/key"}

replace

  • Description: Replaces the value at the specified path with a new value.
  • Parameters:
    • path: JSON Pointer to the location of the value to replace.
    • value: The new value.
  • Example:
    {"op":"replace","path":"/replace/key","value":"newValue"}

move

  • Description: Moves the value from one path to another.
  • Parameters:
    • from: JSON Pointer to the source location of the value to move.
    • path: JSON Pointer to the destination location.
  • Example:
    {"op":"move","from":"/old/key","path":"/new/key"}

copy

  • Description: Copies the value from one path to another.
  • Parameters:
    • from: JSON Pointer to the source location of the value to copy.
    • path: JSON Pointer to the destination location.
  • Example:
    {"op":"copy","from":"/source/key","path":"/target/key"}

test

  • Description: Tests that the specified value matches the value at the path.
  • Parameters:
    • path: JSON Pointer to the location of the value to test.
    • value: The value to compare.
  • Example:
    {"op":"test","path":"/test/key","value":"expectedValue"}

Running tests

composer test

Test cases comes from json-patch/json-patch-tests and extended furthermore to ensure a strict compliance with RFC-6902.

Contributing

Contributions are welcome! 🎉
Feel free to fork the repository if you'd like to contribute to this project!

Please ensure your contributions align with the project's goals and code style:

  • Use composer run static-analyse for static analysis
  • Use composer run pretty-php to reformat your code before commit
  • Provide unit tests for the code you wrote, this project targets 100% coverage
  • It is ok to write alternative handlers to address slower operations, such as working with huge json files
  • Any changes that hurts the package performance in its default configuration must be motivated with a good reason

If you have any questions, need guidance, reporting a bug or suggesting an improvement feel free to open an issue. Every bit helps!

Thank you for helping to improve this project!

License

This software is licensed under the MIT License.

About

Class designed to handle JSON Patch operations in accordance with the RFC 6902 specification

Resources

License

Stars

Watchers

Forks

Languages