├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── README.md ├── bin ├── css2scss └── css2scss.bat ├── composer.json ├── composer.lock ├── composer.phar ├── example.php ├── phpunit.php ├── phpunit.xml ├── src ├── Css2Scss.php └── tokens │ ├── ScssRule.php │ └── ScssRuleList.php └── tests └── Css2ScssTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | php: 3 | code_rating: true 4 | duplication: true 5 | tools: 6 | external_code_coverage: true 7 | php_code_sniffer: true 8 | php_mess_detector: true 9 | php_cs_fixer: 10 | config: { level: psr2 } 11 | filter: 12 | paths: [src/*] -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - hhvm 9 | 10 | before_script: 11 | - composer self-update 12 | - composer install --no-interaction --prefer-source --dev 13 | 14 | script: 15 | - mkdir -p build/logs 16 | - phpunit --coverage-clover build/logs/clover.xml 17 | 18 | after_script: 19 | - wget https://scrutinizer-ci.com/ocular.phar 20 | - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml 21 | 22 | notifications: 23 | email: false 24 | 25 | sudo: false 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://api.travis-ci.org/ortic/css2scss.svg?branch=master)](https://travis-ci.org/ortic/css2scss) 2 | [![Code Rating](https://img.shields.io/scrutinizer/g/ortic/css2scss.svg?style=flat)](https://scrutinizer-ci.com/g/ortic/css2scss/) 3 | [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/ortic/css2scss.svg?style=flat)](https://scrutinizer-ci.com/g/ortic/css2scss/) 4 | 5 | css2less 6 | ======== 7 | 8 | this library aims to convert CSS files into SCSS files. 9 | 10 | Currently used by http://www.css2scss.net/ 11 | 12 | example 13 | ======= 14 | 15 | The code below takes a few CSS instructions and prints them in a more SCSS like form: 16 | 17 | ```php 18 | $cssContent = 'body p { font-family: arial; }'; 19 | $css2lessParser = new \Ortic\Css2Scss\Css2Scss($cssContent); 20 | echo $css2lessParser->getScss(); 21 | ``` 22 | 23 | output: 24 | 25 | ``` 26 | body { 27 | p { 28 | font-family: arial; 29 | } 30 | } 31 | ``` -------------------------------------------------------------------------------- /bin/css2scss: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | argv = $argv; 32 | 33 | // remove first item from array, it only contains a path to the script 34 | array_shift($this->argv); 35 | 36 | $this->parseArguments(); 37 | $this->checkMandatoryArguments(); 38 | $this->run(); 39 | } 40 | 41 | /** 42 | * Loops through the options specified in the getOptions methods and assigns them to the proper class properties 43 | */ 44 | protected function parseArguments() 45 | { 46 | $options = $this->getOptions(); 47 | $currentVariable = null; 48 | 49 | foreach ($this->argv as $argument) { 50 | if (array_key_exists($argument, $options)) { 51 | $currentVariable = $options[$argument]['assignmentTarget']; 52 | } else { 53 | if (is_null($currentVariable)) { 54 | echo "Invalid command line arguments specified!\n\n"; 55 | $this->showHelp(); 56 | die(); 57 | } 58 | $this->$currentVariable = $argument; 59 | $currentVariable = null; 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Checks if all mandatory arguments are specified 66 | */ 67 | protected function checkMandatoryArguments() 68 | { 69 | $options = $this->getOptions(); 70 | foreach ($options as $optionKey => $option) { 71 | if ($option['mandatory']) { 72 | $assignmentTarget = $option['assignmentTarget']; 73 | if (empty($this->$assignmentTarget)) { 74 | echo sprintf("Please specify mandatory argument: %s\n\n", $optionKey); 75 | $this->showHelp(); 76 | die(); 77 | } 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * Show a usage example and all available options 84 | */ 85 | protected function showHelp() 86 | { 87 | echo "Usage:\t\n css2scss --css input.css --scss output.scss\n\n"; 88 | echo "Arguments:\n"; 89 | $options = $this->getOptions(); 90 | foreach ($options as $optionKey => $option) { 91 | echo " " . str_pad($optionKey, 10) . $option['explanation'] . "\n"; 92 | } 93 | } 94 | 95 | /** 96 | * Returns an array of an array having three properties: 97 | * - explanation: An explanation for the user about the argument 98 | * - mandatory: true if mandatory 99 | * - assignmentTarget: The name of the class property where to keep the argument value 100 | * 101 | * @return array options 102 | */ 103 | protected function getOptions() 104 | { 105 | return array( 106 | '--css' => array( 107 | 'explanation' => 'The css file you want to convert to a scss file', 108 | 'mandatory' => true, 109 | 'assignmentTarget' => 'cssFileName', 110 | ), 111 | '--scss' => array( 112 | 'explanation' => '(optional) Path where you want to save the converted file', 113 | 'mandatory' => false, 114 | 'assignmentTarget' => 'scssFileName', 115 | ), 116 | ); 117 | } 118 | 119 | /** 120 | * Starts the conversion 121 | */ 122 | public function run() 123 | { 124 | if (!file_exists($this->cssFileName)) { 125 | echo sprintf("CSS File \"%s\" not found!\n", $this->cssFileName); 126 | die(); 127 | } 128 | 129 | $cssFileContent = file_get_contents($this->cssFileName); 130 | $css2scssParser = new \Ortic\Css2Scss\Css2Scss($cssFileContent); 131 | $scssOutput = $css2scssParser->getScss(); 132 | 133 | if ($this->scssFileName == '') { 134 | echo $scssOutput; 135 | } else { 136 | file_put_contents($this->scssFileName, $scssOutput); 137 | echo sprintf("LESS output saved in \"%s\"\n", $this->scssFileName); 138 | } 139 | } 140 | } 141 | 142 | (new Css2Scss($argv))->run(); 143 | 144 | -------------------------------------------------------------------------------- /bin/css2scss.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | php %~dp0\css2scss %* -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ortic/css2scss", 3 | "description": "css to scss converter", 4 | "minimum-stability": "stable", 5 | "license": "proprietary", 6 | "authors": [ 7 | { 8 | "name": "Remo Laubacher", 9 | "email": "remo.laubacher@ortic.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.4.4", 14 | "natxet/cssmin": "~3.0" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "~4.0", 18 | "satooshi/php-coveralls": "dev-master", 19 | "nette/nette": "~2.1.3", 20 | "apigen/apigen": "2.8.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Ortic\\Css2Scss\\": "src/" 25 | } 26 | }, 27 | "bin": [ 28 | "bin/css2scss.bat", 29 | "bin/css2scss" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /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": "d6644c72e87ed5283508c34625e8225d", 8 | "packages": [ 9 | { 10 | "name": "natxet/CssMin", 11 | "version": "v3.0.4", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/natxet/CssMin.git", 15 | "reference": "92de3fe3ccb4f8298d31952490ef7d5395855c39" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/natxet/CssMin/zipball/92de3fe3ccb4f8298d31952490ef7d5395855c39", 20 | "reference": "92de3fe3ccb4f8298d31952490ef7d5395855c39", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.0" 25 | }, 26 | "type": "library", 27 | "extra": { 28 | "branch-alias": { 29 | "dev-master": "3.0-dev" 30 | } 31 | }, 32 | "autoload": { 33 | "classmap": [ 34 | "src/" 35 | ] 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Joe Scylla", 44 | "email": "joe.scylla@gmail.com", 45 | "homepage": "https://profiles.google.com/joe.scylla" 46 | } 47 | ], 48 | "description": "Minifying CSS", 49 | "homepage": "http://code.google.com/p/cssmin/", 50 | "keywords": [ 51 | "css", 52 | "minify" 53 | ], 54 | "time": "2015-09-25 11:13:11" 55 | } 56 | ], 57 | "packages-dev": [ 58 | { 59 | "name": "andrewsville/php-token-reflection", 60 | "version": "1.4.0", 61 | "source": { 62 | "type": "git", 63 | "url": "https://github.com/Andrewsville/PHP-Token-Reflection.git", 64 | "reference": "e6d0ac2baf66cdf154be34c3d2a2aa1bd4b426ee" 65 | }, 66 | "dist": { 67 | "type": "zip", 68 | "url": "https://api.github.com/repos/Andrewsville/PHP-Token-Reflection/zipball/e6d0ac2baf66cdf154be34c3d2a2aa1bd4b426ee", 69 | "reference": "e6d0ac2baf66cdf154be34c3d2a2aa1bd4b426ee", 70 | "shasum": "" 71 | }, 72 | "require": { 73 | "ext-tokenizer": "*", 74 | "php": ">=5.3.0" 75 | }, 76 | "type": "library", 77 | "autoload": { 78 | "psr-0": { 79 | "TokenReflection": "./" 80 | } 81 | }, 82 | "notification-url": "https://packagist.org/downloads/", 83 | "license": [ 84 | "BSD-3" 85 | ], 86 | "authors": [ 87 | { 88 | "name": "Ondřej Nešpor", 89 | "homepage": "https://github.com/andrewsville" 90 | }, 91 | { 92 | "name": "Jaroslav Hanslík", 93 | "homepage": "https://github.com/kukulich" 94 | } 95 | ], 96 | "description": "Library emulating the PHP internal reflection using just the tokenized source code.", 97 | "homepage": "http://andrewsville.github.com/PHP-Token-Reflection/", 98 | "keywords": [ 99 | "library", 100 | "reflection", 101 | "tokenizer" 102 | ], 103 | "time": "2014-08-06 16:37:08" 104 | }, 105 | { 106 | "name": "apigen/apigen", 107 | "version": "2.8.0", 108 | "source": { 109 | "type": "git", 110 | "url": "https://github.com/apigen/ApiGen.git", 111 | "reference": "4987551632c3ad3fb7e89cf6dc3fe601e45578c3" 112 | }, 113 | "dist": { 114 | "type": "zip", 115 | "url": "https://api.github.com/repos/apigen/ApiGen/zipball/4987551632c3ad3fb7e89cf6dc3fe601e45578c3", 116 | "reference": "4987551632c3ad3fb7e89cf6dc3fe601e45578c3", 117 | "shasum": "" 118 | }, 119 | "require": { 120 | "andrewsville/php-token-reflection": ">=1.3.1", 121 | "dg/texy": ">=2.1.0", 122 | "kukulich/fshl": ">=2.1.0", 123 | "nette/nette": ">=2.0.5", 124 | "php": ">=5.3.0" 125 | }, 126 | "suggest": { 127 | "ext-bz2": "*", 128 | "ext-phar": "*", 129 | "ext-zip": "*", 130 | "ext-zlib": "*" 131 | }, 132 | "bin": [ 133 | "apigen.php" 134 | ], 135 | "type": "library", 136 | "extra": { 137 | "branch-alias": { 138 | "dev-develop": "3.0.0-dev" 139 | } 140 | }, 141 | "autoload": { 142 | "psr-0": { 143 | "ApiGen": "./" 144 | } 145 | }, 146 | "notification-url": "https://packagist.org/downloads/", 147 | "license": [ 148 | "BSD-3-Clause" 149 | ], 150 | "authors": [ 151 | { 152 | "name": "David Grudl", 153 | "homepage": "http://davidgrudl.com" 154 | }, 155 | { 156 | "name": "Ondřej Nešpor", 157 | "homepage": "https://github.com/andrewsville" 158 | }, 159 | { 160 | "name": "Jaroslav Hanslík", 161 | "homepage": "https://github.com/kukulich" 162 | } 163 | ], 164 | "description": "API documentation generator for PHP 5.3+", 165 | "homepage": "http://apigen.org/", 166 | "keywords": [ 167 | "api", 168 | "docblock", 169 | "documentation", 170 | "generator", 171 | "phpDocumentor", 172 | "phpdoc" 173 | ], 174 | "time": "2012-09-29 17:34:02" 175 | }, 176 | { 177 | "name": "dg/texy", 178 | "version": "v2.8.2", 179 | "source": { 180 | "type": "git", 181 | "url": "https://github.com/dg/texy.git", 182 | "reference": "e21cbfcff0a542cf691082df7427911495524ded" 183 | }, 184 | "dist": { 185 | "type": "zip", 186 | "url": "https://api.github.com/repos/dg/texy/zipball/e21cbfcff0a542cf691082df7427911495524ded", 187 | "reference": "e21cbfcff0a542cf691082df7427911495524ded", 188 | "shasum": "" 189 | }, 190 | "require": { 191 | "php": ">=5.4.4" 192 | }, 193 | "replace": { 194 | "dg/texy": "self.version" 195 | }, 196 | "require-dev": { 197 | "nette/tester": "~1.5" 198 | }, 199 | "type": "library", 200 | "autoload": { 201 | "classmap": [ 202 | "src/" 203 | ], 204 | "files": [ 205 | "src/texy.php" 206 | ] 207 | }, 208 | "notification-url": "https://packagist.org/downloads/", 209 | "license": [ 210 | "BSD-3-Clause", 211 | "GPL-2.0", 212 | "GPL-3.0" 213 | ], 214 | "authors": [ 215 | { 216 | "name": "David Grudl", 217 | "homepage": "https://davidgrudl.com" 218 | } 219 | ], 220 | "description": "Texy converts plain text in easy to read Texy syntax into structurally valid (X)HTML. It supports adding of images, links, nested lists, tables and has full support for CSS. Texy supports hyphenation of long words (which reflects language rules), clickable emails and URL (emails are obfuscated against spambots), national typographic single and double quotation marks, ellipses, em dashes, dimension sign, nonbreakable spaces (e.g. in phone numbers), acronyms, arrows and many others. Texy code can optionally contain HTML tags.", 221 | "homepage": "http://texy.info", 222 | "keywords": [ 223 | "html", 224 | "markdown", 225 | "markup language", 226 | "plain text", 227 | "text", 228 | "textile", 229 | "texy", 230 | "wiki", 231 | "xhtml" 232 | ], 233 | "time": "2015-11-07 22:17:49" 234 | }, 235 | { 236 | "name": "doctrine/instantiator", 237 | "version": "1.0.5", 238 | "source": { 239 | "type": "git", 240 | "url": "https://github.com/doctrine/instantiator.git", 241 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 242 | }, 243 | "dist": { 244 | "type": "zip", 245 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 246 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 247 | "shasum": "" 248 | }, 249 | "require": { 250 | "php": ">=5.3,<8.0-DEV" 251 | }, 252 | "require-dev": { 253 | "athletic/athletic": "~0.1.8", 254 | "ext-pdo": "*", 255 | "ext-phar": "*", 256 | "phpunit/phpunit": "~4.0", 257 | "squizlabs/php_codesniffer": "~2.0" 258 | }, 259 | "type": "library", 260 | "extra": { 261 | "branch-alias": { 262 | "dev-master": "1.0.x-dev" 263 | } 264 | }, 265 | "autoload": { 266 | "psr-4": { 267 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 268 | } 269 | }, 270 | "notification-url": "https://packagist.org/downloads/", 271 | "license": [ 272 | "MIT" 273 | ], 274 | "authors": [ 275 | { 276 | "name": "Marco Pivetta", 277 | "email": "ocramius@gmail.com", 278 | "homepage": "http://ocramius.github.com/" 279 | } 280 | ], 281 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 282 | "homepage": "https://github.com/doctrine/instantiator", 283 | "keywords": [ 284 | "constructor", 285 | "instantiate" 286 | ], 287 | "time": "2015-06-14 21:17:01" 288 | }, 289 | { 290 | "name": "guzzle/guzzle", 291 | "version": "v3.9.3", 292 | "source": { 293 | "type": "git", 294 | "url": "https://github.com/guzzle/guzzle3.git", 295 | "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" 296 | }, 297 | "dist": { 298 | "type": "zip", 299 | "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", 300 | "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", 301 | "shasum": "" 302 | }, 303 | "require": { 304 | "ext-curl": "*", 305 | "php": ">=5.3.3", 306 | "symfony/event-dispatcher": "~2.1" 307 | }, 308 | "replace": { 309 | "guzzle/batch": "self.version", 310 | "guzzle/cache": "self.version", 311 | "guzzle/common": "self.version", 312 | "guzzle/http": "self.version", 313 | "guzzle/inflection": "self.version", 314 | "guzzle/iterator": "self.version", 315 | "guzzle/log": "self.version", 316 | "guzzle/parser": "self.version", 317 | "guzzle/plugin": "self.version", 318 | "guzzle/plugin-async": "self.version", 319 | "guzzle/plugin-backoff": "self.version", 320 | "guzzle/plugin-cache": "self.version", 321 | "guzzle/plugin-cookie": "self.version", 322 | "guzzle/plugin-curlauth": "self.version", 323 | "guzzle/plugin-error-response": "self.version", 324 | "guzzle/plugin-history": "self.version", 325 | "guzzle/plugin-log": "self.version", 326 | "guzzle/plugin-md5": "self.version", 327 | "guzzle/plugin-mock": "self.version", 328 | "guzzle/plugin-oauth": "self.version", 329 | "guzzle/service": "self.version", 330 | "guzzle/stream": "self.version" 331 | }, 332 | "require-dev": { 333 | "doctrine/cache": "~1.3", 334 | "monolog/monolog": "~1.0", 335 | "phpunit/phpunit": "3.7.*", 336 | "psr/log": "~1.0", 337 | "symfony/class-loader": "~2.1", 338 | "zendframework/zend-cache": "2.*,<2.3", 339 | "zendframework/zend-log": "2.*,<2.3" 340 | }, 341 | "suggest": { 342 | "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." 343 | }, 344 | "type": "library", 345 | "extra": { 346 | "branch-alias": { 347 | "dev-master": "3.9-dev" 348 | } 349 | }, 350 | "autoload": { 351 | "psr-0": { 352 | "Guzzle": "src/", 353 | "Guzzle\\Tests": "tests/" 354 | } 355 | }, 356 | "notification-url": "https://packagist.org/downloads/", 357 | "license": [ 358 | "MIT" 359 | ], 360 | "authors": [ 361 | { 362 | "name": "Michael Dowling", 363 | "email": "mtdowling@gmail.com", 364 | "homepage": "https://github.com/mtdowling" 365 | }, 366 | { 367 | "name": "Guzzle Community", 368 | "homepage": "https://github.com/guzzle/guzzle/contributors" 369 | } 370 | ], 371 | "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", 372 | "homepage": "http://guzzlephp.org/", 373 | "keywords": [ 374 | "client", 375 | "curl", 376 | "framework", 377 | "http", 378 | "http client", 379 | "rest", 380 | "web service" 381 | ], 382 | "time": "2015-03-18 18:23:50" 383 | }, 384 | { 385 | "name": "kukulich/fshl", 386 | "version": "2.1.0", 387 | "source": { 388 | "type": "git", 389 | "url": "https://github.com/kukulich/fshl.git", 390 | "reference": "974c294ade5d76c0c16b6fe3fd3a584ba999b24f" 391 | }, 392 | "dist": { 393 | "type": "zip", 394 | "url": "https://api.github.com/repos/kukulich/fshl/zipball/974c294ade5d76c0c16b6fe3fd3a584ba999b24f", 395 | "reference": "974c294ade5d76c0c16b6fe3fd3a584ba999b24f", 396 | "shasum": "" 397 | }, 398 | "require": { 399 | "php": ">=5.3" 400 | }, 401 | "type": "library", 402 | "autoload": { 403 | "psr-0": { 404 | "FSHL": "./" 405 | } 406 | }, 407 | "notification-url": "https://packagist.org/downloads/", 408 | "license": [ 409 | "GPL-2.0+" 410 | ], 411 | "authors": [ 412 | { 413 | "name": "Jaroslav Hanslík", 414 | "homepage": "https://github.com/kukulich" 415 | } 416 | ], 417 | "description": "FSHL is a free, open source, universal, fast syntax highlighter written in PHP.", 418 | "homepage": "http://fshl.kukulich.cz/", 419 | "keywords": [ 420 | "highlight", 421 | "library", 422 | "syntax" 423 | ], 424 | "time": "2012-09-08 19:00:07" 425 | }, 426 | { 427 | "name": "nette/nette", 428 | "version": "v2.1.11", 429 | "source": { 430 | "type": "git", 431 | "url": "https://github.com/nette/nette.git", 432 | "reference": "d9bba819f1ac09a4bc5bc330eb83c17bf2926b9a" 433 | }, 434 | "dist": { 435 | "type": "zip", 436 | "url": "https://api.github.com/repos/nette/nette/zipball/d9bba819f1ac09a4bc5bc330eb83c17bf2926b9a", 437 | "reference": "d9bba819f1ac09a4bc5bc330eb83c17bf2926b9a", 438 | "shasum": "" 439 | }, 440 | "require": { 441 | "ext-iconv": "*", 442 | "ext-tokenizer": "*", 443 | "php": ">=5.3.1" 444 | }, 445 | "replace": { 446 | "latte/latte": "self.version", 447 | "nette/application": "self.version", 448 | "nette/bootstrap": "self.version", 449 | "nette/caching": "self.version", 450 | "nette/component-model": "self.version", 451 | "nette/database": "self.version", 452 | "nette/di": "self.version", 453 | "nette/finder": "self.version", 454 | "nette/forms": "self.version", 455 | "nette/http": "self.version", 456 | "nette/mail": "self.version", 457 | "nette/neon": "self.version", 458 | "nette/php-generator": "self.version", 459 | "nette/reflection": "self.version", 460 | "nette/robot-loader": "self.version", 461 | "nette/safe-stream": "self.version", 462 | "nette/security": "self.version", 463 | "nette/tokenizer": "self.version", 464 | "nette/utils": "self.version", 465 | "tracy/tracy": "self.version" 466 | }, 467 | "require-dev": { 468 | "nette/tester": "~1.3" 469 | }, 470 | "suggest": { 471 | "ext-fileinfo": "", 472 | "ext-gd": "", 473 | "ext-mbstring": "", 474 | "ext-pdo": "" 475 | }, 476 | "type": "library", 477 | "autoload": { 478 | "classmap": [ 479 | "Nette/" 480 | ], 481 | "files": [ 482 | "Nette/common/shortcuts.php" 483 | ] 484 | }, 485 | "notification-url": "https://packagist.org/downloads/", 486 | "license": [ 487 | "BSD-3-Clause", 488 | "GPL-2.0", 489 | "GPL-3.0" 490 | ], 491 | "authors": [ 492 | { 493 | "name": "David Grudl", 494 | "homepage": "http://davidgrudl.com" 495 | }, 496 | { 497 | "name": "Nette Community", 498 | "homepage": "http://nette.org/contributors" 499 | } 500 | ], 501 | "description": "Nette Framework - innovative framework for fast and easy development of secured web applications in PHP. Write less, have cleaner code and your work will bring you joy.", 502 | "homepage": "http://nette.org", 503 | "keywords": [ 504 | "Forms", 505 | "database", 506 | "debugging", 507 | "framework", 508 | "mailing", 509 | "mvc", 510 | "templating" 511 | ], 512 | "time": "2015-08-15 09:59:47" 513 | }, 514 | { 515 | "name": "phpdocumentor/reflection-docblock", 516 | "version": "2.0.4", 517 | "source": { 518 | "type": "git", 519 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 520 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 521 | }, 522 | "dist": { 523 | "type": "zip", 524 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 525 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 526 | "shasum": "" 527 | }, 528 | "require": { 529 | "php": ">=5.3.3" 530 | }, 531 | "require-dev": { 532 | "phpunit/phpunit": "~4.0" 533 | }, 534 | "suggest": { 535 | "dflydev/markdown": "~1.0", 536 | "erusev/parsedown": "~1.0" 537 | }, 538 | "type": "library", 539 | "extra": { 540 | "branch-alias": { 541 | "dev-master": "2.0.x-dev" 542 | } 543 | }, 544 | "autoload": { 545 | "psr-0": { 546 | "phpDocumentor": [ 547 | "src/" 548 | ] 549 | } 550 | }, 551 | "notification-url": "https://packagist.org/downloads/", 552 | "license": [ 553 | "MIT" 554 | ], 555 | "authors": [ 556 | { 557 | "name": "Mike van Riel", 558 | "email": "mike.vanriel@naenius.com" 559 | } 560 | ], 561 | "time": "2015-02-03 12:10:50" 562 | }, 563 | { 564 | "name": "phpspec/prophecy", 565 | "version": "v1.5.0", 566 | "source": { 567 | "type": "git", 568 | "url": "https://github.com/phpspec/prophecy.git", 569 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" 570 | }, 571 | "dist": { 572 | "type": "zip", 573 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", 574 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", 575 | "shasum": "" 576 | }, 577 | "require": { 578 | "doctrine/instantiator": "^1.0.2", 579 | "phpdocumentor/reflection-docblock": "~2.0", 580 | "sebastian/comparator": "~1.1" 581 | }, 582 | "require-dev": { 583 | "phpspec/phpspec": "~2.0" 584 | }, 585 | "type": "library", 586 | "extra": { 587 | "branch-alias": { 588 | "dev-master": "1.4.x-dev" 589 | } 590 | }, 591 | "autoload": { 592 | "psr-0": { 593 | "Prophecy\\": "src/" 594 | } 595 | }, 596 | "notification-url": "https://packagist.org/downloads/", 597 | "license": [ 598 | "MIT" 599 | ], 600 | "authors": [ 601 | { 602 | "name": "Konstantin Kudryashov", 603 | "email": "ever.zet@gmail.com", 604 | "homepage": "http://everzet.com" 605 | }, 606 | { 607 | "name": "Marcello Duarte", 608 | "email": "marcello.duarte@gmail.com" 609 | } 610 | ], 611 | "description": "Highly opinionated mocking framework for PHP 5.3+", 612 | "homepage": "https://github.com/phpspec/prophecy", 613 | "keywords": [ 614 | "Double", 615 | "Dummy", 616 | "fake", 617 | "mock", 618 | "spy", 619 | "stub" 620 | ], 621 | "time": "2015-08-13 10:07:40" 622 | }, 623 | { 624 | "name": "phpunit/php-code-coverage", 625 | "version": "2.2.4", 626 | "source": { 627 | "type": "git", 628 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 629 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 630 | }, 631 | "dist": { 632 | "type": "zip", 633 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 634 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 635 | "shasum": "" 636 | }, 637 | "require": { 638 | "php": ">=5.3.3", 639 | "phpunit/php-file-iterator": "~1.3", 640 | "phpunit/php-text-template": "~1.2", 641 | "phpunit/php-token-stream": "~1.3", 642 | "sebastian/environment": "^1.3.2", 643 | "sebastian/version": "~1.0" 644 | }, 645 | "require-dev": { 646 | "ext-xdebug": ">=2.1.4", 647 | "phpunit/phpunit": "~4" 648 | }, 649 | "suggest": { 650 | "ext-dom": "*", 651 | "ext-xdebug": ">=2.2.1", 652 | "ext-xmlwriter": "*" 653 | }, 654 | "type": "library", 655 | "extra": { 656 | "branch-alias": { 657 | "dev-master": "2.2.x-dev" 658 | } 659 | }, 660 | "autoload": { 661 | "classmap": [ 662 | "src/" 663 | ] 664 | }, 665 | "notification-url": "https://packagist.org/downloads/", 666 | "license": [ 667 | "BSD-3-Clause" 668 | ], 669 | "authors": [ 670 | { 671 | "name": "Sebastian Bergmann", 672 | "email": "sb@sebastian-bergmann.de", 673 | "role": "lead" 674 | } 675 | ], 676 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 677 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 678 | "keywords": [ 679 | "coverage", 680 | "testing", 681 | "xunit" 682 | ], 683 | "time": "2015-10-06 15:47:00" 684 | }, 685 | { 686 | "name": "phpunit/php-file-iterator", 687 | "version": "1.4.1", 688 | "source": { 689 | "type": "git", 690 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 691 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 692 | }, 693 | "dist": { 694 | "type": "zip", 695 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 696 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 697 | "shasum": "" 698 | }, 699 | "require": { 700 | "php": ">=5.3.3" 701 | }, 702 | "type": "library", 703 | "extra": { 704 | "branch-alias": { 705 | "dev-master": "1.4.x-dev" 706 | } 707 | }, 708 | "autoload": { 709 | "classmap": [ 710 | "src/" 711 | ] 712 | }, 713 | "notification-url": "https://packagist.org/downloads/", 714 | "license": [ 715 | "BSD-3-Clause" 716 | ], 717 | "authors": [ 718 | { 719 | "name": "Sebastian Bergmann", 720 | "email": "sb@sebastian-bergmann.de", 721 | "role": "lead" 722 | } 723 | ], 724 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 725 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 726 | "keywords": [ 727 | "filesystem", 728 | "iterator" 729 | ], 730 | "time": "2015-06-21 13:08:43" 731 | }, 732 | { 733 | "name": "phpunit/php-text-template", 734 | "version": "1.2.1", 735 | "source": { 736 | "type": "git", 737 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 738 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 739 | }, 740 | "dist": { 741 | "type": "zip", 742 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 743 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 744 | "shasum": "" 745 | }, 746 | "require": { 747 | "php": ">=5.3.3" 748 | }, 749 | "type": "library", 750 | "autoload": { 751 | "classmap": [ 752 | "src/" 753 | ] 754 | }, 755 | "notification-url": "https://packagist.org/downloads/", 756 | "license": [ 757 | "BSD-3-Clause" 758 | ], 759 | "authors": [ 760 | { 761 | "name": "Sebastian Bergmann", 762 | "email": "sebastian@phpunit.de", 763 | "role": "lead" 764 | } 765 | ], 766 | "description": "Simple template engine.", 767 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 768 | "keywords": [ 769 | "template" 770 | ], 771 | "time": "2015-06-21 13:50:34" 772 | }, 773 | { 774 | "name": "phpunit/php-timer", 775 | "version": "1.0.7", 776 | "source": { 777 | "type": "git", 778 | "url": "https://github.com/sebastianbergmann/php-timer.git", 779 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" 780 | }, 781 | "dist": { 782 | "type": "zip", 783 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 784 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 785 | "shasum": "" 786 | }, 787 | "require": { 788 | "php": ">=5.3.3" 789 | }, 790 | "type": "library", 791 | "autoload": { 792 | "classmap": [ 793 | "src/" 794 | ] 795 | }, 796 | "notification-url": "https://packagist.org/downloads/", 797 | "license": [ 798 | "BSD-3-Clause" 799 | ], 800 | "authors": [ 801 | { 802 | "name": "Sebastian Bergmann", 803 | "email": "sb@sebastian-bergmann.de", 804 | "role": "lead" 805 | } 806 | ], 807 | "description": "Utility class for timing", 808 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 809 | "keywords": [ 810 | "timer" 811 | ], 812 | "time": "2015-06-21 08:01:12" 813 | }, 814 | { 815 | "name": "phpunit/php-token-stream", 816 | "version": "1.4.8", 817 | "source": { 818 | "type": "git", 819 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 820 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 821 | }, 822 | "dist": { 823 | "type": "zip", 824 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 825 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 826 | "shasum": "" 827 | }, 828 | "require": { 829 | "ext-tokenizer": "*", 830 | "php": ">=5.3.3" 831 | }, 832 | "require-dev": { 833 | "phpunit/phpunit": "~4.2" 834 | }, 835 | "type": "library", 836 | "extra": { 837 | "branch-alias": { 838 | "dev-master": "1.4-dev" 839 | } 840 | }, 841 | "autoload": { 842 | "classmap": [ 843 | "src/" 844 | ] 845 | }, 846 | "notification-url": "https://packagist.org/downloads/", 847 | "license": [ 848 | "BSD-3-Clause" 849 | ], 850 | "authors": [ 851 | { 852 | "name": "Sebastian Bergmann", 853 | "email": "sebastian@phpunit.de" 854 | } 855 | ], 856 | "description": "Wrapper around PHP's tokenizer extension.", 857 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 858 | "keywords": [ 859 | "tokenizer" 860 | ], 861 | "time": "2015-09-15 10:49:45" 862 | }, 863 | { 864 | "name": "phpunit/phpunit", 865 | "version": "4.8.18", 866 | "source": { 867 | "type": "git", 868 | "url": "https://github.com/sebastianbergmann/phpunit.git", 869 | "reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3" 870 | }, 871 | "dist": { 872 | "type": "zip", 873 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fa33d4ad96481b91df343d83e8c8aabed6b1dfd3", 874 | "reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3", 875 | "shasum": "" 876 | }, 877 | "require": { 878 | "ext-dom": "*", 879 | "ext-json": "*", 880 | "ext-pcre": "*", 881 | "ext-reflection": "*", 882 | "ext-spl": "*", 883 | "php": ">=5.3.3", 884 | "phpspec/prophecy": "^1.3.1", 885 | "phpunit/php-code-coverage": "~2.1", 886 | "phpunit/php-file-iterator": "~1.4", 887 | "phpunit/php-text-template": "~1.2", 888 | "phpunit/php-timer": ">=1.0.6", 889 | "phpunit/phpunit-mock-objects": "~2.3", 890 | "sebastian/comparator": "~1.1", 891 | "sebastian/diff": "~1.2", 892 | "sebastian/environment": "~1.3", 893 | "sebastian/exporter": "~1.2", 894 | "sebastian/global-state": "~1.0", 895 | "sebastian/version": "~1.0", 896 | "symfony/yaml": "~2.1|~3.0" 897 | }, 898 | "suggest": { 899 | "phpunit/php-invoker": "~1.1" 900 | }, 901 | "bin": [ 902 | "phpunit" 903 | ], 904 | "type": "library", 905 | "extra": { 906 | "branch-alias": { 907 | "dev-master": "4.8.x-dev" 908 | } 909 | }, 910 | "autoload": { 911 | "classmap": [ 912 | "src/" 913 | ] 914 | }, 915 | "notification-url": "https://packagist.org/downloads/", 916 | "license": [ 917 | "BSD-3-Clause" 918 | ], 919 | "authors": [ 920 | { 921 | "name": "Sebastian Bergmann", 922 | "email": "sebastian@phpunit.de", 923 | "role": "lead" 924 | } 925 | ], 926 | "description": "The PHP Unit Testing framework.", 927 | "homepage": "https://phpunit.de/", 928 | "keywords": [ 929 | "phpunit", 930 | "testing", 931 | "xunit" 932 | ], 933 | "time": "2015-11-11 11:32:49" 934 | }, 935 | { 936 | "name": "phpunit/phpunit-mock-objects", 937 | "version": "2.3.8", 938 | "source": { 939 | "type": "git", 940 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 941 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 942 | }, 943 | "dist": { 944 | "type": "zip", 945 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 946 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 947 | "shasum": "" 948 | }, 949 | "require": { 950 | "doctrine/instantiator": "^1.0.2", 951 | "php": ">=5.3.3", 952 | "phpunit/php-text-template": "~1.2", 953 | "sebastian/exporter": "~1.2" 954 | }, 955 | "require-dev": { 956 | "phpunit/phpunit": "~4.4" 957 | }, 958 | "suggest": { 959 | "ext-soap": "*" 960 | }, 961 | "type": "library", 962 | "extra": { 963 | "branch-alias": { 964 | "dev-master": "2.3.x-dev" 965 | } 966 | }, 967 | "autoload": { 968 | "classmap": [ 969 | "src/" 970 | ] 971 | }, 972 | "notification-url": "https://packagist.org/downloads/", 973 | "license": [ 974 | "BSD-3-Clause" 975 | ], 976 | "authors": [ 977 | { 978 | "name": "Sebastian Bergmann", 979 | "email": "sb@sebastian-bergmann.de", 980 | "role": "lead" 981 | } 982 | ], 983 | "description": "Mock Object library for PHPUnit", 984 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 985 | "keywords": [ 986 | "mock", 987 | "xunit" 988 | ], 989 | "time": "2015-10-02 06:51:40" 990 | }, 991 | { 992 | "name": "psr/log", 993 | "version": "1.0.0", 994 | "source": { 995 | "type": "git", 996 | "url": "https://github.com/php-fig/log.git", 997 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 998 | }, 999 | "dist": { 1000 | "type": "zip", 1001 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 1002 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 1003 | "shasum": "" 1004 | }, 1005 | "type": "library", 1006 | "autoload": { 1007 | "psr-0": { 1008 | "Psr\\Log\\": "" 1009 | } 1010 | }, 1011 | "notification-url": "https://packagist.org/downloads/", 1012 | "license": [ 1013 | "MIT" 1014 | ], 1015 | "authors": [ 1016 | { 1017 | "name": "PHP-FIG", 1018 | "homepage": "http://www.php-fig.org/" 1019 | } 1020 | ], 1021 | "description": "Common interface for logging libraries", 1022 | "keywords": [ 1023 | "log", 1024 | "psr", 1025 | "psr-3" 1026 | ], 1027 | "time": "2012-12-21 11:40:51" 1028 | }, 1029 | { 1030 | "name": "satooshi/php-coveralls", 1031 | "version": "dev-master", 1032 | "source": { 1033 | "type": "git", 1034 | "url": "https://github.com/satooshi/php-coveralls.git", 1035 | "reference": "2fbf803803d179ab1082807308a67bbd5a760c70" 1036 | }, 1037 | "dist": { 1038 | "type": "zip", 1039 | "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/2fbf803803d179ab1082807308a67bbd5a760c70", 1040 | "reference": "2fbf803803d179ab1082807308a67bbd5a760c70", 1041 | "shasum": "" 1042 | }, 1043 | "require": { 1044 | "ext-json": "*", 1045 | "ext-simplexml": "*", 1046 | "guzzle/guzzle": ">=2.7", 1047 | "php": ">=5.3", 1048 | "psr/log": "1.0.0", 1049 | "symfony/config": ">=2.0", 1050 | "symfony/console": ">=2.0", 1051 | "symfony/stopwatch": ">=2.2", 1052 | "symfony/yaml": ">=2.0" 1053 | }, 1054 | "require-dev": { 1055 | "apigen/apigen": "2.8.*@stable", 1056 | "pdepend/pdepend": "dev-master as 2.0.0", 1057 | "phpmd/phpmd": "dev-master", 1058 | "phpunit/php-invoker": ">=1.1.0,<1.2.0", 1059 | "phpunit/phpunit": "3.7.*@stable", 1060 | "sebastian/finder-facade": "dev-master", 1061 | "sebastian/phpcpd": "1.4.*@stable", 1062 | "squizlabs/php_codesniffer": "1.4.*@stable", 1063 | "theseer/fdomdocument": "dev-master" 1064 | }, 1065 | "suggest": { 1066 | "symfony/http-kernel": "Allows Symfony integration" 1067 | }, 1068 | "bin": [ 1069 | "composer/bin/coveralls" 1070 | ], 1071 | "type": "library", 1072 | "extra": { 1073 | "branch-alias": { 1074 | "dev-master": "0.7-dev" 1075 | } 1076 | }, 1077 | "autoload": { 1078 | "psr-0": { 1079 | "Satooshi\\Component": "src/", 1080 | "Satooshi\\Bundle": "src/" 1081 | } 1082 | }, 1083 | "notification-url": "https://packagist.org/downloads/", 1084 | "license": [ 1085 | "MIT" 1086 | ], 1087 | "authors": [ 1088 | { 1089 | "name": "Kitamura Satoshi", 1090 | "email": "with.no.parachute@gmail.com", 1091 | "homepage": "https://www.facebook.com/satooshi.jp" 1092 | } 1093 | ], 1094 | "description": "PHP client library for Coveralls API", 1095 | "homepage": "https://github.com/satooshi/php-coveralls", 1096 | "keywords": [ 1097 | "ci", 1098 | "coverage", 1099 | "github", 1100 | "test" 1101 | ], 1102 | "time": "2014-11-11 15:35:34" 1103 | }, 1104 | { 1105 | "name": "sebastian/comparator", 1106 | "version": "1.2.0", 1107 | "source": { 1108 | "type": "git", 1109 | "url": "https://github.com/sebastianbergmann/comparator.git", 1110 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 1111 | }, 1112 | "dist": { 1113 | "type": "zip", 1114 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 1115 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 1116 | "shasum": "" 1117 | }, 1118 | "require": { 1119 | "php": ">=5.3.3", 1120 | "sebastian/diff": "~1.2", 1121 | "sebastian/exporter": "~1.2" 1122 | }, 1123 | "require-dev": { 1124 | "phpunit/phpunit": "~4.4" 1125 | }, 1126 | "type": "library", 1127 | "extra": { 1128 | "branch-alias": { 1129 | "dev-master": "1.2.x-dev" 1130 | } 1131 | }, 1132 | "autoload": { 1133 | "classmap": [ 1134 | "src/" 1135 | ] 1136 | }, 1137 | "notification-url": "https://packagist.org/downloads/", 1138 | "license": [ 1139 | "BSD-3-Clause" 1140 | ], 1141 | "authors": [ 1142 | { 1143 | "name": "Jeff Welch", 1144 | "email": "whatthejeff@gmail.com" 1145 | }, 1146 | { 1147 | "name": "Volker Dusch", 1148 | "email": "github@wallbash.com" 1149 | }, 1150 | { 1151 | "name": "Bernhard Schussek", 1152 | "email": "bschussek@2bepublished.at" 1153 | }, 1154 | { 1155 | "name": "Sebastian Bergmann", 1156 | "email": "sebastian@phpunit.de" 1157 | } 1158 | ], 1159 | "description": "Provides the functionality to compare PHP values for equality", 1160 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1161 | "keywords": [ 1162 | "comparator", 1163 | "compare", 1164 | "equality" 1165 | ], 1166 | "time": "2015-07-26 15:48:44" 1167 | }, 1168 | { 1169 | "name": "sebastian/diff", 1170 | "version": "1.3.0", 1171 | "source": { 1172 | "type": "git", 1173 | "url": "https://github.com/sebastianbergmann/diff.git", 1174 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" 1175 | }, 1176 | "dist": { 1177 | "type": "zip", 1178 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", 1179 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", 1180 | "shasum": "" 1181 | }, 1182 | "require": { 1183 | "php": ">=5.3.3" 1184 | }, 1185 | "require-dev": { 1186 | "phpunit/phpunit": "~4.2" 1187 | }, 1188 | "type": "library", 1189 | "extra": { 1190 | "branch-alias": { 1191 | "dev-master": "1.3-dev" 1192 | } 1193 | }, 1194 | "autoload": { 1195 | "classmap": [ 1196 | "src/" 1197 | ] 1198 | }, 1199 | "notification-url": "https://packagist.org/downloads/", 1200 | "license": [ 1201 | "BSD-3-Clause" 1202 | ], 1203 | "authors": [ 1204 | { 1205 | "name": "Kore Nordmann", 1206 | "email": "mail@kore-nordmann.de" 1207 | }, 1208 | { 1209 | "name": "Sebastian Bergmann", 1210 | "email": "sebastian@phpunit.de" 1211 | } 1212 | ], 1213 | "description": "Diff implementation", 1214 | "homepage": "http://www.github.com/sebastianbergmann/diff", 1215 | "keywords": [ 1216 | "diff" 1217 | ], 1218 | "time": "2015-02-22 15:13:53" 1219 | }, 1220 | { 1221 | "name": "sebastian/environment", 1222 | "version": "1.3.2", 1223 | "source": { 1224 | "type": "git", 1225 | "url": "https://github.com/sebastianbergmann/environment.git", 1226 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" 1227 | }, 1228 | "dist": { 1229 | "type": "zip", 1230 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", 1231 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", 1232 | "shasum": "" 1233 | }, 1234 | "require": { 1235 | "php": ">=5.3.3" 1236 | }, 1237 | "require-dev": { 1238 | "phpunit/phpunit": "~4.4" 1239 | }, 1240 | "type": "library", 1241 | "extra": { 1242 | "branch-alias": { 1243 | "dev-master": "1.3.x-dev" 1244 | } 1245 | }, 1246 | "autoload": { 1247 | "classmap": [ 1248 | "src/" 1249 | ] 1250 | }, 1251 | "notification-url": "https://packagist.org/downloads/", 1252 | "license": [ 1253 | "BSD-3-Clause" 1254 | ], 1255 | "authors": [ 1256 | { 1257 | "name": "Sebastian Bergmann", 1258 | "email": "sebastian@phpunit.de" 1259 | } 1260 | ], 1261 | "description": "Provides functionality to handle HHVM/PHP environments", 1262 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1263 | "keywords": [ 1264 | "Xdebug", 1265 | "environment", 1266 | "hhvm" 1267 | ], 1268 | "time": "2015-08-03 06:14:51" 1269 | }, 1270 | { 1271 | "name": "sebastian/exporter", 1272 | "version": "1.2.1", 1273 | "source": { 1274 | "type": "git", 1275 | "url": "https://github.com/sebastianbergmann/exporter.git", 1276 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e" 1277 | }, 1278 | "dist": { 1279 | "type": "zip", 1280 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", 1281 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e", 1282 | "shasum": "" 1283 | }, 1284 | "require": { 1285 | "php": ">=5.3.3", 1286 | "sebastian/recursion-context": "~1.0" 1287 | }, 1288 | "require-dev": { 1289 | "phpunit/phpunit": "~4.4" 1290 | }, 1291 | "type": "library", 1292 | "extra": { 1293 | "branch-alias": { 1294 | "dev-master": "1.2.x-dev" 1295 | } 1296 | }, 1297 | "autoload": { 1298 | "classmap": [ 1299 | "src/" 1300 | ] 1301 | }, 1302 | "notification-url": "https://packagist.org/downloads/", 1303 | "license": [ 1304 | "BSD-3-Clause" 1305 | ], 1306 | "authors": [ 1307 | { 1308 | "name": "Jeff Welch", 1309 | "email": "whatthejeff@gmail.com" 1310 | }, 1311 | { 1312 | "name": "Volker Dusch", 1313 | "email": "github@wallbash.com" 1314 | }, 1315 | { 1316 | "name": "Bernhard Schussek", 1317 | "email": "bschussek@2bepublished.at" 1318 | }, 1319 | { 1320 | "name": "Sebastian Bergmann", 1321 | "email": "sebastian@phpunit.de" 1322 | }, 1323 | { 1324 | "name": "Adam Harvey", 1325 | "email": "aharvey@php.net" 1326 | } 1327 | ], 1328 | "description": "Provides the functionality to export PHP variables for visualization", 1329 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1330 | "keywords": [ 1331 | "export", 1332 | "exporter" 1333 | ], 1334 | "time": "2015-06-21 07:55:53" 1335 | }, 1336 | { 1337 | "name": "sebastian/global-state", 1338 | "version": "1.1.1", 1339 | "source": { 1340 | "type": "git", 1341 | "url": "https://github.com/sebastianbergmann/global-state.git", 1342 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1343 | }, 1344 | "dist": { 1345 | "type": "zip", 1346 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1347 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1348 | "shasum": "" 1349 | }, 1350 | "require": { 1351 | "php": ">=5.3.3" 1352 | }, 1353 | "require-dev": { 1354 | "phpunit/phpunit": "~4.2" 1355 | }, 1356 | "suggest": { 1357 | "ext-uopz": "*" 1358 | }, 1359 | "type": "library", 1360 | "extra": { 1361 | "branch-alias": { 1362 | "dev-master": "1.0-dev" 1363 | } 1364 | }, 1365 | "autoload": { 1366 | "classmap": [ 1367 | "src/" 1368 | ] 1369 | }, 1370 | "notification-url": "https://packagist.org/downloads/", 1371 | "license": [ 1372 | "BSD-3-Clause" 1373 | ], 1374 | "authors": [ 1375 | { 1376 | "name": "Sebastian Bergmann", 1377 | "email": "sebastian@phpunit.de" 1378 | } 1379 | ], 1380 | "description": "Snapshotting of global state", 1381 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1382 | "keywords": [ 1383 | "global state" 1384 | ], 1385 | "time": "2015-10-12 03:26:01" 1386 | }, 1387 | { 1388 | "name": "sebastian/recursion-context", 1389 | "version": "1.0.1", 1390 | "source": { 1391 | "type": "git", 1392 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1393 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba" 1394 | }, 1395 | "dist": { 1396 | "type": "zip", 1397 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba", 1398 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba", 1399 | "shasum": "" 1400 | }, 1401 | "require": { 1402 | "php": ">=5.3.3" 1403 | }, 1404 | "require-dev": { 1405 | "phpunit/phpunit": "~4.4" 1406 | }, 1407 | "type": "library", 1408 | "extra": { 1409 | "branch-alias": { 1410 | "dev-master": "1.0.x-dev" 1411 | } 1412 | }, 1413 | "autoload": { 1414 | "classmap": [ 1415 | "src/" 1416 | ] 1417 | }, 1418 | "notification-url": "https://packagist.org/downloads/", 1419 | "license": [ 1420 | "BSD-3-Clause" 1421 | ], 1422 | "authors": [ 1423 | { 1424 | "name": "Jeff Welch", 1425 | "email": "whatthejeff@gmail.com" 1426 | }, 1427 | { 1428 | "name": "Sebastian Bergmann", 1429 | "email": "sebastian@phpunit.de" 1430 | }, 1431 | { 1432 | "name": "Adam Harvey", 1433 | "email": "aharvey@php.net" 1434 | } 1435 | ], 1436 | "description": "Provides functionality to recursively process PHP variables", 1437 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1438 | "time": "2015-06-21 08:04:50" 1439 | }, 1440 | { 1441 | "name": "sebastian/version", 1442 | "version": "1.0.6", 1443 | "source": { 1444 | "type": "git", 1445 | "url": "https://github.com/sebastianbergmann/version.git", 1446 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1447 | }, 1448 | "dist": { 1449 | "type": "zip", 1450 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1451 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1452 | "shasum": "" 1453 | }, 1454 | "type": "library", 1455 | "autoload": { 1456 | "classmap": [ 1457 | "src/" 1458 | ] 1459 | }, 1460 | "notification-url": "https://packagist.org/downloads/", 1461 | "license": [ 1462 | "BSD-3-Clause" 1463 | ], 1464 | "authors": [ 1465 | { 1466 | "name": "Sebastian Bergmann", 1467 | "email": "sebastian@phpunit.de", 1468 | "role": "lead" 1469 | } 1470 | ], 1471 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1472 | "homepage": "https://github.com/sebastianbergmann/version", 1473 | "time": "2015-06-21 13:59:46" 1474 | }, 1475 | { 1476 | "name": "symfony/config", 1477 | "version": "v2.7.6", 1478 | "source": { 1479 | "type": "git", 1480 | "url": "https://github.com/symfony/config.git", 1481 | "reference": "831f88908b51b9ce945f5e6f402931d1ac544423" 1482 | }, 1483 | "dist": { 1484 | "type": "zip", 1485 | "url": "https://api.github.com/repos/symfony/config/zipball/831f88908b51b9ce945f5e6f402931d1ac544423", 1486 | "reference": "831f88908b51b9ce945f5e6f402931d1ac544423", 1487 | "shasum": "" 1488 | }, 1489 | "require": { 1490 | "php": ">=5.3.9", 1491 | "symfony/filesystem": "~2.3" 1492 | }, 1493 | "type": "library", 1494 | "extra": { 1495 | "branch-alias": { 1496 | "dev-master": "2.7-dev" 1497 | } 1498 | }, 1499 | "autoload": { 1500 | "psr-4": { 1501 | "Symfony\\Component\\Config\\": "" 1502 | } 1503 | }, 1504 | "notification-url": "https://packagist.org/downloads/", 1505 | "license": [ 1506 | "MIT" 1507 | ], 1508 | "authors": [ 1509 | { 1510 | "name": "Fabien Potencier", 1511 | "email": "fabien@symfony.com" 1512 | }, 1513 | { 1514 | "name": "Symfony Community", 1515 | "homepage": "https://symfony.com/contributors" 1516 | } 1517 | ], 1518 | "description": "Symfony Config Component", 1519 | "homepage": "https://symfony.com", 1520 | "time": "2015-10-11 09:39:48" 1521 | }, 1522 | { 1523 | "name": "symfony/console", 1524 | "version": "v2.7.6", 1525 | "source": { 1526 | "type": "git", 1527 | "url": "https://github.com/symfony/console.git", 1528 | "reference": "5efd632294c8320ea52492db22292ff853a43766" 1529 | }, 1530 | "dist": { 1531 | "type": "zip", 1532 | "url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766", 1533 | "reference": "5efd632294c8320ea52492db22292ff853a43766", 1534 | "shasum": "" 1535 | }, 1536 | "require": { 1537 | "php": ">=5.3.9" 1538 | }, 1539 | "require-dev": { 1540 | "psr/log": "~1.0", 1541 | "symfony/event-dispatcher": "~2.1", 1542 | "symfony/process": "~2.1" 1543 | }, 1544 | "suggest": { 1545 | "psr/log": "For using the console logger", 1546 | "symfony/event-dispatcher": "", 1547 | "symfony/process": "" 1548 | }, 1549 | "type": "library", 1550 | "extra": { 1551 | "branch-alias": { 1552 | "dev-master": "2.7-dev" 1553 | } 1554 | }, 1555 | "autoload": { 1556 | "psr-4": { 1557 | "Symfony\\Component\\Console\\": "" 1558 | } 1559 | }, 1560 | "notification-url": "https://packagist.org/downloads/", 1561 | "license": [ 1562 | "MIT" 1563 | ], 1564 | "authors": [ 1565 | { 1566 | "name": "Fabien Potencier", 1567 | "email": "fabien@symfony.com" 1568 | }, 1569 | { 1570 | "name": "Symfony Community", 1571 | "homepage": "https://symfony.com/contributors" 1572 | } 1573 | ], 1574 | "description": "Symfony Console Component", 1575 | "homepage": "https://symfony.com", 1576 | "time": "2015-10-20 14:38:46" 1577 | }, 1578 | { 1579 | "name": "symfony/event-dispatcher", 1580 | "version": "v2.7.6", 1581 | "source": { 1582 | "type": "git", 1583 | "url": "https://github.com/symfony/event-dispatcher.git", 1584 | "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8" 1585 | }, 1586 | "dist": { 1587 | "type": "zip", 1588 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8", 1589 | "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8", 1590 | "shasum": "" 1591 | }, 1592 | "require": { 1593 | "php": ">=5.3.9" 1594 | }, 1595 | "require-dev": { 1596 | "psr/log": "~1.0", 1597 | "symfony/config": "~2.0,>=2.0.5", 1598 | "symfony/dependency-injection": "~2.6", 1599 | "symfony/expression-language": "~2.6", 1600 | "symfony/stopwatch": "~2.3" 1601 | }, 1602 | "suggest": { 1603 | "symfony/dependency-injection": "", 1604 | "symfony/http-kernel": "" 1605 | }, 1606 | "type": "library", 1607 | "extra": { 1608 | "branch-alias": { 1609 | "dev-master": "2.7-dev" 1610 | } 1611 | }, 1612 | "autoload": { 1613 | "psr-4": { 1614 | "Symfony\\Component\\EventDispatcher\\": "" 1615 | } 1616 | }, 1617 | "notification-url": "https://packagist.org/downloads/", 1618 | "license": [ 1619 | "MIT" 1620 | ], 1621 | "authors": [ 1622 | { 1623 | "name": "Fabien Potencier", 1624 | "email": "fabien@symfony.com" 1625 | }, 1626 | { 1627 | "name": "Symfony Community", 1628 | "homepage": "https://symfony.com/contributors" 1629 | } 1630 | ], 1631 | "description": "Symfony EventDispatcher Component", 1632 | "homepage": "https://symfony.com", 1633 | "time": "2015-10-11 09:39:48" 1634 | }, 1635 | { 1636 | "name": "symfony/filesystem", 1637 | "version": "v2.7.6", 1638 | "source": { 1639 | "type": "git", 1640 | "url": "https://github.com/symfony/filesystem.git", 1641 | "reference": "56fd6df73be859323ff97418d97edc1d756df6df" 1642 | }, 1643 | "dist": { 1644 | "type": "zip", 1645 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/56fd6df73be859323ff97418d97edc1d756df6df", 1646 | "reference": "56fd6df73be859323ff97418d97edc1d756df6df", 1647 | "shasum": "" 1648 | }, 1649 | "require": { 1650 | "php": ">=5.3.9" 1651 | }, 1652 | "type": "library", 1653 | "extra": { 1654 | "branch-alias": { 1655 | "dev-master": "2.7-dev" 1656 | } 1657 | }, 1658 | "autoload": { 1659 | "psr-4": { 1660 | "Symfony\\Component\\Filesystem\\": "" 1661 | } 1662 | }, 1663 | "notification-url": "https://packagist.org/downloads/", 1664 | "license": [ 1665 | "MIT" 1666 | ], 1667 | "authors": [ 1668 | { 1669 | "name": "Fabien Potencier", 1670 | "email": "fabien@symfony.com" 1671 | }, 1672 | { 1673 | "name": "Symfony Community", 1674 | "homepage": "https://symfony.com/contributors" 1675 | } 1676 | ], 1677 | "description": "Symfony Filesystem Component", 1678 | "homepage": "https://symfony.com", 1679 | "time": "2015-10-18 20:23:18" 1680 | }, 1681 | { 1682 | "name": "symfony/stopwatch", 1683 | "version": "v2.7.6", 1684 | "source": { 1685 | "type": "git", 1686 | "url": "https://github.com/symfony/stopwatch.git", 1687 | "reference": "f8ab957c17e4b85a73c4df03bdf94ee597f2bd55" 1688 | }, 1689 | "dist": { 1690 | "type": "zip", 1691 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f8ab957c17e4b85a73c4df03bdf94ee597f2bd55", 1692 | "reference": "f8ab957c17e4b85a73c4df03bdf94ee597f2bd55", 1693 | "shasum": "" 1694 | }, 1695 | "require": { 1696 | "php": ">=5.3.9" 1697 | }, 1698 | "type": "library", 1699 | "extra": { 1700 | "branch-alias": { 1701 | "dev-master": "2.7-dev" 1702 | } 1703 | }, 1704 | "autoload": { 1705 | "psr-4": { 1706 | "Symfony\\Component\\Stopwatch\\": "" 1707 | } 1708 | }, 1709 | "notification-url": "https://packagist.org/downloads/", 1710 | "license": [ 1711 | "MIT" 1712 | ], 1713 | "authors": [ 1714 | { 1715 | "name": "Fabien Potencier", 1716 | "email": "fabien@symfony.com" 1717 | }, 1718 | { 1719 | "name": "Symfony Community", 1720 | "homepage": "https://symfony.com/contributors" 1721 | } 1722 | ], 1723 | "description": "Symfony Stopwatch Component", 1724 | "homepage": "https://symfony.com", 1725 | "time": "2015-10-12 12:42:24" 1726 | }, 1727 | { 1728 | "name": "symfony/yaml", 1729 | "version": "v2.7.6", 1730 | "source": { 1731 | "type": "git", 1732 | "url": "https://github.com/symfony/yaml.git", 1733 | "reference": "eca9019c88fbe250164affd107bc8057771f3f4d" 1734 | }, 1735 | "dist": { 1736 | "type": "zip", 1737 | "url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d", 1738 | "reference": "eca9019c88fbe250164affd107bc8057771f3f4d", 1739 | "shasum": "" 1740 | }, 1741 | "require": { 1742 | "php": ">=5.3.9" 1743 | }, 1744 | "type": "library", 1745 | "extra": { 1746 | "branch-alias": { 1747 | "dev-master": "2.7-dev" 1748 | } 1749 | }, 1750 | "autoload": { 1751 | "psr-4": { 1752 | "Symfony\\Component\\Yaml\\": "" 1753 | } 1754 | }, 1755 | "notification-url": "https://packagist.org/downloads/", 1756 | "license": [ 1757 | "MIT" 1758 | ], 1759 | "authors": [ 1760 | { 1761 | "name": "Fabien Potencier", 1762 | "email": "fabien@symfony.com" 1763 | }, 1764 | { 1765 | "name": "Symfony Community", 1766 | "homepage": "https://symfony.com/contributors" 1767 | } 1768 | ], 1769 | "description": "Symfony Yaml Component", 1770 | "homepage": "https://symfony.com", 1771 | "time": "2015-10-11 09:39:48" 1772 | } 1773 | ], 1774 | "aliases": [], 1775 | "minimum-stability": "stable", 1776 | "stability-flags": { 1777 | "satooshi/php-coveralls": 20 1778 | }, 1779 | "prefer-stable": false, 1780 | "prefer-lowest": false, 1781 | "platform": { 1782 | "php": ">=5.4.4" 1783 | }, 1784 | "platform-dev": [] 1785 | } 1786 | -------------------------------------------------------------------------------- /composer.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ortic/css2scss/30274f599ac26f2e1fe24f1de49704742d08b9de/composer.phar -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | getScss(); 13 | -------------------------------------------------------------------------------- /phpunit.php: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Css2Scss.php: -------------------------------------------------------------------------------- 1 | cssContent = $cssContent; 57 | $this->parser = new \CssParser($this->cssContent); 58 | } 59 | 60 | /** 61 | * Iterates through all tokens and extracts the values into variables 62 | */ 63 | protected function extractVariables() 64 | { 65 | $properties = ['color', 'font-family']; 66 | foreach ($properties as $property) { 67 | $this->variables[$property] = []; 68 | } 69 | 70 | foreach ($this->tokens as $token) { 71 | if ($token instanceof \CssRulesetDeclarationToken && in_array($token->Property, $properties)) { 72 | if (!array_key_exists($token->Value, $this->variables[$token->Property])) { 73 | $this->variables[$token->Property][$token->Value] = $token->Property . '_' . (count($this->variables[$token->Property]) + 1); 74 | 75 | } 76 | $token->Value = '$' . $this->variables[$token->Property][$token->Value]; 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Returns a string containing all variables to be printed in the output 83 | * 84 | * @return string 85 | */ 86 | protected function getVariables() 87 | { 88 | $return = ''; 89 | foreach ($this->variables as $properties) { 90 | foreach ($properties as $variable => $property) { 91 | $return .= "\${$property}: {$variable};\n"; 92 | } 93 | } 94 | $return .= "\n"; 95 | return $return; 96 | } 97 | 98 | /** 99 | * Returns a string containing the SCSS content matching the CSS input 100 | * @return string 101 | */ 102 | public function getScss($extractVariables = false) 103 | { 104 | $this->tokens = $this->parser->getTokens(); 105 | 106 | // extract variables 107 | if ($extractVariables) { 108 | $this->extractVariables(); 109 | } 110 | 111 | $this->buildNestedTree(); 112 | 113 | $return = ''; 114 | 115 | // print variables 116 | if ($extractVariables) { 117 | $return .= $this->getVariables(); 118 | } 119 | 120 | foreach ($this->scssTree as $node) { 121 | // @TODO this format method shouldn't be in this class.. 122 | $return .= $this->ruleSetList->formatTokenAsScss($node) . "\n"; 123 | } 124 | 125 | $return .= $this->ruleSetList->scssify(); 126 | 127 | return $return; 128 | } 129 | 130 | /** 131 | * Build a nested tree based on the flat CSS tokens 132 | */ 133 | protected function buildNestedTree() 134 | { 135 | // this variable is true, if we're within a ruleset, e.g. p { .. here .. } 136 | // we have to normalize them 137 | $withinRulset = false; 138 | $ruleSet = null; 139 | $this->ruleSetList = new ScssRuleList(); 140 | 141 | foreach ($this->tokens as $token) { 142 | // we have to skip some tokens, their information is redundant 143 | if ($token instanceof \CssAtMediaStartToken || 144 | $token instanceof \CssAtMediaEndToken 145 | ) { 146 | continue; 147 | } 148 | 149 | // we have to build a hierarchy with CssRulesetStartToken, CssRulesetEndToken 150 | if ($token instanceof \CssRulesetStartToken) { 151 | $withinRulset = true; 152 | $ruleSet = new ScssRule($token->Selectors); 153 | } elseif ($token instanceof \CssRulesetEndToken) { 154 | $withinRulset = false; 155 | if ($ruleSet) { 156 | $this->ruleSetList->addRule($ruleSet); 157 | } 158 | $ruleSet = null; 159 | } else { 160 | // as long as we're in a ruleset, we're adding all token to a custom array 161 | // this will be lessified once we've found CssRulesetEndToken and then added 162 | // to the actual $lessTree variable 163 | if ($withinRulset) { 164 | $ruleSet->addToken($token); 165 | } else { 166 | $this->scssTree[] = $token; 167 | } 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/tokens/ScssRule.php: -------------------------------------------------------------------------------- 1 | selectors = $selectors; 20 | } 21 | 22 | /** 23 | * Add new node to rule 24 | * @param $token 25 | */ 26 | public function addToken($token) 27 | { 28 | $this->tokens[] = $token; 29 | } 30 | 31 | /** 32 | * Returns the list of selectors (e.g. #logo img) 33 | * @return array 34 | */ 35 | public function getSelectors() 36 | { 37 | return $this->selectors; 38 | } 39 | 40 | /** 41 | * Returns a list of tokens/nodes for the current selector 42 | * @return array 43 | */ 44 | public function getTokens() 45 | { 46 | return $this->tokens; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/tokens/ScssRuleList.php: -------------------------------------------------------------------------------- 1 | list[] = $rule; 20 | } 21 | 22 | /** 23 | * Build and returns a tree for the CSS input 24 | * @return array 25 | */ 26 | protected function getTree() 27 | { 28 | $output = array(); 29 | 30 | foreach ($this->list as $ruleSet) { 31 | $selectors = $ruleSet->getSelectors(); 32 | 33 | foreach ($ruleSet->getTokens() as $token) { 34 | if(is_object($token) && get_class($token) === 'CssCommentToken') continue; 35 | 36 | $this->parseTreeNode($output, $selectors, $token); 37 | } 38 | } 39 | return $output; 40 | } 41 | 42 | /** 43 | * Add support for direct descendants operator by aligning the spaces properly. 44 | * the code below supports "html >p" since we split by spaces. A selector "html > p" would cause an 45 | * additional tree level, we therefore normalize them with the two lines below. 46 | * 47 | * @param string $selector 48 | * @return string 49 | */ 50 | protected function parseDirectDescendants($selector) 51 | { 52 | $selector = str_replace('> ', '>', $selector); 53 | $selector = str_replace('>', ' >', $selector); 54 | return $selector; 55 | } 56 | 57 | /** 58 | * Support for pseudo classes by adding a space before ":" and also "&" to let scss know that there 59 | * shouldn't be a space when concatenating the nested selectors to a single css rule. We have to 60 | * ignore every colon if it's wrapped by :not(...) as we don't nest this in SCSS. 61 | * 62 | * @param string $selector 63 | * @return string 64 | */ 65 | protected function parsePseudoClasses($selector) 66 | { 67 | $nestedPseudo = false; 68 | $lastCharacterColon = false; 69 | $selectorOut = ''; 70 | for ($i = 0; $i < strlen($selector); $i++) { 71 | $c = $selector[$i]; 72 | 73 | // Don't parse anything between (..) and [..] 74 | $nestedPseudo = ($c === '(' || $c === '[') || $nestedPseudo; 75 | $nestedPseudo = !($c === ')' || $c === ']') && $nestedPseudo; 76 | 77 | if ($nestedPseudo === false && $c === ':' && $lastCharacterColon === false) { 78 | $selectorOut .= ' &'; 79 | $lastCharacterColon = true; 80 | } 81 | else { 82 | $lastCharacterColon = false; 83 | } 84 | 85 | $selectorOut .= $c; 86 | } 87 | return $selectorOut; 88 | } 89 | 90 | /** 91 | * Parse CSS input part into a SCSS node 92 | * @param $output 93 | * @param $selectors 94 | * @param $token 95 | */ 96 | protected function parseTreeNode(&$output, $selectors, $token) 97 | { 98 | foreach ($token->MediaTypes as $mediaType) { 99 | // make sure we're aware of our media type 100 | if (!array_key_exists($mediaType, $output)) { 101 | $output[$mediaType] = array(); 102 | } 103 | 104 | foreach ($selectors as $selector) { 105 | // add declaration token to output for each selector 106 | $currentNode = &$output[$mediaType]; 107 | 108 | $selector = $this->parseDirectDescendants($selector); 109 | $selector = $this->parsePseudoClasses($selector); 110 | 111 | // selectors like "html body" must be split into an array so we can 112 | // easily nest them 113 | $selectorPath = preg_split('[ ]', $selector, -1, PREG_SPLIT_NO_EMPTY); 114 | foreach ($selectorPath as $selectorPathItem) { 115 | if (!array_key_exists($selectorPathItem, $currentNode)) { 116 | $currentNode[$selectorPathItem] = array(); 117 | } 118 | $currentNode = &$currentNode[$selectorPathItem]; 119 | } 120 | 121 | $currentNode['@rules'][] = $this->formatTokenAsScss($token); 122 | } 123 | } 124 | } 125 | 126 | /** 127 | * Format SCSS nodes in a nicer way with indentation and proper brackets 128 | * @param $token 129 | * @param int $level 130 | * @return string 131 | */ 132 | public function formatTokenAsScss(\aCssToken $token, $level = 0) 133 | { 134 | $indentation = str_repeat("\t", $level); 135 | 136 | if ($token instanceof \CssRulesetDeclarationToken) { 137 | return $indentation . $token->Property . ": " . $token->Value . ($token->IsImportant ? " !important" : "") . ($token->IsLast ? "" : ";"); 138 | } elseif ($token instanceof \CssAtKeyframesStartToken) { 139 | return $indentation . "@" . $token->AtRuleName . " " . $token->Name . " {"; 140 | } elseif ($token instanceof \CssAtKeyframesRulesetStartToken) { 141 | return $indentation . "\t" . implode(",", $token->Selectors) . " {"; 142 | } elseif ($token instanceof \CssAtKeyframesRulesetEndToken) { 143 | return $indentation . "\t" . "}"; 144 | } elseif ($token instanceof \CssAtKeyframesRulesetDeclarationToken) { 145 | return $indentation . "\t\t" . $token->Property . ": " . $token->Value . ($token->IsImportant ? " !important" : "") . ($token->IsLast ? "" : ";"); 146 | } elseif ($token instanceof \CssAtCharsetToken) { 147 | return $indentation . "@charset " . $token->Charset . ";"; 148 | } elseif ($token instanceof \CssAtFontFaceStartToken) { 149 | return "@font-face {"; 150 | } elseif ($token instanceof \CssAtFontFaceDeclarationToken) { 151 | return $indentation . "\t" . $token->Property . ": " . $token->Value . ($token->IsImportant ? " !important" : "") . ($token->IsLast ? "" : ";"); 152 | } else { 153 | return $indentation . $token; 154 | } 155 | } 156 | 157 | protected function formatAsScss($selector, $level = 0) 158 | { 159 | $return = ''; 160 | $indentation = str_repeat("\t", $level); 161 | foreach ($selector as $nodeKey => $node) { 162 | $return .= $indentation . "{$nodeKey} {\n"; 163 | 164 | foreach ($node as $subNodeKey => $subNodes) { 165 | if ($subNodeKey === '@rules') { 166 | foreach ($subNodes as $subNode) { 167 | $return .= $indentation . "\t" . $subNode . "\n"; 168 | } 169 | } else { 170 | $return .= $this->formatAsScss(array($subNodeKey => $subNodes), $level + 1); 171 | } 172 | } 173 | 174 | $return .= $indentation . "}\n"; 175 | 176 | } 177 | return $return; 178 | } 179 | 180 | public function scssify() 181 | { 182 | $tree = $this->getTree(); 183 | $return = ''; 184 | 185 | foreach ($tree as $mediaType => $node) { 186 | if ($mediaType == 'all') { 187 | $return .= $this->formatAsScss($node); 188 | } else { 189 | $return .= "@media {$mediaType} {\n"; 190 | $return .= $this->formatAsScss($node, 1); 191 | $return .= "}\n"; 192 | } 193 | } 194 | 195 | return $return; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /tests/Css2ScssTest.php: -------------------------------------------------------------------------------- 1 | p { font-size: 12px; }', "html {\n\t>p {\n\t\tfont-size: 12px;\n\t}\n}\n", false), 19 | array('html> p { font-size: 12px; }', "html {\n\t>p {\n\t\tfont-size: 12px;\n\t}\n}\n", false), 20 | array('html>p { font-size: 12px; }', "html {\n\t>p {\n\t\tfont-size: 12px;\n\t}\n}\n", false), 21 | array('html, body { margin: 0; }', "html {\n\tmargin: 0;\n}\nbody {\n\tmargin: 0;\n}\n", false), 22 | array('a:hover { text-decoration: none; }', "a {\n\t&:hover {\n\t\ttext-decoration: none;\n\t}\n}\n", false), 23 | array('button::-moz-focus-inner { border: 0; }', "button {\n\t&::-moz-focus-inner {\n\t\tborder: 0;\n\t}\n}\n", false), 24 | array('a[href^="javascript:"]:after { border: 0; }', "a[href^=\"javascript:\"] {\n\t&:after {\n\t\tborder: 0;\n\t}\n}\n", false), 25 | array('a { color: white; }', "\$color_1: white;\n\na {\n\tcolor: \$color_1;\n}\n", true), 26 | ); 27 | } 28 | 29 | /** 30 | * @dataProvider providerSnippets 31 | */ 32 | public function testSnippets($css, $scss, $extractVariables) 33 | { 34 | $css2scssParser = new Ortic\Css2Scss\Css2Scss($css); 35 | $scssOutput = $css2scssParser->getScss($extractVariables); 36 | 37 | $scssOutput = $this->normalizeLineEndings($scssOutput); 38 | $scssContent = $this->normalizeLineEndings($scss); 39 | 40 | $this->assertEquals($scssOutput, $scssContent); 41 | } 42 | 43 | public function testParseSimpleFile() 44 | { 45 | $cssContent = <<getScss(); 130 | 131 | $scssOutput = $this->normalizeLineEndings($scssOutput); 132 | $scssContent = $this->normalizeLineEndings($scssContent); 133 | 134 | $this->assertEquals($scssOutput, $scssContent); 135 | } 136 | } 137 | --------------------------------------------------------------------------------