• Stars
    star
    153
  • Rank 234,963 (Top 5 %)
  • Language
    PHP
  • License
    MIT License
  • Created almost 3 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A PHP package to work with Tiptap content

Tiptap for PHP

Latest Version on Packagist GitHub Tests Action Status Total Downloads License Chat Sponsor

A PHP package to work with Tiptap content. You can transform Tiptap-compatible JSON to HTML, and the other way around, sanitize your content, or just modify it.

Installation

You can install the package via composer:

composer require ueberdosis/tiptap-php

Usage

The PHP package mimics large parts of the JavaScript package. If you know your way around Tiptap, the PHP syntax will feel familiar to you.

Convert Tiptap HTML to JSON

Let’s start by converting a HTML snippet to a PHP array with a Tiptap-compatible structure:

(new Tiptap\Editor)
    ->setContent('<p>Example Text</p>')
    ->getDocument();

// Returns:
// ['type' => 'doc', 'content' => …]

You can get a JSON string in PHP, too.

(new Tiptap\Editor)
    ->setContent('<p>Example Text</p>')
    ->getJSON();

// Returns:
// {"type": "doc", "content": …}

Convert Tiptap JSON to HTML

The other way works aswell. Just pass a JSON string or an PHP array to generate the HTML.

(new Tiptap\Editor)
    ->setContent([
        'type' => 'doc',
        'content' => [
            [
                'type' => 'paragraph',
                'content' => [
                    [
                        'type' => 'text',
                        'text' => 'Example Text',
                    ],
                ]
            ]
        ],
    ])
    ->getHTML();

// Returns:
// <h1>Example Text</h1>

This doesn’t fully adhere to the ProseMirror schema. Some things are supported too, for example aren’t marks allowed in a CodeBlock.

If you need better schema support, create an issue with the feature you’re missing.

Syntax highlighting for code blocks with highlight.php

The 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.

Swapping our the default one works like that:

(new Tiptap\Editor([
    'extensions' => [
        new \Tiptap\Extensions\StarterKit([
            'codeBlock' => false,
        ]),
        new \Tiptap\Nodes\CodeBlockHighlight(),
    ],
]))
->setContent('<pre><code>&lt;?php phpinfo()</code></pre>')
->getHTML();

// Returns:
// <pre><code class="hljs php"><span class="hljs-meta">&lt;?php</span> phpinfo()</code></pre>

This is still unstyled. You need to load a CSS file to add colors to the output, for example like that:

<link rel="stylesheet" href="//unpkg.com/@highlightjs/[email protected]/styles/default.min.css">

Boom, syntax highlighting! By the way, this is powered by the amazing scrivo/highlight.php.

Syntax highlighting for code blocks with Shiki (Requires Node.js)

There is an alternate syntax highlighter that utilizes 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, 1) you must install the shiki npm package, 2) Shiki code highlighting works by injecting inline styles so pulling in a external css file is not required, 3) you can use most VS Code themes to highlight your code.

To use the Shiki extension, first install the npm package

npm install shiki

Then follow the example below:

(new Tiptap\Editor([
    'extensions' => [
        new \Tiptap\Extensions\StarterKit([
            'codeBlock' => false,
        ]),
        new \Tiptap\Nodes\CodeBlockShiki,
    ],
]))
->setContent('<pre><code>&lt;?php phpinfo()</code></pre>')
->getHTML();

To configure the theme or default language for code blocks pass additonal configuration into the constructor as show below:

(new Tiptap\Editor([
    'extensions' => [
        new \Tiptap\Extensions\StarterKit([
            'codeBlock' => false,
        ]),
        new \Tiptap\Nodes\CodeBlockShiki([
            'theme' => 'github-dark', // default: nord, see https://github.com/shikijs/shiki/blob/main/docs/themes.md
            'defaultLanguage' => 'php' // default: html, see https://github.com/shikijs/shiki/blob/main/docs/languages.md
            'guessLanguage' => true // default: true, if the language isn’t passed, it tries to guess the language with highlight.php
        ]),
    ],
]))
->setContent('<pre><code>&lt;?php phpinfo()</code></pre>')
->getHTML();

Under the hood the Shiki extension utilizes Shiki PHP by Spatie, so please see the documentation for additional details and considerations.

Convert content to plain text

Content can also be transformed to plain text, for example to put it into a search index.

(new Editor)
    ->setContent('<h1>Heading</h1><p>Paragraph</p>')
    ->getText();

// Returns:
// "Heading
//
// Paragraph"

What’s coming between blocks can be configured, too.

(new Editor)
    ->setContent('<h1>Heading</h1><p>Paragraph</p>')
    ->getText([
        'blockSeparator' => "\n",
    ]);

// Returns:
// "Heading
// Paragraph"

Sanitize content

A 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.

It’ll return the same format you’re using as the input format.

(new Tiptap\Editor)
    ->sanitize('<p>Example Text<script>alert("HACKED!")</script></p>');

// Returns:
// '<p>Example Text</p>'

Modifying the content

With 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.

Warning: You need to add & to the parameter. Thats keeping a reference to the original item and allows to modify the original one, instead of just a copy.

$editor->descendants(function (&$node) {
    if ($node->type !== 'heading') {
        return;
    }

    $node->attrs->level = 1;
});

Configuration

Pass 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.

new Tiptap\Editor([
    'content' => '<p>Example Text</p>',
    'extensions' => [
        new Tiptap\Extensions\StarterKit,
    ],
])

The StarterKit is loaded by default. If you just want to use that, there’s no need to set it.

Extensions

By default, the StarterKit is loaded, but you can pass a custom array of extensions aswell.

new Tiptap\Editor([
    'extensions' => [
        new Tiptap\Extensions\StarterKit,
        new Tiptap\Nodes\Link,
    ],
])

Configure extensions

Some 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.

new Tiptap\Editor([
    'extensions' => [
        // …
        new Tiptap\Nodes\Heading([
            'levels' => [1, 2, 3],
        ]),
    ],
])

You can pass custom HTML attributes through the configuration, too.

new Tiptap\Editor([
    'extensions' => [
        // …
        new Tiptap\Nodes\Heading([
            'HTMLAttributes' => [
                'class' => 'my-custom-class',
            ],
        ]),
    ],
])

For the StarterKit, it’s slightly different, but works as you are used to from the JavaScript package.

new Tiptap\Editor([
    'extensions' => [
        new Tiptap\Extensions\StarterKit([
            'codeBlock' => false,
            'heading' => [
                'HTMLAttributes' => [
                    'class' => 'my-custom-class',
                ],
            ]
        ]),
    ],
])

Extend existing extensions

If you need to change minor details of the supported extensions, you can just extend an extension.

<?php

class CustomBold extends \Tiptap\Marks\Bold
{
    public function renderHTML($mark)
    {
        // Renders <b> instead of <strong>
        return ['b', 0]
    }
}

new Tiptap\Editor([
    'extensions' => [
        new Paragraph,
        new Text,
        new CustomBold,
    ],
])

Custom extensions

You 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.

Make sure to dig through the extensions in this repository to learn more about the PHP extension API.

<?php

use Tiptap\Core\Node;

class CustomNode extends Node
{
    public static $name = 'customNode';
    
    public static $priority = 100;

    public function addOptions()
    {
        return [
            'HTMLAttributes' => [],
        ];
    }

    public function parseHTML()
    {
        return [
            [
                'tag' => 'my-custom-tag[data-id]',
            ],
            [
                'tag' => 'my-custom-tag',
                'getAttrs' => function ($DOMNode) {
                    return ! \Tiptap\Utils\InlineStyle::hasAttribute($DOMNode, [
                        'background-color' => '#000000',
                    ]) ? null : false;
                },
            ],
            [
                'style' => 'background-color',
                'getAttrs' => function ($value) {
                    return (bool) preg_match('/^(black)$/', $value) ? null : false;
                },
            ],
        ];
    }

    public function renderHTML($node)
    {
        return ['my-custom-tag', ['class' => 'foobar'], 0]
    }
}

Extension priority

Extensions are evaluated in the order of descending priority. By default, all Nodes, Marks, and Extensions, have a priority value of 100.

Priority 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 which has evaluation priority over the ListItem Node.

Testing

composer test

You can install nodemon (npm install -g nodemon) to keep the test suite running and watch for file changes:

composer test-watch

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

More Repositories

1

tiptap

The headless rich text editor framework for web artisans.
TypeScript
23,822
star
2

hocuspocus

The CRDT Yjs WebSocket backend for conflict-free real-time collaboration in your app.
TypeScript
1,011
star
3

alldocs.app

Online text file converter
PHP
224
star
4

awesome-tiptap

⚡ Delightful Tiptap packages and resources
168
star
5

glyphfinder

Unicode character search for macOS & Windows.
JavaScript
131
star
6

mouseless

Keyboard shortcut training and look-up for macOS.
JavaScript
131
star
7

prosemirror-to-html

Takes ProseMirror JSON and renders HTML.
PHP
79
star
8

html-to-prosemirror

Takes HTML and outputs ProseMirror compatible JSON.
PHP
72
star
9

tiptap-next

The headless editor framework for web artisans.
68
star
10

pandoc

A PHP wrapper for Pandoc to convert any text format in any other text format
PHP
67
star
11

vue-mailchimp-subscribe

Renderless Vue.js component for Mailchimp list subscriptions
JavaScript
48
star
12

hocuspocus-laravel

Integrates Hocuspocus into Laravel with a few clicks
PHP
20
star
13

glyphfinder-landingpage

Landingpage for Glyphfinder
Vue
19
star
14

keyboard-symbol

Convert key names to symbols like ⌘, ⇧, ⌥
JavaScript
16
star
15

mouseless-landingpage

Landingpage for Mouseless
JavaScript
15
star
16

create-tiptap-extension

Create a new Tiptap extension in seconds.
JavaScript
15
star
17

prosemirror-php

Work with ProseMirror JSON in PHP.
PHP
14
star
18

build-tools

🛠️ Docker image with CI/CD tools
Shell
9
star
19

commonmark-hint-extension

Render :::important Hints::: in league/commonmark
PHP
7
star
20

laravel-docker-health-check

A simple package that makes it easier to work with Laravel and Docker in production.
PHP
7
star
21

window-shortcuts

Get menu shortcuts by window owner name
Swift
7
star
22

tiptap-collab-replit

Vue
4
star
23

gitlab-ci-build-tools

🦊 Docker Image with tools for GitLab CI
Shell
2
star
24

commonmark-embed-extension

Render embeds from URLs
PHP
2
star