src/Patch.php
public function generate($sourceDocument, $targetDocument)
    {
        if ($sourceDocument === null || $targetDocument === null) {
            return $targetDocument;
        }

        if ($sourceDocument == new \stdClass()) {
            return null;
        }

        if (is_array($sourceDocument)) {
            if ($sourceDocument !== $targetDocument) {
                return $targetDocument;
            }

            return null;
        }

        $patchDocument = new \stdClass();
        $sourceDocumentVars = get_object_vars($sourceDocument);
        $targetDocumentVars = get_object_vars($targetDocument);

        foreach ($targetDocumentVars as $var => $value) {
            if (!in_array($var, array_keys($sourceDocumentVars))
                || !in_array($value, array_values($sourceDocumentVars))
            ) {
                $patchDocument->$var = $value;
            }
        }

        foreach ($sourceDocumentVars as $var => $value) {
            if ($targetDocumentVars === []) {
                $patchDocument->$var = null;
                break;
            }

            if (is_object($value)) {
                if ($sourceDocument->$var !== null && is_object($sourceDocument->$var)) {
                    $subPatch = $this->generate($sourceDocument->$var, $targetDocument->$var);
                    if ($subPatch !== null) {
                        $patchDocument->$var = $subPatch;
                    }
                }
            } elseif (!in_array($var, array_keys($targetDocumentVars))
                      || !in_array($value, array_values($targetDocumentVars))) {
                $sourceDocument->$var = null;
                if (!in_array($var, array_keys($targetDocumentVars))) {
                    $patchDocument->$var = null;
                }
            }
        }

        if (count(get_object_vars($patchDocument)) > 0) {
            return $patchDocument;
        }

        return null;
    }
public function generate($sourceDocument, $targetDocument)
    {
        if ($sourceDocument === null || $targetDocument === null) {
            return $targetDocument;
        }

        if ($sourceDocument == new \stdClass()) {
            return null;
        }

        if (is_array($sourceDocument)) {
            if ($sourceDocument !== $targetDocument) {
                return $targetDocument;
            }

            return null;
        }

        $patchDocument = new \stdClass();
        $sourceDocumentVars = get_object_vars($sourceDocument);
        $targetDocumentVars = get_object_vars($targetDocument);

        foreach ($targetDocumentVars as $var => $value) {
            if (!in_array($var, array_keys($sourceDocumentVars))
                || !in_array($value, array_values($sourceDocumentVars))
            ) {
                $patchDocument->$var = $value;
            }
        }

        foreach ($sourceDocumentVars as $var => $value) {
            if ($targetDocumentVars === []) {
                $patchDocument->$var = null;
                break;
            }

            if (is_object($value)) {
                if ($sourceDocument->$var !== null && is_object($sourceDocument->$var)) {
                    $subPatch = $this->generate($sourceDocument->$var, $targetDocument->$var);
                    if ($subPatch !== null) {
                        $patchDocument->$var = $subPatch;
                    }
                }
            } elseif (!in_array($var, array_keys($targetDocumentVars))
                      || !in_array($value, array_values($targetDocumentVars))) {
                $sourceDocument->$var = null;
                if (!in_array($var, array_keys($targetDocumentVars))) {
                    $patchDocument->$var = null;
                }
            }
        }

        if (count(get_object_vars($patchDocument)) > 0) {
            return $patchDocument;
        }

        return null;
    }
Generate patch for given source document via target document.

@param mixed $sourceDocument
@param mixed $targetDocument

@return mixed