{"id":16329814,"url":"https://github.com/ueberdosis/tiptap-php","last_synced_at":"2025-05-14T22:08:33.798Z","repository":{"id":37906445,"uuid":"367600521","full_name":"ueberdosis/tiptap-php","owner":"ueberdosis","description":"A PHP package to work with Tiptap content","archived":false,"fork":false,"pushed_at":"2025-03-08T15:14:40.000Z","size":382,"stargazers_count":216,"open_issues_count":13,"forks_count":32,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-11T10:25:20.093Z","etag":null,"topics":["php","prosemirror","tiptap"],"latest_commit_sha":null,"homepage":"https://tiptap.dev","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ueberdosis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ueberdosis","open_collective":"tiptap","custom":"https://tiptap.dev/pricing"}},"created_at":"2021-05-15T10:19:01.000Z","updated_at":"2025-05-07T22:25:15.000Z","dependencies_parsed_at":"2022-07-21T00:29:35.093Z","dependency_job_id":"71756b36-fd28-42a2-9282-ee184f38595b","html_url":"https://github.com/ueberdosis/tiptap-php","commit_stats":{"total_commits":176,"total_committers":9,"mean_commits":"19.555555555555557","dds":"0.24431818181818177","last_synced_commit":"1d4be936f81b47514fd46ae506e74bc57cd26eef"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":"spatie/package-skeleton-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberdosis%2Ftiptap-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberdosis%2Ftiptap-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberdosis%2Ftiptap-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ueberdosis%2Ftiptap-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ueberdosis","download_url":"https://codeload.github.com/ueberdosis/tiptap-php/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254235700,"owners_count":22036964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","prosemirror","tiptap"],"created_at":"2024-10-10T23:17:18.212Z","updated_at":"2025-05-14T22:08:33.726Z","avatar_url":"https://github.com/ueberdosis.png","language":"PHP","funding_links":["https://github.com/sponsors/ueberdosis","https://opencollective.com/tiptap","https://tiptap.dev/pricing"],"categories":["PHP"],"sub_categories":[],"readme":"# Tiptap for PHP\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/ueberdosis/tiptap-php.svg?style=flat-square)](https://packagist.org/packages/ueberdosis/tiptap-php)\n[![GitHub Tests Action Status](https://github.com/ueberdosis/tiptap-php/actions/workflows/run-tests.yml/badge.svg)](https://github.com/ueberdosis/tiptap-php/actions/workflows/run-tests.yml)\n[![Total Downloads](https://img.shields.io/packagist/dt/ueberdosis/tiptap-php.svg?style=flat-square)](https://packagist.org/packages/ueberdosis/tiptap-php)\n[![License](https://img.shields.io/packagist/l/ueberdosis/tiptap-php?style=flat-square)](https://packagist.org/packages/ueberdosis/tiptap-php)\n[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.gg/WtJ49jGshW)\n[![Sponsor](https://img.shields.io/static/v1?label=Sponsor\u0026message=%E2%9D%A4\u0026logo=GitHub)](https://github.com/sponsors/ueberdosis)\n\nA PHP package to work with [Tiptap](https://tiptap.dev/) content. You can transform Tiptap-compatible JSON to HTML, and the other way around, sanitize your content, or just modify it.\n\n## Installation\nYou can install the package via composer:\n\n```bash\ncomposer require ueberdosis/tiptap-php\n```\n\n## Usage\nThe PHP package mimics large parts of the JavaScript package. If you know your way around Tiptap, the PHP syntax will feel familiar to you.\n\n### Convert Tiptap HTML to JSON\nLet’s start by converting a HTML snippet to a PHP array with a Tiptap-compatible structure:\n\n```php\n(new \\Tiptap\\Editor)\n    -\u003esetContent('\u003cp\u003eExample Text\u003c/p\u003e')\n    -\u003egetDocument();\n\n// Returns:\n// ['type' =\u003e 'doc', 'content' =\u003e …]\n```\n\nYou can get a JSON string in PHP, too.\n\n```php\n(new \\Tiptap\\Editor)\n    -\u003esetContent('\u003cp\u003eExample Text\u003c/p\u003e')\n    -\u003egetJSON();\n\n// Returns:\n// {\"type\": \"doc\", \"content\": …}\n```\n\n### Convert Tiptap JSON to HTML\nThe other way works aswell. Just pass a JSON string or an PHP array to generate the HTML.\n\n```php\n(new \\Tiptap\\Editor)\n    -\u003esetContent([\n        'type' =\u003e 'doc',\n        'content' =\u003e [\n            [\n                'type' =\u003e 'paragraph',\n                'content' =\u003e [\n                    [\n                        'type' =\u003e 'text',\n                        'text' =\u003e 'Example Text',\n                    ],\n                ]\n            ]\n        ],\n    ])\n    -\u003egetHTML();\n\n// Returns:\n// \u003ch1\u003eExample Text\u003c/h1\u003e\n```\n\nThis doesn’t fully adhere to the ProseMirror schema. Some things are supported too, for example aren’t marks allowed in a `CodeBlock`.\n\nIf you need better schema support, create an issue with the feature you’re missing.\n\n### Syntax highlighting for code blocks with [highlight.php](https://github.com/scrivo/highlight.php)\nThe default `CodeBlock` extension doesn’t add syntax highlighting to your code blocks. However, if you want to add syntax highlighting to your code blocks, there’s a special `CodeBlockHighlight` extension.\n\nSwapping our the default one works like that:\n\n```php\n(new \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        new \\Tiptap\\Extensions\\StarterKit([\n            'codeBlock' =\u003e false,\n        ]),\n        new \\Tiptap\\Nodes\\CodeBlockHighlight(),\n    ],\n]))\n-\u003esetContent('\u003cpre\u003e\u003ccode\u003e\u0026lt;?php phpinfo()\u003c/code\u003e\u003c/pre\u003e')\n-\u003egetHTML();\n\n// Returns:\n// \u003cpre\u003e\u003ccode class=\"hljs php\"\u003e\u003cspan class=\"hljs-meta\"\u003e\u0026lt;?php\u003c/span\u003e phpinfo()\u003c/code\u003e\u003c/pre\u003e\n```\n\nThis is still unstyled. You need to [load a CSS file](https://highlightjs.org/download/) to add colors to the output, for example like that:\n\n```html\n\u003clink rel=\"stylesheet\" href=\"//unpkg.com/@highlightjs/cdn-assets@11.4.0/styles/default.min.css\"\u003e\n```\n\nBoom, syntax highlighting! By the way, this is powered by the amazing [scrivo/highlight.php](https://github.com/scrivo/highlight.php).\n\n### Syntax highlighting for code blocks with [Shiki](https://github.com/shikijs/shiki) (Requires Node.js)\nThere is an alternate syntax highlighter that utilizes [Shiki](https://github.com/shikijs/shiki). Shiki is a beautiful syntax highlighter powered by the same language engine that many code editors use. The major differences from the `CodeBlockHighlight` extensions are:\n\n1. you must install the `shiki` npm package.\n2. Shiki code highlighting works by injecting inline styles so pulling in a external css file is not required.\n3. you can use most VS Code themes to highlight your code.\n\nTo use the Shiki extension, first install the npm package\n\n```bash\nnpm install shiki\n```\n\nThen follow the example below:\n\n```php\n(new \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        new \\Tiptap\\Extensions\\StarterKit([\n            'codeBlock' =\u003e false,\n        ]),\n        new \\Tiptap\\Nodes\\CodeBlockShiki(),\n    ],\n]))\n-\u003esetContent('\u003cpre\u003e\u003ccode\u003e\u0026lt;?php phpinfo()\u003c/code\u003e\u003c/pre\u003e')\n-\u003egetHTML();\n```\n\nTo configure the theme or default language for code blocks pass additonal configuration into the constructor as show below:\n\n```php\n(new \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        new \\Tiptap\\Extensions\\StarterKit([\n            'codeBlock' =\u003e false,\n        ]),\n        new \\Tiptap\\Nodes\\CodeBlockShiki([\n            'theme' =\u003e 'github-dark', // default: nord, see https://github.com/shikijs/shiki/blob/main/docs/themes.md\n            'defaultLanguage' =\u003e 'php', // default: html, see https://github.com/shikijs/shiki/blob/main/docs/languages.md\n            'guessLanguage' =\u003e true, // default: true, if the language isn’t passed, it tries to guess the language with highlight.php\n        ]),\n    ],\n]))\n-\u003esetContent('\u003cpre\u003e\u003ccode\u003e\u0026lt;?php phpinfo()\u003c/code\u003e\u003c/pre\u003e')\n-\u003egetHTML();\n```\n\nUnder the hood the Shiki extension utilizes [Shiki PHP by Spatie](https://github.com/spatie/shiki-php), so please see the documentation for additional details and considerations.\n\n### Convert content to plain text\nContent can also be transformed to plain text, for example to put it into a search index.\n\n```php\n(new \\Tiptap\\Editor)\n    -\u003esetContent('\u003ch1\u003eHeading\u003c/h1\u003e\u003cp\u003eParagraph\u003c/p\u003e')\n    -\u003egetText();\n\n// Returns:\n// \"Heading\n//\n// Paragraph\"\n```\n\nWhat’s coming between blocks can be configured, too.\n\n```php\n(new \\Tiptap\\Editor)\n    -\u003esetContent('\u003ch1\u003eHeading\u003c/h1\u003e\u003cp\u003eParagraph\u003c/p\u003e')\n    -\u003egetText([\n        'blockSeparator' =\u003e \"\\n\",\n    ]);\n\n// Returns:\n// \"Heading\n// Paragraph\"\n```\n\n### Sanitize content\nA great use case for the PHP package is to clean (or “sanitize”) the content. You can do that with the `sanitize()` method. Works with JSON strings, PHP arrays and HTML.\n\nIt’ll return the same format you’re using as the input format.\n\n```php\n(new \\Tiptap\\Editor)\n    -\u003esanitize('\u003cp\u003eExample Text\u003cscript\u003ealert(\"HACKED!\")\u003c/script\u003e\u003c/p\u003e');\n\n// Returns:\n// '\u003cp\u003eExample Text\u003c/p\u003e'\n```\n\n### Modifying the content\nWith the `descendants()` method you can loop through all nodes recursively as you are used to from the JavaScript package. But in PHP, you can even modify the node to update attributes and all that.\n\n\u003e Warning: You need to add `\u0026` to the parameter. Thats keeping a reference to the original item and allows to modify the original one, instead of just a copy.\n\n```php\n$editor-\u003edescendants(function (\u0026$node) {\n    if ($node-\u003etype !== 'heading') {\n        return;\n    }\n\n    $node-\u003eattrs-\u003elevel = 1;\n});\n```\n\n### Configuration\nPass the configuration to the constructor of the editor. There’s not much to configure, but at least you can pass the initial content and load specific extensions.\n\n```php\nnew \\Tiptap\\Editor([\n    'content' =\u003e '\u003cp\u003eExample Text\u003c/p\u003e',\n    'extensions' =\u003e [\n        new \\Tiptap\\Extensions\\StarterKit,\n    ],\n])\n```\n\nThe `StarterKit` is loaded by default. If you just want to use that, there’s no need to set it.\n\n### Extensions\nBy default, the [`StarterKit`](https://tiptap.dev/api/extensions/starter-kit) is loaded, but you can pass a custom array of extensions aswell.\n\n```php\nnew \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        new \\Tiptap\\Extensions\\StarterKit,\n        new \\Tiptap\\Marks\\Link,\n    ],\n])\n```\n\n### Configure extensions\nSome extensions can be configured. Just pass an array to the constructor, that’s it. We’re aiming to support the same configuration as the JavaScript package.\n\n```php\nnew \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        // …\n        new \\Tiptap\\Nodes\\Heading([\n            'levels' =\u003e [1, 2, 3],\n        ]),\n    ],\n])\n```\n\nYou can pass custom HTML attributes through the configuration, too.\n\n```php\nnew \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        // …\n        new \\Tiptap\\Nodes\\Heading([\n            'HTMLAttributes' =\u003e [\n                'class' =\u003e 'my-custom-class',\n            ],\n        ]),\n    ],\n])\n```\n\nFor the `StarterKit`, it’s slightly different, but works as you are used to from the JavaScript package.\n\n```php\nnew \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        new Tiptap\\Extensions\\StarterKit([\n            'codeBlock' =\u003e false,\n            'heading' =\u003e [\n                'HTMLAttributes' =\u003e [\n                    'class' =\u003e 'my-custom-class',\n                ],\n            ]\n        ]),\n    ],\n])\n```\n\n### Extend existing extensions\nIf you need to change minor details of the supported extensions, you can just extend an extension.\n\n```php\n\u003c?php\n\nclass CustomBold extends \\Tiptap\\Marks\\Bold\n{\n    public function renderHTML($mark)\n    {\n        // Renders \u003cb\u003e instead of \u003cstrong\u003e\n        return ['b', 0]\n    }\n}\n\nnew \\Tiptap\\Editor([\n    'extensions' =\u003e [\n        new Paragraph,\n        new Text,\n        new CustomBold,\n    ],\n])\n```\n\n#### Custom extensions\nYou can even build custom extensions. If you are used to the JavaScript API, you will be surprised how much of that works in PHP, too. 🤯 Find a simple example below.\n\nMake sure to dig through the extensions in this repository to learn more about the PHP extension API.\n\n```php\n\u003c?php\n\nuse Tiptap\\Core\\Node;\n\nclass CustomNode extends Node\n{\n    public static $name = 'customNode';\n    \n    public static $priority = 100;\n\n    public function addOptions()\n    {\n        return [\n            'HTMLAttributes' =\u003e [],\n        ];\n    }\n\n    public function parseHTML()\n    {\n        return [\n            [\n                'tag' =\u003e 'my-custom-tag[data-id]',\n            ],\n            [\n                'tag' =\u003e 'my-custom-tag',\n                'getAttrs' =\u003e function ($DOMNode) {\n                    return ! \\Tiptap\\Utils\\InlineStyle::hasAttribute($DOMNode, [\n                        'background-color' =\u003e '#000000',\n                    ]) ? null : false;\n                },\n            ],\n            [\n                'style' =\u003e 'background-color',\n                'getAttrs' =\u003e function ($value) {\n                    return (bool) preg_match('/^(black)$/', $value) ? null : false;\n                },\n            ],\n        ];\n    }\n\n    public function renderHTML($node)\n    {\n        return ['my-custom-tag', ['class' =\u003e 'foobar'], 0]\n    }\n}\n```\n\n#### Extension priority\n\nExtensions are evaluated in the order of descending priority. By default, all Nodes, Marks, and Extensions, have a priority value of `100`.\n\nPriority should be defined when creating a Node extension to match markup that could be matched be other Nodes - an example of this is the [TaskItem Node](src/Nodes/TaskItem.php) which has evaluation priority over the [ListItem Node](src/Nodes/ListItem.php).\n\n## Testing\n```bash\ncomposer test\n```\n\nYou can install nodemon (`npm install -g nodemon`) to keep the test suite running and watch for file changes:\n\n```bash\ncomposer test-watch\n```\n\n## Contributing\nPlease see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.\n\n## Security Vulnerabilities\nPlease review [our security policy](../../security/policy) on how to report security vulnerabilities.\n\n## Credits\n- [Hans Pagel](https://github.com/hanspagel)\n- [All Contributors](../../contributors)\n\n## License\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fueberdosis%2Ftiptap-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fueberdosis%2Ftiptap-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fueberdosis%2Ftiptap-php/lists"}