├── .gitignore ├── bin ├── configdiff └── configdiff.php ├── phpunit.xml.dist ├── composer.json ├── README.md ├── tests └── ConstantParserTest.php ├── src └── ConstantParser.php └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /bin/configdiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 16 | 17 | 18 | tests 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wmde/configdiff", 3 | "description": "Show differences between constants defined in PHP files", 4 | "license": "GPL-2.0", 5 | "version": "1.0.0", 6 | "authors": [ 7 | { 8 | "name": "Gabriel Birke", 9 | "email": "gabriel.birke@wikimedia.de" 10 | } 11 | ], 12 | "keywords": [ "configuration", "parser", "const" ], 13 | "homepage": "https://github.com/wmde/configdiff", 14 | "autoload": { 15 | "psr-4": { 16 | "WMDE\\ConfigDiff\\": "src" 17 | } 18 | }, 19 | "bin": [ "bin/configdiff" ], 20 | "require": {}, 21 | "require-dev": { 22 | "mediawiki/mediawiki-codesniffer": "0.3.0" 23 | }, 24 | "scripts": { 25 | "cs": [ 26 | "phpcs --standard=vendor/mediawiki/mediawiki-codesniffer/MediaWiki --extensions=php,php5,inc --ignore=vendor -p ." 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # configdiff 2 | `configdiff` is a command-line tool to compare two PHP configuration files containing configuration values defined as constants. One file being the configuration "template" or "distribution version", the other being the concrete config file. `configdiff` checks whether both files contain the same constant names and outputs the missing and/or superfluous constant names. 3 | 4 | The script should be called as part of a deployment to guard against missing configuration values, e.g. when new values have been added to the template as part of ongoing development. 5 | 6 | ## Installation 7 | 8 | composer require wmde/configdiff 9 | 10 | ## Running the script 11 | If the `vendor/bin` path (project-local or global) is in your `$PATH`, you can just call 12 | 13 | configdiff TEMPLATE_FILE CONFIGURATION_FILE 14 | 15 | Otherwise you call 16 | 17 | php vendor/bin/configdiff.php TEMPLATE_FILE CONFIGURATION_FILE 18 | -------------------------------------------------------------------------------- /tests/ConstantParserTest.php: -------------------------------------------------------------------------------- 1 | parser = new ConstantParser(); 10 | } 11 | 12 | public function testGivenEmptySource_constantsAreEmpty() { 13 | $this->assertEquals( [], $this->parser->getConstants( '' ) ); 14 | } 15 | 16 | public function testGivenOneDefine_itIsReturned() { 17 | $php = ' '"bar"' ]; 19 | $this->assertEquals( $expected, $this->parser->getConstants( $php ) ); 20 | } 21 | 22 | public function testGivenMultipleDefines_theyAreReturned() { 23 | $php = ' '"bar"', 26 | '"quu"' => 'foo."quux"', 27 | '"NULL"' => 'null' 28 | ]; 29 | $this->assertEquals( $expected, $this->parser->getConstants( $php ) ); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /bin/configdiff.php: -------------------------------------------------------------------------------- 1 | getConstants( file_get_contents( $templateFile ) ); 44 | $configConstants = $parser->getConstants( file_get_contents( $configFile ) ); 45 | 46 | $templateKeys = array_keys( $templateConstants ); 47 | $configKeys = array_keys( $configConstants ); 48 | 49 | $extra = array_diff( $configKeys, $templateKeys ); 50 | $missing = array_diff( $templateKeys, $configKeys ); 51 | 52 | if ( empty( $missing ) && empty( $extra ) ) { 53 | exit; 54 | } 55 | 56 | if ( $missing ) { 57 | echo "The following configuration keys are missing:\n"; 58 | echo implode( "\n", $missing ) . "\n"; 59 | } 60 | 61 | if ( $extra ) { 62 | echo "The following configuration keys are set without equivalent in template file:\n"; 63 | echo implode( "\n", $extra ) . "\n"; 64 | } 65 | -------------------------------------------------------------------------------- /src/ConstantParser.php: -------------------------------------------------------------------------------- 1 | constants = []; 20 | $this->state = self::S_OTHER; 21 | while ( $token = next( $tokens ) ) { 22 | $this->consumeToken( $token ); 23 | } 24 | return $this->constants; 25 | } 26 | 27 | private function consumeToken( $token ) { 28 | switch ( $this->state ) { 29 | case self::S_OTHER: 30 | if ( $this->isDefineStatement( $token ) ) { 31 | $this->state = self::S_DEFINE; 32 | } 33 | return; 34 | case self::S_DEFINE: 35 | if ( $token == '(' ) { 36 | $this->state = self::S_KEY; 37 | $this->currentKey = ''; 38 | } 39 | return; 40 | case self::S_KEY: 41 | if ( $token == "," ) { 42 | $this->state = self::S_VALUE; 43 | $this->currentValue = ''; 44 | return; 45 | } 46 | $this->currentKey .= $this->getTokenContents( $token ); 47 | return; 48 | case self::S_VALUE: 49 | if ( $token == ")" ) { 50 | $this->state = self::S_OTHER; 51 | $this->constants[$this->currentKey] = $this->currentValue; 52 | return; 53 | } 54 | $this->currentValue .= $this->getTokenContents( $token ); 55 | return; 56 | } 57 | } 58 | 59 | private function getTokenContents( $token ) { 60 | if ( !is_array( $token ) ) { 61 | return $token; 62 | } 63 | if ( $token[0] == T_WHITESPACE ) { 64 | return ''; 65 | } 66 | return $token[1]; 67 | } 68 | 69 | private function isDefineStatement( $token ) { 70 | return is_array( $token ) && $token[0] == T_STRING && $token[1] == 'define'; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "d94a3cffe6933b0fe3066c63c68d4bc7", 8 | "content-hash": "9fbb53f29d6d0fb2916b25824de8ab6f", 9 | "packages": [], 10 | "packages-dev": [ 11 | { 12 | "name": "mediawiki/mediawiki-codesniffer", 13 | "version": "v0.3.0", 14 | "source": { 15 | "type": "git", 16 | "url": "https://github.com/wikimedia/mediawiki-tools-codesniffer.git", 17 | "reference": "cb25db9fba6b3d3b6cc6ee95a87772ff1302929b" 18 | }, 19 | "dist": { 20 | "type": "zip", 21 | "url": "https://api.github.com/repos/wikimedia/mediawiki-tools-codesniffer/zipball/cb25db9fba6b3d3b6cc6ee95a87772ff1302929b", 22 | "reference": "cb25db9fba6b3d3b6cc6ee95a87772ff1302929b", 23 | "shasum": "" 24 | }, 25 | "require": { 26 | "squizlabs/php_codesniffer": "2.3.0" 27 | }, 28 | "require-dev": { 29 | "jakub-onderka/php-parallel-lint": "0.8.*", 30 | "phpunit/phpunit": "~4.1.0" 31 | }, 32 | "type": "library", 33 | "notification-url": "https://packagist.org/downloads/", 34 | "license": [ 35 | "GPL-2.0+" 36 | ], 37 | "description": "MediaWiki CodeSniffer Standards", 38 | "homepage": "https://www.mediawiki.org/wiki/Manual:Coding_conventions/PHP", 39 | "keywords": [ 40 | "codesniffer", 41 | "mediawiki" 42 | ], 43 | "time": "2015-06-19 16:36:59" 44 | }, 45 | { 46 | "name": "squizlabs/php_codesniffer", 47 | "version": "2.3.0", 48 | "source": { 49 | "type": "git", 50 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 51 | "reference": "5046b0e01c416fc2b06df961d0673c85bcdc896c" 52 | }, 53 | "dist": { 54 | "type": "zip", 55 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5046b0e01c416fc2b06df961d0673c85bcdc896c", 56 | "reference": "5046b0e01c416fc2b06df961d0673c85bcdc896c", 57 | "shasum": "" 58 | }, 59 | "require": { 60 | "ext-tokenizer": "*", 61 | "ext-xmlwriter": "*", 62 | "php": ">=5.1.2" 63 | }, 64 | "bin": [ 65 | "scripts/phpcs", 66 | "scripts/phpcbf" 67 | ], 68 | "type": "library", 69 | "extra": { 70 | "branch-alias": { 71 | "dev-master": "2.0.x-dev" 72 | } 73 | }, 74 | "autoload": { 75 | "classmap": [ 76 | "CodeSniffer.php", 77 | "CodeSniffer/CLI.php", 78 | "CodeSniffer/Exception.php", 79 | "CodeSniffer/File.php", 80 | "CodeSniffer/Fixer.php", 81 | "CodeSniffer/Report.php", 82 | "CodeSniffer/Reporting.php", 83 | "CodeSniffer/Sniff.php", 84 | "CodeSniffer/Tokens.php", 85 | "CodeSniffer/Reports/", 86 | "CodeSniffer/Tokenizers/", 87 | "CodeSniffer/DocGenerators/", 88 | "CodeSniffer/Standards/AbstractPatternSniff.php", 89 | "CodeSniffer/Standards/AbstractScopeSniff.php", 90 | "CodeSniffer/Standards/AbstractVariableSniff.php", 91 | "CodeSniffer/Standards/IncorrectPatternException.php", 92 | "CodeSniffer/Standards/Generic/Sniffs/", 93 | "CodeSniffer/Standards/MySource/Sniffs/", 94 | "CodeSniffer/Standards/PEAR/Sniffs/", 95 | "CodeSniffer/Standards/PSR1/Sniffs/", 96 | "CodeSniffer/Standards/PSR2/Sniffs/", 97 | "CodeSniffer/Standards/Squiz/Sniffs/", 98 | "CodeSniffer/Standards/Zend/Sniffs/" 99 | ] 100 | }, 101 | "notification-url": "https://packagist.org/downloads/", 102 | "license": [ 103 | "BSD-3-Clause" 104 | ], 105 | "authors": [ 106 | { 107 | "name": "Greg Sherwood", 108 | "role": "lead" 109 | } 110 | ], 111 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 112 | "homepage": "http://www.squizlabs.com/php-codesniffer", 113 | "keywords": [ 114 | "phpcs", 115 | "standards" 116 | ], 117 | "time": "2015-03-04 02:07:03" 118 | } 119 | ], 120 | "aliases": [], 121 | "minimum-stability": "stable", 122 | "stability-flags": [], 123 | "prefer-stable": false, 124 | "prefer-lowest": false, 125 | "platform": [], 126 | "platform-dev": [] 127 | } 128 | --------------------------------------------------------------------------------