Skip to content

Commit c7eed7e

Browse files
authored
Merge pull request #204 from DorZuberi/add-apache2-language-support
2 parents 0c220ab + 2feefeb commit c7eed7e

10 files changed

Lines changed: 341 additions & 1 deletion

File tree

src/Highlighter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Tempest\Highlight;
66

77
use ReflectionClass;
8+
use Tempest\Highlight\Languages\Apache\ApacheLanguage;
89
use Tempest\Highlight\Languages\Base\Injections\GutterInjection;
910
use Tempest\Highlight\Languages\Bash\BashLanguage;
1011
use Tempest\Highlight\Languages\BBCode\BBCodeLanguage;
@@ -56,7 +57,8 @@ final class Highlighter
5657

5758
public function __construct(private readonly Theme $theme = new CssTheme())
5859
{
59-
$this->addLanguage(new BashLanguage())
60+
$this->addLanguage(new ApacheLanguage())
61+
->addLanguage(new BashLanguage())
6062
->addLanguage(new BBCodeLanguage())
6163
->addLanguage(new BladeLanguage())
6264
->addLanguage(new CssLanguage())
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Highlight\Languages\Apache;
6+
7+
use Override;
8+
use Tempest\Highlight\Languages\Apache\Patterns\ApacheCloseTagPattern;
9+
use Tempest\Highlight\Languages\Apache\Patterns\ApacheCommentPattern;
10+
use Tempest\Highlight\Languages\Apache\Patterns\ApacheDirectivePattern;
11+
use Tempest\Highlight\Languages\Apache\Patterns\ApacheFlagPattern;
12+
use Tempest\Highlight\Languages\Apache\Patterns\ApacheOpenTagPattern;
13+
use Tempest\Highlight\Languages\Base\BaseLanguage;
14+
use Tempest\Highlight\Languages\Php\Patterns\DoubleQuoteValuePattern;
15+
16+
final class ApacheLanguage extends BaseLanguage
17+
{
18+
public function getName(): string
19+
{
20+
return 'apache';
21+
}
22+
23+
#[Override]
24+
public function getAliases(): array
25+
{
26+
return [
27+
'apacheconf',
28+
'htaccess',
29+
];
30+
}
31+
32+
#[Override]
33+
public function getPatterns(): array
34+
{
35+
return [
36+
...parent::getPatterns(),
37+
new ApacheCommentPattern(),
38+
new ApacheDirectivePattern(),
39+
new ApacheOpenTagPattern(),
40+
new ApacheCloseTagPattern(),
41+
new ApacheFlagPattern(),
42+
new DoubleQuoteValuePattern(),
43+
];
44+
}
45+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Highlight\Languages\Apache\Patterns;
6+
7+
use Tempest\Highlight\IsPattern;
8+
use Tempest\Highlight\Pattern;
9+
use Tempest\Highlight\PatternTest;
10+
use Tempest\Highlight\Tokens\TokenTypeEnum;
11+
12+
#[PatternTest(input: '</VirtualHost>', output: 'VirtualHost')]
13+
#[PatternTest(input: '</Directory>', output: 'Directory')]
14+
#[PatternTest(input: '</IfModule>', output: 'IfModule')]
15+
final readonly class ApacheCloseTagPattern implements Pattern
16+
{
17+
use IsPattern;
18+
19+
public function getPattern(): string
20+
{
21+
return '<\/(?<match>[A-Za-z]\w*)';
22+
}
23+
24+
public function getTokenType(): TokenTypeEnum
25+
{
26+
return TokenTypeEnum::KEYWORD;
27+
}
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Highlight\Languages\Apache\Patterns;
6+
7+
use Tempest\Highlight\IsPattern;
8+
use Tempest\Highlight\Pattern;
9+
use Tempest\Highlight\PatternTest;
10+
use Tempest\Highlight\Tokens\TokenTypeEnum;
11+
12+
#[PatternTest(input: '# This is a comment', output: '# This is a comment')]
13+
#[PatternTest(input: 'ServerName example.com # inline', output: '# inline')]
14+
final readonly class ApacheCommentPattern implements Pattern
15+
{
16+
use IsPattern;
17+
18+
public function getPattern(): string
19+
{
20+
return '(?<match>#.*)';
21+
}
22+
23+
public function getTokenType(): TokenTypeEnum
24+
{
25+
return TokenTypeEnum::COMMENT;
26+
}
27+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Highlight\Languages\Apache\Patterns;
6+
7+
use Tempest\Highlight\IsPattern;
8+
use Tempest\Highlight\Pattern;
9+
use Tempest\Highlight\PatternTest;
10+
use Tempest\Highlight\Tokens\TokenTypeEnum;
11+
12+
#[PatternTest(input: 'ServerName example.com', output: 'ServerName')]
13+
#[PatternTest(input: 'DocumentRoot /var/www/html', output: 'DocumentRoot')]
14+
#[PatternTest(input: ' RewriteRule ^(.*)$ index.php [L]', output: 'RewriteRule')]
15+
#[PatternTest(input: 'Listen 80', output: 'Listen')]
16+
#[PatternTest(input: 'SSLEngine on', output: 'SSLEngine')]
17+
final readonly class ApacheDirectivePattern implements Pattern
18+
{
19+
use IsPattern;
20+
21+
private const DIRECTIVES = [
22+
'ServerRoot', 'Listen', 'LoadModule',
23+
'ServerAdmin', 'ServerName', 'ServerAlias',
24+
'DocumentRoot', 'ErrorLog', 'CustomLog', 'LogLevel', 'LogFormat',
25+
'DirectoryIndex', 'Options', 'AllowOverride',
26+
'Require', 'Order', 'Allow', 'Deny',
27+
'Include', 'IncludeOptional',
28+
'RewriteEngine', 'RewriteCond', 'RewriteRule', 'RewriteBase',
29+
'SSLEngine', 'SSLCertificateFile', 'SSLCertificateKeyFile', 'SSLCertificateChainFile',
30+
'ProxyPass', 'ProxyPassReverse', 'ProxyPreserveHost',
31+
'Header', 'RequestHeader',
32+
'SetEnv', 'SetEnvIf', 'PassEnv',
33+
'Redirect', 'RedirectMatch', 'RedirectPermanent',
34+
'Alias', 'ScriptAlias',
35+
'AddType', 'AddHandler', 'AddOutputFilterByType',
36+
'ErrorDocument',
37+
'Timeout', 'KeepAlive', 'MaxKeepAliveRequests', 'KeepAliveTimeout',
38+
'AccessFileName', 'DefaultType', 'HostnameLookups',
39+
'UseCanonicalName', 'ServerSignature', 'ServerTokens',
40+
'TypesConfig', 'MIMEMagicFile',
41+
'ExpiresActive', 'ExpiresByType', 'ExpiresDefault',
42+
];
43+
44+
public function getPattern(): string
45+
{
46+
$directives = implode('|', self::DIRECTIVES);
47+
48+
return "/^\s*(?<match>{$directives})\b/m";
49+
}
50+
51+
public function getTokenType(): TokenTypeEnum
52+
{
53+
return TokenTypeEnum::KEYWORD;
54+
}
55+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Highlight\Languages\Apache\Patterns;
6+
7+
use Tempest\Highlight\IsPattern;
8+
use Tempest\Highlight\Pattern;
9+
use Tempest\Highlight\PatternTest;
10+
use Tempest\Highlight\Tokens\TokenTypeEnum;
11+
12+
#[PatternTest(input: 'SSLEngine on', output: 'on')]
13+
#[PatternTest(input: 'SSLEngine off', output: 'off')]
14+
#[PatternTest(input: 'Options All', output: 'All')]
15+
#[PatternTest(input: 'Options None', output: 'None')]
16+
#[PatternTest(input: 'Require all granted', output: 'granted')]
17+
#[PatternTest(input: 'Require all denied', output: 'denied')]
18+
final readonly class ApacheFlagPattern implements Pattern
19+
{
20+
use IsPattern;
21+
22+
public function getPattern(): string
23+
{
24+
return '/\b(?<match>on|off|All|None|granted|denied)\b/';
25+
}
26+
27+
public function getTokenType(): TokenTypeEnum
28+
{
29+
return TokenTypeEnum::VALUE;
30+
}
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Highlight\Languages\Apache\Patterns;
6+
7+
use Tempest\Highlight\IsPattern;
8+
use Tempest\Highlight\Pattern;
9+
use Tempest\Highlight\PatternTest;
10+
use Tempest\Highlight\Tokens\TokenTypeEnum;
11+
12+
#[PatternTest(input: '<VirtualHost *:80>', output: 'VirtualHost')]
13+
#[PatternTest(input: '<Directory /var/www>', output: 'Directory')]
14+
#[PatternTest(input: '<Location /admin>', output: 'Location')]
15+
#[PatternTest(input: '<IfModule mod_ssl.c>', output: 'IfModule')]
16+
#[PatternTest(input: '<FilesMatch "\.php$">', output: 'FilesMatch')]
17+
final readonly class ApacheOpenTagPattern implements Pattern
18+
{
19+
use IsPattern;
20+
21+
public function getPattern(): string
22+
{
23+
return '<(?<match>[A-Za-z]\w*)';
24+
}
25+
26+
public function getTokenType(): TokenTypeEnum
27+
{
28+
return TokenTypeEnum::KEYWORD;
29+
}
30+
}

tests/Bench/Fixtures/apache.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Apache2 Virtual Host Configuration
2+
3+
<VirtualHost *:80>
4+
ServerAdmin webmaster@example.com
5+
ServerName example.com
6+
ServerAlias www.example.com
7+
DocumentRoot /var/www/html
8+
9+
<Directory /var/www/html>
10+
Options -Indexes +FollowSymLinks
11+
AllowOverride All
12+
Require all granted
13+
</Directory>
14+
15+
ErrorLog ${APACHE_LOG_DIR}/error.log
16+
CustomLog ${APACHE_LOG_DIR}/access.log combined
17+
18+
RewriteEngine on
19+
RewriteCond %{HTTPS} off
20+
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
21+
</VirtualHost>
22+
23+
<VirtualHost *:443>
24+
ServerName example.com
25+
DocumentRoot /var/www/html
26+
27+
SSLEngine on
28+
SSLCertificateFile "/etc/ssl/certs/server.crt"
29+
SSLCertificateKeyFile "/etc/ssl/private/server.key"
30+
31+
<IfModule mod_headers.c>
32+
Header always set Strict-Transport-Security "max-age=31536000"
33+
</IfModule>
34+
35+
ProxyPass /api http://localhost:3000
36+
ProxyPassReverse /api http://localhost:3000
37+
</VirtualHost>

tests/Bench/HighlighterBench.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ final class HighlighterBench
1717
private const string FIXTURES_DIR = __DIR__ . '/Fixtures';
1818

1919
public const array LANGUAGES = [
20+
'apache' => 'apache.txt',
2021
'bash' => 'bash.txt',
2122
'bbcode' => 'bbcode.txt',
2223
'blade' => 'blade.txt',
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Languages\Apache;
6+
7+
use PHPUnit\Framework\Attributes\DataProvider;
8+
use PHPUnit\Framework\TestCase;
9+
use Tempest\Highlight\Highlighter;
10+
11+
final class ApacheLanguageTest extends TestCase
12+
{
13+
#[DataProvider('provide_highlight_cases')]
14+
public function test_highlight(string $content, string $expected): void
15+
{
16+
$highlighter = new Highlighter();
17+
18+
$this->assertSame(
19+
$expected,
20+
$highlighter->parse($content, 'apache'),
21+
);
22+
23+
$this->assertSame($expected, $highlighter->parse($content, 'apacheconf'));
24+
$this->assertSame($expected, $highlighter->parse($content, 'htaccess'));
25+
}
26+
27+
public static function provide_highlight_cases(): iterable
28+
{
29+
return [
30+
'comments' => [
31+
<<<'APACHE'
32+
# This is a comment
33+
ServerName example.com
34+
APACHE,
35+
'<span class="hl-comment"># This is a comment</span>
36+
<span class="hl-keyword">ServerName</span> example.com',
37+
],
38+
'virtual host block' => [
39+
<<<'APACHE'
40+
<VirtualHost *:80>
41+
ServerName example.com
42+
DocumentRoot "/var/www/html"
43+
</VirtualHost>
44+
APACHE,
45+
'&lt;<span class="hl-keyword">VirtualHost</span> *:80&gt;
46+
<span class="hl-keyword">ServerName</span> example.com
47+
<span class="hl-keyword">DocumentRoot</span> <span class="hl-value">&quot;/var/www/html&quot;</span>
48+
&lt;/<span class="hl-keyword">VirtualHost</span>&gt;',
49+
],
50+
'rewrite rules' => [
51+
<<<'APACHE'
52+
RewriteEngine on
53+
RewriteCond %{REQUEST_FILENAME} !-f
54+
RewriteRule ^(.*)$ index.php [L]
55+
APACHE,
56+
'<span class="hl-keyword">RewriteEngine</span> <span class="hl-value">on</span>
57+
<span class="hl-keyword">RewriteCond</span> %{REQUEST_FILENAME} !-f
58+
<span class="hl-keyword">RewriteRule</span> ^(.*)$ index.php [L]',
59+
],
60+
'ssl configuration' => [
61+
<<<'APACHE'
62+
SSLEngine on
63+
SSLCertificateFile "/etc/ssl/certs/server.crt"
64+
APACHE,
65+
'<span class="hl-keyword">SSLEngine</span> <span class="hl-value">on</span>
66+
<span class="hl-keyword">SSLCertificateFile</span> <span class="hl-value">&quot;/etc/ssl/certs/server.crt&quot;</span>',
67+
],
68+
'directory block with flags' => [
69+
<<<'APACHE'
70+
<Directory /var/www>
71+
Options All
72+
AllowOverride None
73+
Require all granted
74+
</Directory>
75+
APACHE,
76+
'&lt;<span class="hl-keyword">Directory</span> /var/www&gt;
77+
<span class="hl-keyword">Options</span> <span class="hl-value">All</span>
78+
<span class="hl-keyword">AllowOverride</span> <span class="hl-value">None</span>
79+
<span class="hl-keyword">Require</span> all <span class="hl-value">granted</span>
80+
&lt;/<span class="hl-keyword">Directory</span>&gt;',
81+
],
82+
];
83+
}
84+
}

0 commit comments

Comments
 (0)