Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.1.0] - 2023-06-13
- Set PHP minimum version in composer
- Throws UnknownThemeException
- Added tests
- Added tests

## [3.0.0] - 2025-11-21

### Added
- Support for `<pre><code>` pattern (in addition to `<pre>`) for better Markdown compatibility
- Support for `class="language-*"` attribute on `<code>` tag for language detection
- Fluent interface for `showLineNumbers()` and `showActionPanel()` methods
- `HighlighterFactory` class for creating highlighter instances based on language
- `CodeBlockWrapper` class for separating presentation logic from highlighting
- `LanguageNormalizer` class for normalizing language identifiers with aliases support
- Custom exceptions: `InvalidLanguageException`, `InvalidThemeException`, `ThemeNotSetException`
- PHP as default language for code blocks without specified language
- Comprehensive test suite

### Changed
- **BREAKING**: Removed Singleton pattern from all highlighter classes (`HighlighterPHP`, `HighlighterXML`, `HighlighterBash`)
- **BREAKING**: Made `HighlighterBase` an abstract class (cannot be instantiated directly)
- **BREAKING**: Added `ext-dom` PHP extension requirement (previously optional with regex fallback)
- Improved HTML attribute parsing using `DOMDocument` with regex fallback
- Enhanced syntax highlighting logic in `HighlighterBase`
- Improved XML/HTML highlighting: fixed line-by-line processing, proper attribute highlighting
- Updated PHPDoc comments throughout the codebase

### Fixed
- Fixed issue with extra empty lines at the beginning and end of code blocks
- Fixed line numbering
74 changes: 56 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
PHPHighlight is a PHP syntax highlighting library that can be easily customized and extended.

## How it works
The library parses the text, finds the \<pre> tag, reads the attributes (data-lang, data-file, data-theme) and highlights the syntax of the code block.
The library parses the text, finds the `<pre>` and `<pre><code>` tags, reads the attributes (data-lang, data-file, data-theme) and highlights the syntax of the code block.

**Recommended:** Use `<pre><code>` pattern for better semantics and compatibility with Markdown output.

Supports style customization. Here are examples of styling:

<img width="757" height="309" src="https://demyanov.dev/sites/default/files/images/phphighlight2.png" alt="styling example">
<img width="757" height="309" src="examples/img/scr_01.png" alt="styling example">

## Requirements
PHP 8.1+
Expand All @@ -19,7 +21,7 @@ $ composer require demyanovs/php-highlight
```

## Usage
See full example here [index.php](../master/examples/index.php)
See full example in [examples/index.php](examples/index.php)
```php
<?php

Expand All @@ -29,7 +31,7 @@ use Demyanovs\PHPHighlight\Highlighter;
use Demyanovs\PHPHighlight\Themes\ObsidianTheme;

$text = '
<pre data-file="php-highlight/examples/index.php" data-lang="php">
<pre><code class="language-php" data-file="php-highlight/examples/index.php">
&lt;?php
abstract class AbstractClass
{
Expand Down Expand Up @@ -62,13 +64,12 @@ class ConcreteClass extends AbstractClass
$class = new ConcreteClass;
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";
</pre>
</code></pre>
';

$highlighter = new Highlighter($text, ObsidianTheme::TITLE);
// Configuration
$highlighter->showLineNumbers(true);
$highlighter->showActionPanel(true);
$highlighter = (new Highlighter($text, ObsidianTheme::TITLE))
->showLineNumbers(true)
->showActionPanel(true);
echo $highlighter->parse();
```

Expand All @@ -78,19 +79,53 @@ $highlighter->showLineNumbers(true);
$highlighter->showActionPanel(true);
```

You can set following attributes in \<pre> tag
\<pre data-lang="php" data-file="example.php" data-theme="drakuala">
* lang - a language of the text. This affects how the parser will highlight the syntax.
* file - show file name in action panel.
* theme - allows to overwrite the global theme.
You can set following attributes in `<pre>` or `<code>` tags:
```html
<pre><code class="language-php" data-file="example.php" data-theme="darkula">
// or
<pre data-lang="php" data-file="example.php" data-theme="darkula"><code>
```

* `data-lang` or `class="language-*"` - a language of the text. This affects how the parser will highlight the syntax.
* `data-file` - show file name in action panel.
* `data-theme` - allows to overwrite the global theme.

**Note:** `class="language-*"` on `<code>` tag is automatically recognized (common in Markdown output).

### How to create a custom theme
To create a custom theme you need to create an instance of Demyanovs\PHPHighlight\Themes\Theme class
To create a custom theme you need to create an instance of `Demyanovs\PHPHighlight\Themes\Theme` class
and pass it to Highlighter as a third argument:
```php
$defaultColorSchemaDto = new DefaultColorSchemaDto(...);
$PHPColorSchemaDto = new PHPColorSchemaDto(...);
$XMLColorSchemaDto = new XMLColorSchemaDto(...);
use Demyanovs\PHPHighlight\Highlighter;
use Demyanovs\PHPHighlight\Themes\Theme;
use Demyanovs\PHPHighlight\Themes\Dto\DefaultColorSchemaDto;
use Demyanovs\PHPHighlight\Themes\Dto\PHPColorSchemaDto;
use Demyanovs\PHPHighlight\Themes\Dto\XMLColorSchemaDto;

$defaultColorSchemaDto = new DefaultColorSchemaDto(
'#000000', // background
'#ffffff', // default text
'#888888', // comment
'#ff0000', // keyword
'#00ff00', // string
'#0000ff', // number
'#ffff00' // variable
);

$PHPColorSchemaDto = new PHPColorSchemaDto(
'#0000BB', // keyword
'#FF8000', // variable
'#fbc201', // function
'#007700', // string
'#DD0000' // comment
);

$XMLColorSchemaDto = new XMLColorSchemaDto(
'#008000', // tag
'#7D9029', // attribute
'#BA2121', // string
'#BC7A00' // comment
);

$myTheme = new Theme(
'myThemeTitle',
Expand Down Expand Up @@ -124,5 +159,8 @@ Pull requests are welcome. For major changes, please open an issue first to disc

Please make sure to update tests as appropriate.

## Changelog
See [CHANGELOG.md](./CHANGELOG.md) for a list of changes and version history.

## License
[MIT](./LICENSE.md)
9 changes: 6 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
}
],
"require": {
"php": ">=8.1"
"php": ">=8.1",
"ext-dom": "*"
},
"autoload": {
"psr-4": {
Expand All @@ -31,13 +32,15 @@
},
"require-dev": {
"phpunit/phpunit": "^10.2",
"squizlabs/php_codesniffer": "*",
"squizlabs/php_codesniffer": "^3.13",
"doctrine/coding-standard": "^12.0"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"sort-packages": true,
"prefer-stable": true
},
"scripts": {
"test": "./vendor/bin/phpunit",
Expand Down
4 changes: 4 additions & 0 deletions examples/css/highlighter.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@
.code-highlighter .line-number {
display: block;
}

.code-block-wrapper .code-block {
display: block;
}
Binary file added examples/img/scr_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 15 additions & 16 deletions examples/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

$text = '
<h2>PHP</h2>
<pre data-file="php-highlight/examples/index.php" data-lang="php">
<pre><code class="language-php" data-file="php-highlight/examples/index.php">
&lt;?php
abstract class AbstractClass
{
Expand Down Expand Up @@ -48,10 +48,10 @@ public function prefixName(string $name): string
$class = new ConcreteClass;
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";
</pre>
</code></pre>

<h2>JavaScript</h2>
<pre data-file="example.js" data-lang="js">
<pre><code class="language-javascript" data-file="example.js">
// Arrow functions let us omit the `function` keyword. Here `long_example`
// points to an anonymous function value.
const long_example = (input1, input2) => {
Expand All @@ -65,10 +65,10 @@ public function prefixName(string $name): string

long_example(2, 3); // Prints "Hello, World!" and returns 5.
short_example(2); // Returns 7.
</pre>
</code></pre>

<h2>Bash</h2>
<pre data-file="example.sh" data-lang="bash">
<pre><code class="language-bash" data-file="example.sh">
#!/bin/bash
read -p "Enter number : " n
if test $n -ge 0
Expand All @@ -77,10 +77,10 @@ public function prefixName(string $name): string
else
echo "$n number is negative number."
fi
</pre>
</code></pre>

<h2>Go</h2>
<pre data-lang="go" data-file="main.go">
<pre><code class="language-go" data-file="main.go">
package main

import "fmt"
Expand All @@ -104,9 +104,9 @@ public function prefixName(string $name): string
fmt.Scanln()
fmt.Println("done")
}
</pre>
</code></pre>
<h2>Xml</h2>
<pre data-lang="xml" data-file="recipe.xml">
<pre><code class="language-xml" data-file="recipe.xml">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE recipe>
<recipe name="bread" preptime="5min" cooktime="180min">
Expand All @@ -130,10 +130,10 @@ public function prefixName(string $name): string
</step>
</instructions>
</recipe>
</pre>
</code></pre>

<h2>HTML</h2>
<pre data-lang="html" data-file="index.html">
<pre><code class="language-html" data-file="index.html">
<!DOCTYPE html>
<title>Title</title>

Expand All @@ -150,18 +150,17 @@ function $init() {return true;}
Mix all ingredients and knead thoroughly.
</div>
</body>
</pre>
</code></pre>
';

require_once '../vendor/autoload.php';

use Demyanovs\PHPHighlight\Highlighter;
use Demyanovs\PHPHighlight\Themes\ObsidianTheme;

$highlighter = new Highlighter($text, ObsidianTheme::TITLE);
// Configuration
$highlighter->showLineNumbers(true);
$highlighter->showActionPanel(true);
$highlighter = (new Highlighter($text, ObsidianTheme::TITLE))
->showLineNumbers(true)
->showActionPanel(true);
echo $highlighter->parse();

?>
Expand Down
2 changes: 2 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@
<exclude name="SlevomatCodingStandard.Commenting.RequireOneLineDocComment.MultiLineDocComment"/>
<exclude name="SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed"/>
<exclude name="SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix"/>
<exclude name="Generic.Formatting.SpaceAfterNot.Incorrect"/>
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSame"/>
<exclude name="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint"/>
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace"/>
</rule>

<exclude-pattern>examples/</exclude-pattern>
<exclude-pattern>tests/</exclude-pattern>
</ruleset>
Loading