diff --git a/src/Highlighter.php b/src/Highlighter.php index 866fe91..d8ab5ea 100644 --- a/src/Highlighter.php +++ b/src/Highlighter.php @@ -5,6 +5,7 @@ namespace Tempest\Highlight; use ReflectionClass; +use Tempest\Highlight\Languages\Apache\ApacheLanguage; use Tempest\Highlight\Languages\Base\Injections\GutterInjection; use Tempest\Highlight\Languages\Bash\BashLanguage; use Tempest\Highlight\Languages\BBCode\BBCodeLanguage; @@ -55,7 +56,8 @@ final class Highlighter public function __construct(private readonly Theme $theme = new CssTheme()) { - $this->addLanguage(new BashLanguage()) + $this->addLanguage(new ApacheLanguage()) + ->addLanguage(new BashLanguage()) ->addLanguage(new BBCodeLanguage()) ->addLanguage(new BladeLanguage()) ->addLanguage(new CssLanguage()) diff --git a/src/Languages/Apache/ApacheLanguage.php b/src/Languages/Apache/ApacheLanguage.php new file mode 100644 index 0000000..b8dc6e0 --- /dev/null +++ b/src/Languages/Apache/ApacheLanguage.php @@ -0,0 +1,45 @@ +', output: 'VirtualHost')] +#[PatternTest(input: '', output: 'Directory')] +#[PatternTest(input: '', output: 'IfModule')] +final readonly class ApacheCloseTagPattern implements Pattern +{ + use IsPattern; + + public function getPattern(): string + { + return '<\/(?[A-Za-z]\w*)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::KEYWORD; + } +} diff --git a/src/Languages/Apache/Patterns/ApacheCommentPattern.php b/src/Languages/Apache/Patterns/ApacheCommentPattern.php new file mode 100644 index 0000000..2209ccc --- /dev/null +++ b/src/Languages/Apache/Patterns/ApacheCommentPattern.php @@ -0,0 +1,27 @@ +#.*)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::COMMENT; + } +} diff --git a/src/Languages/Apache/Patterns/ApacheDirectivePattern.php b/src/Languages/Apache/Patterns/ApacheDirectivePattern.php new file mode 100644 index 0000000..5b81b3f --- /dev/null +++ b/src/Languages/Apache/Patterns/ApacheDirectivePattern.php @@ -0,0 +1,55 @@ +{$directives})\b/m"; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::KEYWORD; + } +} diff --git a/src/Languages/Apache/Patterns/ApacheFlagPattern.php b/src/Languages/Apache/Patterns/ApacheFlagPattern.php new file mode 100644 index 0000000..fa240f2 --- /dev/null +++ b/src/Languages/Apache/Patterns/ApacheFlagPattern.php @@ -0,0 +1,31 @@ +on|off|All|None|granted|denied)\b/'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::VALUE; + } +} diff --git a/src/Languages/Apache/Patterns/ApacheOpenTagPattern.php b/src/Languages/Apache/Patterns/ApacheOpenTagPattern.php new file mode 100644 index 0000000..22643b9 --- /dev/null +++ b/src/Languages/Apache/Patterns/ApacheOpenTagPattern.php @@ -0,0 +1,30 @@ +', output: 'VirtualHost')] +#[PatternTest(input: '', output: 'Directory')] +#[PatternTest(input: '', output: 'Location')] +#[PatternTest(input: '', output: 'IfModule')] +#[PatternTest(input: '', output: 'FilesMatch')] +final readonly class ApacheOpenTagPattern implements Pattern +{ + use IsPattern; + + public function getPattern(): string + { + return '<(?[A-Za-z]\w*)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::KEYWORD; + } +} diff --git a/tests/Bench/Fixtures/apache.txt b/tests/Bench/Fixtures/apache.txt new file mode 100644 index 0000000..d289e05 --- /dev/null +++ b/tests/Bench/Fixtures/apache.txt @@ -0,0 +1,37 @@ +# Apache2 Virtual Host Configuration + + + ServerAdmin webmaster@example.com + ServerName example.com + ServerAlias www.example.com + DocumentRoot /var/www/html + + + Options -Indexes +FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + RewriteEngine on + RewriteCond %{HTTPS} off + RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + + + + ServerName example.com + DocumentRoot /var/www/html + + SSLEngine on + SSLCertificateFile "/etc/ssl/certs/server.crt" + SSLCertificateKeyFile "/etc/ssl/private/server.key" + + + Header always set Strict-Transport-Security "max-age=31536000" + + + ProxyPass /api http://localhost:3000 + ProxyPassReverse /api http://localhost:3000 + diff --git a/tests/Bench/HighlighterBench.php b/tests/Bench/HighlighterBench.php index 9c8d83a..d24e4c5 100644 --- a/tests/Bench/HighlighterBench.php +++ b/tests/Bench/HighlighterBench.php @@ -17,6 +17,7 @@ final class HighlighterBench private const string FIXTURES_DIR = __DIR__ . '/Fixtures'; public const array LANGUAGES = [ + 'apache' => 'apache.txt', 'bash' => 'bash.txt', 'bbcode' => 'bbcode.txt', 'blade' => 'blade.txt', diff --git a/tests/Languages/Apache/ApacheLanguageTest.php b/tests/Languages/Apache/ApacheLanguageTest.php new file mode 100644 index 0000000..22623df --- /dev/null +++ b/tests/Languages/Apache/ApacheLanguageTest.php @@ -0,0 +1,84 @@ +assertSame( + $expected, + $highlighter->parse($content, 'apache'), + ); + + $this->assertSame($expected, $highlighter->parse($content, 'apacheconf')); + $this->assertSame($expected, $highlighter->parse($content, 'htaccess')); + } + + public static function provide_highlight_cases(): iterable + { + return [ + 'comments' => [ + <<<'APACHE' + # This is a comment + ServerName example.com + APACHE, + '# This is a comment +ServerName example.com', + ], + 'virtual host block' => [ + <<<'APACHE' + + ServerName example.com + DocumentRoot "/var/www/html" + + APACHE, + '<VirtualHost *:80> + ServerName example.com + DocumentRoot "/var/www/html" +</VirtualHost>', + ], + 'rewrite rules' => [ + <<<'APACHE' + RewriteEngine on + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [L] + APACHE, + 'RewriteEngine on +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ index.php [L]', + ], + 'ssl configuration' => [ + <<<'APACHE' + SSLEngine on + SSLCertificateFile "/etc/ssl/certs/server.crt" + APACHE, + 'SSLEngine on +SSLCertificateFile "/etc/ssl/certs/server.crt"', + ], + 'directory block with flags' => [ + <<<'APACHE' + + Options All + AllowOverride None + Require all granted + + APACHE, + '<Directory /var/www> + Options All + AllowOverride None + Require all granted +</Directory>', + ], + ]; + } +}