├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── phpcs.xml.dist ├── phpstan.neon.dist ├── phpunit.xml.dist ├── src ├── Declaration │ ├── BooleanDeclaration.php │ ├── Charset.php │ ├── Declaration.php │ ├── EndOfLine.php │ ├── Factory.php │ ├── GenericDeclaration.php │ ├── IndentSize.php │ ├── IndentStyle.php │ ├── InsertFinalNewline.php │ ├── MaxLineLength.php │ ├── TabWidth.php │ ├── TrimTrailingWhitespace.php │ └── UnsetDeclaration.php ├── EditorConfig.php ├── EditorConfigFile.php ├── Exception │ └── InvalidValue.php └── Section.php └── tests ├── Declaration ├── CharsetTest.php ├── EndOfLineTest.php ├── FactoryTest.php ├── GenericDeclarationTest.php ├── IndentSizeTest.php ├── IndentStyleTest.php ├── InsertFinalNewlineTest.php ├── MaxLineLengthTest.php ├── TabWidthTest.php ├── TrimTrailingWhitespaceTest.php └── UnsetDeclarationTest.php ├── EditorConfigFileTest.php ├── EditorConfigTest.php ├── SectionTest.php └── data ├── .editorconfig ├── editorconfig ├── empty_editorconfig ├── invalid_editorconfig ├── invalid_root_editorconfig ├── lib └── test.js ├── root_editorconfig ├── test.json ├── test.yml └── testfile.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{yaml,yml,js,json,json.dist,feature}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | phpstan: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - uses: php-actions/composer@v5 19 | with: 20 | php_version: 7.3 21 | 22 | - name: PHPStan Static Analysis 23 | uses: php-actions/phpstan@v2 24 | with: 25 | php_version: 7.3 26 | configuration: phpstan.neon.dist 27 | 28 | phpunit: 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - uses: actions/checkout@v2 33 | 34 | - uses: php-actions/composer@v5 35 | with: 36 | php_version: 7.3 37 | 38 | - name: Run PHPUnit tests 39 | uses: php-actions/phpunit@v2 40 | with: 41 | version: 9.5 42 | php_version: 7.3 43 | configuration: phpunit.xml.dist 44 | 45 | phpcs: 46 | runs-on: ubuntu-latest 47 | 48 | steps: 49 | - uses: actions/checkout@v2 50 | 51 | - name: Setup PHP Action 52 | uses: shivammathur/setup-php@2.11.0 53 | with: 54 | php-version: '7.3' 55 | 56 | - name: Cache Composer packages 57 | id: composer-cache 58 | uses: actions/cache@v2 59 | with: 60 | path: vendor 61 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 62 | restore-keys: | 63 | ${{ runner.os }}-php- 64 | 65 | - name: Install composer packages 66 | run: | 67 | composer install --prefer-dist --no-progress 68 | 69 | - name: Run phpcs 70 | run: | 71 | bin/phpcs 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .phpcs-cache 2 | .phpunit.result.cache 3 | composer.lock 4 | phpunit.xml 5 | /bin/ 6 | /build/ 7 | /vendor/ 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [0.1.3] - 2021-06-02 9 | ### Fixed 10 | - Fixed issues with globbing files on Windows 11 | 12 | ## [0.1.2] - 2021-05-31 13 | ### Fixed 14 | - Updated dependencies 15 | - Dev dependencies now support PHP8 16 | 17 | ## [0.1.1] - 2021-05-03 18 | ### Changed 19 | - Validation for rules is now case-insensitive 20 | 21 | ### Fixed 22 | - Supports comments beginning with # 23 | - Better support for curly braces in editorconfig rules 24 | 25 | ## [0.1.0] - 2019-12-04 26 | Initial Release 27 | ### Added 28 | - Resolves editor settings for file/path 29 | - Prints resolved editor settings 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2019 Jason Silkey 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EditorConfig PHP 2 | PHP implementation of [EditorConfig](https://editorconfig.org) 3 | 4 | ## Installation 5 | 6 | Install with [Composer](https://getcomposer.org): 7 | 8 | ``` 9 | composer require idiosyncratic/editorconfig 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```php 15 | getConfigForPath(__FILE__); 25 | 26 | // Print matching configuration rules as string 27 | print $ec->printConfigForPath(__FILE__) . PHP_EOL; 28 | ``` 29 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "idiosyncratic/editorconfig", 3 | "description": "PHP implementation of EditorConfig", 4 | "type": "library", 5 | "license": "ISC", 6 | "authors": [ 7 | { 8 | "name": "Jason Silkey", 9 | "email": "jason@jasonsilkey.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.3" 14 | }, 15 | "require-dev": { 16 | "idiosyncratic/coding-standard": "^2.0", 17 | "php-parallel-lint/php-console-highlighter": "^0.5", 18 | "php-parallel-lint/php-parallel-lint": "^1.3", 19 | "phploc/phploc": "^7.0", 20 | "phpstan/phpstan": "^0.12", 21 | "phpunit/phpunit": "^9.5", 22 | "sebastian/phpcpd": "^6.0" 23 | }, 24 | "minimum-stability": "dev", 25 | "prefer-stable": true, 26 | "config": { 27 | "preferred-install": "dist", 28 | "sort-packages": true, 29 | "optimize-autoloader": true, 30 | "discard-changes": true, 31 | "bin-dir" : "bin" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Idiosyncratic\\EditorConfig\\": "src" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Idiosyncratic\\EditorConfig\\": "tests" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | src 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 7 3 | paths: 4 | - src 5 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | ./src 19 | 20 | 21 | 22 | 23 | ./tests 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Declaration/BooleanDeclaration.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 21 | $this->getName() 22 | ); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Declaration/Charset.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 31 | $this->getName() 32 | ); 33 | } 34 | } 35 | 36 | public function getName() : string 37 | { 38 | return 'charset'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Declaration/Declaration.php: -------------------------------------------------------------------------------- 1 | getTypedValue($value); 26 | 27 | $this->setStringValue($value); 28 | 29 | $this->validateValue($typedValue); 30 | 31 | $this->setValue($typedValue); 32 | } 33 | 34 | public function getName() : string 35 | { 36 | return $this->name; 37 | } 38 | 39 | /** 40 | * @return mixed 41 | */ 42 | final public function getValue() 43 | { 44 | return $this->value; 45 | } 46 | 47 | final public function getStringValue() : string 48 | { 49 | return $this->stringValue; 50 | } 51 | 52 | /** 53 | * @param mixed $value 54 | */ 55 | public function validateValue($value) : void 56 | { 57 | return; 58 | } 59 | 60 | final public function __toString() : string 61 | { 62 | return sprintf('%s=%s', $this->getName(), $this->getStringValue()); 63 | } 64 | 65 | protected function setName(string $name) : void 66 | { 67 | $this->name = strtolower($name); 68 | } 69 | 70 | /** 71 | * @return mixed 72 | */ 73 | protected function getTypedValue(string $value) 74 | { 75 | if (in_array($value, ['true', 'false']) === true) { 76 | return $value === 'true'; 77 | } 78 | 79 | if (is_numeric($value) === true && (string) (int) $value === $value) { 80 | return (int) $value; 81 | } 82 | 83 | return $value; 84 | } 85 | 86 | final protected function setStringValue(string $value) : void 87 | { 88 | $this->stringValue = $value; 89 | } 90 | 91 | /** 92 | * @param mixed $value 93 | */ 94 | final protected function setValue($value) : void 95 | { 96 | $this->value = $value; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Declaration/EndOfLine.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 29 | $this->getName() 30 | ); 31 | } 32 | } 33 | 34 | public function getName() : string 35 | { 36 | return 'end_of_line'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Declaration/Factory.php: -------------------------------------------------------------------------------- 1 | $method($value); 27 | } 28 | 29 | return new GenericDeclaration($name, $value); 30 | } 31 | 32 | /** 33 | * @param mixed $value 34 | */ 35 | public function getIndentStyle($value) : IndentStyle 36 | { 37 | return new IndentStyle($value); 38 | } 39 | 40 | /** 41 | * @param mixed $value 42 | */ 43 | public function getCharset($value) : Charset 44 | { 45 | return new Charset($value); 46 | } 47 | 48 | /** 49 | * @param mixed $value 50 | */ 51 | public function getEndOfLine($value) : EndOfLine 52 | { 53 | return new EndOfLine($value); 54 | } 55 | 56 | /** 57 | * @param mixed $value 58 | */ 59 | public function getInsertFinalNewline($value) : InsertFinalNewline 60 | { 61 | return new InsertFinalNewline($value); 62 | } 63 | 64 | /** 65 | * @param mixed $value 66 | */ 67 | public function getTrimTrailingWhitespace($value) : TrimTrailingWhitespace 68 | { 69 | return new TrimTrailingWhitespace($value); 70 | } 71 | 72 | /** 73 | * @param mixed $value 74 | */ 75 | public function getIndentSize($value) : IndentSize 76 | { 77 | return new IndentSize($value); 78 | } 79 | 80 | /** 81 | * @param mixed $value 82 | */ 83 | public function getTabWidth($value) : TabWidth 84 | { 85 | return new TabWidth($value); 86 | } 87 | 88 | /** 89 | * @param mixed $value 90 | */ 91 | public function getMaxLineLength($value) : MaxLineLength 92 | { 93 | return new MaxLineLength($value); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Declaration/GenericDeclaration.php: -------------------------------------------------------------------------------- 1 | setName($name); 12 | 13 | parent::__construct($value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Declaration/IndentSize.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 26 | $this->getName() 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Declaration/IndentStyle.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 28 | $this->getName() 29 | ); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Declaration/InsertFinalNewline.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 26 | $this->getName() 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Declaration/TabWidth.php: -------------------------------------------------------------------------------- 1 | getStringValue(), 26 | $this->getName() 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Declaration/TrimTrailingWhitespace.php: -------------------------------------------------------------------------------- 1 | setName($name); 12 | 13 | parent::__construct('unset'); 14 | } 15 | 16 | /** 17 | * @return mixed 18 | */ 19 | protected function getTypedValue(string $value) 20 | { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/EditorConfig.php: -------------------------------------------------------------------------------- 1 | */ 21 | private $configFiles = []; 22 | 23 | /** 24 | * @return array 25 | */ 26 | public function getConfigForPath(string $path) : array 27 | { 28 | $configFiles = $this->locateConfigFiles($path); 29 | 30 | $root = false; 31 | 32 | $configuration = []; 33 | 34 | $configFile = array_pop($configFiles); 35 | 36 | while ($configFile !== null) { 37 | $configuration = array_merge($configuration, $configFile->getConfigForPath($path)); 38 | $configFile = array_pop($configFiles); 39 | } 40 | 41 | foreach ($configuration as $key => $declaration) { 42 | if ($declaration->getValue() !== null) { 43 | continue; 44 | } 45 | 46 | unset($configuration[$key]); 47 | } 48 | 49 | return $configuration; 50 | } 51 | 52 | public function printConfigForPath(string $path) : string 53 | { 54 | $config = $this->getConfigForPath($path); 55 | 56 | return implode("\n", $config); 57 | } 58 | 59 | /** 60 | * @return array 61 | */ 62 | private function locateConfigFiles(string $path) : array 63 | { 64 | $files = []; 65 | 66 | $stop = false; 67 | 68 | $parent = ''; 69 | 70 | while ($parent !== $path) { 71 | $editorConfigFile = realpath(sprintf('%s%s.editorconfig', $path, DIRECTORY_SEPARATOR)); 72 | 73 | if ($editorConfigFile !== false && is_file($editorConfigFile) && is_readable($editorConfigFile)) { 74 | $file = $this->getConfigFile($editorConfigFile); 75 | 76 | $files[] = $file; 77 | 78 | if ($file->isRoot() === true) { 79 | break; 80 | } 81 | } 82 | 83 | $path = dirname($path); 84 | $parent = dirname($path); 85 | } 86 | 87 | return $files; 88 | } 89 | 90 | private function getConfigFile(string $path) : EditorConfigFile 91 | { 92 | return $this->configFiles[$path] ?? $this->configFiles[$path] = new EditorConfigFile($path); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/EditorConfigFile.php: -------------------------------------------------------------------------------- 1 | */ 38 | private $sections = []; 39 | 40 | /** @var Factory */ 41 | private $declarationFactory; 42 | 43 | public function __construct(string $path, ?Factory $declarationFactory = null) 44 | { 45 | $this->declarationFactory = $declarationFactory ?? new Factory(); 46 | 47 | if (is_file($path) === false || is_readable($path) === false) { 48 | throw new RuntimeException(sprintf('File %s does not exist or is not readable', $path)); 49 | } 50 | 51 | $content = $this->cleanContent($path); 52 | 53 | $this->path = $path; 54 | 55 | $this->parse($content); 56 | } 57 | 58 | public function __toString() : string 59 | { 60 | $preamble = $this->isRoot() === true ? "root=true\n" : ''; 61 | 62 | $sections = []; 63 | 64 | foreach ($this->sections as $section) { 65 | $sections[] = (string) $section; 66 | } 67 | 68 | return sprintf('%s%s', $preamble, implode("\n", $sections)); 69 | } 70 | 71 | public function isRoot() : bool 72 | { 73 | return $this->isRoot; 74 | } 75 | 76 | public function getPath() : string 77 | { 78 | return $this->path; 79 | } 80 | 81 | /** 82 | * @return array 83 | */ 84 | public function getConfigForPath(string $path) : array 85 | { 86 | $configuration = []; 87 | 88 | foreach ($this->sections as $section) { 89 | if ($section->matches($path) === false) { 90 | continue; 91 | } 92 | 93 | $configuration = array_merge($configuration, $section->getDeclarations()); 94 | } 95 | 96 | return $configuration; 97 | } 98 | 99 | private function parse(string $content) : void 100 | { 101 | $this->fileContent = $content; 102 | 103 | $content = preg_replace('/^\s/m', '', $this->fileContent) ?? $this->fileContent; 104 | 105 | $parsedContent = $this->parseIniString($content); 106 | 107 | if (isset($parsedContent['root']) === true) { 108 | $this->setIsRoot($parsedContent['root']); 109 | } 110 | 111 | foreach ($parsedContent as $glob => $declarations) { 112 | if (is_array($declarations) === false) { 113 | continue; 114 | } 115 | 116 | $this->sections[] = new Section( 117 | $this->getGlobPrefix($glob), 118 | $glob, 119 | $declarations, 120 | $this->declarationFactory 121 | ); 122 | } 123 | } 124 | 125 | private function setIsRoot(string $isRoot) : void 126 | { 127 | if (in_array($isRoot, ['true', 'false']) === false) { 128 | throw new InvalidValue('root', $isRoot); 129 | } 130 | 131 | $this->isRoot = $isRoot === 'true'; 132 | } 133 | 134 | private function getGlobPrefix(string $glob) : string 135 | { 136 | return strpos($glob, '/') === 0 ? dirname($this->path) : '**/'; 137 | } 138 | 139 | /** 140 | * @return array 141 | */ 142 | private function parseIniString(string $content) : array 143 | { 144 | $parsedContent = parse_ini_string($content, true, INI_SCANNER_RAW); 145 | 146 | return is_array($parsedContent) === true ? $parsedContent : []; 147 | } 148 | 149 | private function cleanContent(string $path) : string 150 | { 151 | $content = file_get_contents($path); 152 | 153 | if ($content === false) { 154 | return ''; 155 | } 156 | 157 | return preg_replace('/#.*$/m', '', $content) ?? $content; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Exception/InvalidValue.php: -------------------------------------------------------------------------------- 1 | */ 31 | private $declarations = []; 32 | 33 | /** @var Factory */ 34 | private $declarationFactory; 35 | 36 | /** 37 | * @param array $declarations 38 | */ 39 | public function __construct( 40 | string $globPrefix, 41 | string $glob, 42 | array $declarations, 43 | Factory $declarationFactory 44 | ) { 45 | $this->globPrefix = $globPrefix; 46 | 47 | $this->glob = $glob; 48 | 49 | $this->declarationFactory = $declarationFactory; 50 | 51 | $this->setDeclarations($declarations); 52 | } 53 | 54 | public function __toString() : string 55 | { 56 | return sprintf( 57 | "[%s]\n%s\n", 58 | $this->glob, 59 | implode("\n", $this->getDeclarations()) 60 | ); 61 | } 62 | 63 | /** 64 | * @return array 65 | */ 66 | public function getDeclarations() : array 67 | { 68 | return $this->declarations; 69 | } 70 | 71 | public function matches(string $path) : bool 72 | { 73 | // normalize path to unix-style directory separator, 74 | // because the glob pattern assumes linux-style directory separators 75 | $path = str_replace('\\', '/', $path); 76 | 77 | if (preg_match('#{(.*)}#', $this->glob) === 1) { 78 | return $this->matchesWithCurlBracesExpansion($path); 79 | } 80 | 81 | $pattern = sprintf('%s%s', $this->globPrefix, $this->glob); 82 | 83 | return fnmatch($pattern, $path); 84 | } 85 | 86 | /** 87 | * @param array $declarations 88 | */ 89 | private function setDeclarations(array $declarations) : void 90 | { 91 | foreach ($declarations as $name => $value) { 92 | $this->setDeclaration($name, $value); 93 | } 94 | } 95 | 96 | /** 97 | * @param mixed $value 98 | */ 99 | private function setDeclaration(string $name, $value) : void 100 | { 101 | $declaration = $this->declarationFactory->getDeclaration($name, $value); 102 | 103 | $this->declarations[$declaration->getName()] = $declaration; 104 | } 105 | 106 | /** 107 | * @return mixed 108 | */ 109 | public function __get(string $property) 110 | { 111 | if (isset($this->declarations[$property]) === true) { 112 | return $this->declarations[$property]; 113 | } 114 | 115 | $trace = debug_backtrace(); 116 | 117 | throw new ErrorException(sprintf( 118 | 'Undefined property: %s in %s on line %s', 119 | $property, 120 | $trace[0]['file'], 121 | $trace[0]['line'] 122 | )); 123 | } 124 | 125 | public function __isset(string $property) : bool 126 | { 127 | return array_key_exists($property, $this->declarations); 128 | } 129 | 130 | private function matchesWithCurlBracesExpansion(string $path) : bool 131 | { 132 | preg_match_all('#(?.*){(?.*)}#', $this->glob, $matches, PREG_SET_ORDER); 133 | 134 | foreach ($matches as $match) { 135 | if (array_key_exists('subpattern', $match) === false) { 136 | continue; 137 | } 138 | 139 | $subPatterns = explode(',', $match['subpattern']); 140 | foreach ($subPatterns as $subPattern) { 141 | $pattern = sprintf('%s%s', $this->globPrefix, $match['prefix'] . $subPattern); 142 | 143 | if (fnmatch($pattern, $path)) { 144 | return true; 145 | } 146 | } 147 | } 148 | 149 | return false; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tests/Declaration/CharsetTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(sprintf('charset=%s', $charset), (string) $declaration); 18 | } 19 | } 20 | 21 | public function testInvalidValue() 22 | { 23 | $this->expectException(InvalidValue::class); 24 | $declaration = new Charset('true'); 25 | 26 | $this->expectException(InvalidValue::class); 27 | $declaration = new Charset('spaces'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Declaration/EndOfLineTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(sprintf('end_of_line=%s', $eol), (string) $declaration); 18 | } 19 | } 20 | 21 | public function testInvalidValues() 22 | { 23 | $this->expectException(DomainException::class); 24 | $declaration = new EndOfLine('true'); 25 | 26 | $this->expectException(DomainException::class); 27 | $declaration = new EndOfLine('spaces'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Declaration/FactoryTest.php: -------------------------------------------------------------------------------- 1 | 'space', 17 | 'indent_size' => '4', 18 | 'tab_width' => '4', 19 | 'end_of_line' => 'lf', 20 | 'charset' => 'utf-8', 21 | 'trim_trailing_whitespace' => 'true', 22 | 'insert_final_newline' => 'false', 23 | 'max_line_length' => 'off', 24 | ]; 25 | 26 | $factory = new Factory(); 27 | 28 | foreach ($declarations as $key => $value) { 29 | $declaration = $factory->getDeclaration($key, $value); 30 | $this->assertEquals($key, $declaration->getName()); 31 | } 32 | } 33 | 34 | public function testUnsetDeclaration() 35 | { 36 | $factory = new Factory(); 37 | 38 | $indentSize = $factory->getDeclaration('indent_size', 'unset'); 39 | 40 | $this->assertInstanceOf(UnsetDeclaration::class, $indentSize); 41 | } 42 | 43 | public function testUnknownDeclaration() 44 | { 45 | $factory = new Factory(); 46 | 47 | $justification = $factory->getDeclaration('justification', 'left'); 48 | 49 | $this->assertInstanceOf(GenericDeclaration::class, $justification); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Declaration/GenericDeclarationTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('declaration', $declaration->getName()); 15 | } 16 | 17 | public function testGetValue() : void 18 | { 19 | $declaration = new GenericDeclaration('declaration', 'string'); 20 | $this->assertIsString($declaration->getValue()); 21 | $this->assertEquals('string', $declaration->getValue()); 22 | 23 | $declaration = new GenericDeclaration('declaration', '1'); 24 | $this->assertIsInt($declaration->getValue()); 25 | $this->assertSame(1, $declaration->getValue()); 26 | $this->assertSame('1', $declaration->getStringValue()); 27 | 28 | $declaration = new GenericDeclaration('declaration', 'true'); 29 | $this->assertIsBool($declaration->getValue()); 30 | $this->assertTrue($declaration->getValue()); 31 | $this->assertSame('true', $declaration->getStringValue()); 32 | 33 | $declaration = new GenericDeclaration('declaration', '1.1'); 34 | $this->assertIsString($declaration->getValue()); 35 | $this->assertSame('1.1', $declaration->getValue()); 36 | $this->assertSame('1.1', $declaration->getStringValue()); 37 | } 38 | 39 | public function testToString() : void 40 | { 41 | $declaration = new GenericDeclaration('declaration', 'string'); 42 | $this->assertEquals('declaration=string', (string) $declaration); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Declaration/IndentSizeTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('indent_size=tab', (string) $declaration); 17 | 18 | $declaration = new IndentSize('4'); 19 | $this->assertEquals('indent_size=4', (string) $declaration); 20 | $this->assertSame(4, $declaration->getValue()); 21 | } 22 | 23 | public function testInvalidValueType() 24 | { 25 | $this->expectException(InvalidValue::class); 26 | $declaration = new IndentSize('true'); 27 | } 28 | 29 | public function testInvalidValueValue() 30 | { 31 | $this->expectException(InvalidValue::class); 32 | $declaration = new IndentSize('four'); 33 | } 34 | 35 | public function testInvalidNegativeIntegerValue() 36 | { 37 | $this->expectException(InvalidValue::class); 38 | $declaration = new IndentSize('-1'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Declaration/IndentStyleTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('indent_style', $declaration->getName()); 17 | $this->assertEquals('tab', $declaration->getValue()); 18 | 19 | $declaration = new IndentStyle('space'); 20 | $this->assertEquals('indent_style', $declaration->getName()); 21 | $this->assertEquals('space', $declaration->getValue()); 22 | } 23 | 24 | public function testInvalidValues() 25 | { 26 | $this->expectException(InvalidValue::class); 27 | $declaration = new IndentStyle('true'); 28 | 29 | $this->expectException(InvalidValue::class); 30 | $declaration = new IndentStyle('spaces'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Declaration/InsertFinalNewlineTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('insert_final_newline=false', (string) $declaration); 17 | 18 | $declaration = new InsertFinalNewline('true'); 19 | $this->assertEquals('insert_final_newline=true', (string) $declaration); 20 | } 21 | 22 | public function testInvalidValues() 23 | { 24 | $this->expectException(InvalidValue::class); 25 | $declaration = new InsertFinalNewline('4'); 26 | 27 | $this->expectException(InvalidValue::class); 28 | $declaration = new InsertFinalNewline('four'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Declaration/MaxLineLengthTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('max_line_length=off', (string) $declaration); 16 | 17 | $declaration = new MaxLineLength('4'); 18 | $this->assertEquals('max_line_length=4', (string) $declaration); 19 | $this->assertSame(4, $declaration->getValue()); 20 | } 21 | 22 | public function testInvalidValues() 23 | { 24 | $this->expectException(InvalidValue::class); 25 | $declaration = new MaxLineLength('true'); 26 | 27 | $this->expectException(InvalidValue::class); 28 | $declaration = new MaxLineLength('four'); 29 | 30 | $this->expectException(InvalidValue::class); 31 | $declaration = new MaxLineLength('-1'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Declaration/TabWidthTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('tab_width=4', (string) $declaration); 16 | $this->assertSame(4, $declaration->getValue()); 17 | } 18 | 19 | public function testInvalidValues() 20 | { 21 | $this->expectException(InvalidValue::class); 22 | $declaration = new TabWidth('true'); 23 | 24 | $this->expectException(InvalidValue::class); 25 | $declaration = new TabWidth('four'); 26 | 27 | $this->expectException(InvalidValue::class); 28 | $declaration = new TabWidth('-1'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Declaration/TrimTrailingWhitespaceTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('trim_trailing_whitespace=false', (string) $declaration); 17 | 18 | $declaration = new TrimTrailingWhitespace('true'); 19 | $this->assertEquals('trim_trailing_whitespace=true', (string) $declaration); 20 | } 21 | 22 | public function testInvalidIntValue() 23 | { 24 | $this->expectException(DomainException::class); 25 | $declaration = new TrimTrailingWhitespace('4'); 26 | } 27 | 28 | public function testInvalidStringValue() 29 | { 30 | $this->expectException(DomainException::class); 31 | $declaration = new TrimTrailingWhitespace('four'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Declaration/UnsetDeclarationTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('indent_style', $declaration->getName()); 19 | 20 | $this->assertNull($declaration->getValue()); 21 | 22 | $this->assertEquals('indent_style=unset', (string) $declaration); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/EditorConfigFileTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(EditorConfigFile::class, $file); 20 | 21 | $this->assertFalse($file->isRoot()); 22 | 23 | $this->assertEquals($path, $file->getPath()); 24 | } 25 | 26 | public function testGetPath(): void 27 | { 28 | $path = __DIR__.'/data/editorconfig'; 29 | 30 | $file = new EditorConfigFile($path); 31 | 32 | $this->assertEquals($path, $file->getPath()); 33 | } 34 | 35 | public function testEmptyFile(): void 36 | { 37 | $path = __DIR__.'/data/empty_editorconfig'; 38 | 39 | $file = new EditorConfigFile($path); 40 | 41 | $this->assertEquals('', trim((string)$file)); 42 | } 43 | 44 | public function testRootFile(): void 45 | { 46 | $path = __DIR__.'/data/root_editorconfig'; 47 | 48 | $file = new EditorConfigFile($path); 49 | 50 | $this->assertTrue($file->isRoot()); 51 | 52 | $this->assertTrue(strpos((string)$file, 'root=true') === 0); 53 | } 54 | 55 | public function testInvalidRootValue(): void 56 | { 57 | $path = __DIR__.'/data/invalid_root_editorconfig'; 58 | 59 | $this->expectException(InvalidValue::class); 60 | 61 | $file = new EditorConfigFile($path); 62 | } 63 | 64 | 65 | public function testFileDoesNotExist(): void 66 | { 67 | $this->expectException(RuntimeException::class); 68 | 69 | $file = new EditorConfigFile(__DIR__); 70 | } 71 | 72 | public function testEmptyIndentSize(): void 73 | { 74 | $path = __DIR__.'/data/editorconfig'; 75 | 76 | $file = new EditorConfigFile($path); 77 | 78 | $config = $file->getConfigForPath(__DIR__); 79 | 80 | $this->assertFalse(isset($config['indent_size'])); 81 | } 82 | 83 | /** 84 | * @dataProvider configForPath 85 | */ 86 | public function testGetConfigForPath(string $pathToFile, int $expectedIndentSize): void 87 | { 88 | $path = __DIR__.'/data/editorconfig'; 89 | 90 | $file = new EditorConfigFile($path); 91 | 92 | $config = $file->getConfigForPath($pathToFile); 93 | 94 | $this->assertEquals($expectedIndentSize, $config['indent_size']->getValue()); 95 | } 96 | 97 | public function configForPath(): array 98 | { 99 | return [ 100 | 'This .php file has an indentation of 4' => [__FILE__, 4], 101 | 'The test.json file has an indentation of 2' => [__DIR__.'/data/test.json', 2], 102 | 'The test.yml has an indentation of 98' => [__DIR__.'/data/test.yml', 98], 103 | 'The test.js has an indentation of 27' => [__DIR__.'/data/lib/test.js', 27], 104 | ]; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tests/EditorConfigTest.php: -------------------------------------------------------------------------------- 1 | getConfigForPath(__FILE__); 17 | 18 | $this->assertEquals(4, $config['indent_size']->getValue()); 19 | 20 | $config = $ec->printConfigForPath(__DIR__ . '/data/testfile.php'); 21 | 22 | $this->assertFalse(strpos($config, 'indent_size=4')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/SectionTest.php: -------------------------------------------------------------------------------- 1 | '4', 20 | 'indent_style' => 'space', 21 | ], 22 | new Factory() 23 | ); 24 | 25 | $this->assertEquals('space', $section->indent_style->getValue()); 26 | $this->assertEquals(4, $section->indent_size->getValue()); 27 | $this->assertFalse(isset($section->tab_width)); 28 | } 29 | 30 | public function testMatchingWindowsPath() : void 31 | { 32 | $section = new Section( 33 | '**/', 34 | '*.php', 35 | [ 36 | 'indent_size' => '4', 37 | 'indent_style' => 'space', 38 | ], 39 | new Factory() 40 | ); 41 | 42 | $this->assertTrue($section->matches('my\\composer.php')); 43 | } 44 | 45 | public function testGetMissingDeclaration() : void 46 | { 47 | $section = new Section( 48 | '**/', 49 | '*.php', 50 | [ 51 | 'indent_size' => '4', 52 | 'indent_style' => 'space', 53 | ], 54 | new Factory() 55 | ); 56 | 57 | $this->expectException(ErrorException::class); 58 | 59 | $section->tab_width; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/data/.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_size = unset 3 | -------------------------------------------------------------------------------- /tests/data/editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | trim_trailing_whitespace = true 5 | charset = utf-8 6 | indent_style = space 7 | 8 | [*.php] 9 | indent_size = 4 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yaml,js,json,json.dist,feature}] 15 | indent_size = 2 16 | 17 | # Indentation override for all JS under lib directory 18 | [lib/**.js] 19 | indent_style = space 20 | indent_size = 27 21 | 22 | # Matches the exact files either package.json or travis.yml 23 | [{package.json,test.yml}] 24 | indent_style = space 25 | indent_size = 98 26 | 27 | #[*.some] 28 | -------------------------------------------------------------------------------- /tests/data/empty_editorconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idiosyncratic-code/editorconfig-php/3445fa4a1e00f95630d4edc729c2effb116db19b/tests/data/empty_editorconfig -------------------------------------------------------------------------------- /tests/data/invalid_editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | somekey[something]=somevalue 3 | -------------------------------------------------------------------------------- /tests/data/invalid_root_editorconfig: -------------------------------------------------------------------------------- 1 | root=yes 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{yaml,yml,js,json,json.dist,feature}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /tests/data/lib/test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idiosyncratic-code/editorconfig-php/3445fa4a1e00f95630d4edc729c2effb116db19b/tests/data/lib/test.js -------------------------------------------------------------------------------- /tests/data/root_editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{yaml,yml,js,json,json.dist,feature}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /tests/data/test.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idiosyncratic-code/editorconfig-php/3445fa4a1e00f95630d4edc729c2effb116db19b/tests/data/test.json -------------------------------------------------------------------------------- /tests/data/test.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idiosyncratic-code/editorconfig-php/3445fa4a1e00f95630d4edc729c2effb116db19b/tests/data/test.yml -------------------------------------------------------------------------------- /tests/data/testfile.php: -------------------------------------------------------------------------------- 1 |