First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,3 @@
build
vendor
composer.lock

View file

@ -0,0 +1,15 @@
language: php
php:
- 5.3.3
- 5.4
- 5.5
before_script:
- composer self-update
- composer install --no-interaction --prefer-source --dev
script:
- ant phplint
- ant phpcs
- ant phpunit

View file

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<project default="build">
<!-- Set executables according to OS -->
<condition property="phpunit" value="${basedir}/vendor/bin/phpunit.bat" else="${basedir}/vendor/bin/phpunit">
<os family="windows" />
</condition>
<condition property="phpcs" value="${basedir}/vendor/bin/phpcs.bat" else="${basedir}/vendor/bin/phpcs">
<os family="windows" />
</condition>
<condition property="parallel-lint" value="${basedir}/vendor/bin/parallel-lint.bat" else="${basedir}/vendor/bin/parallel-lint">
<os family="windows" />
</condition>
<condition property="var-dump-check" value="${basedir}/vendor/bin/var-dump-check.bat" else="${basedir}/vendor/bin/var-dump-check">
<os family="windows"/>
</condition>
<!-- Use colors in output can be disabled when calling ant with -Duse-colors=false -->
<property name="use-colors" value="true" />
<condition property="colors-arg.color" value="--colors" else="">
<equals arg1="${use-colors}" arg2="true" />
</condition>
<condition property="colors-arg.no-colors" value="" else="--no-colors">
<equals arg1="${use-colors}" arg2="true" />
</condition>
<!-- Targets -->
<target name="prepare" description="Create build directory">
<mkdir dir="${basedir}/build/logs" />
</target>
<target name="phplint" description="Check syntax errors in PHP files">
<exec executable="${parallel-lint}" failonerror="true">
<arg line='--exclude ${basedir}/vendor/' />
<arg line='${colors-arg.no-colors}' />
<arg line='${basedir}' />
</exec>
</target>
<target name="var-dump-check" description="Check PHP files for forgotten variable dumps">
<exec executable="${var-dump-check}" failonerror="true">
<arg line='--exclude ${basedir}/vendor/' />
<arg line='${colors-arg.no-colors}' />
<arg line='${basedir}' />
</exec>
</target>
<target name="phpcs" depends="prepare" description="Check PHP code style">
<delete file="${basedir}/build/logs/checkstyle.xml" quiet="true" />
<exec executable="${phpcs}">
<arg line='--extensions=php' />
<arg line='--standard="${basedir}/vendor/jakub-onderka/php-code-style/ruleset.xml"' />
<arg line='--report-checkstyle="${basedir}/build/logs/checkstyle.xml"' />
<arg line='--report-full' />
<arg line='"${basedir}/src"' />
</exec>
</target>
<target name="phpunit" depends="prepare" description="PHP unit">
<delete file="${basedir}/build/logs/phpunit.xml" quiet="true" />
<exec executable="${phpunit}">
<arg line='--configuration ${basedir}/phpunit.xml' />
<arg line='-d memory_limit=256M' />
<arg line='--log-junit "${basedir}/build/logs/phpunit.xml"' />
<arg line='${colors-arg.color}' />
</exec>
</target>
<target name="phpunit-coverage" depends="prepare" description="PHP unit with code coverage">
<delete file="${basedir}/build/logs/phpunit.xml" quiet="true" />
<delete file="${basedir}/build/logs/clover.xml" quiet="true" />
<delete dir="${basedir}/build/coverage" quiet="true" />
<mkdir dir="${basedir}/build/coverage" />
<exec executable="${phpunit}">
<arg line='--configuration ${basedir}/phpunit.xml' />
<arg line='-d memory_limit=256M' />
<arg line='--log-junit "${basedir}/build/logs/phpunit.xml"' />
<arg line='--coverage-clover "${basedir}/build/logs/clover.xml"' />
<arg line='--coverage-html "${basedir}/build/coverage/"' />
<arg line='${colors-arg.color}' />
</exec>
</target>
<target name="build" depends="phplint,var-dump-check,phpcs,phpunit" />
</project>

View file

@ -0,0 +1,24 @@
{
"name": "jakub-onderka/php-console-color",
"license": "BSD-2-Clause",
"version": "0.1",
"authors": [
{
"name": "Jakub Onderka",
"email": "jakub.onderka@gmail.com"
}
],
"autoload": {
"psr-0": {"JakubOnderka\\PhpConsoleColor": "src/"}
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
"jakub-onderka/php-parallel-lint": "0.*",
"jakub-onderka/php-var-dump-check": "0.*",
"squizlabs/php_codesniffer": "1.*",
"jakub-onderka/php-code-style": "1.0"
}
}

View file

@ -0,0 +1,38 @@
<?php
$loader = require_once __DIR__ . '/vendor/autoload.php';
$consoleColor = new JakubOnderka\PhpConsoleColor\ConsoleColor();
echo "Colors are supported: " . ($consoleColor->isSupported() ? 'Yes' : 'No') . "\n";
echo "256 colors are supported: " . ($consoleColor->are256ColorsSupported() ? 'Yes' : 'No') . "\n\n";
if ($consoleColor->isSupported()) {
foreach ($consoleColor->getPossibleStyles() as $style) {
echo $consoleColor->apply($style, $style) . "\n";
}
}
echo "\n";
if ($consoleColor->are256ColorsSupported()) {
echo "Foreground colors:\n";
for ($i = 1; $i <= 255; $i++) {
echo $consoleColor->apply("color_$i", str_pad($i, 6, ' ', STR_PAD_BOTH));
if ($i % 15 === 0) {
echo "\n";
}
}
echo "\nBackground colors:\n";
for ($i = 1; $i <= 255; $i++) {
echo $consoleColor->apply("bg_color_$i", str_pad($i, 6, ' ', STR_PAD_BOTH));
if ($i % 15 === 0) {
echo "\n";
}
}
echo "\n";
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite>
<directory>tests/*</directory>
</testsuite>
</testsuites>
<!-- Ignore vendor folder for code coverage -->
<filter>
<blacklist>
<directory>vendor</directory>
</blacklist>
</filter>
</phpunit>

View file

@ -0,0 +1,280 @@
<?php
namespace JakubOnderka\PhpConsoleColor;
class ConsoleColor
{
const FOREGROUND = 38,
BACKGROUND = 48;
const COLOR256_REGEXP = '~^(bg_)?color_([0-9]{1,3})$~';
const RESET_STYLE = 0;
/** @var bool */
private $isSupported;
/** @var bool */
private $forceStyle = false;
/** @var array */
private $styles = array(
'none' => null,
'bold' => '1',
'dark' => '2',
'italic' => '3',
'underline' => '4',
'blink' => '5',
'reverse' => '7',
'concealed' => '8',
'default' => '39',
'black' => '30',
'red' => '31',
'green' => '32',
'yellow' => '33',
'blue' => '34',
'magenta' => '35',
'cyan' => '36',
'light_gray' => '37',
'dark_gray' => '90',
'light_red' => '91',
'light_green' => '92',
'light_yellow' => '93',
'light_blue' => '94',
'light_magenta' => '95',
'light_cyan' => '96',
'white' => '97',
'bg_default' => '49',
'bg_black' => '40',
'bg_red' => '41',
'bg_green' => '42',
'bg_yellow' => '43',
'bg_blue' => '44',
'bg_magenta' => '45',
'bg_cyan' => '46',
'bg_light_gray' => '47',
'bg_dark_gray' => '100',
'bg_light_red' => '101',
'bg_light_green' => '102',
'bg_light_yellow' => '103',
'bg_light_blue' => '104',
'bg_light_magenta' => '105',
'bg_light_cyan' => '106',
'bg_white' => '107',
);
/** @var array */
private $themes = array();
public function __construct()
{
$this->isSupported = $this->isSupported();
}
/**
* @param string|array $style
* @param string $text
* @return string
* @throws InvalidStyleException
* @throws \InvalidArgumentException
*/
public function apply($style, $text)
{
if (!$this->isStyleForced() && !$this->isSupported()) {
return $text;
}
if (is_string($style)) {
$style = array($style);
}
if (!is_array($style)) {
throw new \InvalidArgumentException("Style must be string or array.");
}
$sequences = array();
foreach ($style as $s) {
if (isset($this->themes[$s])) {
$sequences = array_merge($sequences, $this->themeSequence($s));
} else if ($this->isValidStyle($s)) {
$sequences[] = $this->styleSequence($s);
} else {
throw new InvalidStyleException($s);
}
}
$sequences = array_filter($sequences, function ($val) {
return $val !== null;
});
if (empty($sequences)) {
return $text;
}
return $this->escSequence(implode(';', $sequences)) . $text . $this->escSequence(self::RESET_STYLE);
}
/**
* @param bool $forceStyle
*/
public function setForceStyle($forceStyle)
{
$this->forceStyle = (bool) $forceStyle;
}
/**
* @return bool
*/
public function isStyleForced()
{
return $this->forceStyle;
}
/**
* @param array $themes
* @throws InvalidStyleException
* @throws \InvalidArgumentException
*/
public function setThemes(array $themes)
{
$this->themes = array();
foreach ($themes as $name => $styles) {
$this->addTheme($name, $styles);
}
}
/**
* @param string $name
* @param array|string $styles
* @throws \InvalidArgumentException
* @throws InvalidStyleException
*/
public function addTheme($name, $styles)
{
if (is_string($styles)) {
$styles = array($styles);
}
if (!is_array($styles)) {
throw new \InvalidArgumentException("Style must be string or array.");
}
foreach ($styles as $style) {
if (!$this->isValidStyle($style)) {
throw new InvalidStyleException($style);
}
}
$this->themes[$name] = $styles;
}
/**
* @return array
*/
public function getThemes()
{
return $this->themes;
}
/**
* @param string $name
* @return bool
*/
public function hasTheme($name)
{
return isset($this->themes[$name]);
}
/**
* @param string $name
*/
public function removeTheme($name)
{
unset($this->themes[$name]);
}
/**
* @return bool
*/
public function isSupported()
{
if (DIRECTORY_SEPARATOR === '\\') {
return getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON';
}
return function_exists('posix_isatty') && @posix_isatty(STDOUT);
}
/**
* @return bool
*/
public function are256ColorsSupported()
{
return DIRECTORY_SEPARATOR === '/' && strpos(getenv('TERM'), '256color') !== false;
}
/**
* @return array
*/
public function getPossibleStyles()
{
return array_keys($this->styles);
}
/**
* @param string $name
* @return string
* @throws InvalidStyleException
*/
private function themeSequence($name)
{
$sequences = array();
foreach ($this->themes[$name] as $style) {
$sequences[] = $this->styleSequence($style);
}
return $sequences;
}
/**
* @param string $style
* @return string
* @throws InvalidStyleException
*/
private function styleSequence($style)
{
if (array_key_exists($style, $this->styles)) {
return $this->styles[$style];
}
if (!$this->are256ColorsSupported()) {
return null;
}
preg_match(self::COLOR256_REGEXP, $style, $matches);
$type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND;
$value = $matches[2];
return "$type;5;$value";
}
/**
* @param string $style
* @return bool
*/
private function isValidStyle($style)
{
return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style);
}
/**
* @param string|int $value
* @return string
*/
private function escSequence($value)
{
return "\033[{$value}m";
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace JakubOnderka\PhpConsoleColor;
class InvalidStyleException extends \Exception
{
public function __construct($styleName)
{
parent::__construct("Invalid style $styleName.");
}
}

View file

@ -0,0 +1,184 @@
<?php
use JakubOnderka\PhpConsoleColor\ConsoleColor;
class ConsoleColorWithForceSupport extends ConsoleColor
{
private $isSupportedForce = true;
private $are256ColorsSupportedForce = true;
public function setIsSupported($isSupported)
{
$this->isSupportedForce = $isSupported;
}
public function isSupported()
{
return $this->isSupportedForce;
}
public function setAre256ColorsSupported($are256ColorsSupported)
{
$this->are256ColorsSupportedForce = $are256ColorsSupported;
}
public function are256ColorsSupported()
{
return $this->are256ColorsSupportedForce;
}
}
class ConsoleColorTest extends \PHPUnit_Framework_TestCase
{
/** @var ConsoleColorWithForceSupport */
private $uut;
protected function setUp()
{
$this->uut = new ConsoleColorWithForceSupport();
}
public function testNone()
{
$output = $this->uut->apply('none', 'text');
$this->assertEquals("text", $output);
}
public function testBold()
{
$output = $this->uut->apply('bold', 'text');
$this->assertEquals("\033[1mtext\033[0m", $output);
}
public function testBoldColorsAreNotSupported()
{
$this->uut->setIsSupported(false);
$output = $this->uut->apply('bold', 'text');
$this->assertEquals("text", $output);
}
public function testBoldColorsAreNotSupportedButAreForced()
{
$this->uut->setIsSupported(false);
$this->uut->setForceStyle(true);
$output = $this->uut->apply('bold', 'text');
$this->assertEquals("\033[1mtext\033[0m", $output);
}
public function testDark()
{
$output = $this->uut->apply('dark', 'text');
$this->assertEquals("\033[2mtext\033[0m", $output);
}
public function testBoldAndDark()
{
$output = $this->uut->apply(array('bold', 'dark'), 'text');
$this->assertEquals("\033[1;2mtext\033[0m", $output);
}
public function test256ColorForeground()
{
$output = $this->uut->apply('color_255', 'text');
$this->assertEquals("\033[38;5;255mtext\033[0m", $output);
}
public function test256ColorWithoutSupport()
{
$this->uut->setAre256ColorsSupported(false);
$output = $this->uut->apply('color_255', 'text');
$this->assertEquals("text", $output);
}
public function test256ColorBackground()
{
$output = $this->uut->apply('bg_color_255', 'text');
$this->assertEquals("\033[48;5;255mtext\033[0m", $output);
}
public function test256ColorForegroundAndBackground()
{
$output = $this->uut->apply(array('color_200', 'bg_color_255'), 'text');
$this->assertEquals("\033[38;5;200;48;5;255mtext\033[0m", $output);
}
public function testSetOwnTheme()
{
$this->uut->setThemes(array('bold_dark' => array('bold', 'dark')));
$output = $this->uut->apply(array('bold_dark'), 'text');
$this->assertEquals("\033[1;2mtext\033[0m", $output);
}
public function testAddOwnTheme()
{
$this->uut->addTheme('bold_own', 'bold');
$output = $this->uut->apply(array('bold_own'), 'text');
$this->assertEquals("\033[1mtext\033[0m", $output);
}
public function testAddOwnThemeArray()
{
$this->uut->addTheme('bold_dark', array('bold', 'dark'));
$output = $this->uut->apply(array('bold_dark'), 'text');
$this->assertEquals("\033[1;2mtext\033[0m", $output);
}
public function testOwnWithStyle()
{
$this->uut->addTheme('bold_dark', array('bold', 'dark'));
$output = $this->uut->apply(array('bold_dark', 'italic'), 'text');
$this->assertEquals("\033[1;2;3mtext\033[0m", $output);
}
public function testHasAndRemoveTheme()
{
$this->assertFalse($this->uut->hasTheme('bold_dark'));
$this->uut->addTheme('bold_dark', array('bold', 'dark'));
$this->assertTrue($this->uut->hasTheme('bold_dark'));
$this->uut->removeTheme('bold_dark');
$this->assertFalse($this->uut->hasTheme('bold_dark'));
}
public function testApplyInvalidArgument()
{
$this->setExpectedException('\InvalidArgumentException');
$this->uut->apply(new stdClass(), 'text');
}
public function testApplyInvalidStyleName()
{
$this->setExpectedException('\JakubOnderka\PhpConsoleColor\InvalidStyleException');
$this->uut->apply('invalid', 'text');
}
public function testApplyInvalid256Color()
{
$this->setExpectedException('\JakubOnderka\PhpConsoleColor\InvalidStyleException');
$this->uut->apply('color_2134', 'text');
}
public function testThemeInvalidStyle()
{
$this->setExpectedException('\JakubOnderka\PhpConsoleColor\InvalidStyleException');
$this->uut->addTheme('invalid', array('invalid'));
}
public function testForceStyle()
{
$this->assertFalse($this->uut->isStyleForced());
$this->uut->setForceStyle(true);
$this->assertTrue($this->uut->isStyleForced());
}
public function testGetPossibleStyles()
{
$this->assertInternalType('array', $this->uut->getPossibleStyles());
$this->assertNotEmpty($this->uut->getPossibleStyles());
}
}

View file

@ -0,0 +1,2 @@
<?php
$loader = require_once __DIR__ . '/../vendor/autoload.php';

View file

@ -0,0 +1,4 @@
/.idea/
/build/
/vendor/
/composer.lock

View file

@ -0,0 +1,21 @@
language: php
php:
- 5.3.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
- hhvm-nightly
matrix:
allowed_failures:
- php: 7.0
- php: hhvm-nightly
before_script:
- composer install --no-interaction --prefer-source
script:
- ant

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013 Jakub Onderka
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,40 @@
PHP Console Highlighter
=======================
Highlight PHP code in console (terminal).
Example
-------
![Example](http://jakubonderka.github.io/php-console-highlight-example.png)
Install
-------
Just create a `composer.json` file and run the `php composer.phar install` command to install it:
```json
{
"require": {
"jakub-onderka/php-console-highlighter": "0.*"
}
}
```
Usage
-------
```php
<?php
use JakubOnderka\PhpConsoleColor\ConsoleColor;
use JakubOnderka\PhpConsoleHighlighter\Highlighter;
require __DIR__ . '/vendor/autoload.php';
$highlighter = new Highlighter(new ConsoleColor());
$fileContent = file_get_contents(__FILE__);
echo $highlighter->getWholeFile($fileContent);
```
------
[![Build Status](https://travis-ci.org/JakubOnderka/PHP-Console-Highlighter.svg?branch=master)](https://travis-ci.org/JakubOnderka/PHP-Console-Highlighter)

View file

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<project default="build">
<!-- Set executables according to OS -->
<condition property="phpunit" value="${basedir}/vendor/bin/phpunit.bat" else="${basedir}/vendor/bin/phpunit">
<os family="windows" />
</condition>
<condition property="phpcs" value="${basedir}/vendor/bin/phpcs.bat" else="${basedir}/vendor/bin/phpcs">
<os family="windows" />
</condition>
<condition property="parallel-lint" value="${basedir}/vendor/bin/parallel-lint.bat" else="${basedir}/vendor/bin/parallel-lint">
<os family="windows" />
</condition>
<condition property="var-dump-check" value="${basedir}/vendor/bin/var-dump-check.bat" else="${basedir}/vendor/bin/var-dump-check">
<os family="windows"/>
</condition>
<!-- Use colors in output can be disabled when calling ant with -Duse-colors=false -->
<property name="use-colors" value="true" />
<condition property="colors-arg.color" value="--colors" else="">
<equals arg1="${use-colors}" arg2="true" />
</condition>
<condition property="colors-arg.no-colors" value="" else="--no-colors">
<equals arg1="${use-colors}" arg2="true" />
</condition>
<!-- Targets -->
<target name="prepare" description="Create build directory">
<mkdir dir="${basedir}/build/logs" />
</target>
<target name="phplint" description="Check syntax errors in PHP files">
<exec executable="${parallel-lint}" failonerror="true">
<arg line='--exclude ${basedir}/vendor/' />
<arg line='${colors-arg.no-colors}' />
<arg line='${basedir}' />
</exec>
</target>
<target name="var-dump-check" description="Check PHP files for forgotten variable dumps">
<exec executable="${var-dump-check}" failonerror="true">
<arg line='--exclude ${basedir}/vendor/' />
<arg line='${colors-arg.no-colors}' />
<arg line='${basedir}' />
</exec>
</target>
<target name="phpcs" depends="prepare" description="Check PHP code style">
<delete file="${basedir}/build/logs/checkstyle.xml" quiet="true" />
<exec executable="${phpcs}">
<arg line='--extensions=php' />
<arg line='--standard="${basedir}/vendor/jakub-onderka/php-code-style/ruleset.xml"' />
<arg line='--report-checkstyle="${basedir}/build/logs/checkstyle.xml"' />
<arg line='--report-full' />
<arg line='"${basedir}/src"' />
</exec>
</target>
<target name="phpunit" depends="prepare" description="PHP unit">
<delete file="${basedir}/build/logs/phpunit.xml" quiet="true" />
<exec executable="${phpunit}">
<arg line='--configuration ${basedir}/phpunit.xml' />
<arg line='-d memory_limit=256M' />
<arg line='--log-junit "${basedir}/build/logs/phpunit.xml"' />
<arg line='${colors-arg.color}' />
</exec>
</target>
<target name="phpunit-coverage" depends="prepare" description="PHP unit with code coverage">
<delete file="${basedir}/build/logs/phpunit.xml" quiet="true" />
<delete file="${basedir}/build/logs/clover.xml" quiet="true" />
<delete dir="${basedir}/build/coverage" quiet="true" />
<mkdir dir="${basedir}/build/coverage" />
<exec executable="${phpunit}" failonerror="true">
<arg line='--configuration ${basedir}/phpunit.xml' />
<arg line='-d memory_limit=256M' />
<arg line='--log-junit "${basedir}/build/logs/phpunit.xml"' />
<arg line='--coverage-clover "${basedir}/build/logs/clover.xml"' />
<arg line='--coverage-html "${basedir}/build/coverage/"' />
<arg line='${colors-arg.color}' />
</exec>
</target>
<target name="build" depends="phplint,var-dump-check,phpcs,phpunit" />
</project>

View file

@ -0,0 +1,26 @@
{
"name": "jakub-onderka/php-console-highlighter",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Jakub Onderka",
"email": "acci@acci.cz",
"homepage": "http://www.acci.cz/"
}
],
"autoload": {
"psr-0": {"JakubOnderka\\PhpConsoleHighlighter": "src/"}
},
"require": {
"php": ">=5.3.0",
"jakub-onderka/php-console-color": "~0.1"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"jakub-onderka/php-parallel-lint": "~0.5",
"jakub-onderka/php-var-dump-check": "~0.1",
"squizlabs/php_codesniffer": "~1.5",
"jakub-onderka/php-code-style": "~1.0"
}
}

View file

@ -0,0 +1,10 @@
<?php
use JakubOnderka\PhpConsoleColor\ConsoleColor;
use JakubOnderka\PhpConsoleHighlighter\Highlighter;
require __DIR__ . '/../vendor/autoload.php';
$highlighter = new Highlighter(new ConsoleColor());
$fileContent = file_get_contents(__FILE__);
echo $highlighter->getCodeSnippet($fileContent, 3);

View file

@ -0,0 +1,10 @@
<?php
use JakubOnderka\PhpConsoleColor\ConsoleColor;
use JakubOnderka\PhpConsoleHighlighter\Highlighter;
require __DIR__ . '/../vendor/autoload.php';
$highlighter = new Highlighter(new ConsoleColor());
$fileContent = file_get_contents(__FILE__);
echo $highlighter->getWholeFile($fileContent);

View file

@ -0,0 +1,10 @@
<?php
use JakubOnderka\PhpConsoleColor\ConsoleColor;
use JakubOnderka\PhpConsoleHighlighter\Highlighter;
require __DIR__ . '/../vendor/autoload.php';
$highlighter = new Highlighter(new ConsoleColor());
$fileContent = file_get_contents(__FILE__);
echo $highlighter->getWholeFileWithLineNumbers($fileContent);

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite>
<directory>tests/*</directory>
</testsuite>
</testsuites>
<!-- Ignore vendor folder for code coverage -->
<filter>
<blacklist>
<directory>vendor</directory>
</blacklist>
</filter>
</phpunit>

View file

@ -0,0 +1,267 @@
<?php
namespace JakubOnderka\PhpConsoleHighlighter;
use JakubOnderka\PhpConsoleColor\ConsoleColor;
class Highlighter
{
const TOKEN_DEFAULT = 'token_default',
TOKEN_COMMENT = 'token_comment',
TOKEN_STRING = 'token_string',
TOKEN_HTML = 'token_html',
TOKEN_KEYWORD = 'token_keyword';
const ACTUAL_LINE_MARK = 'actual_line_mark',
LINE_NUMBER = 'line_number';
/** @var ConsoleColor */
private $color;
/** @var array */
private $defaultTheme = array(
self::TOKEN_STRING => 'red',
self::TOKEN_COMMENT => 'yellow',
self::TOKEN_KEYWORD => 'green',
self::TOKEN_DEFAULT => 'default',
self::TOKEN_HTML => 'cyan',
self::ACTUAL_LINE_MARK => 'red',
self::LINE_NUMBER => 'dark_gray',
);
/**
* @param ConsoleColor $color
*/
public function __construct(ConsoleColor $color)
{
$this->color = $color;
foreach ($this->defaultTheme as $name => $styles) {
if (!$this->color->hasTheme($name)) {
$this->color->addTheme($name, $styles);
}
}
}
/**
* @param string $source
* @param int $lineNumber
* @param int $linesBefore
* @param int $linesAfter
* @return string
* @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
* @throws \InvalidArgumentException
*/
public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2)
{
$tokenLines = $this->getHighlightedLines($source);
$offset = $lineNumber - $linesBefore - 1;
$offset = max($offset, 0);
$length = $linesAfter + $linesBefore + 1;
$tokenLines = array_slice($tokenLines, $offset, $length, $preserveKeys = true);
$lines = $this->colorLines($tokenLines);
return $this->lineNumbers($lines, $lineNumber);
}
/**
* @param string $source
* @return string
* @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
* @throws \InvalidArgumentException
*/
public function getWholeFile($source)
{
$tokenLines = $this->getHighlightedLines($source);
$lines = $this->colorLines($tokenLines);
return implode(PHP_EOL, $lines);
}
/**
* @param string $source
* @return string
* @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
* @throws \InvalidArgumentException
*/
public function getWholeFileWithLineNumbers($source)
{
$tokenLines = $this->getHighlightedLines($source);
$lines = $this->colorLines($tokenLines);
return $this->lineNumbers($lines);
}
/**
* @param string $source
* @return array
*/
private function getHighlightedLines($source)
{
$source = str_replace(array("\r\n", "\r"), "\n", $source);
$tokens = $this->tokenize($source);
return $this->splitToLines($tokens);
}
/**
* @param string $source
* @return array
*/
private function tokenize($source)
{
$tokens = token_get_all($source);
$output = array();
$currentType = null;
$buffer = '';
foreach ($tokens as $token) {
if (is_array($token)) {
switch ($token[0]) {
case T_INLINE_HTML:
$newType = self::TOKEN_HTML;
break;
case T_COMMENT:
case T_DOC_COMMENT:
$newType = self::TOKEN_COMMENT;
break;
case T_ENCAPSED_AND_WHITESPACE:
case T_CONSTANT_ENCAPSED_STRING:
$newType = self::TOKEN_STRING;
break;
case T_WHITESPACE:
break;
case T_OPEN_TAG:
case T_OPEN_TAG_WITH_ECHO:
case T_CLOSE_TAG:
case T_STRING:
case T_VARIABLE:
// Constants
case T_DIR:
case T_FILE:
case T_METHOD_C:
case T_DNUMBER:
case T_LNUMBER:
case T_NS_C:
case T_LINE:
case T_CLASS_C:
case T_FUNC_C:
//case T_TRAIT_C:
$newType = self::TOKEN_DEFAULT;
break;
default:
// Compatibility with PHP 5.3
if (defined('T_TRAIT_C') && $token[0] === T_TRAIT_C) {
$newType = self::TOKEN_DEFAULT;
} else {
$newType = self::TOKEN_KEYWORD;
}
}
} else {
$newType = $token === '"' ? self::TOKEN_STRING : self::TOKEN_KEYWORD;
}
if ($currentType === null) {
$currentType = $newType;
}
if ($currentType != $newType) {
$output[] = array($currentType, $buffer);
$buffer = '';
$currentType = $newType;
}
$buffer .= is_array($token) ? $token[1] : $token;
}
if (isset($newType)) {
$output[] = array($newType, $buffer);
}
return $output;
}
/**
* @param array $tokens
* @return array
*/
private function splitToLines(array $tokens)
{
$lines = array();
$line = array();
foreach ($tokens as $token) {
foreach (explode("\n", $token[1]) as $count => $tokenLine) {
if ($count > 0) {
$lines[] = $line;
$line = array();
}
if ($tokenLine === '') {
continue;
}
$line[] = array($token[0], $tokenLine);
}
}
$lines[] = $line;
return $lines;
}
/**
* @param array $tokenLines
* @return array
* @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
* @throws \InvalidArgumentException
*/
private function colorLines(array $tokenLines)
{
$lines = array();
foreach ($tokenLines as $lineCount => $tokenLine) {
$line = '';
foreach ($tokenLine as $token) {
list($tokenType, $tokenValue) = $token;
if ($this->color->hasTheme($tokenType)) {
$line .= $this->color->apply($tokenType, $tokenValue);
} else {
$line .= $tokenValue;
}
}
$lines[$lineCount] = $line;
}
return $lines;
}
/**
* @param array $lines
* @param null|int $markLine
* @return string
* @throws \JakubOnderka\PhpConsoleColor\InvalidStyleException
*/
private function lineNumbers(array $lines, $markLine = null)
{
end($lines);
$lineStrlen = strlen(key($lines) + 1);
$snippet = '';
foreach ($lines as $i => $line) {
if ($markLine !== null) {
$snippet .= ($markLine === $i + 1 ? $this->color->apply(self::ACTUAL_LINE_MARK, ' > ') : ' ');
}
$snippet .= $this->color->apply(self::LINE_NUMBER, str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| ');
$snippet .= $line . PHP_EOL;
}
return $snippet;
}
}

View file

@ -0,0 +1,263 @@
<?php
namespace JakubOnderka\PhpConsoleHighlighter;
class HighlighterTest extends \PHPUnit_Framework_TestCase
{
/** @var Highlighter */
private $uut;
protected function getConsoleColorMock()
{
$mock = $this->getMock('\JakubOnderka\PhpConsoleColor\ConsoleColor');
$mock->expects($this->any())
->method('apply')
->will($this->returnCallback(function ($style, $text) {
return "<$style>$text</$style>";
}));
$mock->expects($this->any())
->method('hasTheme')
->will($this->returnValue(true));
return $mock;
}
protected function setUp()
{
$this->uut = new Highlighter($this->getConsoleColorMock());
}
protected function compare($original, $expected)
{
$output = $this->uut->getWholeFile($original);
$this->assertEquals($expected, $output);
}
public function testVariable()
{
$this->compare(
<<<EOL
<?php
echo \$a;
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>echo </token_keyword><token_default>\$a</token_default><token_keyword>;</token_keyword>
EOL
);
}
public function testInteger()
{
$this->compare(
<<<EOL
<?php
echo 43;
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>echo </token_keyword><token_default>43</token_default><token_keyword>;</token_keyword>
EOL
);
}
public function testFloat()
{
$this->compare(
<<<EOL
<?php
echo 43.3;
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>echo </token_keyword><token_default>43.3</token_default><token_keyword>;</token_keyword>
EOL
);
}
public function testHex()
{
$this->compare(
<<<EOL
<?php
echo 0x43;
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>echo </token_keyword><token_default>0x43</token_default><token_keyword>;</token_keyword>
EOL
);
}
public function testBasicFunction()
{
$this->compare(
<<<EOL
<?php
function plus(\$a, \$b) {
return \$a + \$b;
}
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>function </token_keyword><token_default>plus</token_default><token_keyword>(</token_keyword><token_default>\$a</token_default><token_keyword>, </token_keyword><token_default>\$b</token_default><token_keyword>) {</token_keyword>
<token_keyword> return </token_keyword><token_default>\$a </token_default><token_keyword>+ </token_keyword><token_default>\$b</token_default><token_keyword>;</token_keyword>
<token_keyword>}</token_keyword>
EOL
);
}
public function testStringNormal()
{
$this->compare(
<<<EOL
<?php
echo 'Ahoj světe';
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>echo </token_keyword><token_string>'Ahoj světe'</token_string><token_keyword>;</token_keyword>
EOL
);
}
public function testStringDouble()
{
$this->compare(
<<<EOL
<?php
echo "Ahoj světe";
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_keyword>echo </token_keyword><token_string>"Ahoj světe"</token_string><token_keyword>;</token_keyword>
EOL
);
}
public function testInstanceof()
{
$this->compare(
<<<EOL
<?php
\$a instanceof stdClass;
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_default>\$a </token_default><token_keyword>instanceof </token_keyword><token_default>stdClass</token_default><token_keyword>;</token_keyword>
EOL
);
}
/*
* Constants
*/
public function testConstant()
{
$constants = array(
'__FILE__',
'__LINE__',
'__CLASS__',
'__FUNCTION__',
'__METHOD__',
'__TRAIT__',
'__DIR__',
'__NAMESPACE__'
);
foreach ($constants as $constant) {
$this->compare(
<<<EOL
<?php
$constant;
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_default>$constant</token_default><token_keyword>;</token_keyword>
EOL
);
}
}
/*
* Comments
*/
public function testComment()
{
$this->compare(
<<<EOL
<?php
/* Ahoj */
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_comment>/* Ahoj */</token_comment>
EOL
);
}
public function testDocComment()
{
$this->compare(
<<<EOL
<?php
/** Ahoj */
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_comment>/** Ahoj */</token_comment>
EOL
);
}
public function testInlineComment()
{
$this->compare(
<<<EOL
<?php
// Ahoj
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_comment>// Ahoj</token_comment>
EOL
);
}
public function testHashComment()
{
$this->compare(
<<<EOL
<?php
# Ahoj
EOL
,
<<<EOL
<token_default><?php</token_default>
<token_comment># Ahoj</token_comment>
EOL
);
}
public function testEmpty()
{
$this->compare(
''
,
''
);
}
}

View file

@ -0,0 +1,2 @@
<?php
$loader = require_once __DIR__ . '/../vendor/autoload.php';