PK CV2<E E
composer.locknu W+A {
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "05648596310cb2e31a3d246b2361472b",
"content-hash": "9cbee9f681ef37314a8b4ed424696ce6",
"packages": [
{
"name": "henrikbjorn/lurker",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/flint/Lurker.git",
"reference": "ab45f9cefe600065cc3137a238217598d3a1d062"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/flint/Lurker/zipball/ab45f9cefe600065cc3137a238217598d3a1d062",
"reference": "ab45f9cefe600065cc3137a238217598d3a1d062",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/config": "~2.2",
"symfony/event-dispatcher": "~2.2"
},
"suggest": {
"ext-inotify": ">=0.1.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Lurker": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Yaroslav Kiliba",
"email": "om.dattaya@gmail.com"
},
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com"
},
{
"name": "Henrik Bjrnskov",
"email": "henrik@bjrnskov.dk"
}
],
"description": "Resource Watcher.",
"keywords": [
"filesystem",
"resource",
"watching"
],
"time": "2015-10-27 09:19:19"
},
{
"name": "symfony/config",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "41ee6c70758f40fa1dbf90d019ae0a66c4a09e74"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/41ee6c70758f40fa1dbf90d019ae0a66c4a09e74",
"reference": "41ee6c70758f40fa1dbf90d019ae0a66c4a09e74",
"shasum": ""
},
"require": {
"php": ">=5.3.9",
"symfony/filesystem": "~2.3|~3.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Config\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"time": "2016-01-03 15:33:41"
},
{
"name": "symfony/console",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
"reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.8|~3.0",
"symfony/process": "~2.8|~3.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/process": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2015-12-22 10:39:06"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "ee278f7c851533e58ca307f66305ccb9188aceda"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ee278f7c851533e58ca307f66305ccb9188aceda",
"reference": "ee278f7c851533e58ca307f66305ccb9188aceda",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.0,>=2.0.5|~3.0.0",
"symfony/dependency-injection": "~2.6|~3.0.0",
"symfony/expression-language": "~2.6|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2016-01-13 10:28:07"
},
{
"name": "symfony/filesystem",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/c2e59d11dccd135dc8f00ee97f34fe1de842e70c",
"reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2015-12-22 10:39:06"
},
{
"name": "symfony/finder",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "c90fabdd97e431ee19b6383999cf35334dff27da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/c90fabdd97e431ee19b6383999cf35334dff27da",
"reference": "c90fabdd97e431ee19b6383999cf35334dff27da",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2016-01-14 08:26:52"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/49ff736bd5d41f45240cec77b44967d76e0c3d25",
"reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2015-11-20 09:19:13"
},
{
"name": "symfony/process",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/f4794f1d00f0746621be3020ffbd8c5e0b217ee3",
"reference": "f4794f1d00f0746621be3020ffbd8c5e0b217ee3",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2015-12-23 11:04:02"
}
],
"packages-dev": [
{
"name": "andrewsville/php-token-reflection",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/Andrewsville/PHP-Token-Reflection.git",
"reference": "e6d0ac2baf66cdf154be34c3d2a2aa1bd4b426ee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Andrewsville/PHP-Token-Reflection/zipball/e6d0ac2baf66cdf154be34c3d2a2aa1bd4b426ee",
"reference": "e6d0ac2baf66cdf154be34c3d2a2aa1bd4b426ee",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-0": {
"TokenReflection": "./"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3"
],
"authors": [
{
"name": "Ondřej Nešpor",
"homepage": "https://github.com/andrewsville"
},
{
"name": "Jaroslav Hanslík",
"homepage": "https://github.com/kukulich"
}
],
"description": "Library emulating the PHP internal reflection using just the tokenized source code.",
"homepage": "http://andrewsville.github.com/PHP-Token-Reflection/",
"keywords": [
"library",
"reflection",
"tokenizer"
],
"time": "2014-08-06 16:37:08"
},
{
"name": "codeception/aspect-mock",
"version": "0.5.4",
"source": {
"type": "git",
"url": "https://github.com/Codeception/AspectMock.git",
"reference": "5191529dd18490a47a4cbafc100069b0dbc89040"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/AspectMock/zipball/5191529dd18490a47a4cbafc100069b0dbc89040",
"reference": "5191529dd18490a47a4cbafc100069b0dbc89040",
"shasum": ""
},
"require": {
"goaop/framework": "0.6.*",
"php": ">=5.4.0",
"symfony/finder": "~2.4"
},
"require-dev": {
"codeception/base": "~2.1",
"codeception/specify": "~0.3",
"codeception/verify": "~0.2"
},
"type": "library",
"autoload": {
"psr-0": {
"AspectMock": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"authors": [
{
"name": "Michael Bodnarchuk",
"email": "davert.php@mailican.com"
}
],
"description": "Experimental Mocking Framework powered by Aspects",
"time": "2016-01-08 12:39:13"
},
{
"name": "codeception/base",
"version": "2.1.5",
"source": {
"type": "git",
"url": "https://github.com/Codeception/base.git",
"reference": "c68232e5e780bc9d75d8878e7a606f9946a7ed63"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/base/zipball/c68232e5e780bc9d75d8878e7a606f9946a7ed63",
"reference": "c68232e5e780bc9d75d8878e7a606f9946a7ed63",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/psr7": "~1.0",
"php": ">=5.4.0",
"phpunit/phpunit": "~4.8.0",
"symfony/browser-kit": ">=2.4|<3.1",
"symfony/console": ">=2.4|<3.1",
"symfony/css-selector": ">=2.4|<3.1",
"symfony/dom-crawler": ">=2.4|<3.1",
"symfony/event-dispatcher": ">=2.4|<3.1",
"symfony/finder": ">=2.4|<3.1",
"symfony/yaml": ">=2.4|<3.1"
},
"require-dev": {
"codeception/specify": "~0.3",
"facebook/php-sdk-v4": "~4.0",
"flow/jsonpath": "~0.2",
"monolog/monolog": "~1.8",
"pda/pheanstalk": "~2.0",
"videlalvaro/php-amqplib": "~2.4"
},
"suggest": {
"codeception/phpbuiltinserver": "Extension to start and stop PHP built-in web server for your tests",
"codeception/specify": "BDD-style code blocks",
"codeception/verify": "BDD-style assertions",
"monolog/monolog": "Log test steps",
"phpseclib/phpseclib": "Extension required to use the SFTP option in the FTP Module."
},
"bin": [
"codecept"
],
"type": "library",
"extra": {
"branch-alias": []
},
"autoload": {
"psr-4": {
"Codeception\\": "src\\Codeception",
"Codeception\\Extension\\": "ext"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Bodnarchuk",
"email": "davert@mail.ua",
"homepage": "http://codegyre.com"
}
],
"description": "BDD-style testing framework",
"homepage": "http://codeception.com/",
"keywords": [
"BDD",
"TDD",
"acceptance testing",
"functional testing",
"unit testing"
],
"time": "2016-01-10 17:21:08"
},
{
"name": "codeception/verify",
"version": "0.2.7",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Verify.git",
"reference": "66e5074905f4d9590ddb805d123fe632f4baa488"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/Verify/zipball/66e5074905f4d9590ddb805d123fe632f4baa488",
"reference": "66e5074905f4d9590ddb805d123fe632f4baa488",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"type": "library",
"autoload": {
"files": [
"src/Codeception/function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"authors": [
{
"name": "Michael Bodnarchuk",
"email": "davert.php@mailican.com",
"homepage": "http://codeception.com"
}
],
"description": "BDD assertion library for PHPUnit",
"time": "2014-01-22 14:40:33"
},
{
"name": "doctrine/annotations",
"version": "v1.2.7",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
"reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
"shasum": ""
},
"require": {
"doctrine/lexer": "1.*",
"php": ">=5.3.2"
},
"require-dev": {
"doctrine/cache": "1.*",
"phpunit/phpunit": "4.*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Annotations\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Docblock Annotations Parser",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"annotations",
"docblock",
"parser"
],
"time": "2015-08-31 12:32:49"
},
{
"name": "doctrine/instantiator",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
"shasum": ""
},
"require": {
"php": ">=5.3,<8.0-DEV"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"ext-pdo": "*",
"ext-phar": "*",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://github.com/doctrine/instantiator",
"keywords": [
"constructor",
"instantiate"
],
"time": "2015-06-14 21:17:01"
},
{
"name": "doctrine/lexer",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Lexer\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"lexer",
"parser"
],
"time": "2014-09-09 13:34:57"
},
{
"name": "goaop/framework",
"version": "0.6.1",
"source": {
"type": "git",
"url": "https://github.com/goaop/framework.git",
"reference": "4411e951d06d7041e3128fefd6fe6a0a344cd1ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/goaop/framework/zipball/4411e951d06d7041e3128fefd6fe6a0a344cd1ba",
"reference": "4411e951d06d7041e3128fefd6fe6a0a344cd1ba",
"shasum": ""
},
"require": {
"andrewsville/php-token-reflection": "~1.3",
"doctrine/annotations": "~1.0",
"jakubledl/dissect": "~1.0",
"php": ">=5.3.0"
},
"require-dev": {
"symfony/console": "~2.1"
},
"suggest": {
"symfony/console": "Enables the usage of the command-line tool."
},
"bin": [
"bin/warmup"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.6-dev"
}
},
"autoload": {
"psr-4": {
"Go\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Lisachenko Alexander",
"homepage": "https://github.com/lisachenko"
}
],
"description": "Library for aspect-oriented programming in PHP.",
"homepage": "http://go.aopphp.com/",
"keywords": [
"aop",
"aspect",
"library",
"php"
],
"time": "2015-07-05 11:37:33"
},
{
"name": "guzzlehttp/psr7",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982",
"reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "PSR-7 message implementation",
"keywords": [
"http",
"message",
"stream",
"uri"
],
"time": "2015-11-03 01:34:55"
},
{
"name": "jakubledl/dissect",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/jakubledl/dissect.git",
"reference": "d3a391de31e45a247e95cef6cf58a91c05af67c4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jakubledl/dissect/zipball/d3a391de31e45a247e95cef6cf58a91c05af67c4",
"reference": "d3a391de31e45a247e95cef6cf58a91c05af67c4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/console": "~2.1"
},
"suggest": {
"symfony/console": "for the command-line tool"
},
"bin": [
"bin/dissect.php",
"bin/dissect"
],
"type": "library",
"autoload": {
"psr-0": {
"Dissect": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"unlicense"
],
"authors": [
{
"name": "Jakub Lédl",
"email": "jakubledl@gmail.com"
}
],
"description": "Lexing and parsing in pure PHP",
"homepage": "https://github.com/jakubledl/dissect",
"keywords": [
"ast",
"lexing",
"parser",
"parsing"
],
"time": "2013-01-29 21:29:14"
},
{
"name": "natxet/CssMin",
"version": "v3.0.4",
"source": {
"type": "git",
"url": "https://github.com/natxet/CssMin.git",
"reference": "92de3fe3ccb4f8298d31952490ef7d5395855c39"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/natxet/CssMin/zipball/92de3fe3ccb4f8298d31952490ef7d5395855c39",
"reference": "92de3fe3ccb4f8298d31952490ef7d5395855c39",
"shasum": ""
},
"require": {
"php": ">=5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Joe Scylla",
"email": "joe.scylla@gmail.com",
"homepage": "https://profiles.google.com/joe.scylla"
}
],
"description": "Minifying CSS",
"homepage": "http://code.google.com/p/cssmin/",
"keywords": [
"css",
"minify"
],
"time": "2015-09-25 11:13:11"
},
{
"name": "patchwork/jsqueeze",
"version": "v1.0.7",
"source": {
"type": "git",
"url": "https://github.com/tchwork/jsqueeze.git",
"reference": "f90a933213534b93e4ff3c2c3026ff7458f7721b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tchwork/jsqueeze/zipball/f90a933213534b93e4ff3c2c3026ff7458f7721b",
"reference": "f90a933213534b93e4ff3c2c3026ff7458f7721b",
"shasum": ""
},
"require": {
"php": ">=5.1.4"
},
"type": "library",
"autoload": {
"psr-0": {
"JSqueeze": "class/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"(Apache-2.0 or GPL-2.0)"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
}
],
"description": "Efficient JavaScript minification in PHP",
"homepage": "https://github.com/tchwork/jsqueeze",
"keywords": [
"compression",
"javascript",
"minification"
],
"time": "2015-03-25 10:11:08"
},
{
"name": "pear/archive_tar",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/pear/Archive_Tar.git",
"reference": "fc2937c0e5a2a1c62a378d16394893172f970064"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/fc2937c0e5a2a1c62a378d16394893172f970064",
"reference": "fc2937c0e5a2a1c62a378d16394893172f970064",
"shasum": ""
},
"require": {
"pear/pear-core-minimal": "^1.10.0alpha2",
"php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-bz2": "bz2 compression support.",
"ext-xz": "lzma2 compression support.",
"ext-zlib": "Gzip compression support."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"psr-0": {
"Archive_Tar": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"./"
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Vincent Blavet",
"email": "vincent@phpconcept.net"
},
{
"name": "Greg Beaver",
"email": "greg@chiaraquartet.net"
},
{
"name": "Michiel Rook",
"email": "mrook@php.net"
}
],
"description": "Tar file management class",
"homepage": "https://github.com/pear/Archive_Tar",
"keywords": [
"archive",
"tar"
],
"time": "2015-08-05 12:31:03"
},
{
"name": "pear/console_getopt",
"version": "v1.4.1",
"source": {
"type": "git",
"url": "https://github.com/pear/Console_Getopt.git",
"reference": "82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pear/Console_Getopt/zipball/82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f",
"reference": "82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Console": "./"
}
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"./"
],
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Greg Beaver",
"email": "cellog@php.net",
"role": "Helper"
},
{
"name": "Andrei Zmievski",
"email": "andrei@php.net",
"role": "Lead"
},
{
"name": "Stig Bakken",
"email": "stig@php.net",
"role": "Developer"
}
],
"description": "More info available on: http://pear.php.net/package/Console_Getopt",
"time": "2015-07-20 20:28:12"
},
{
"name": "pear/pear-core-minimal",
"version": "v1.10.1",
"source": {
"type": "git",
"url": "https://github.com/pear/pear-core-minimal.git",
"reference": "cae0f1ce0cb5bddb611b0a652d322905a65a5896"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/cae0f1ce0cb5bddb611b0a652d322905a65a5896",
"reference": "cae0f1ce0cb5bddb611b0a652d322905a65a5896",
"shasum": ""
},
"require": {
"pear/console_getopt": "~1.3",
"pear/pear_exception": "~1.0"
},
"replace": {
"rsky/pear-core-min": "self.version"
},
"type": "library",
"autoload": {
"psr-0": {
"": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"src/"
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Christian Weiske",
"email": "cweiske@php.net",
"role": "Lead"
}
],
"description": "Minimal set of PEAR core files to be used as composer dependency",
"time": "2015-10-17 11:41:19"
},
{
"name": "pear/pear_exception",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/pear/PEAR_Exception.git",
"reference": "8c18719fdae000b690e3912be401c76e406dd13b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/8c18719fdae000b690e3912be401c76e406dd13b",
"reference": "8c18719fdae000b690e3912be401c76e406dd13b",
"shasum": ""
},
"require": {
"php": ">=4.4.0"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"type": "class",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"PEAR": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"."
],
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Helgi Thormar",
"email": "dufuz@php.net"
},
{
"name": "Greg Beaver",
"email": "cellog@php.net"
}
],
"description": "The PEAR Exception base class.",
"homepage": "https://github.com/pear/PEAR_Exception",
"keywords": [
"exception"
],
"time": "2015-02-10 20:07:52"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"suggest": {
"dflydev/markdown": "~1.0",
"erusev/parsedown": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-0": {
"phpDocumentor": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "mike.vanriel@naenius.com"
}
],
"time": "2015-02-03 12:10:50"
},
{
"name": "phpspec/prophecy",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7",
"reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"phpdocumentor/reflection-docblock": "~2.0",
"sebastian/comparator": "~1.1"
},
"require-dev": {
"phpspec/phpspec": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"psr-0": {
"Prophecy\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
},
{
"name": "Marcello Duarte",
"email": "marcello.duarte@gmail.com"
}
],
"description": "Highly opinionated mocking framework for PHP 5.3+",
"homepage": "https://github.com/phpspec/prophecy",
"keywords": [
"Double",
"Dummy",
"fake",
"mock",
"spy",
"stub"
],
"time": "2015-08-13 10:07:40"
},
{
"name": "phpunit/php-code-coverage",
"version": "2.2.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "~1.3",
"sebastian/environment": "^1.3.2",
"sebastian/version": "~1.0"
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~4"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.2.1",
"ext-xmlwriter": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"time": "2015-10-06 15:47:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
"filesystem",
"iterator"
],
"time": "2015-06-21 13:08:43"
},
{
"name": "phpunit/php-text-template",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"time": "2015-06-21 13:50:34"
},
{
"name": "phpunit/php-timer",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
"reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
"timer"
],
"time": "2015-06-21 08:01:12"
},
{
"name": "phpunit/php-token-stream",
"version": "1.4.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
"reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Wrapper around PHP's tokenizer extension.",
"homepage": "https://github.com/sebastianbergmann/php-token-stream/",
"keywords": [
"tokenizer"
],
"time": "2015-09-15 10:49:45"
},
{
"name": "phpunit/phpunit",
"version": "4.8.21",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c",
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "~2.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": ">=1.0.6",
"phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.1",
"sebastian/diff": "~1.2",
"sebastian/environment": "~1.3",
"sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0",
"sebastian/version": "~1.0",
"symfony/yaml": "~2.1|~3.0"
},
"suggest": {
"phpunit/php-invoker": "~1.1"
},
"bin": [
"phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.8.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "https://phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"time": "2015-12-12 07:45:58"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "2.3.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": ">=5.3.3",
"phpunit/php-text-template": "~1.2",
"sebastian/exporter": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for PHPUnit",
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
],
"time": "2015-10-02 06:51:40"
},
{
"name": "psr/http-message",
"version": "1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2015-05-04 20:22:00"
},
{
"name": "sebastian/comparator",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
"reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/diff": "~1.2",
"sebastian/exporter": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides the functionality to compare PHP values for equality",
"homepage": "http://www.github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
"time": "2015-07-26 15:48:44"
},
{
"name": "sebastian/diff",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Kore Nordmann",
"email": "mail@kore-nordmann.de"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Diff implementation",
"homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
"diff"
],
"time": "2015-12-08 07:14:41"
},
{
"name": "sebastian/environment",
"version": "1.3.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "6e7133793a8e5a5714a551a8324337374be209df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df",
"reference": "6e7133793a8e5a5714a551a8324337374be209df",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
"homepage": "http://www.github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
"hhvm"
],
"time": "2015-12-02 08:37:27"
},
{
"name": "sebastian/exporter",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "7ae5513327cb536431847bcc0c10edba2701064e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
"reference": "7ae5513327cb536431847bcc0c10edba2701064e",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"time": "2015-06-21 07:55:53"
},
{
"name": "sebastian/global-state",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"suggest": {
"ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"time": "2015-10-12 03:26:01"
},
{
"name": "sebastian/recursion-context",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "913401df809e99e4f47b27cdd781f4a258d58791"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791",
"reference": "913401df809e99e4f47b27cdd781f4a258d58791",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2015-11-11 19:50:13"
},
{
"name": "sebastian/version",
"version": "1.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
"reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
"shasum": ""
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2015-06-21 13:59:46"
},
{
"name": "symfony/browser-kit",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
"reference": "334a58c0def6dfcbe4bb57c6d2a8c06c6cc77679"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/334a58c0def6dfcbe4bb57c6d2a8c06c6cc77679",
"reference": "334a58c0def6dfcbe4bb57c6d2a8c06c6cc77679",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/dom-crawler": "~2.8|~3.0"
},
"require-dev": {
"symfony/css-selector": "~2.8|~3.0",
"symfony/process": "~2.8|~3.0"
},
"suggest": {
"symfony/process": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\BrowserKit\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
"time": "2015-12-26 13:39:53"
},
{
"name": "symfony/css-selector",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3",
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\CssSelector\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jean-François Simon",
"email": "jeanfrancois.simon@sensiolabs.com"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2015-12-05 17:45:07"
},
{
"name": "symfony/dom-crawler",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d",
"reference": "7c622b0c9fb8bdb146d6dfa86c5f91dcbfdbc11d",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
"symfony/css-selector": "~2.8|~3.0"
},
"suggest": {
"symfony/css-selector": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\DomCrawler\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2015-12-26 13:42:31"
},
{
"name": "symfony/yaml",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "3df409958a646dad2bc5046c3fb671ee24a1a691"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691",
"reference": "3df409958a646dad2bc5046c3fb671ee24a1a691",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-12-26 13:39:53"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.4.0"
},
"platform-dev": []
}
PK CVxC C LICENSEnu W+A The MIT License (MIT)
Copyright (c) 2014 Codegyre developers team
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
PK CVij;z] ] robonu W+A #!/usr/bin/env php
execute();
PK CVƙ^ .travis.ymlnu W+A language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
sudo: false
cache:
directories:
- vendor
- $HOME/.composer/cache
before_script:
- 'if [ "$TRAVIS_PHP_VERSION" = "5.4" ]; then rm composer.lock; fi'
- composer install -n --prefer-source
script: "./robo test"
PK CV>D D codeception.ymlnu W+A actor: Guy
paths:
tests: tests
log: tests/_log
data: tests/_data
helpers: tests/_helpers
settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
modules:
config:
Db:
dsn: ''
user: ''
password: ''
dump: tests/_data/dump.sql
PK CV^. . RoboFile.phpnu W+A getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$methodName = $method->getName();
$getter = preg_match('/^(get|has|is)/', $methodName);
$setter = preg_match('/^(set|unset)/', $methodName);
$argPrototypeList = [];
$argNameList = [];
$needsImplementation = false;
foreach ($method->getParameters() as $arg) {
$argDescription = '$' . $arg->name;
$argNameList[] = $argDescription;
if ($arg->isOptional()) {
$argDescription = $argDescription . ' = ' . str_replace("\n", "", var_export($arg->getDefaultValue(), true));
// We will create wrapper methods for any method that
// has default parameters.
$needsImplementation = true;
}
$argPrototypeList[] = $argDescription;
}
$argPrototypeString = implode(', ', $argPrototypeList);
$argNameListString = implode(', ', $argNameList);
if ($methodName[0] != '_') {
$methodDescriptions[] = "@method $methodName($argPrototypeString)";
if ($getter) {
$immediateMethods[] = " public function $methodName($argPrototypeString)\n {\n return \$this->delegate->$methodName($argNameListString);\n }";
} elseif ($setter) {
$immediateMethods[] = " public function $methodName($argPrototypeString)\n {\n \$this->delegate->$methodName($argNameListString);\n return \$this;\n }";
} elseif ($needsImplementation) {
// Include an implementation for the wrapper method if necessary
$methodImplementations[] = " protected function _$methodName($argPrototypeString)\n {\n \$this->delegate->$methodName($argNameListString);\n }";
}
}
}
$classNameParts = explode('\\', $className);
$delegate = array_pop($classNameParts);
$delegateNamespace = implode('\\', $classNameParts);
if (empty($wrapperClassName)) {
$wrapperClassName = $delegate;
}
$replacements['{delegateNamespace}'] = $delegateNamespace;
$replacements['{delegate}'] = $delegate;
$replacements['{wrapperClassName}'] = $wrapperClassName;
$replacements['{taskname}'] = "task$delegate";
$replacements['{methodList}'] = $leadingCommentChars . implode("\n$leadingCommentChars", $methodDescriptions);
$replacements['{immediateMethods}'] = "\n\n" . implode("\n\n", $immediateMethods);
$replacements['{methodImplementations}'] = "\n\n" . implode("\n\n", $methodImplementations);
$template = file_get_contents(__DIR__ . "/GeneratedWrapper.tmpl");
$template = str_replace(array_keys($replacements), array_values($replacements), $template);
print $template;
}
public function release()
{
$this->yell("Releasing Robo");
$this->docs();
$this->taskGitStack()
->add('-A')
->commit("auto-update")
->pull()
->push()
->run();
$this->pharPublish();
$this->publish();
$this->taskGitHubRelease(\Robo\Runner::VERSION)
->uri('Codegyre/Robo')
->askDescription()
->run();
$this->versionBump();
}
public function test($args = "")
{
return $this->taskCodecept()
->args($args)
->run();
}
public function changed($addition)
{
$this->taskChangelog()
->version(\Robo\Runner::VERSION)
->change($addition)
->run();
}
public function versionBump($version = null)
{
if (!$version) {
$versionParts = explode('.', \Robo\Runner::VERSION);
$versionParts[count($versionParts)-1]++;
$version = implode('.', $versionParts);
}
$this->taskReplaceInFile(__DIR__.'/src/Runner.php')
->from("VERSION = '".\Robo\Runner::VERSION."'")
->to("VERSION = '".$version."'")
->run();
}
/**
* generate docs
*/
public function docs()
{
$collection = $this->collection();
$files = Finder::create()->files()->name('*.php')->in('src/Task');
$docs = [];
foreach ($files as $file) {
if ($file->getFileName() == 'loadTasks.php') {
continue;
}
if ($file->getFileName() == 'loadShortcuts.php') {
continue;
}
$ns = $file->getRelativePath();
if (!$ns) {
continue;
}
$class = basename(substr($file, 0, -4));
class_exists($class = "Robo\\Task\\$ns\\$class");
$docs[$ns][] = $class;
}
ksort($docs);
foreach ($docs as $ns => $tasks) {
$taskGenerator = $this->taskGenDoc("docs/tasks/$ns.md");
$taskGenerator->filterClasses(function (\ReflectionClass $r) {
return !($r->isAbstract() or $r->isTrait()) and $r->implementsInterface('Robo\Contract\TaskInterface');
})->prepend("# $ns Tasks");
sort($tasks);
foreach ($tasks as $class) {
$taskGenerator->docClass($class);
}
$taskGenerator->filterMethods(
function (\ReflectionMethod $m) {
if ($m->isConstructor() or $m->isDestructor() or $m->isStatic()) {
return false;
}
return !in_array($m->name, ['run', '', '__call', 'getCommand', 'getPrinted']) and $m->isPublic(); // methods are not documented
}
)->processClassSignature(
function ($c) {
return "## " . preg_replace('~Task$~', '', $c->getShortName()) . "\n";
}
)->processClassDocBlock(
function (\ReflectionClass $c, $doc) {
$doc = preg_replace('~@method .*?(.*?)\)~', '* `$1)` ', $doc);
$doc = str_replace('\\'.$c->getName(), '', $doc);
return $doc;
}
)->processMethodSignature(
function (\ReflectionMethod $m, $text) {
return str_replace('#### *public* ', '* `', $text) . '`';
}
)->processMethodDocBlock(
function (\ReflectionMethod $m, $text) {
return $text ? ' ' . trim(strtok($text, "\n"), "\n") : '';
}
)->addToCollection($collection);
}
$collection->run();
}
/**
* Builds a site in gh-pages branch. Uses mkdocs
*/
public function publish()
{
$current_branch = exec('git rev-parse --abbrev-ref HEAD');
$collection = $this->collection();
$this->taskGitStack()
->checkout('site')
->merge('master')
->addToCollection($collection);
$this->taskGitStack()
->checkout($current_branch)
->addAsCompletion($collection);
$this->taskFilesystemStack()
->copy('CHANGELOG.md', 'docs/changelog.md')
->addToCollection($collection);
$this->taskFilesystemStack()
->remove('docs/changelog.md')
->addAsCompletion($collection);
$this->taskExec('mkdocs gh-deploy')
->addToCollection($collection);
$collection->run();
}
public function pharBuild()
{
$collection = $this->collection();
$this->taskComposerInstall()
->noDev()
->printed(false)
->addToCollection($collection);
$packer = $this->taskPackPhar('robo.phar');
$files = Finder::create()->ignoreVCS(true)
->files()
->name('*.php')
->path('src')
->path('vendor')
->exclude('symfony/config/Tests')
->exclude('symfony/console/Tests')
->exclude('symfony/event-dispatcher/Tests')
->exclude('symfony/filesystem/Tests')
->exclude('symfony/finder/Tests')
->exclude('symfony/process/Tests')
->exclude('henrikbjorn/lurker/tests')
->in(__DIR__);
foreach ($files as $file) {
$packer->addFile($file->getRelativePathname(), $file->getRealPath());
}
$packer->addFile('robo','robo')
->executable('robo')
->addToCollection($collection);
$this->taskComposerInstall()
->printed(false)
->addToCollection($collection);
$collection->run();
}
public function pharInstall()
{
$this->taskExec('sudo cp')
->arg('robo.phar')
->arg('/usr/bin/robo')
->run();
}
public function pharPublish()
{
$this->pharBuild();
$this->_rename('robo.phar', 'robo-release.phar');
$this->taskGitStack()->checkout('gh-pages')->run();
$this->taskFilesystemStack()
->remove('robo.phar')
->rename('robo-release.phar', 'robo.phar')
->run();
$this->taskGitStack()
->add('robo.phar')
->commit('robo.phar published')
->push('origin','gh-pages')
->checkout('master')
->run();
}
public function tryWatch()
{
$this->taskWatch()->monitor(['composer.json', 'composer.lock'], function() {
$this->taskComposerUpdate()->run();
})->run();
}
public function tryInput()
{
$answer = $this->ask('how are you?');
$this->say('You are '.$answer);
$yes = $this->confirm('Do you want one more question?');
if (!$yes) return;
$lang = $this->askDefault('what is your favorite scripting language?', 'PHP');
$this->say($lang);
$pin = $this->askHidden('Ok, now tell your PIN code (it is hidden)');
$this->yell('Ha-ha, your pin code is: '.$pin);
$this->say('Bye!');
}
/**
* Test parallel execution
*/
public function tryPara()
{
$this->taskParallelExec()
->process('php ~/demos/robotests/parascript.php hey')
->process('php ~/demos/robotests/parascript.php hoy')
->process('php ~/demos/robotests/parascript.php gou')
->process('php ~/demos/robotests/parascript.php die')
->run();
}
public function tryOptbool($opts = ['silent|s' => false])
{
if (!$opts['silent']) $this->say("Hello, world");
}
public function tryServer()
{
$this->taskServer(8000)
->dir('site')
->arg('site/index.php')
->run();
}
public function tryOpenBrowser()
{
$this->taskOpenBrowser([
'http://robo.li',
'https://github.com/Codegyre/Robo'
])
->run();
}
public function tryInteractive()
{
new SomeTask();
$this->_exec('php -r "echo php_sapi_name();"');
}
public function tryError()
{
$result = $this->taskExec('ls xyzzy' . date('U'))->run();
}
public function trySuccess()
{
$result = $this->taskExec('pwd')->run();
}
}
PK CV`
composer.jsonnu W+A {
"name": "codegyre/robo",
"description": "Modern task runner",
"license": "MIT",
"authors": [
{
"name": "Davert",
"email": "davert.php@resend.cc"
}
],
"autoload":{
"psr-4":{
"Robo\\":"src"
}
},
"bin":["robo"],
"require": {
"php": ">=5.4.0",
"symfony/finder": "~2.5|~3.0",
"symfony/console": "~2.5|~3.0",
"symfony/process": "~2.5|~3.0",
"symfony/filesystem": "~2.5|~3.0",
"henrikbjorn/lurker": "~1.0"
},
"require-dev": {
"patchwork/jsqueeze": "~1.0",
"natxet/CssMin": "~3.0",
"pear/archive_tar": "~1.0",
"codeception/base": "~2.1.5",
"codeception/verify": "0.2.*",
"codeception/aspect-mock": "0.5.4"
},
"suggest": {
"pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively."
}
}
PK CVy y CHANGELOG.mdnu W+A # Changelog
#### 0.6.0
* Added `--load-from` option to make Robo start RoboFiles from other directories. Use it like `robo --load-from /path/to/where/RobFile/located`.
* Robo will not ask to create RoboFile if it does not exist, `init` command should be used.
* [ImageMinify] task added by @gabor-udvari
* [OpenBrowser] task added by @oscarotero
* [FlattenDir] task added by @gabor-udvari
* Robo Runner can easily extended for custom runner by passing RoboClass and RoboFile parameters to constructor. By @rdeutz See #232
#### 0.5.4
* [WriteToFile] Fixed by @gabor-udvari: always writing to file regardless whether any changes were made or not. This can bring the taskrunner into an inifinite loop if a replaced file is being watched.
* [Scss] task added, requires `leafo/scssphp` library to compile by @gabor-udvari
* [PhpSpec] TAP formatter added by @orls
* [Less] Added ability to set import dir for less compilers by @MAXakaWIZARD
* [Less] fixed passing closure as compiler by @pr0nbaer
* [Sass] task added by *2015-08-31*
#### 0.5.3
* [Rsync] Ability to use remote shell with identity file by @Mihailoff
* [Less] Task added by @burzum
* [PHPUnit] allow to test specific files with `files` parameter by @burzum.
* [GitStack] `tag` added by @SebSept
* [Concat] Fixing concat, it breaks some files if there is no new line. @burzum *2015-03-03-13*
* [Minify] BC fix to support Jsqueeze 1.x and 2.x @burzum *2015-03-12*
* [PHPUnit] Replace log-xml with log-junit @vkunz *2015-03-06*
* [Minify] Making it possible to pass options to the JS minification @burzum *2015-03-05*
* [CopyDir] Create destination recursively @boedah *2015-02-28*
#### 0.5.2
* [Phar] do not compress phar if more than 1000 files included (causes internal PHP error) *2015-02-24*
* _copyDir and _mirrorDir shortcuts fixed by @boedah *2015-02-24*
* [File\Write] methods replace() and regexReplace() added by @asterixcapri *2015-02-24*
* [Codecept] Allow to set custom name of coverage file raw name by @raistlin *2015-02-24*
* [Ssh] Added property `remoteDir` by @boedah *2015-02-24*
* [PhpServer] fixed passing arguments to server *2015-02-24*
#### 0.5.1
* [Exec] fixed execution of background jobs, processes persist till the end of PHP script *2015-01-27*
* [Ssh] Fixed SSH task by @Butochnikov *2015-01-27*
* [CopyDir] fixed shortcut usage by @boedah *2015-01-27*
* Added default value options for Configuration trait by @TamasBarta *2015-01-27*
#### 0.5.0
Refactored core
* All traits moved to `Robo\Common` namespace
* Interfaces moved to `Robo\Contract` namespace
* All task extend `Robo\Task\BaseTask` to use common IO.
* All classes follow PSR-4 standard
* Tasks are loaded into RoboFile with `loadTasks` trait
* One-line tasks are available as shortcuts loaded by `loadShortucts` and used like `$this->_exec('ls')`
* Robo runner is less coupled. Output can be set by `\Robo\Config::setOutput`, `RoboFile` can be changed to any provided class.
* Tasks can be used outside of Robo runner (inside a project)
* Timer for long-running tasks added
* Tasks can be globally configured (WIP) via `Robo\Config` class.
* Updated to Symfony >= 2.5
* IO methods added `askHidden`, `askDefault`, `confirm`
* TaskIO methods added `printTaskError`, `printTaskSuccess` with different formatting.
* [Docker] Tasks added
* [Gulp] Task added by @schorsch3000
#### 0.4.7
* [Minify] Task added by @Rarst. Requires additional dependencies installed *2014-12-26*
* [Help command is populated from annotation](https://github.com/Codegyre/Robo/pull/71) by @jonsa *2014-12-26*
* Allow empty values as defaults to optional options by @jonsa *2014-12-26*
* `PHP_WINDOWS_VERSION_BUILD` constant is used to check for Windows in tasks by @boedah *2014-12-26*
* [Copy][EmptyDir] Fixed infinite loop by @boedah *2014-12-26*
* [ApiGen] Task added by @drobert *2014-12-26*
* [FileSystem] Equalized `copy` and `chmod` argument to defaults by @Rarst (BC break) *2014-12-26*
* [FileSystem] Added missing umask argument to chmod() method of FileSystemStack by @Rarst
* [SemVer] Fixed file read and exit code
* [Codeception] fixed codeception coverageHtml option by @gunfrank *2014-12-26*
* [phpspec] Task added by @SebSept *2014-12-26*
* Shortcut options: if option name is like foo|f, assign f as shortcut by @jschnare *2014-12-26*
* [Rsync] Shell escape rsync exclude pattern by @boedah. Fixes #77 (BC break) *2014-12-26*
* [Npm] Task added by @AAlakkad *2014-12-26*
#### 0.4.6
* [Exec] Output from buffer is not spoiled by special chars *2014-10-17*
* [PHPUnit] detect PHPUnit on Windows or when is globally installed with Composer *2014-10-17*
* Output: added methods askDefault and confirm by @bkawakami *2014-10-17*
* [Svn] Task added by @anvi *2014-08-13*
* [Stack] added dir and printed options *2014-08-12*
* [ExecTask] now uses Executable trait with printed, dir, arg, option methods added *2014-08-12*
#### 0.4.5
* [Watch] bugfix: Watch only tracks last file if given array of files #46 *2014-08-05*
* All executable tasks can configure working directory with `dir` option
* If no value for an option is provided, assume it's a VALUE_NONE option. #47 by @pfaocle
* [Changelog] changed style *2014-06-27*
* [GenMarkDown] fixed formatting annotations *2014-06-27*
#### 0.4.4 06/05/2014
* Output can be disabled in all executable tasks by ->printed(false)
* disabled timeouts by default in ParallelExec
* better descriptions for Result output
* changed ParallelTask to display failed process in list
* Changed Output to be stored globally in Robo\Runner class
* Added **SshTask** by @boedah
* Added **RsyncTask** by @boedah
* false option added to proceess* callbacks in GenMarkDownTask to skip processing
#### 0.4.3 05/21/2014
* added `SemVer` task by **@jadb**
* `yell` output method added
* task `FileSystemStack` added
* `MirrorDirTask` added by **@devster**
* switched to Symfony Filesystem component
* options can be used to commands
* array arguments can be used in commands
#### 0.4.2 05/09/2014
* ask can now hide answers
* Trait Executable added to provide standard way for passing arguments and options
* added ComposerDumpAutoload task by **@pmcjury**
* added FileSystem task by **@jadb**
* added CommonStack metatsk to have similar interface for all stacked tasks by **@jadb**
* arguments and options can be passed into variable and used in exec task
* passing options into commands
#### 0.4.1 05/05/2014
* [BC] `taskGit` task renamed to `taskGitStack` for compatibility
* unit and functional tests added
* all command tasks now use Symfony\Process to execute them
* enabled Bower and Concat tasks
* added `printed` param to Exec task
* codeception `suite` method now returns `$this`
* timeout options added to Exec task
#### 0.4.0 04/27/2014
* Codeception task added
* PHPUnit task improved
* Bower task added by @jadb
* ParallelExec task added
* Symfony Process component used for execution
* Task descriptions taken from first line of annotations
* `CommandInterface` added to use tasks as parameters
#### 0.3.3 02/25/2014
* PHPUnit basic task
* fixed doc generation
#### 0.3.5 02/21/2014
* changed generated init template
#### 0.3.4 02/21/2014
* [PackPhar] ->executable command will remove hashbang when generated stub file
* [Git][Exec] stopOnFail option for Git and Exec stack
* [ExecStack] shortcut for executing bash commands in stack
#### 0.3.2 02/20/2014
* release process now includes phar
* phar executable method added
* git checkout added
* phar pack created
#### 0.3.0 02/11/2014
* Dynamic configuration via magic methods
* added WriteToFile task
* Result class for managing exit codes and error messages
#### 0.2.0 01/29/2014
* Merged Tasks and Traits to same file
* Added Watcher task
* Added GitHubRelease task
* Added Changelog task
* Added ReplaceInFile taskPK CV GeneratedWrapper.tmplnu W+A task{wrapperClassName}()
* ...
* ->run();
*
* // one line
* ...
*
* ?>
* ```
*
{methodList}
*/
class {wrapperClassName} extends StackBasedTask
{
protected $delegate;
public function __construct()
{
$this->{delegate} = new {delegate}();
}
protected function getDelegate()
{
return $this->delegate;
}{immediateMethods}{methodImplementations}
}
PK CV7; tests/unit.suite.ymlnu W+A # Codeception Test Suite Configuration
# suite for unit (internal) tests.
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
class_name: CodeGuy
modules:
enabled: [Asserts, CodeHelper]
PK CVS#~O O tests/cli.suite.ymlnu W+A class_name: CliGuy
modules:
enabled: [Cli, Filesystem, Asserts, CliHelper]
PK CVu=/ tests/_data/TestedRoboFile.phpnu W+A false, 'to' => null]) {
}
/**
* Calculate the fibonacci sequence between two numbers.
*
* Graphic output will look like
* +----+---+-------------+
* | | | |
* | |-+-| |
* |----+-+-+ |
* | | |
* | | |
* | | |
* +--------+-------------+
*
* @param int $start Number to start from
* @param $steps int Number of steps to perform
* @param array $opts
* @option $graphic Display the sequence graphically using cube
* representation
*/
public function fibonacci($start, $steps, $opts = ['graphic' => false])
{
}
/** Compact doc comment */
public function compact()
{
}
}
PK CV6IN9 9 tests/_data/dump.sqlnu W+A /* Replace this file with actual dump of your database */PK CV` - tests/_data/claypit/some/deeply/existing_filenu W+A some existing file
PK CV 1 tests/_data/claypit/some/deeply/nested/structu.renu W+A Just a file
PK CVbC 9 tests/_data/claypit/some_destination/deeply/existing_filenu W+A some_destination existing file
PK CVB tests/_data/claypit/box/robo.txtnu W+A HELLOROBOPK CV tests/_data/claypit/a.txtnu W+A APK CV1J tests/_data/claypit/b.txtnu W+A BPK CVxt tests/_bootstrap.phpnu W+A init([
'debug' => true,
'includePaths' => [
__DIR__.'/../src',
__DIR__.'/../vendor/symfony/process',
__DIR__.'/../vendor/symfony/console',
]
]);PK CViθNH H tests/unit/TaskInfoTest.phpnu W+A infoFor('printMe')->getDescription())
->equals('Prints something to console');
verify($this->infoFor('installMe')->getDescription())
->equals('installs something');
verify($this->infoFor('updateMe')->getDescription())
->equals('updates something');
}
public function testAnnotationName()
{
verify($this->infoFor('printMe')->getName())
->equals('print:me');
verify($this->infoFor('installMe')->getName())
->equals('install');
verify($this->infoFor('buildMultipleDatabases')->getName())
->equals('build:multiple-databases');
}
public function testParams()
{
$args = $this->infoFor('installMe')->getArguments();
verify($args)->hasKey('param1');
verify($args)->hasKey('param2');
verify($args['param2'])->equals('optional');
}
public function testOptions()
{
$args = $this->infoFor('useOptions')->getArguments();
verify($args)->hasKey('param');
verify($args)->hasntKey('options');
$opts = $this->infoFor('useOptions')->getOptions();
verify(array_keys($opts))->contains('debug');
verify(array_keys($opts))->contains('output');
verify($opts['debug'])->true();
verify($opts['output'])->false();
}
/**
* Prints something to console
*/
public function printMe() {}
/**
* Really useful method
* @name install
* @desc installs something
*/
public function installMe($param1, $param2 = 'optional') {}
/**
* Really useful method
* @description updates something
*/
public function updateMe() {}
public function useOptions($param, $options = ['debug' => true, 'output' => false]) {}
public function buildMultipleDatabases() {}
}PK CV>fĆ tests/unit/ConfigurationTest.phpnu W+A run())->equals('value-a');
$taskB = new ConfigurationTestTaskB();
verify($taskB->run())->equals('value-b');
}
}
class ConfigurationTestTaskA extends BaseTask
{
public function run()
{
return $this->getConfigValue('key');
}
}
class ConfigurationTestTaskB extends BaseTask
{
public function run()
{
return $this->getConfigValue('key');
}
}
PK CVd7g g tests/unit/OutputTest.phpnu W+A dialog = new Symfony\Component\Console\Helper\QuestionHelper;
}
public function testSay()
{
$this->say('Hello, world!');
$char = strncasecmp(PHP_OS, 'WIN', 3) == 0 ? '>' : '➜';
$this->guy->seeInOutput($char . ' Hello, world!');
}
public function testAskReply()
{
$this->expectedAnswer = 'jon';
verify($this->ask('What is your name?'))->equals('jon');
$this->guy->seeOutputEquals('? What is your name? ');
}
public function testAskMethod()
{
$this->dialog = $this->getMock('\Symfony\Component\Console\Helper\QuestionHelper', ['ask']);
$this->dialog->expects($this->once())
->method('ask');
$this->ask('What is your name?');
}
public function testAskHiddenMethod()
{
$this->dialog = $this->getMock('\Symfony\Component\Console\Helper\QuestionHelper', ['ask']);
$this->dialog->expects($this->once())
->method('ask');
$this->ask('What is your name?', true);
}
public function testYell()
{
$this->yell('Buuuu!');
$this->guy->seeInOutput('Buuuu!');
}
protected function getDialog()
{
$stream = fopen('php://memory', 'r+', false);
fputs($stream, $this->expectedAnswer);
rewind($stream);
$this->dialog->setInputStream($stream);
return $this->dialog;
}
}PK CVUfo tests/unit/RunnerTest.phpnu W+A runner = new \Robo\Runner();
}
public function testHandleError()
{
$tmpLevel = error_reporting();
$this->assertFalse($this->runner->handleError());
error_reporting(0);
$this->assertTrue($this->runner->handleError());
error_reporting($tmpLevel);
}
public function testErrorIsHandled()
{
$tmpLevel = error_reporting();
// Set error_get_last to a known state. Note that it can never be
// reset; see http://php.net/manual/en/function.error-get-last.php
@trigger_error('control');
$error_description = error_get_last();
$this->assertEquals('control', $error_description['message']);
@trigger_error('');
$error_description = error_get_last();
$this->assertEquals('', $error_description['message']);
// Set error_reporting to a non-zero value. In this instance,
// 'trigger_error' would abort our test script, so we use
// @trigger_error so that execution will continue. With our
// error handler in place, the value of error_get_last() does
// not change.
error_reporting(E_USER_ERROR);
set_error_handler(array($this->runner, 'handleError'));
@trigger_error('test error', E_USER_ERROR);
$error_description = error_get_last();
$this->assertEquals('', $error_description['message']);
// Set error_reporting to zero. Now, even 'trigger_error'
// does not abort execution. The value of error_get_last()
// still does not change.
error_reporting(0);
trigger_error('test error 2', E_USER_ERROR);
$error_description = error_get_last();
$this->assertEquals('', $error_description['message']);
error_reporting($tmpLevel);
}
}
PK CVa4X tests/unit/ResultTest.phpnu W+A 10]);
$this->guy->seeInOutput('The foo barred');
$this->guy->seeInOutput('Exit code 1');
$this->guy->seeInOutput('10s');
$this->guy->seeInOutput('[ResultDummyTask]');
$this->assertSame($task, $result->getTask());
$this->assertEquals(1, $result->getExitCode());
$this->assertEquals('The foo barred', $result->getMessage());
$this->assertEquals(['time' => 10], $result->getData());
$taskClone = $result->cloneTask();
$this->assertNotSame($task, $taskClone);
$this->assertInstanceOf('Robo\Contract\TaskInterface', $taskClone);
}
public function testArrayAccess()
{
$task = new ResultDummyTask();
$result = new Result($task, 1, 'The foo barred', ['time' => 10]);
$this->assertEquals($result['time'], 10);
}
}
class ResultDummyTask implements \Robo\Contract\TaskInterface
{
public function run()
{
}
}
PK CVV`
`
tests/unit/ApplicationTest.phpnu W+A app = new \Robo\Application('Robo', \Robo\Runner::VERSION);
$this->app->addCommandsFromClass(self::ROBOFILE);
}
public function testAllowEmptyValuesAsDefaultsToOptionalOptions()
{
$command = $this->createCommand('hello');
$yell = $command->getDefinition()->getOption('yell');
verify($yell->isValueOptional())
->equals(false);
verify($yell->getDefault())
->equals(false);
$to = $command->getDefinition()->getOption('to');
verify($to->isValueOptional())
->equals(true);
verify($to->getDefault())
->equals(null);
}
public function testCommandDocumentation()
{
$command = $this->createCommand('fibonacci');
verify($command->getDescription())
->equals('Calculate the fibonacci sequence between two numbers.');
}
public function testCommandCompactDocumentation()
{
$command = $this->createCommand('compact');
verify($command->getDescription())
->equals('Compact doc comment');
}
public function testCommandArgumentDocumentation()
{
$command = $this->createCommand('fibonacci');
$start = $command->getDefinition()->getArgument('start');
verify($start->getDescription())
->equals('Number to start from');
$steps = $command->getDefinition()->getArgument('steps');
verify($steps->getDescription())
->equals('Number of steps to perform');
}
public function testCommandOptionDocumentation()
{
$command = $this->createCommand('fibonacci');
$graphic = $command->getDefinition()->getOption('graphic');
verify($graphic->getDescription())
->equals('Display the sequence graphically using cube representation');
}
public function testCommandHelpDocumentation()
{
$command = $this->createCommand('fibonacci');
verify($command->getHelp())
->contains(' +----+---+');
}
public function testCommandNaming()
{
$this->assertNotNull($this->app->find('generate:user-avatar'));
}
protected function createCommand($name)
{
return $this->app->createCommand(new \Robo\TaskInfo(self::ROBOFILE, $name));
}
}
PK CV P P tests/unit/_bootstrap.phpnu W+A baseBower = test::double('Robo\Task\Bower\Base', [
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testBowerInstall()
{
$bower = test::double('Robo\Task\Bower\Install', ['executeCommand' => null]);
$this->taskBowerInstall('bower')->run();
$bower->verifyInvoked('executeCommand', ['bower install']);
}
public function testBowerUpdate()
{
$bower = test::double('Robo\Task\Bower\Update', ['executeCommand' => null]);
$this->taskBowerUpdate('bower')->run();
$bower->verifyInvoked('executeCommand', ['bower update']);
}
public function testBowerInstallCommand()
{
verify(
$this->taskBowerInstall('bower')->getCommand()
)->equals('bower install');
verify(
$this->taskBowerInstall('bower')->getCommand()
)->equals('bower install');
verify(
$this->taskBowerInstall('bower')
->allowRoot()
->forceLatest()
->offline()
->noDev()
->getCommand()
)->equals('bower install --allow-root --force-latest --offline --production');
}
}PK CV $ tests/unit/Task/ParallelExecTest.phpnu W+A process = test::double('Symfony\Component\Process\Process', [
'run' => false,
'start' => false,
'isRunning' => false,
'getOutput' => 'Hello world',
'getExitCode' => 0
]);
}
public function testParallelExec()
{
$result = $this->taskParallelExec()
->process('ls 1')
->process('ls 2')
->process('ls 3')
->run();
$this->process->verifyInvokedMultipleTimes('start', 3);
verify($result->getExitCode())->equals(0);
$this->guy->seeInOutput("3 processes finished");
}
}PK CVI"y/ / $ tests/unit/Task/CommandStackTest.phpnu W+A 'some-executable'
));
verify($commandStack
->exec('some-executable status')
->getCommand()
)->equals('some-executable status');
}
public function testExecStackCommandIsNotTrimmedIfHavingSameCharsAsExecutable()
{
$commandStack = Stub::make('Robo\Task\CommandStack', array(
'executable' => 'some-executable'
));
verify($commandStack
->exec('status')
->getCommand()
)->equals('some-executable status');
}
}
PK CV$ tests/unit/Task/ApiGenTest.phpnu W+A apigen = test::double('Robo\Task\ApiGen\ApiGen', [
'executeCommand' => null,
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testPHPUnitCommand()
{
// need an explicit Traversable
$skippedPaths = new \SplDoublyLinkedList();
$skippedPaths->push('a');
$skippedPaths->push('b');
// going for 'bang for the buck' here re: test converage
$task = $this->taskApiGen('apigen')
->config('./apigen.neon')
->source('src') // single string value of Traversable
->extensions('php') // single string value of List
->exclude(array('test', 'tmp')) // array value of Traversable
->skipDocPath($skippedPaths) // multi-value of Traversable
->charset(array('utf8','iso88591')) // array of List
->internal('no') // boolean as supported "no"
->php(true) // boolean as boolean
->tree('Y') // boolean as string
->debug('n');
$cmd = 'apigen --config ./apigen.neon --source src --extensions php --exclude test --exclude tmp --skip-doc-path a --skip-doc-path b --charset utf8,iso88591 --internal no --php yes --tree yes --debug no';
verify($task->getCommand())->equals($cmd);
$task->run();
$this->apigen->verifyInvoked('executeCommand', [$cmd]);
}
}
PK CV tests/unit/Task/PhpspecTest.phpnu W+A phpspec = test::double('Robo\Task\Testing\Phpspec', [
'executeCommand' => null,
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testPhpSpecRun()
{
$this->taskPhpspec('phpspec')->run();
$this->phpspec->verifyInvoked('executeCommand', ['phpspec run']);
}
public function testPHPSpecCommand()
{
$task = $this->taskPhpspec('phpspec')
->stopOnFail()
->noCodeGeneration()
->quiet()
->verbose('vv')
->noAnsi()
->noInteraction()
->format('pretty');
verify($task->getCommand())->equals('phpspec run --stop-on-failure --no-code-generation --quiet -vv --no-ansi --no-interaction --format pretty');
$task->run();
$this->phpspec->verifyInvoked('executeCommand', ['phpspec run --stop-on-failure --no-code-generation --quiet -vv --no-ansi --no-interaction --format pretty']);
}
}PK CV tests/unit/Task/SvnTest.phpnu W+A svn = test::double('Robo\Task\Vcs\SvnStack', [
'executeCommand' => new \AspectMock\Proxy\Anything(),
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testSvnStackRun()
{
$this->svn->construct()->update()->add()->run();
$this->svn->verifyInvoked('executeCommand', ['svn update && svn add']);
}
public function testSvnStackCommands()
{
verify(
$this->taskSvnStack('guest', 'foo')
->checkout('svn://server/trunk')
->update()
->add()
->commit('changed')
->getCommand()
)->equals("svn --username guest --password foo checkout svn://server/trunk && svn --username guest --password foo update && svn --username guest --password foo add && svn --username guest --password foo commit -m 'changed'");
}
}
PK CVh#f f " tests/unit/Task/CollectionTest.phpnu W+A add('a-name', $taskA)
->add('b-name', $taskB);
// We add methods of our task instances as before and
// after tasks. These methods have access to the task
// class' fields, and may modify them as needed.
$collection
->after('a-name', [$taskA, 'parenthesizer'])
->after('a-name', [$taskA, 'emphasizer'])
->after('b-name', [$taskB, 'emphasizer'])
->after('b-name', [$taskB, 'parenthesizer'])
->after('b-name', [$taskB, 'parenthesizer'], 'special-name');
$result = $collection->run();
// verify(var_export($result->getData(), true))->equals('');
// Ensure that the results have the correct key values
verify(implode(',', array_keys($result->getData())))->equals('a-name,b-name,special-name');
// Verify that all of the after tasks ran in
// the correct order.
verify($result['a-name']['a'])->equals('*(value-a)*');
verify($result['b-name']['b'])->equals('(*value-b*)');
// Note that the last after task is added with a special name;
// its results therefore show up under the name given, rather
// than being stored under the name of the task it was added after.
verify($result['special-name']['b'])->equals('((*value-b*))');
}
}
class CollectionTestTask extends BaseTask
{
protected $key;
protected $value;
public function __construct($key, $value)
{
$this->key = $key;
$this->value = $value;
}
public function run()
{
return $this->getValue();
}
protected function getValue()
{
$result = Result::success($this);
$result[$this->key] = $this->value;
return $result;
}
// Note that by returning a value with the same
// key as the result, we overwrite the value generated
// by the primary task method ('run()'). If we returned
// a result with a different key, then both values
// would appear in the result.
public function parenthesizer()
{
$this->value = "({$this->value})";
return $this->getValue();
}
public function emphasizer()
{
$this->value = "*{$this->value}*";
return $this->getValue();
}
}
PK CV^ tests/unit/Task/GitTest.phpnu W+A git = test::double('Robo\Task\Vcs\GitStack', [
'executeCommand' => new \AspectMock\Proxy\Anything(),
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testGitStackRun()
{
$this->taskGitStack('git')->stopOnFail()->add('-A')->pull()->run();
$this->git->verifyInvoked('executeCommand', ['git add -A']);
$this->git->verifyInvoked('executeCommand', ['git pull']);
$this->taskGitStack('git')->add('-A')->pull()->run();
$this->git->verifyInvoked('executeCommand', ['git add -A && git pull']);
}
public function testGitStackCommands()
{
verify(
$this->taskGitStack()
->cloneRepo('http://github.com/Codegyre/Robo')
->pull()
->add('-A')
->commit('changed')
->push()
->tag('0.6.0')
->push('origin','0.6.0')
->getCommand()
)->equals("git clone http://github.com/Codegyre/Robo && git pull && git add -A && git commit -m 'changed' && git push && git tag 0.6.0 && git push origin 0.6.0");
}
}PK CV
tests/unit/Task/SshTest.phpnu W+A taskSshExec('remote.example.com', 'user')
->exec('ls -la')
->exec('chmod g+x logs')
->getCommand()
)->equals("ssh user@remote.example.com 'ls -la && chmod g+x logs'");
}
public function testStopOnFail()
{
verify(
$this->taskSshExec('remote.example.com', 'user')
->stopOnFail(false)
->exec('one')
->exec('two')
->getCommand()
)->equals("ssh user@remote.example.com 'one ; two'");
}
/**
* Sets static configuration, then runs task without working dir, with working dir and again without.
*/
public function testWorkingDirectoryStaticConfiguration()
{
\Robo\Task\Remote\Ssh::configure('remoteDir', '/some-dir');
verify(
$this->taskSshExec('remote.example.com', 'user')
->exec('echo test')
->getCommand()
)->equals("ssh user@remote.example.com 'cd \"/some-dir\" && echo test'");
verify(
$this->taskSshExec('remote.example.com', 'user')
->remoteDir('/other-dir')
->exec('echo test')
->getCommand()
)->equals("ssh user@remote.example.com 'cd \"/other-dir\" && echo test'");
verify(
$this->taskSshExec('remote.example.com', 'user')
->exec('echo test')
->getCommand()
)->equals("ssh user@remote.example.com 'cd \"/some-dir\" && echo test'");
\Robo\Task\Remote\Ssh::configure('remoteDir', null);
verify(
$this->taskSshExec('remote.example.com', 'user')
->exec('echo test')
->getCommand()
)->equals("ssh user@remote.example.com 'echo test'");
}
}
PK CV-tFQ Q tests/unit/Task/PHPUnitTest.phpnu W+A phpunit = test::double('Robo\Task\Testing\PHPUnit', [
'executeCommand' => null,
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testPhpUnitRun()
{
$isWindows = defined('PHP_WINDOWS_VERSION_MAJOR');
$command = $isWindows ? 'call vendor/bin/phpunit' : 'vendor/bin/phpunit';
$this->taskPHPUnit()->run();
$this->phpunit->verifyInvoked('executeCommand', [$command]);
}
public function testPHPUnitCommand()
{
$task = $this->taskPHPUnit('phpunit')
->bootstrap('bootstrap.php')
->filter('Model')
->group('important')
->xml('result.xml')
->debug();
verify($task->getCommand())->equals('phpunit --bootstrap bootstrap.php --filter Model --group important --log-junit result.xml --debug');
$task->run();
$this->phpunit->verifyInvoked('executeCommand', ['phpunit --bootstrap bootstrap.php --filter Model --group important --log-junit result.xml --debug']);
}
}PK CVa F+ + tests/unit/Task/NpmTest.phpnu W+A baseNpm = test::double('Robo\Task\Npm\Base', [
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testNpmInstall()
{
$npm = test::double('Robo\Task\Npm\Install', ['executeCommand' => null]);
$this->taskNpmInstall('npm')->run();
$npm->verifyInvoked('executeCommand', ['npm install']);
}
public function testNpmUpdate()
{
$npm = test::double('Robo\Task\Npm\Update', ['executeCommand' => null]);
$this->taskNpmUpdate('npm')->run();
$npm->verifyInvoked('executeCommand', ['npm update']);
}
public function testNpmInstallCommand()
{
verify(
$this->taskNpmInstall('npm')->getCommand()
)->equals('npm install');
verify(
$this->taskNpmInstall('npm')->getCommand()
)->equals('npm install');
verify(
$this->taskNpmInstall('npm')
->noDev()
->getCommand()
)->equals('npm install --production');
}
}PK CV,v~
~
tests/unit/Task/GulpTest.phpnu W+A baseGulp = test::double('Robo\Task\Gulp\Base', [
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testGulpRun()
{
$isWindows = defined('PHP_WINDOWS_VERSION_MAJOR');
if ($isWindows) {
verify(
$this->taskGulpRun('default','gulp')->getCommand()
)->equals('gulp "default"');
verify(
$this->taskGulpRun('another','gulp')->getCommand()
)->equals('gulp "another"');
verify(
$this->taskGulpRun('anotherWith weired!("\') Chars','gulp')->getCommand()
)->equals('gulp "anotherWith weired!(\"\') Chars"');
verify(
$this->taskGulpRun('default','gulp')->silent()->getCommand()
)->equals('gulp "default" --silent');
verify(
$this->taskGulpRun('default','gulp')->noColor()->getCommand()
)->equals('gulp "default" --no-color');
verify(
$this->taskGulpRun('default','gulp')->color()->getCommand()
)->equals('gulp "default" --color');
verify(
$this->taskGulpRun('default','gulp')->simple()->getCommand()
)->equals('gulp "default" --tasks-simple');
} else {
verify(
$this->taskGulpRun('default','gulp')->getCommand()
)->equals('gulp \'default\'');
verify(
$this->taskGulpRun('another','gulp')->getCommand()
)->equals('gulp \'another\'');
verify(
$this->taskGulpRun('anotherWith weired!("\') Chars','gulp')->getCommand()
)->equals("gulp 'anotherWith weired!(\"'\\'') Chars'");
verify(
$this->taskGulpRun('default','gulp')->silent()->getCommand()
)->equals('gulp \'default\' --silent');
verify(
$this->taskGulpRun('default','gulp')->noColor()->getCommand()
)->equals('gulp \'default\' --no-color');
verify(
$this->taskGulpRun('default','gulp')->color()->getCommand()
)->equals('gulp \'default\' --color');
verify(
$this->taskGulpRun('default','gulp')->simple()->getCommand()
)->equals('gulp \'default\' --tasks-simple');
}
}
}PK CV5%L L ! tests/unit/Task/PHPServerTest.phpnu W+A process = test::double('Symfony\Component\Process\Process', [
'run' => false,
'start' => false,
'getOutput' => 'Hello world',
'getExitCode' => 0
]);
test::double('Robo\Task\Development\PhpServer', ['getOutput' => new \Symfony\Component\Console\Output\NullOutput()]);
}
public function testServerBackgroundRun()
{
$this->taskServer('8000')->background()->run();
$this->process->verifyInvoked('start');
}
public function testServerRun()
{
$this->taskServer('8000')->run();
$this->process->verifyInvoked('run');
}
public function testServerCommand()
{
if (strtolower(PHP_OS) === 'linux') {
$expectedCommand = 'exec php -S 127.0.0.1:8000 -t web';
} else {
$expectedCommand = 'php -S 127.0.0.1:8000 -t web';
}
verify(
$this->taskServer('8000')
->host('127.0.0.1')
->dir('web')
->getCommand()
)->equals($expectedCommand);
}
}
PK CV< # tests/unit/Task/CodeceptionTest.phpnu W+A command = $isWindows ? 'call vendor/bin/codecept run' : 'vendor/bin/codecept run';
$this->codecept = test::double('Robo\Task\Testing\Codecept', [
'executeCommand' => null,
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testCodeceptionCommand()
{
verify($this->taskCodecept()->getCommand())->equals($this->command);
verify(trim($this->taskCodecept('codecept.phar')->getCommand()))->equals('codecept.phar run');
}
public function testCodeceptionRun()
{
$this->taskCodecept()->run();
$this->codecept->verifyInvoked('executeCommand', [$this->command]);
}
public function testCodeceptOptions()
{
verify($this->taskCodecept('codecept')
->suite('unit')
->test('Codeception/Command')
->group('core')
->env('process1')
->coverage()
->getCommand()
)->equals('codecept run --group core --env process1 --coverage unit Codeception/Command');
verify($this->taskCodecept('codecept')
->test('tests/unit/Codeception')
->configFile('~/Codeception')
->xml('result.xml')
->html()
->getCommand()
)->equals('codecept run -c ~/Codeception --xml result.xml --html tests/unit/Codeception');
verify($this->taskCodecept()->debug()->getCommand())->contains(' --debug');
verify($this->taskCodecept()->silent()->getCommand())->contains(' --silent');
verify($this->taskCodecept()->excludeGroup('g')->getCommand())->contains(' --skip-group g');
verify($this->taskCodecept()->tap()->getCommand())->contains('--tap');
verify($this->taskCodecept()->json()->getCommand())->contains('--json');
}
}PK CVU U tests/unit/Task/SemVerTest.phpnu W+A null]);
$res = $this->taskSemVer()
->increment('major')
->prerelease('RC')
->increment('patch')
->run();
verify($res->getMessage())->equals('v1.0.1-RC.1');
$semver->verifyInvoked('dump');
}
public function testThrowsExceptionWhenSemverFileNotWriteable()
{
\PHPUnit_Framework_TestCase::setExpectedExceptionRegExp(
'Robo\Exception\TaskException',
'/Failed to write semver file./'
);
$this->taskSemVer('/.semver')
->increment('major')
->run();
}
}
PK CVqt9 9 tests/unit/Task/RsyncTest.phpnu W+A taskRsync()
->fromPath('src/')
->toHost('localhost')
->toUser('dev')
->toPath('/var/www/html/app/')
->recursive()
->excludeVcs()
->checksum()
->wholeFile()
->verbose()
->progress()
->humanReadable()
->stats()
->getCommand()
)->equals(
sprintf(
'rsync --recursive --exclude %s --exclude %s --exclude %s --checksum --whole-file --verbose --progress --human-readable --stats %s %s',
escapeshellarg('.git/'),
escapeshellarg('.svn/'),
escapeshellarg('.hg/'),
escapeshellarg('src/'),
escapeshellarg('dev@localhost:/var/www/html/app/')
)
);
verify(
$this->taskRsync()
->fromPath('src/foo bar/baz')
->toHost('localhost')
->toUser('dev')
->toPath('/var/path/with/a space')
->getCommand()
)->equals(
sprintf(
'rsync %s %s',
escapeshellarg('src/foo bar/baz'),
escapeshellarg('dev@localhost:/var/path/with/a space')
)
);
}
}
PK CVVIKF F tests/unit/Task/ComposerTest.phpnu W+A baseComposer = test::double('Robo\Task\Composer\Base', [
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
}
// tests
public function testComposerInstall()
{
$composer = test::double('Robo\Task\Composer\Install', ['executeCommand' => null]);
$this->taskComposerInstall('composer')->run();
$composer->verifyInvoked('executeCommand', ['composer install']);
$this->taskComposerInstall('composer')
->preferSource()
->run();
$composer->verifyInvoked('executeCommand', ['composer install --prefer-source']);
$this->taskComposerInstall('composer')
->optimizeAutoloader()
->run();
$composer->verifyInvoked('executeCommand', ['composer install --optimize-autoloader']);
}
public function testComposerUpdate()
{
$composer = test::double('Robo\Task\Composer\Update', ['executeCommand' => null]);
$this->taskComposerUpdate('composer')->run();
$composer->verifyInvoked('executeCommand', ['composer update']);
$this->taskComposerUpdate('composer')
->optimizeAutoloader()
->run();
$composer->verifyInvoked('executeCommand', ['composer update --optimize-autoloader']);
}
public function testComposerDumpAutoload()
{
$composer = test::double('Robo\Task\Composer\DumpAutoload', ['executeCommand' => null]);
$this->taskComposerDumpAutoload('composer')->run();
$composer->verifyInvoked('executeCommand', ['composer dump-autoload']);
$this->taskComposerDumpAutoload('composer')
->noDev()
->run();
$composer->verifyInvoked('executeCommand', ['composer dump-autoload --no-dev']);
$this->taskComposerDumpAutoload('composer')
->optimize()
->run();
$composer->verifyInvoked('executeCommand', ['composer dump-autoload --optimize']);
$this->taskComposerDumpAutoload('composer')
->optimize()
->noDev()
->run();
$composer->verifyInvoked('executeCommand', ['composer dump-autoload --optimize --no-dev']);
}
public function testComposerInstallCommand()
{
verify(
$this->taskComposerInstall('composer')->getCommand()
)->equals('composer install');
verify(
$this->taskComposerInstall('composer')
->noDev()
->preferDist()
->optimizeAutoloader()
->getCommand()
)->equals('composer install --prefer-dist --no-dev --optimize-autoloader');
}
public function testComposerUpdateCommand()
{
verify(
$this->taskComposerUpdate('composer')->getCommand()
)->equals('composer update');
verify(
$this->taskComposerUpdate('composer')
->noDev()
->preferDist()
->getCommand()
)->equals('composer update --prefer-dist --no-dev');
verify(
$this->taskComposerUpdate('composer')
->noDev()
->preferDist()
->optimizeAutoloader()
->getCommand()
)->equals('composer update --prefer-dist --no-dev --optimize-autoloader');
}
public function testComposerDumpAutoloadCommand()
{
verify(
$this->taskComposerDumpAutoload('composer')->getCommand()
)->equals('composer dump-autoload');
verify(
$this->taskComposerDumpAutoload('composer')
->noDev()
->getCommand()
)->equals('composer dump-autoload --no-dev');
verify(
$this->taskComposerDumpAutoload('composer')
->optimize()
->getCommand()
)->equals('composer dump-autoload --optimize');
verify(
$this->taskComposerDumpAutoload('composer')
->optimize()
->noDev()
->getCommand()
)->equals('composer dump-autoload --optimize --no-dev');
}
}PK CV tests/unit/Task/ExecTaskTest.phpnu W+A process = test::double('Symfony\Component\Process\Process', [
'run' => false,
'start' => false,
'getOutput' => 'Hello world',
'getExitCode' => 0
]);
test::double('Robo\Task\Base\Exec', ['getOutput' => new \Symfony\Component\Console\Output\NullOutput()]);
}
public function testExec()
{
$result = $this->taskExec('ls')->run();
$this->process->verifyInvoked('run');
verify($result->getMessage())->equals('Hello world');
verify($result->getExitCode())->equals(0);
}
public function testExecInBackground()
{
$result = $this->taskExec('ls')->background()->run();
$this->process->verifyInvoked('start');
$this->process->verifyNeverInvoked('run');
verify('exit code was not received', $result->getExitCode())->notEquals(100);
}
public function testGetCommand()
{
verify($this->taskExec('ls')->getCommand())->equals('ls');
}
public function testExecStack()
{
$this->taskExecStack()
->exec('ls')
->exec('cd /')
->exec('cd home')
->run();
$this->process->verifyInvoked('run', 3);
}
public function testExecStackCommand()
{
verify($this->taskExecStack()
->exec('ls')
->exec('cd /')
->exec('cd home')
->getCommand()
)->equals('ls && cd / && cd home');
}
};PK CV͌ . tests/unit/Common/ResourceExistenceChecker.phpnu W+A apigen = test::double('Robo\Task\ApiGen\ApiGen', [
'executeCommand' => null,
'getOutput' => new \Symfony\Component\Console\Output\NullOutput()
]);
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
$this->testDir = __DIR__ . '..' . DS . '..' . DS . 'data' . DS;
$this->testFile = $this->testDir . 'dump.sql';
}
/**
* testCheckResources
*/
public function testCheckResources()
{
$this->assertTrue($this->checkResources($this->testDir, 'dir'));
$this->assertTrue($this->checkResources([
$this->testDir,
$this->testFile
]));
}
/**
* @expectException \InvalidArgumentException
*/
public function testCheckResourcesException()
{
$this->checkResources('does not exist', 'invalid type');
}
/**
* testCheckResource
*/
public function testCheckResource()
{
$this->assertTrue($this->checkResource($this->testDir, 'dir'));
$this->assertTrue($this->checkResource($this->testDir, 'fileAndDir'));
$this->assertTrue($this->checkResource($this->testFile, 'file'));
$this->assertTrue($this->checkResource($this->testFile, 'fileAndDir'));
$this->assertFalse($this->checkResource('does-not-exist', 'dir'));
$this->assertFalse($this->checkResource('does-not-exist', 'fileAndDir'));
$this->assertFalse($this->checkResource('does-not-exist', 'file'));
$this->assertFalse($this->checkResource('does-not-exist', 'fileAndDir'));
}
/**
* testIsDir
*/
public function testIsDir()
{
$this->assertTrue($this->isDir($this->testDir));
$this->assertFalse($this->isDir('does-not-exist'));
}
/**
* testIsFile
*/
public function testIsFile()
{
$this->assertTrue($this->isFile($this->testFile));
$this->assertFalse($this->isFile($this->testDir . 'does-not-exist'));
}
}
PK CVXz z tests/cli/WriteFileCest.phpnu W+A amInPath(codecept_data_dir('sandbox'));
}
public function writeFewLines(CliGuy $I)
{
$I->wantTo('write lines with WriteToFile task');
$I->taskWriteToFile('blogpost.md')
->line('****')
->line('hello world')
->line('****')
->run();
$I->seeFileFound('blogpost.md');
$I->seeFileContentsEqual(<<taskWriteToFile('a.txt')
->append()
->line('hello world')
->run();
$I->seeFileFound('a.txt');
$I->seeFileContentsEqual(<<taskWriteToFile('a.txt')
->line('****')
->textFromFile('b.txt')
->line("C")
->run();
$I->seeFileFound('a.txt');
$I->seeFileContentsEqual(<<taskReplaceInFile('a.txt')
->from('A')
->to('B')
->run();
$I->seeFileFound('a.txt');
$I->seeFileContentsEqual('B');
}
public function replaceMultipleInFile(CliGuy $I)
{
$I->taskReplaceInFile('box/robo.txt')
->from(array('HELLO', 'ROBO'))
->to(array('Hello ', 'robo.li!'))
->run();
$I->seeFileFound('box/robo.txt');
$I->seeFileContentsEqual('Hello robo.li!');
}
}
PK CV
l! ! tests/cli/DeleteDirCept.phpnu W+A wantTo('delete dir with DeleteDirTask');
$I->amInPath(codecept_data_dir());
$I->seeFileFound('robo.txt', 'sandbox');
$I->taskDeleteDir(['sandbox/box'])
->run();
$I->dontSeeFileFound('box', 'sandbox');
$I->dontSeeFileFound('robo.txt', 'sandbox');
PK CVP#jE E tests/cli/CleanDirCept.phpnu W+A wantTo('clean dir with DeleteDirTask');
$I->amInPath(codecept_data_dir());
$I->seeFileFound('robo.txt', 'sandbox');
$I->taskCleanDir(['sandbox'])
->run();
$I->dontSeeFileFound('box', 'sandbox');
$I->dontSeeFileFound('robo.txt', 'sandbox');
$I->dontSeeFileFound('a.txt' , 'sandbox');PK CV| tests/cli/FlattenDirCept.phpnu W+A wantTo('flatten dir with FlattenDir task');
$I->amInPath(codecept_data_dir().'sandbox');
$I->taskFlattenDir([
'some/deeply/nested/*.re' => 'flattened',
'*.txt' => 'flattened'
])
->run();
$I->seeDirFound('flattened');
$I->seeFileFound('structu.re', 'flattened');
$I->seeFileFound('a.txt', 'flattened');
$I->seeFileFound('b.txt', 'flattened');
PK CV!k
tests/cli/ConcatCept.phpnu W+A wantTo('concat files using Concat Task');
$I->amInPath(codecept_data_dir() . 'sandbox');
$I->taskConcat(['a.txt', 'b.txt'])
->to('merged.txt')
->run();
$I->seeFileFound('merged.txt');
$I->seeFileContentsEqual("A\nB\n");PK CVQyJ J tests/cli/ExecCest.phpnu W+A taskExec($command)->run();
verify($res->getMessage())->contains('src');
verify($res->getMessage())->contains('codeception.yml');
}
}PK CV{
tests/cli/PackExtractCept.phpnu W+A wantTo('archive directory and then extract it again with Archive and Extract tasks');
$I->amInPath(codecept_data_dir().'sandbox');
$I->seeDirFound('some/deeply/nested');
$I->seeFileFound('structu.re', 'some/deeply/nested');
$I->seeFileFound('existing_file', 'some/deeply');
// Test a bunch of archive types that we support
foreach (['zip', 'tar', 'tar.gz', 'tar.bz2', 'tgz'] as $archiveType) {
// First, take everything from the folder 'some/deeply' and make
// an archive for it located in 'deep'
$I->taskPack("deeply.$archiveType")
->add(['deep' => 'some/deeply'])
->run();
$I->seeFileFound("deeply.$archiveType");
// We are next going to extract the archive we created, this time
// putting it into a folder called "extracted-$archiveType" (different
// for each archive type we test). We rely on the default behavior
// of our extractor to remove the top-level directory in the archive
// ("deeply").
$I->taskExtract("deeply.$archiveType")
->to("extracted-$archiveType")
->preserveTopDirectory(false) // this is the default
->run();
$I->seeDirFound("extracted-$archiveType");
$I->seeDirFound("extracted-$archiveType/nested");
$I->seeFileFound('structu.re', "extracted-$archiveType/nested");
// Next, we'll extract the same archive again, this time preserving
// the top-level folder.
$I->taskExtract("deeply.$archiveType")
->to("preserved-$archiveType")
->preserveTopDirectory()
->run();
$I->seeDirFound("preserved-$archiveType");
$I->seeDirFound("preserved-$archiveType/deep/nested");
$I->seeFileFound('structu.re', "preserved-$archiveType/deep/nested");
// Make another archive, this time composed of fanciful locations
$I->taskPack("composed.$archiveType")
->add(['a/b/existing_file' => 'some/deeply/existing_file'])
->add(['x/y/z/structu.re' => 'some/deeply/nested/structu.re'])
->run();
$I->seeFileFound("composed.$archiveType");
// Extract our composed archive, and see if the resulting file
// structure matches expectations.
$I->taskExtract("composed.$archiveType")
->to("decomposed-$archiveType")
->preserveTopDirectory()
->run();
$I->seeDirFound("decomposed-$archiveType");
$I->seeDirFound("decomposed-$archiveType/x/y/z");
$I->seeFileFound('structu.re', "decomposed-$archiveType/x/y/z");
$I->seeDirFound("decomposed-$archiveType/a/b");
$I->seeFileFound('existing_file', "decomposed-$archiveType/a/b");
}
PK CVu-nD D tests/cli/_bootstrap.phpnu W+A wantTo('copy dir recursively with CopyDir task');
$I->amInPath(codecept_data_dir() . 'sandbox');
$I->seeDirFound('some/deeply/nested');
$I->seeFileFound('structu.re', 'some/deeply/nested');
$I->taskCopyDir(['some/deeply' => 'some_destination/deeply'])
->run();
$I->seeDirFound('some_destination/deeply/nested');
$I->seeFileFound('structu.re', 'some_destination/deeply/nested');
PK CV Ӄ ( tests/cli/CopyDirOverwritesFilesCept.phpnu W+A wantTo('overwrite a file with CopyDir task');
$I->amInPath(codecept_data_dir() . 'sandbox');
$I->seeDirFound('some');
$I->seeFileFound('existing_file', 'some');
$I->taskCopyDir(['some' => 'some_destination'])
->run();
$I->seeFileFound('existing_file', 'some_destination/deeply');
$I->openFile('some_destination/deeply/existing_file');
$I->seeInThisFile('some existing file');
PK CV6 6 ! tests/cli/FileSystemStackCest.phpnu W+A amInPath(codecept_data_dir().'sandbox');
}
public function toCreateDir(CliGuy $I)
{
$I->taskFileSystemStack()
->mkdir('log')
->touch('log/error.txt')
->run();
$I->seeFileFound('log/error.txt');
}
public function toDeleteFile(CliGuy $I)
{
$I->taskFileSystemStack()
->stopOnFail()
->remove('a.txt')
->run();
$I->dontSeeFileFound('a.txt');
}
}
PK CV* tests/cli/CollectionCest.phpnu W+A amInPath(codecept_data_dir().'sandbox');
}
public function toCreateDirViaCollection(CliGuy $I)
{
// Set up a collection to add tasks to
$collection = $I->collection();
// Set up a filesystem stack, but use addToCollection() to defer execution
$I->taskFileSystemStack()
->mkdir('log')
->touch('log/error.txt')
->addToCollection($collection);
// FileSystemStack has not run yet, so file should not be found.
$I->dontSeeFileFound('log/error.txt');
// Run the task collection; now the files should be present
$collection->run();
$I->seeFileFound('log/error.txt');
$I->seeDirFound('log');
}
public function toUseATmpDirAndConfirmItIsDeleted(CliGuy $I)
{
// Set up a collection to add tasks to
$collection = $I->collection();
// Get a temporary directory to work in. Note that we get a
// name back, but the directory is not created until the task
// runs. This technically is not thread-safe, but we create
// a random name, so it is unlikely to conflict.
$tmpPath = $I->taskTmpDir()
->addToCollection($collection)
->getPath();
// We can create the temporary directory early by running
// 'runWithoutCompletion()'. n.b. if we called 'run()' at
// this point, the collection's 'complete()' method would be
// called, and the temporary directory would be deleted.
$mktmpResult = $collection->runWithoutCompletion();
$I->assertEquals($mktmpResult['path'], $tmpPath, "Tmp dir result matches accessor.");
$I->seeDirFound($tmpPath);
// Set up a filesystem stack, but use addToCollection() to defer execution
$I->taskFileSystemStack()
->mkdir("$tmpPath/tmp")
->touch("$tmpPath/tmp/error.txt")
->rename("$tmpPath/tmp", "$tmpPath/log")
->addToCollection($collection);
// Copy our tmp directory to a location that is not transient
$I->taskCopyDir([$tmpPath => 'copied'])
->addToCollection($collection);
// FileSystemStack has not run yet, so no files should be found.
$I->dontSeeFileFound("$tmpPath/tmp/error.txt");
$I->dontSeeFileFound("$tmpPath/log/error.txt");
$I->dontSeeFileFound('copied/log/error.txt');
// Run the task collection
$result = $collection->run();
$I->assertEquals(0, $result->getExitCode(), $result->getMessage());
// The file 'error.txt' should have been copied into the "copied" dir
$I->seeFileFound('copied/log/error.txt');
// $tmpPath should be deleted after $collection->run() completes.
$I->dontSeeFileFound("$tmpPath/tmp/error.txt");
$I->dontSeeFileFound("$tmpPath/log/error.txt");
}
public function toUseATmpDirAndChangeWorkingDirectory(CliGuy $I)
{
// Set up a collection to add tasks to
$collection = $I->collection();
$cwd = getcwd();
// Get a temporary directory to work in. Note that we get a
// name back, but the directory is not created until the task
// runs. This technically is not thread-safe, but we create
// a random name, so it is unlikely to conflict.
$tmpPath = $I->taskTmpDir()
->cwd()
->addToCollection($collection)
->getPath();
// Set up a filesystem stack, but use addToCollection() to defer execution.
// Note that since we used 'cwd()' above, the relative file paths
// used below will be inside the temporary directory.
$I->taskFileSystemStack()
->mkdir("log")
->touch("log/error.txt")
->addToCollection($collection);
// Copy our tmp directory to a location that is not transient
$I->taskCopyDir(['log' => "$cwd/copied2"])
->addToCollection($collection);
// FileSystemStack has not run yet, so no files should be found.
$I->dontSeeFileFound("$tmpPath/log/error.txt");
$I->dontSeeFileFound('$cwd/copied2/log/error.txt');
// Run the task collection
$result = $collection->run();
$I->assertEquals(0, $result->getExitCode(), $result->getMessage());
// The file 'error.txt' should have been copied into the "copied" dir
$I->seeFileFound("$cwd/copied2/error.txt");
// $tmpPath should be deleted after $collection->run() completes.
$I->dontSeeFileFound("$tmpPath/log/error.txt");
// Make sure that 'log' was created in the temporary directory, not
// at the current working directory.
$I->dontSeeFileFound("$cwd/log/error.txt");
// Make sure that our working directory was restored.
$finalWorkingDir = getcwd();
$I->assertEquals($cwd, $finalWorkingDir);
}
public function toCreateATmpFileAndConfirmItIsDeleted(CliGuy $I)
{
// Set up a collection to add tasks to
$collection = $I->collection();
// Write to a temporary file. Note that we can get the path
// to the tempoary file that will be created, even though the
// the file is not created until the task collecction runs.
$tmpPath = $I->taskTmpFile('tmp', '.txt')
->line("This is a test file")
->addToCollection($collection)
->getPath();
// Copy our tmp directory to a location that is not transient
$I->taskFileSystemStack()
->copy($tmpPath, 'copied.txt')
->addToCollection($collection);
// FileSystemStack has not run yet, so no files should be found.
$I->dontSeeFileFound("$tmpPath");
$I->dontSeeFileFound('copied.txt');
// Run the task collection
$result = $collection->run();
$I->assertEquals(0, $result->getExitCode(), $result->getMessage());
// The file 'copied.txt' should have been copied from the tmp file
$I->seeFileFound('copied.txt');
// $tmpPath should be deleted after $collection->run() completes.
$I->dontSeeFileFound("$tmpPath");
}
public function toUseATmpDirWithAlternateSyntax(CliGuy $I)
{
$collection = $I->collection();
// This test is equivalent to toUseATmpDirAndConfirmItIsDeleted,
// but uses a different technique to create a collection of tasks.
// We start off the same way, using addToCollection() to add our temporary
// directory task to the collection, so that we have easy access to the
// temporary directory's path via the getPath() method.
$tmpPath = $I->taskTmpDir()
->addToCollection($collection)
->getPath();
// Now, rather than creating a series of tasks and adding them
// all with addToCollection(), we will add them directly to the collection
// via the add() method.
$result = $collection->add(
[
$I->taskFileSystemStack()->mkdir("$tmpPath/log")->touch("$tmpPath/log/error.txt"),
$I->taskCopyDir([$tmpPath => 'copied3']),
]
)->run();
// The results of this operation should be the same.
$I->assertEquals(0, $result->getExitCode(), $result->getMessage());
$I->seeFileFound('copied3/log/error.txt');
$I->dontSeeFileFound("$tmpPath/log/error.txt");
}
public function toCreateATmpDirUsingShortcut(CliGuy $I)
{
// Create a temporary directory, using our function name as
// the prefix for the directory name.
$tmpPath = $I->shortcutTmpDir(__FUNCTION__);
$I->seeFileFound($tmpPath);
// Creating a temporary directory without a task collection will
// cause the temporary directory to be deleted when the program
// terminates. We can force it to clean up sooner by calling
// TransientManager::complete(); note that this deletes ALL global tmp
// directories, so this is not thread-safe! Useful in tests, though.
Temporary::complete();
$I->dontSeeFileFound($tmpPath);
}
}
PK CV' tests/cli/CopyDirCept.phpnu W+A wantTo('copy dir with CopyDir task');
$I->amInPath(codecept_data_dir().'sandbox');
$I->taskCopyDir(['box' => 'bin'])
->run();
$I->seeDirFound('bin');
$I->seeFileFound('robo.txt', 'bin');
PK CVep p tests/cli/ShortcutsCest.phpnu W+A amInPath(codecept_data_dir('sandbox'));
}
public function useTheCopyDirShortcut(CliGuy $I)
{
$I->wantTo('copy dir with _copyDir shortcut');
$I->shortcutCopyDir('box', 'bin');
$I->seeDirFound('bin');
$I->seeFileFound('robo.txt', 'bin');
}
public function useTheMirrorDirShortcut(CliGuy $I)
{
$I->wantTo('mirror dir with _mirrorDir shortcut');
$I->shortcutMirrorDir('box', 'bin');
$I->seeDirFound('bin');
$I->seeFileFound('robo.txt', 'bin');
}
}
PK CV? # tests/cli/FlattenDirParentsCept.phpnu W+A wantTo('flatten dir with FlattenDir task including parents');
$I->amInPath(codecept_data_dir().'sandbox');
$I->taskFlattenDir(['some/deeply/nested/*.re'])
->includeParents(array(1,1))
->parentDir('some')
->to('flattened')
->run();
$I->seeDirFound('flattened/deeply/nested');
$I->seeFileFound('structu.re', 'flattened/deeply/nested');
PK CV tests/_log/.gitignorenu W+A PK CVHN N tests/_helpers/CliGuy.phpnu W+A assertContains($value, TestPrinter::$output);
}
public function seeOutputEquals($value)
{
$this->assertEquals($value, TestPrinter::$output);
}
}
class TestPrinter extends NullOutput {
static $output = '';
public function writeln($messages, $type = self::OUTPUT_NORMAL)
{
static::$output .= $messages."\n";
}
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
static::$output .= $messages;
}
}PK CVeQ tests/_helpers/TestHelper.phpnu W+A assertTrue(is_dir($dir) && file_exists($dir), "Directory does not exist");
}
public function _before(\Codeception\TestCase $test) {
$this->getModule('Filesystem')->copyDir(codecept_data_dir().'claypit', codecept_data_dir().'sandbox');
Config::setOutput(new NullOutput());
}
public function _after(\Codeception\TestCase $test) {
$this->getModule('Filesystem')->deleteDir(codecept_data_dir().'sandbox');
Config::setOutput(new ConsoleOutput());
chdir(codecept_root_dir());
}
}
PK CV\Ǡ tests/_helpers/WebHelper.phpnu W+A ](http://robo.li/robo.phar)
```
wget http://robo.li/robo.phar
```
To install globally put `robo.phar` in `/usr/bin`.
```
chmod +x robo.phar && sudo mv robo.phar /usr/bin/robo
```
Now you can use it just like `robo`.
### Composer
* Add `"codegyre/robo": "*"` to `composer.json`.
* Run `composer install`
* Use `vendor/bin/robo` to execute Robo tasks.
## Usage
All tasks are defined as **public methods** in `RoboFile.php`. It can be created by running `robo`.
All protected methods in traits that start with `task` prefix are tasks and can be configured and executed in your tasks.
## Examples
The best way to learn Robo by example is to take a look into [its own RoboFile](https://github.com/Codegyre/Robo/blob/master/RoboFile.php)
or [RoboFile of Codeception project](https://github.com/Codeception/Codeception/blob/master/RoboFile.php)
Here are some snippets from them:
---
Run acceptance test with local server and selenium server started.
``` php
taskServer(8000)
->background()
->dir('web')
->run();
// running Selenium server in background
$this->taskExec('java -jar ' . $seleniumPath)
->background()
->run();
// loading Symfony Command and running with passed argument
$this->taskCommand(new \Codeception\Command\Run('run'))
->arg('suite','acceptance')
->run();
}
}
?>
```
If you execute `robo` you will see this task added to list of available task with name: `test:acceptance`.
To execute it you shoud run `robo test:acceptance`. You may change path to selenium server by passing new path as a argument:
```
robo test:acceptance "C:\Downloads\selenium.jar"
```
Using `watch` task so you can use it for running tests or building assets.
``` php
taskWatch()->monitor('composer.json', function() {
$this->taskComposerUpdate()->run();
})->run();
}
}
?>
```
---
Cleaning logs and cache
``` php
taskCleanDir([
'app/cache'
'app/logs'
])->run();
$this->taskDeleteDir([
'web/assets/tmp_uploads',
])->run();
}
?>
```
This task cleans `app/cache` and `app/logs` dirs (ignoring .gitignore and .gitkeep files)
Can be executed by running:
```
robo clean
```
----
Creating Phar archive
``` php
function buildPhar()
{
$files = Finder::create()->ignoreVCS(true)->files()->name('*.php')->in(__DIR__);
$packer = $this->taskPackPhar('robo.phar');
foreach ($files as $file) {
$packer->addFile($file->getRelativePathname(), $file->getRealPath());
}
$packer->addFile('robo','robo')
->executable('robo')
->run();
}
```
---
## We need more tasks!
Create your own tasks and send them as Pull Requests or create packages prefixed with `robo-` on Packagist.
## Credits
Follow [@robo_php](http://twitter.com/robo_php) for updates.
Created by Michael Bodnarchuk [@davert](http://twitter.com/davert).
## License
MIT
PK CVLx兩
src/Tasks.phpnu W+A fail();
// Make sure that our completion functions do not run twice.
$collection->reset();
}
/**
* Call the complete method of all of the registered objects.
*/
public static function complete()
{
// Run the collection of tasks. This will also run the
// completion tasks.
$collection = static::getCollection();
$collection->run();
// Make sure that our completion functions do not run twice.
$collection->reset();
}
}
PK CV#a* * src/Collection/CallableTask.phpnu W+A fn = $fn;
}
public function run()
{
$result = call_user_func($this->fn);
// If the function returns no result, then count it
// as a success.
if (!isset($result)) {
$result = Result::success($this);
}
// If the function returns a result, it must either return
// a \Robo\Result or an exit code. In the later case, we
// convert it to a \Robo\Result.
if (!$result instanceof Result) {
$result = new Result($this, $result);
}
return $result;
}
}
PK CVii i src/Collection/Collectable.phpnu W+A addToCollection($collection) instead of $task->run() to queue
* up this task for execution later, when $collection->run() is executed.
*/
trait Collectable
{
public function addToCollection(Collection $collection, $taskName = Collection::UNNAMEDTASK, TaskInterface $rollbackTask = null)
{
return $this->addCollectableToCollection($this, $collection, $taskName, $rollbackTask);
}
public function addAsRollback(Collection $collection)
{
$collection->rollback($this);
return $this;
}
public function addAsCompletion(Collection $collection)
{
$collection->completion($this);
return $this;
}
public function addToCollectionAndIgnoreErrors(Collection $collection, $taskName = Collection::UNNAMEDTASK)
{
return $this->addCollectableToCollection($collection->ignoreErrorsTaskWrapper($this), $collection, $taskName);
}
private function addCollectableToCollection($task, Collection $collection, $taskName = Collection::UNNAMEDTASK, TaskInterface $rollbackTask = null)
{
$collection->add($taskName, $task);
if ($rollbackTask) {
$collection->rollback($rollbackTask);
}
return $this;
}
}
PK CV2 src/Collection/TaskWrapper.phpnu W+A collection = $collection;
$this->task = ($task instanceof self) ? $task->getTask() : $task;
$this->rollbackTask = $rollbackTask;
}
public function getTask()
{
return $this->task;
}
/**
* Before running this task, register its rollback and completion
* handlers on its collection. The reason this class exists is to
* defer registration of rollback and completion tasks until 'run()' time.
*/
public function run()
{
if ($this->rollbackTask) {
$this->collection->registerRollback($this->rollbackTask);
}
if ($this->task instanceof RollbackInterface) {
$this->collection->registerRollback([$this->task, 'rollback']);
}
if ($this->task instanceof CompletionInterface) {
$this->collection->registerCompletion([$this->task, 'complete']);
}
return $this->task->run();
}
/**
* Make this wrapper object act like the class it wraps.
*/
public function __call($function, $args)
{
return call_user_func_array(array($this->task, $function), $args);
}
}
PK CV'P>٢ src/Collection/Element.phpnu W+A task = $task;
}
public function before($before, $name)
{
if ($name) {
$this->before[$name] = $before;
} else {
$this->before[] = $before;
}
}
public function after($after, $name)
{
if ($name) {
$this->after[$name] = $after;
} else {
$this->after[] = $after;
}
}
public function getBefore()
{
return $this->before;
}
public function getAfter()
{
return $this->after;
}
public function getTask()
{
return $this->task;
}
public function getTaskList()
{
return array_merge($this->before, [$this->task], $this->after);
}
}
PK CVTL| src/Collection/loadTasks.phpnu W+A collection();
* $this->taskFileSystemStack()
* ->mkdir('logs')
* ->touch('logs/.gitignore')
* ->chgrp('logs', 'www-data')
* ->symlink('/var/log/nginx/error.log', 'logs/error.log')
* ->addToCollection($collection, $this->taskDeleteDir('logs'));
* /// ... add other tasks to collection via addToCollection()
* $collection->run();
*
* ?>
* ```
*/
class Collection implements TaskInterface
{
// Unnamed tasks are assigned an arbitrary numeric index
// in the task list. Any numeric value may be used, but the
// UNNAMEDTASK constant is recommended for clarity.
const UNNAMEDTASK = 0;
protected $taskStack = [];
protected $rollbackStack = [];
protected $completionStack = [];
protected $previousResult;
/**
* Constructor.
*/
public function __construct()
{
$this->previousResult = Result::success($this);
}
/**
* Add a task or a list of tasks to our task collection. Each task
* will run via its 'run()' method once (and if) all of the tasks
* added before it complete successfully. If the task also implements
* RollbackInterface, then it will be rolled back via its 'rollback()'
* method ONLY if its 'run()' method completes successfully, and some
* task added after it fails.
*
* @param string|TaskInterface|TaskInterface[]
* An optional name for the task -- missing or NULL for unnamed tasks.
* Names are used for positioning before and after tasks. If
* a name is not provided, then the first parameter may contain
* a task to add, or an array of tasks to add.
* @param TaskInterface
* If the first parameter is a string, then the second parameter
* holds a single task to add to our collection.
*/
public function add($name, $task = null)
{
// If '$name' was unspecified, then the single parameter provided
// is the task or Callable object. Make $name 'UNNAMEDTASK'.
if (!is_string($name) && ($task == null)) {
$task = $name;
$name = self::UNNAMEDTASK;
}
// If $task is an array (and isn't a Callable), then add every item
// in the array individually.
if (!is_callable($task) && is_array($task)) {
return $this->addTaskList($task);
}
// Otherwise, add the named (or unnamed) task.
return $this->addTask($name, $task);
}
/**
* Add a rollback task to our task collection. A rollback task
* will execute ONLY if all of the tasks added before it complete
* successfully, AND some task added after it fails.
*
* @param TaskInterface
* The rollback task to add. Note that the 'run()' method of the
* task executes, not its 'rollback()' method. To use the 'rollback()'
* method, add the task via 'Collection::add()' instead.
*/
public function rollback($rollbackTask)
{
// Wrap the task as necessary.
$rollbackTask = $this->wrapTask($rollbackTask);
$collection = $this;
$rollbackRegistrationTask = $this->wrapTask(function () use ($collection, $rollbackTask) {
$collection->registerRollback($rollbackTask);
});
$this->addToTaskStack(self::UNNAMEDTASK, $rollbackRegistrationTask);
return $this;
}
/**
* Add a completion task to our task collection. A completion task
* will execute EITHER after all tasks succeed, OR immediatley after
* any task fails. Completion tasks never cause errors to be returned
* from Collection::run(), even if they fail.
*
* @param TaskInterface
* The completion task to add. Note that the 'run()' method of the
* task executes, just as if the task was added normally.
*/
public function completion($completionTask)
{
// Wrap the task as necessary.
$completionTask = $this->wrapTask($completionTask);
$collection = $this;
$completionRegistrationTask = $this->wrapTask(function () use ($collection, $completionTask) {
$collection->registerCompletion($completionTask);
});
$this->addToTaskStack(self::UNNAMEDTASK, $completionRegistrationTask);
return $this;
}
/**
* Add a task before an existing named task.
*
* @param string
* The name of the task to insert before. The named task MUST exist.
* @param TaskInterface
* The task to add.
* @param string
* The name of the task to add. If not provided, will be associated
* with the named task it was added before.
*/
public function before($name, $task, $nameOfTaskToAdd = self::UNNAMEDTASK)
{
// Wrap the task as necessary.
$task = $this->wrapTask($task);
$existingTask = $this->namedTask($name);
$existingTask->before($task, $nameOfTaskToAdd);
return $this;
}
/**
* Add a task after an existing named task.
*
* @param string
* The name of the task to insert before. The named task MUST exist.
* @param TaskInterface
* The task to add.
* @param string
* The name of the task to add. If not provided, will be associated
* with the named task it was added after.
*/
public function after($name, $task, $nameOfTaskToAdd = self::UNNAMEDTASK)
{
// Wrap the task as necessary.
$task = $this->wrapTask($task);
$existingTask = $this->namedTask($name);
$existingTask->after($task, $nameOfTaskToAdd);
return $this;
}
/**
* Wrap the provided task in a wrapper that will ignore
* any errors or exceptions that may be produced. This
* is useful, for example, in adding optional cleanup tasks
* at the beginning of a task collection, to remove previous
* results which may or may not exist.
*
* TODO: Provide some way to specify which sort of errors
* are ignored, so that 'file not found' may be ignored,
* but 'permission denied' reported?
*/
public function ignoreErrorsTaskWrapper($task)
{
$task = $this->wrapTask($task);
return function () use ($task) {
$data = [];
try {
$result = $task->run();
$message = $result->getMessage();
$data = $result->getData();
$data['exitcode'] = $result->getExitCode();
} catch (Exception $e) {
$message = $e->getMessage();
}
return Result::success($task, $message, $data);
};
}
/**
* Return the list of task names added to this collection.
*/
public function taskNames()
{
return array_keys($this->taskStack);
}
/**
* Test to see if a specified task name exists.
* n.b. before() and after() require that the named
* task exist; use this function to test first, if
* unsure.
*/
public function hasTask($name)
{
return array_key_exists($name, $this->taskStack);
}
/**
* Test to see if the given name is an unnamed task, or
* something functionally equivalent. Any numeric index
* is renumbered when added to the collection.
*/
public static function isUnnamedTask($name)
{
return is_numeric($name);
}
/**
* Find an existing named task.
*
* @param string
* The name of the task to insert before. The named task MUST exist.
* @returns Element
* The task group for the named task. Generally this is only
* used to call 'before()' and 'after()'.
*/
protected function namedTask($name)
{
if (!$this->hasTask($name)) {
throw new \RuntimeException("Could not find task named $name");
}
return $this->taskStack[$name];
}
/**
* Add a list of tasks to our task collection. This is
* protected because clients should just call 'add()'.
*
* @param TaskInterface[]
* An array of tasks to run with rollback protection
*/
protected function addTaskList($tasks)
{
foreach ($tasks as $name => $task) {
$this->addTask($name, $task);
}
return $this;
}
/**
* Add a task to our task collection. If there is a later failure,
* then run the provided rollback operation. The rollback() method of
* the task will also be executed, if the task implements
* RollbackInterface. addTask is protected because clients should
* just call 'add()'.
*
* @param string
* A name for the task, used for positioning before and after tasks.
* @param TaskInterface
* The task to run
*/
protected function addTask($name, $task)
{
// Wrap the task as necessary.
$task = $this->wrapTask($task);
$this->addToTaskStack($name, new TaskWrapper($this, $task));
return $this;
}
/**
* If the task needs to be wrapped, create whatever wrapper objects are
* needed for it.
*/
protected function wrapTask($task)
{
// If the caller provided a function pointer instead of a TaskInstance,
// then wrap it in a CallableTask.
if (is_callable($task)) {
$task = new CallableTask($task, $this);
}
return $task;
}
/**
* Add the provided task to our task list.
*/
protected function addToTaskStack($name, $task)
{
// All tasks are stored in a task group so that we have a place
// to hang 'before' and 'after' tasks.
$taskGroup = new Element($task);
// If a task name is not provided, then we'll let php pick
// the array index.
if (static::isUnnamedTask($name)) {
$this->taskStack[] = $taskGroup;
return $this;
}
// If we are replacing an existing task with the
// same name, ensure that our new task is added to
// the end.
$this->taskStack[$name] = $taskGroup;
return $this;
}
/**
* Register a rollback task to run if there is any failure.
*
* Clients are free to add tasks to the rollback stack as
* desired; however, usually it is preferable to call
* Collection::rollback() instead. With that function,
* the rollback function will only be called if all of the
* tasks added before it complete successfully, AND some later
* task fails.
*
* One example of a good use-case for registering a callback
* function directly is to add a task that sends notification
* when a task fails.
*
* @param TaskInterface
* The rollback task to run on failure.
*/
public function registerRollback($rollbackTask)
{
// Wrap the task as necessary.
$rollbackTask = $this->wrapTask($rollbackTask);
if ($rollbackTask) {
$this->rollbackStack[] = $rollbackTask;
}
return $this;
}
/**
* Register a completion task to run once all other tasks finish.
* Completion tasks run whether or not a rollback operation was
* triggered. They do not trigger rollbacks if they fail.
*
* The typical use-case for a completion function is to clean up
* temporary objects (e.g. temporary folders). The preferred
* way to do that, though, is to use Temporary::wrap().
*
* On failures, completion tasks will run after all rollback tasks.
* If one task collection is nested inside another task collection,
* then the nested collection's completion tasks will run as soon as
* the nested task completes; they are not deferred to the end of
* the containing collection's execution.
*
* @param TaskInterface
* The completion task to run at the end of all other operations.
*/
public function registerCompletion($completionTask)
{
// Wrap the task as necessary.
$completionTask = $this->wrapTask($completionTask);
if ($completionTask) {
$this->completionStack[] = $completionTask;
}
return $this;
}
/**
* Run our tasks, and roll back if necessary.
*/
public function run()
{
$result = $this->runWithoutCompletion();
$this->complete();
return $result;
}
/**
* Like 'run()', but does not call complete().
* Allows caller to continue adding tasks to the
* same collection, e.g. perhaps to re-use a temporary
* directory or other temporary which will persist
* until 'run()' or 'complete()' is called.
*/
public function runWithoutCompletion()
{
// If there were some tasks that were run before, and they
// failed, subsequent calls to run() will do nothing further,
// and will continue to return the same error result.
$result = $this->previousResult;
if ($result->wasSuccessful()) {
foreach ($this->taskStack as $name => $taskGroup) {
$taskList = $taskGroup->getTaskList();
$result = $this->runTaskList($name, $taskList, $result);
if (!$result->wasSuccessful()) {
$this->fail();
return $result;
}
}
$this->taskStack = [];
}
$this->previousResult = $result;
return $result;
}
/**
* Force the rollback functions to run
*/
public function fail()
{
$this->runRollbackTasks();
$this->complete();
return $this;
}
/**
* Force the completion functions to run
*/
public function complete()
{
$this->runTaskListIgnoringFailures($this->completionStack);
$this->reset();
return $this;
}
/**
* Reset this collection, removing all tasks.
*/
public function reset()
{
$this->taskStack = [];
$this->completionStack = [];
$this->rollbackStack = [];
$this->incrementalResults = Result::success($this);
return $this;
}
/**
* Run all of our rollback tasks.
*
* Note that Collection does not implement RollbackInterface, but
* it may still be used as a task inside another task collection
* (i.e. you can nest task collections, if desired).
*/
protected function runRollbackTasks()
{
$this->runTaskListIgnoringFailures($this->rollbackStack);
// Erase our rollback stack once we have finished rolling
// everything back. This will allow us to potentially use
// a command collection more than once (e.g. to retry a
// failed operation after doing some error recovery).
$this->rollbackStack = [];
}
/**
* Run every task in a list, but only up to the first failure.
* Return the failing result, or success if all tasks run.
*/
protected function runTaskList($name, $taskList, $result)
{
try {
foreach ($taskList as $taskName => $task) {
$taskResult = $task->run();
// If the current task returns an error code, then stop
// execution and signal a rollback.
if (!$taskResult->wasSuccessful()) {
return $taskResult;
}
// We accumulate our results into a field so that tasks that
// have a reference to the collection may examine and modify
// the incremental results, if they wish.
$key = static::isUnnamedTask($taskName) ? $name : $taskName;
$result = $this->accumulateResults($key, $result, $taskResult);
}
} catch (Exception $e) {
// Tasks typically should not throw, but if one does, we will
// convert it into an error and roll back.
// TODO: should we re-throw it again instead?
return new Result($this, -1, $e->getMessage(), $result->getData());
}
return $result;
}
/**
* Add the results from the most recent task to the accumulated
* results from all tasks that have run so far, merging data
* as necessary.
*/
public function accumulateResults($key, Result $result, Result $taskResult)
{
// If the result is not set or is not a Result, then ignore it
if (isset($result) && ($result instanceof Result)) {
// If the task is unnamed, then all of its data elements
// just get merged in at the top-level of the final Result object.
if (static::isUnnamedTask($key)) {
$result->merge($taskResult);
} elseif (isset($result[$key])) {
// There can only be one task with a given name; however, if
// there are tasks added 'before' or 'after' the named task,
// then the results from these will be stored under the same
// name unless they are given a name of their own when added.
$current = $result[$key];
$result[$key] = $taskResult->merge($current);
} else {
$result[$key] = $taskResult;
}
}
return $result;
}
/**
* Run all of the tasks in a provided list, ignoring failures.
* This is used to roll back or complete.
*/
protected function runTaskListIgnoringFailures($taskList)
{
foreach ($taskList as $task) {
try {
$task->run();
} catch (Exception $e) {
// Ignore rollback failures.
}
}
}
}
PK CV} src/Exception/TaskException.phpnu W+A taskPack(
* )
* ->add('README') // Puts file 'README' in archive at the root
* ->add('project') // Puts entire contents of directory 'project' in archinve inside 'project'
* ->addFile('dir/file.txt', 'file.txt') // Takes 'file.txt' from cwd and puts it in archive inside 'dir'.
* ->run();
* ?>
* ```
*/
class Pack extends BaseTask implements PrintedInterface
{
use \Robo\Common\DynamicParams;
use \Robo\Common\Timer;
use \Robo\Common\PHPStatus;
/**
* The list of items to be packed into the archive.
*
* @var array
*/
private $items = [];
/**
* The full path to the archive to be created.
*
* @var string
*/
private $archiveFile;
/**
* Construct the class.
*
* @param string $folder The full path to the folder and subfolders to pack.
* @param string $zipname The full path and name of the zipfile to create.
*
* @since 1.0
*/
public function __construct($archive)
{
$this->archiveFile = $archive;
}
/**
* Satisfy the parent requirement.
*
* @return bool Always returns true.
*
* @since 1.0
*/
public function getPrinted()
{
return true;
}
/**
* Add an item to the archive. Like file_exists(), the parameter
* may be a file or a directory.
*
* @var string
* Relative path and name of item to store in archive
* @var string
* Absolute or relative path to file or directory's location in filesystem
*/
public function addFile($placementLocation, $filesystemLocation)
{
$this->items[$placementLocation] = $filesystemLocation;
return $this;
}
/**
* Alias for addFile, in case anyone has angst about using
* addFile with a directory.
*
* @var string
* Relative path and name of directory to store in archive
* @var string
* Absolute or relative path to directory or directory's location in filesystem
*/
public function addDir($placementLocation, $filesystemLocation)
{
$this->addFile($placementLocation, $filesystemLocation);
return $this;
}
/**
* Add a file or directory, or list of same to the archive.
*
* @var string|array
* If given a string, should contain the relative filesystem path to the
* the item to store in archive; this will also be used as the item's
* path in the archive, so absolute paths should not be used here.
* If given an array, the key of each item should be the path to store
* in the archive, and the value should be the filesystem path to the
* item to store.
*/
public function add($item)
{
if (is_array($item)) {
$this->items = array_merge($this->items, $item);
} else {
$this->addFile($item, $item);
}
return $this;
}
/**
* Create a zip archive for distribution.
*
* @return bool True on success | False on failure.
*
* @since 1.0
*/
public function run()
{
$this->startTimer();
// Use the file extension to determine what kind of archive to create.
$fileInfo = new \SplFileInfo($this->archiveFile);
$extension = strtolower($fileInfo->getExtension());
if (empty($extension)) {
return Result::error($this, "Archive filename must use an extension (e.g. '.zip') to specify the kind of archive to create.");
}
try {
// Inform the user which archive we are creating
$this->printTaskInfo("Creating archive {$this->archiveFile}");
if ($extension == 'zip') {
$result = $this->archiveZip($this->archiveFile, $this->items);
} else {
$result = $this->archiveTar($this->archiveFile, $this->items);
}
$this->printTaskSuccess("{$this->archiveFile} created.");
} catch (Exception $e) {
$this->printTaskError("Could not create {$this->archiveFile}. ".$e->getMessage());
$result = Result::error($this);
}
$this->stopTimer();
$result['time'] = $this->getExecutionTime();
return $result;
}
protected function archiveTar($archiveFile, $items)
{
$tar_object = new \Archive_Tar($archiveFile);
foreach ($items as $placementLocation => $fileSystemLocation) {
$p_remove_dir = $fileSystemLocation;
$p_add_dir = $placementLocation;
if (is_file($fileSystemLocation)) {
$p_remove_dir = dirname($fileSystemLocation);
$p_add_dir = dirname($placementLocation);
if (basename($fileSystemLocation) != basename($placementLocation)) {
return Result::error($this, "Tar archiver does not support renaming files during extraction; could not add $fileSystemLocation as $placementLocation.");
}
}
if (!$tar_object->addModify([$fileSystemLocation], $p_add_dir, $p_remove_dir)) {
return Result::error($this, "Could not add $fileSystemLocation to the archive.");
}
}
return Result::success($this);
}
protected function archiveZip($archiveFile, $items)
{
$result = $this->checkExtension('zip archiver', 'zlib');
if (!$result->wasSuccessful()) {
return $result;
}
$zip = new \ZipArchive($archiveFile, \ZipArchive::CREATE);
if (!$zip->open($archiveFile, \ZipArchive::CREATE)) {
return Result::error($this, "Could not create zip archive {$archiveFile}");
}
$result = $this->addItemsToZip($zip, $items);
$zip->close();
return $result;
}
protected function addItemsToZip($zip, $items)
{
foreach ($items as $placementLocation => $fileSystemLocation) {
if (is_dir($fileSystemLocation)) {
$finder = new Finder();
$finder->files()->in($fileSystemLocation);
foreach ($finder as $file) {
if (!$zip->addFile($file->getRealpath(), "{$placementLocation}/{$file->getRelativePathname()}")) {
return Result::error($this, "Could not add directory $fileSystemLocation to the archive; error adding {$file->getRealpath()}.");
}
}
} elseif (is_file($fileSystemLocation)) {
if (!$zip->addFile($fileSystemLocation, $placementLocation)) {
return Result::error($this, "Could not add file $fileSystemLocation to the archive.");
}
} else {
return Result::error($this, "Could not find $fileSystemLocation for the archive.");
}
}
return Result::success($this);
}
}
PK CVhS7M M src/Task/Archive/Extract.phpnu W+A taskExtract($archivePath)
* ->to($destination)
* ->preserveTopDirectory(false) // the default
* ->run();
* ?>
* ```
*
* @method to(string) location to store extracted files
*/
class Extract extends BaseTask
{
use \Robo\Common\DynamicParams;
use \Robo\Common\Timer;
use \Robo\Common\PHPStatus;
protected $filename;
protected $to;
private $preserveTopDirectory = false;
public function __construct($filename)
{
$this->filename = $filename;
}
public function run()
{
if (!file_exists($this->filename)) {
$this->printTaskError("File {$this->filename} does not exist");
return false;
}
if (!($mimetype = static::archiveType($this->filename))) {
$this->printTaskError("Could not determine type of archive for {$this->filename}");
return false;
}
// We will first extract to $extractLocation and then move to $this->to
$extractLocation = static::getTmpDir();
@mkdir($extractLocation);
@mkdir(dirname($this->to));
$this->startTimer();
$this->printTaskInfo("Extracting {$this->filename}");
// Perform the extraction of a zip file.
if (($mimetype == 'application/zip') || ($mimetype == 'application/x-zip')) {
$result = $this->extractZip($extractLocation);
} else {
// Otherwise we have a possibly-compressed Tar file.
$result = $this->extractTar($extractLocation);
}
if ($result->wasSuccessful()) {
$this->printTaskInfo("{$this->filename} extracted");
// Now, we want to move the extracted files to $this->to. There
// are two possibilities that we must consider:
//
// (1) Archived files were encapsulated in a folder with an arbitrary name
// (2) There was no encapsulating folder, and all the files in the archive
// were extracted into $extractLocation
//
// In the case of (1), we want to move and rename the encapsulating folder
// to $this->to.
//
// In the case of (2), we will just move and rename $extractLocation.
$filesInExtractLocation = glob("$extractLocation/*");
$hasEncapsulatingFolder = ((count($filesInExtractLocation) == 1) && is_dir($filesInExtractLocation[0]));
if ($hasEncapsulatingFolder && !$this->preserveTopDirectory) {
$result = (new FileSystemStack())->rename($filesInExtractLocation[0], $this->to)->run();
@rmdir($extractLocation);
} else {
$result = (new FileSystemStack())->rename($extractLocation, $this->to)->run();
}
}
$this->stopTimer();
$result['time'] = $this->getExecutionTime();
return $result;
}
protected function extractZip($extractLocation)
{
$result = $this->checkExtension('zip extracter', 'zlib');
if (!$result->wasSuccessful()) {
return $result;
}
$zip = new \ZipArchive();
if (($status = $zip->open($this->filename)) !== true) {
return Result::error($this, "Could not open zip archive {$this->filename}");
}
if (!$zip->extractTo($extractLocation)) {
return Result::error($this, "Could not extract zip archive {$this->filename}");
}
$zip->close();
return Result::success($this);
}
protected function extractTar($extractLocation)
{
$tar_object = new \Archive_Tar($this->filename);
if (!$tar_object->extract($extractLocation)) {
return Result::error($this, "Could not extract tar archive {$this->filename}");
}
return Result::success($this);
}
protected static function archiveType($filename)
{
$content_type = false;
if (class_exists('finfo')) {
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$content_type = $finfo->file($filename);
// If finfo cannot determine the content type, then we will try other methods
if ($content_type == 'application/octet-stream') {
$content_type = false;
}
}
// Examing the file's magic header bytes.
if (!$content_type) {
if ($file = fopen($filename, 'rb')) {
$first = fread($file, 2);
fclose($file);
if ($first !== false) {
// Interpret the two bytes as a little endian 16-bit unsigned int.
$data = unpack('v', $first);
switch ($data[1]) {
case 0x8b1f:
// First two bytes of gzip files are 0x1f, 0x8b (little-endian).
// See http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
$content_type = 'application/x-gzip';
break;
case 0x4b50:
// First two bytes of zip files are 0x50, 0x4b ('PK') (little-endian).
// See http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
$content_type = 'application/zip';
break;
case 0x5a42:
// First two bytes of bzip2 files are 0x5a, 0x42 ('BZ') (big-endian).
// See http://en.wikipedia.org/wiki/Bzip2#File_format
$content_type = 'application/x-bzip2';
break;
}
}
}
}
// 3. Lastly if above methods didn't work, try to guess the mime type from
// the file extension. This is useful if the file has no identificable magic
// header bytes (for example tarballs).
if (!$content_type) {
// Remove querystring from the filename, if present.
$filename = basename(current(explode('?', $filename, 2)));
$extension_mimetype = array(
'.tar.gz' => 'application/x-gzip',
'.tgz' => 'application/x-gzip',
'.tar' => 'application/x-tar',
);
foreach ($extension_mimetype as $extension => $ct) {
if (substr($filename, -strlen($extension)) === $extension) {
$content_type = $ct;
break;
}
}
}
return $content_type;
}
protected static function getTmpDir()
{
return getcwd().'/tmp'.rand().time();
}
}
PK CVVV src/Task/Archive/loadTasks.phpnu W+A taskDockerBuild()->run();
*
* $this->taskDockerBuild('path/to/dir')
* ->tag('database')
* ->run();
*
* ?>
*
* ```
*
* Class Build
* @package Robo\Task\Docker
*/
class Build extends Base
{
protected $path;
public function __construct($path = '.')
{
$this->command = "docker build";
$this->path = $path;
}
public function getCommand()
{
return $this->command . ' ' . $this->arguments . ' ' . $this->path;
}
public function tag($tag)
{
return $this->option('-t', $tag);
}
}PK CVoF src/Task/Docker/Base.phpnu W+A printTaskInfo("Running ".$this->getCommand()."");
return $this->executeCommand($this->getCommand());
}
abstract public function getCommand();
} PK CV\ src/Task/Docker/Exec.phpnu W+A taskDockerRun('test_env')
* ->detached()
* ->run();
*
* $this->taskDockerExec($test)
* ->interactive()
* ->exec('./runtests')
* ->run();
*
* // alternatively use commands from other tasks
*
* $this->taskDockerExec($test)
* ->interactive()
* ->exec($this->taskCodecept()->suite('acceptance'))
* ->run();
* ?>
* ```
*
*/
class Exec extends Base
{
use CommandReceiver;
protected $command = "docker exec";
protected $cid;
protected $run = '';
public function __construct($cidOrResult)
{
$this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult;
}
public function detached()
{
$this->option('-d');
return $this;
}
public function interactive()
{
$this->option('-i');
return $this;
}
public function exec($command)
{
$this->run = $this->receiveCommand($command);
return $this;
}
public function getCommand()
{
return $this->command . ' ' . $this->arguments . ' ' . $this->cid.' '.$this->run;
}
}PK CVk>[ src/Task/Docker/Commit.phpnu W+A taskDockerCommit($containerId)
* ->name('my/database')
* ->run();
*
* // alternatively you can take the result from DockerRun task:
*
* $result = $this->taskDockerRun('db')
* ->exec('./prepare_database.sh')
* ->run();
*
* $task->dockerCommit($result)
* ->name('my/database')
* ->run();
* ```
*/
class Commit extends Base
{
protected $command = "docker commit";
protected $name;
protected $cid;
public function __construct($cidOrResult)
{
$this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult;
}
public function getCommand()
{
return $this->command . ' ' . $this->cid . ' ' . $this->name . ' ' . $this->arguments;
}
public function name($name)
{
$this->name = $name;
return $this;
}
}PK CVdD src/Task/Docker/Start.phpnu W+A taskDockerStart($cidOrResult)
* ->run();
* ?>
* ```
*/
class Start extends Base
{
protected $command = "docker start";
protected $cid;
public function __construct($cidOrResult)
{
$this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult;
}
public function getCommand()
{
return $this->command . ' ' . $this->arguments . ' ' . $this->cid;
}
}PK CV7:80 src/Task/Docker/Pull.phpnu W+A taskDockerPull('wordpress')
* ->run();
*
* ?>
* ```
*
*/
class Pull extends Base
{
function __construct($image)
{
$this->command = "docker pull $image ";
}
public function getCommand()
{
return $this->command . ' ' . $this->arguments;
}
}PK CVK.Z Z src/Task/Docker/loadTasks.phpnu W+A getData();
if (isset($data['cid'])) return $data['cid'];
return null;
}
public function getContainerName()
{
$data = $this->getData();
if (isset($data['name'])) return $data['name'];
return null;
}
} PK CVJ src/Task/Docker/Remove.phpnu W+A taskDockerRemove($container)
* ->run();
* ?>
* ```
*
*/
class Remove extends Base
{
function __construct($container)
{
$this->command = "docker rm $container ";
}
public function getCommand()
{
return $this->command . ' ' . $this->arguments;
}
}PK CV src/Task/Docker/Stop.phpnu W+A taskDockerStop($cidOrResult)
* ->run();
* ?>
* ```
*/
class Stop extends Base
{
protected $command = "docker stop";
protected $cid;
public function __construct($cidOrResult)
{
$this->cid = $cidOrResult instanceof Result ? $cidOrResult->getCid() : $cidOrResult;
}
public function getCommand()
{
return $this->command . ' ' . $this->arguments . ' ' . $this->cid;
}
}PK CV]f* src/Task/Docker/Run.phpnu W+A taskDockerRun('mysql')->run();
*
* $result = $this->taskDockerRun('my_db_image')
* ->env('DB', 'database_name')
* ->volume('/path/to/data', '/data')
* ->detached()
* ->publish(3306)
* ->name('my_mysql')
* ->run();
*
* // retrieve container's cid:
* $this->say("Running container ".$result->getCid());
*
* // execute script inside container
* $result = $this->taskDockerRun('db')
* ->exec('prepare_test_data.sh')
* ->run();
*
* $this->taskDockerCommit($result)
* ->name('test_db')
* ->run();
*
* // link containers
* $mysql = $this->taskDockerRun('mysql')
* ->name('wp_db') // important to set name for linked container
* ->env('MYSQL_ROOT_PASSWORD', '123456')
* ->run();
*
* $this->taskDockerRun('wordpress')
* ->link($mysql)
* ->publish(80, 8080)
* ->detached()
* ->run();
*
* ?>
* ```
*
*/
class Run extends Base
{
use CommandReceiver;
protected $image = '';
protected $run = '';
protected $cidFile;
protected $name;
function __construct($image)
{
$this->image = $image;
}
public function getPrinted()
{
return $this->isPrinted;
}
public function getCommand()
{
if ($this->isPrinted) {
$this->option('-i');
}
if ($this->cidFile) {
$this->option('cidfile', $this->cidFile);
}
return trim('docker run ' . $this->arguments . ' ' . $this->image . ' ' . $this->run);
}
public function detached()
{
$this->option('-d');
return $this;
}
public function interactive()
{
$this->option('-i');
return $this;
}
public function exec($run)
{
$this->run = $this->receiveCommand($run);
return $this;
}
public function volume($from, $to = null)
{
$volume = $to ? "$from:$to" : $from;
$this->option('-v', $volume);
return $this;
}
public function env($variable, $value = null)
{
$env = $value ? "$variable=$value" : $variable;
return $this->option("-e", $env);
}
public function publish($port = null, $portTo = null)
{
if (!$port) {
return $this->option('-P');
}
if ($portTo) {
$port = "$port:$portTo";
}
return $this->option('-p', $port);
}
public function containerWorkdir($dir)
{
return $this->option('-w', $dir);
}
public function user($user)
{
return $this->option('-u', $user);
}
public function privileged()
{
return $this->option('--privileged');
}
public function name($name)
{
$this->name = $name;
return $this->option('name', $name);
}
public function link($name, $alias)
{
if ($name instanceof Result) {
$name = $name->getContainerName();
}
$this->option('link', "$name:$alias");
return $this;
}
public function run()
{
$this->cidFile = sys_get_temp_dir() . '/docker_' . uniqid() . '.cid';
$result = parent::run();
$time = $result->getExecutionTime();
$cid = $this->getCid();
return new Result($this, $result->getExitCode(), $result->getMessage(), ['cid' => $cid, 'time' => $time, 'name' => $this->name]);
}
protected function getCid()
{
if (!$this->cidFile) {
return null;
}
$cid = trim(file_get_contents($this->cidFile));
@unlink($this->cidFile);
return $cid;
}
}PK CV<|[ src/Task/File/Replace.phpnu W+A taskReplaceInFile('VERSION')
* ->from('0.2.0')
* ->to('0.3.0')
* ->run();
*
* $this->taskReplaceInFile('README.md')
* ->from(date('Y')-1)
* ->to(date('Y'))
* ->run();
*
* $this->taskReplaceInFile('config.yml')
* ->regex('~^service:~')
* ->to('services:')
* ->run();
*
* $this->taskReplaceInFile('box/robo.txt')
* ->from(array('##dbname##', '##dbhost##'))
* ->to(array('robo', 'localhost'))
* ->run();
* ?>
* ```
*
* @method regex(string) regex to match string to be replaced
* @method from(string|array) string(s) to be replaced
* @method to(string|array) value(s) to be set as a replacement
*/
class Replace extends BaseTask
{
use \Robo\Common\DynamicParams;
protected $filename;
protected $from;
protected $to;
protected $regex;
public function __construct($filename)
{
$this->filename = $filename;
}
function run()
{
if (!file_exists($this->filename)) {
$this->printTaskError("File {$this->filename} does not exist");
return false;
}
$text = file_get_contents($this->filename);
if ($this->regex) {
$text = preg_replace($this->regex, $this->to, $text, -1, $count);
} else {
$text = str_replace($this->from, $this->to, $text, $count);
}
if ($count > 0) {
$res = file_put_contents($this->filename, $text);
if ($res === false) {
return Result::error($this, "Error writing to file {$this->filename}.");
}
$this->printTaskSuccess("{$this->filename} updated. $count items replaced");
} else {
$this->printTaskInfo("{$this->filename} unchanged. $count items replaced");
}
return Result::success($this, '', ['replaced' => $count]);
}
}
PK CVf src/Task/File/Write.phpnu W+A taskWriteToFile('blogpost.md')
* ->line('-----')
* ->line(date('Y-m-d').' '.$title)
* ->line('----')
* ->run();
* ?>
* ```
*
* @method append()
*/
class Write extends BaseTask
{
use \Robo\Common\DynamicParams;
protected $filename;
protected $append = false;
public function __construct($filename)
{
$this->filename = $filename;
}
/**
* add a line.
*
* @param string $line
*
* @return Write The current instance
*/
public function line($line)
{
$this->text($line . "\n");
return $this;
}
/**
* add more lines.
*
* @param array $lines
*
* @return Write The current instance
*/
public function lines(array $lines)
{
$this->text(implode("\n", $lines) . "\n");
return $this;
}
/**
* add a text.
*
* @param string $text
*
* @return Write The current instance
*/
public function text($text)
{
$this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
return $this;
}
/**
* add a text from a file.
*
* Note that the file is read in the run() method of this task.
* To load text from the current state of a file (e.g. one that may
* be deleted or altered by other tasks prior the execution of this one),
* use:
* $task->text(file_get_contents($filename));
*
* @param string $filename
*
* @return Write The current instance
*/
public function textFromFile($filename)
{
$this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
return $this;
}
/**
* substitute a placeholder with value, placeholder must be enclosed by `{}`.
*
* @param string $name
* @param string $val
*
* @return Write The current instance
*/
public function place($name, $val)
{
$this->replace('{'.$name.'}', $val);
return $this;
}
/**
* replace any string with value.
*
* @param string $string
* @param string $replacement
*
* @return Write The current instance
*/
public function replace($string, $replacement)
{
$this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
return $this;
}
/**
* replace any string with value using regular expression.
*
* @param string $pattern
* @param string $replacement
*
* @return Write The current instance
*/
public function regexReplace($pattern, $replacement)
{
$this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
return $this;
}
protected function textFromFileCollect($contents, $filename)
{
return $contents . file_get_contents($filename);
}
protected function replaceCollect($contents, $string, $replacement)
{
return str_replace($string, $replacement, $contents);
}
protected function regexReplaceCollect($contents, $pattern, $replacement)
{
return preg_replace($pattern, $replacement, $contents);
}
protected function textCollect($contents, $text)
{
return $contents . $text;
}
protected function getContents()
{
$contents = "";
if ($this->append) {
$contents = file_get_contents($this->filename);
}
foreach ($this->stack as $action) {
$command = array_shift($action);
if (method_exists($this, $command)) {
array_unshift($action, $contents);
$contents = call_user_func_array([$this, $command], $action);
}
}
return $contents;
}
public function run()
{
$this->printTaskInfo("Writing to {$this->filename}.");
$contents = $this->getContents();
if (!file_exists(dirname($this->filename))) {
mkdir(dirname($this->filename), 0777, true);
}
$res = file_put_contents($this->filename, $contents);
if ($res === false) {
return Result::error($this, "File {$this->filename} couldn't be created");
}
return Result::success($this);
}
public function getPath()
{
return $this->filename;
}
}
PK CV>k k src/Task/File/loadTasks.phpnu W+A taskConcat([
* 'web/assets/screen.css',
* 'web/assets/print.css',
* 'web/assets/theme.css'
* ])
* ->to('web/assets/style.css')
* ->run()
* ?>
* ```
*/
class Concat extends BaseTask
{
use ResourceExistenceChecker;
/**
* @var array|Iterator files
*/
protected $files;
/**
* @var string dst
*/
protected $dst;
/**
* Constructor.
*
* @param array|Iterator $files
*/
public function __construct($files)
{
$this->files = $files;
}
/**
* set the destination file
*
* @param string $dst
*
* @return Concat The current instance
*/
public function to($dst)
{
$this->dst = $dst;
return $this;
}
/**
* {@inheritdoc}
*/
public function run()
{
if (is_null($this->dst) || "" === $this->dst) {
return Result::error($this, 'You must specify a destination file with to() method.');
}
if (!$this->checkResources($this->files, 'file')) {
return Result::error($this, 'Source files are missing!');
}
if (file_exists($this->dst) && !is_writable($this->dst)) {
return Result::error($this, 'Destination already exists and cannot be overwritten.');
}
$dump = '';
foreach ($this->files as $path) {
foreach (glob($path) as $file) {
$dump .= file_get_contents($file) . "\n";
}
}
$this->printTaskInfo(sprintf('Writing %s', $this->dst));
$dst = $this->dst . '.part';
$write_result = file_put_contents($dst, $dump);
if (false === $write_result) {
@unlink($dst);
return Result::error($this, 'File write failed.');
}
// Cannot be cross-volume; should always succeed.
@rename($dst, $this->dst);
return Result::success($this);
}
}
PK CVa src/Task/File/TmpFile.phpnu W+A taskTmpFile()
* ->line('-----')
* ->line(date('Y-m-d').' '.$title)
* ->line('----')
* ->addToCollection($collection)
* ->getPath();
* ?>
* ```
*/
class TmpFile extends Write implements CompletionInterface
{
public function __construct($filename = 'tmp', $extension = '', $baseDir = '', $includeRandomPart = true)
{
if (empty($base)) {
$base = sys_get_temp_dir();
}
if ($includeRandomPart) {
$random = static::randomString();
$filename = "{$filename}_{$random}";
}
$filename .= $extension;
parent::__construct("{$base}/{$filename}");
}
/**
* Generate a suitably random string to use as the suffix for our
* temporary file.
*/
private static function randomString($length = 12)
{
return substr(str_shuffle('23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'), 0, $length);
}
/**
* Delete this file when our collection completes.
* If this temporary file is not part of a collection,
* then it will be deleted when the program terminates,
* presuming that it was created by taskTmpFile() or _tmpFile().
*/
public function complete()
{
unlink($this->getPath());
}
}
PK CVX src/Task/Base/ParallelExec.phpnu W+A taskParallelExec()
* ->process('php ~/demos/script.php hey')
* ->process('php ~/demos/script.php hoy')
* ->process('php ~/demos/script.php gou')
* ->run();
* ?>
* ```
*
*
* @method \Robo\Task\Base\ParallelExec timeout(int $timeout) stops process if it runs longer then `$timeout` (seconds)
* @method \Robo\Task\Base\ParallelExec idleTimeout(int $timeout) stops process if it does not output for time longer then `$timeout` (seconds)
*/
class ParallelExec extends BaseTask implements CommandInterface, PrintedInterface
{
use Timer;
use \Robo\Common\DynamicParams;
use \Robo\Common\CommandReceiver;
protected $processes = [];
protected $timeout = null;
protected $idleTimeout = null;
protected $isPrinted = false;
public function getPrinted()
{
return $this->isPrinted;
}
public function printed($isPrinted = true)
{
$this->isPrinted = $isPrinted;
return $this;
}
public function process($command)
{
$this->processes[] = new Process($this->receiveCommand($command));
return $this;
}
public function getCommand()
{
return implode(' && ', $this->processes);
}
public function run()
{
foreach ($this->processes as $process) {
/** @var $process Process **/
$process->setIdleTimeout($this->idleTimeout);
$process->setTimeout($this->timeout);
$process->start();
$this->printTaskInfo($process->getCommandLine());
}
$progress = new ProgressBar($this->getOutput());
$progress->start(count($this->processes));
$running = $this->processes;
$progress->display();
$this->startTimer();
while (true) {
foreach ($running as $k => $process) {
try {
$process->checkTimeout();
} catch (ProcessTimedOutException $e) {
}
if (!$process->isRunning()) {
$progress->advance();
if ($this->isPrinted) {
$this->getOutput()->writeln("");
$this->printTaskInfo("Output for " . $process->getCommandLine()." ");
$this->getOutput()->writeln($process->getOutput(), OutputInterface::OUTPUT_RAW);
if ($process->getErrorOutput()) {
$this->getOutput()->writeln("" . $process->getErrorOutput() . "");
}
}
unset($running[$k]);
}
}
if (empty($running)) {
break;
}
usleep(1000);
}
$this->getOutput()->writeln("");
$this->stopTimer();
$errorMessage = '';
$exitCode = 0;
foreach ($this->processes as $p) {
if ($p->getExitCode() === 0) continue;
$errorMessage .= "'" . $p->getCommandLine() . "' exited with code ". $p->getExitCode()." \n";
$exitCode = max($exitCode, $p->getExitCode());
}
if (!$errorMessage) $this->printTaskSuccess(count($this->processes) . " processes finished running");
return new Result($this, $exitCode, $errorMessage, ['time' => $this->getExecutionTime()]);
}
}PK CVj~ src/Task/Base/Exec.phpnu W+A taskExec('compass')->arg('watch')->run();
* // or use shortcut
* $this->_exec('compass watch');
*
* $this->taskExec('compass watch')->background()->run();
*
* if ($this->taskExec('phpunit .')->run()->wasSuccessful()) {
* $this->say('tests passed');
* }
*
* ?>
* ```
*/
class Exec extends BaseTask implements CommandInterface, PrintedInterface
{
use \Robo\Common\CommandReceiver;
use \Robo\Common\ExecOneCommand;
static $instances = [];
protected $command;
protected $background = false;
protected $timeout = null;
protected $idleTimeout = null;
protected $env = null;
/**
* @var Process
*/
protected $process;
public function __construct($command)
{
$this->command = $this->receiveCommand($command);
}
public function getCommand()
{
return trim($this->command . $this->arguments);
}
/**
* Executes command in background mode (asynchronously)
*
* @return $this
*/
public function background()
{
self::$instances[] = $this;
$this->background = true;
return $this;
}
/**
* Stop command if it runs longer then $timeout in seconds
*
* @param $timeout
* @return $this
*/
public function timeout($timeout)
{
$this->timeout = $timeout;
return $this;
}
/**
* Stops command if it does not output something for a while
*
* @param $timeout
* @return $this
*/
public function idleTimeout($timeout)
{
$this->idleTimeout = $timeout;
return $this;
}
/**
* Sets the environment variables for the command
*
* @param $env
* @return $this
*/
public function env(array $env)
{
$this->env = $env;
return $this;
}
public function __destruct()
{
$this->stop();
}
protected function stop()
{
if ($this->background && $this->process->isRunning()) {
$this->process->stop();
$this->printTaskInfo("Stopped ".$this->getCommand()."");
}
}
public function run()
{
$command = $this->getCommand();
$dir = $this->workingDirectory ? " in " . $this->workingDirectory : "";
$this->printTaskInfo("Running {$command}$dir");
$this->process = new Process($command);
$this->process->setTimeout($this->timeout);
$this->process->setIdleTimeout($this->idleTimeout);
$this->process->setWorkingDirectory($this->workingDirectory);
if (isset($this->env)) {
$this->process->setEnv($this->env);
}
if (!$this->background and !$this->isPrinted) {
$this->startTimer();
$this->process->run();
$this->stopTimer();
return new Result($this, $this->process->getExitCode(), $this->process->getOutput(), ['time' => $this->getExecutionTime()]);
}
if (!$this->background and $this->isPrinted) {
$this->startTimer();
$this->process->run(
function ($type, $buffer) {
print($buffer);
}
);
$this->stopTimer();
return new Result($this, $this->process->getExitCode(), $this->process->getOutput(), ['time' => $this->getExecutionTime()]);
}
try {
$this->process->start();
} catch (\Exception $e) {
return Result::error($this, $e->getMessage());
}
return Result::success($this);
}
static function stopRunningJobs()
{
foreach (self::$instances as $instance) {
if ($instance) unset($instance);
}
}
}
if (function_exists('pcntl_signal')) {
pcntl_signal(SIGTERM, ['Robo\Task\Base\Exec', 'stopRunningJobs']);
}
register_shutdown_function(['Robo\Task\Base\Exec', 'stopRunningJobs']);
PK CV src/Task/Base/loadShortcuts.phpnu W+A run();
}
} PK CV3P src/Task/Base/ExecStack.phpnu W+A taskExecStack()
* ->stopOnFail()
* ->exec('mkdir site')
* ->exec('cd site')
* ->run();
*
* ?>
* ```
*
* @method $this stopOnFail()
*/
class ExecStack extends CommandStack
{
}
PK CVV V src/Task/Base/SymfonyCommand.phpnu W+A taskSymfonyCommand(new \Codeception\Command\Run('run'))
* ->arg('suite','acceptance')
* ->opt('debug')
* ->run();
*
* // Artisan Command
* $this->taskSymfonyCommand(new ModelGeneratorCommand())
* ->arg('name', 'User')
* ->run();
* ?>
* ```
*/
class SymfonyCommand extends BaseTask
{
/**
* @var SymfonyCommand
*/
protected $command;
/**
* @var InputInterface
*/
protected $input;
public function __construct(Command $command)
{
$this->command = $command;
$this->input = [];
}
public function arg($arg, $value)
{
$this->input[$arg] = $value;
return $this;
}
public function opt($option, $value = null)
{
$this->input["--$option"] = $value;
return $this;
}
public function run()
{
$this->printTaskInfo("Running command ".$this->command->getName());
return new Result($this,
$this->command->run(new ArrayInput($this->input), $this->getOutput())
);
}
}
PK CVأX src/Task/Base/loadTasks.phpnu W+A taskWatch()
* ->monitor('composer.json', function() {
* $this->taskComposerUpdate()->run();
* })->monitor('src', function() {
* $this->taskExec('phpunit')->run();
* })->run();
* ?>
* ```
*/
class Watch extends BaseTask
{
protected $closure;
protected $monitor = [];
protected $bindTo;
public function __construct($bindTo)
{
$this->bindTo = $bindTo;
}
public function monitor($paths, \Closure $callable)
{
if (!is_array($paths)) {
$paths = [$paths];
}
$this->monitor[] = [$paths, $callable];
return $this;
}
public function run()
{
$watcher = new ResourceWatcher();
foreach ($this->monitor as $k => $monitor) {
$closure = $monitor[1];
$closure->bindTo($this->bindTo);
foreach ($monitor[0] as $i => $dir) {
$watcher->track("fs.$k.$i", $dir, FilesystemEvent::MODIFY);
$this->printTaskInfo("Watching $dir for changes...");
$watcher->addListener("fs.$k.$i", $closure);
}
}
$watcher->start();
return Result::success($this);
}
}
PK CVux src/Task/Npm/Base.phpnu W+A option('production');
return $this;
}
public function __construct($pathToNpm = null)
{
if ($pathToNpm) {
$this->command = $pathToNpm;
} elseif (is_executable('/usr/bin/npm')) {
$this->command = '/usr/bin/npm';
} elseif (is_executable('/usr/local/bin/npm')) {
$this->command = '/usr/local/bin/npm';
} else {
throw new TaskException(__CLASS__, "Executable not found.");
}
}
public function getCommand()
{
return "{$this->command} {$this->action}{$this->arguments}";
}
}
PK CVD% src/Task/Npm/Update.phpnu W+A taskNpmUpdate()->run();
*
* // prefer dist with custom path
* $this->taskNpmUpdate('path/to/my/npm')
* ->noDev()
* ->run();
* ?>
* ```
*/
class Update extends Base
{
protected $action = 'update';
public function run()
{
$this->printTaskInfo('Update Npm packages: ' . $this->arguments);
return $this->executeCommand($this->getCommand());
}
}PK CVb8 8 src/Task/Npm/Install.phpnu W+A taskNpmInstall()->run();
*
* // prefer dist with custom path
* $this->taskNpmInstall('path/to/my/npm')
* ->noDev()
* ->run();
* ?>
* ```
*/
class Install extends Base implements CommandInterface
{
protected $action = 'install';
public function run()
{
$this->printTaskInfo('Install Npm packages: ' . $this->arguments);
return $this->executeCommand($this->getCommand());
}
}PK CVHg src/Task/Npm/loadTasks.phpnu W+A prefer = '--prefer-dist';
return $this;
}
/**
* adds `prefer-source` option to composer
*
* @return $this
*/
public function preferSource()
{
$this->prefer = '--prefer-source';
return $this;
}
/**
* adds `no-dev` option to composer
*
* @return $this
*/
public function noDev()
{
$this->dev = '--no-dev';
return $this;
}
/**
* adds `optimize-autoloader` option to composer
*
* @return $this
*/
public function optimizeAutoloader()
{
$this->optimizeAutoloader = '--optimize-autoloader';
return $this;
}
public function __construct($pathToComposer = null)
{
$this->command = $pathToComposer;
if (!$this->command) {
$this->command = $this->findComposer();
}
}
protected function findComposer()
{
if (file_exists('composer.phar')) {
return 'php composer.phar';
}
$composer = exec('which composer');
if (!$composer) {
throw new TaskException(__CLASS__, "Neither local composer.phar nor global composer installation not found");
}
return $composer;
}
public function getCommand()
{
$this->option($this->prefer)
->option($this->dev)
->option($this->optimizeAutoloader);
return "{$this->command} {$this->action}{$this->arguments}";
}
}
PK CVei src/Task/Composer/Update.phpnu W+A taskComposerUpdate()->run();
*
* // prefer dist with custom path
* $this->taskComposerUpdate('path/to/my/composer.phar')
* ->preferDist()
* ->run();
*
* // optimize autoloader with custom path
* $this->taskComposerUpdate('path/to/my/composer.phar')
* ->optimizeAutoloader()
* ->run();
* ?>
* ```
*/
class Update extends Base
{
protected $action = 'update';
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Updating Packages: ' . $command);
return $this->executeCommand($command);
}
}PK CVk^ src/Task/Composer/Install.phpnu W+A taskComposerInstall()->run();
*
* // prefer dist with custom path
* $this->taskComposerInstall('path/to/my/composer.phar')
* ->preferDist()
* ->run();
*
* // optimize autoloader with custom path
* $this->taskComposerInstall('path/to/my/composer.phar')
* ->optimizeAutoloader()
* ->run();
* ?>
* ```
*/
class Install extends Base
{
protected $action = 'install';
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Installing Packages: ' . $command);
return $this->executeCommand($command);
}
}PK CV< " src/Task/Composer/DumpAutoload.phpnu W+A taskComposerDumpAutoload()->run();
*
* // dump auto loader with custom path
* $this->taskComposerDumpAutoload('path/to/my/composer.phar')
* ->preferDist()
* ->run();
*
* // optimize autoloader dump with custom path
* $this->taskComposerDumpAutoload('path/to/my/composer.phar')
* ->optimize()
* ->run();
*
* // optimize autoloader dump with custom path and no dev
* $this->taskComposerDumpAutoload('path/to/my/composer.phar')
* ->optimize()
* ->noDev()
* ->run();
* ?>
* ```
*/
class DumpAutoload extends Base
{
protected $action = 'dump-autoload';
protected $optimize;
public function optimize()
{
$this->optimize = "--optimize";
return $this;
}
public function getCommand()
{
$this->option($this->optimize);
return parent::getCommand();
}
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Dumping Autoloader: '.$command);
return $this->executeCommand($command);
}
}
PK CV!Ǭ src/Task/Composer/loadTasks.phpnu W+A friz()
* ->fraz()
* ->frob();
*
* We presume that the existing library throws an exception on error.
*
* You want:
*
* $result = $this->taskFrobinator($a, $b, $c)
* ->friz()
* ->fraz()
* ->frob()
* ->run();
*
* Execution is deferred until run(), and a Robo\Result instance is
* returned. Additionally, using Robo will covert Exceptions
* into RoboResult objects.
*
* To create a new Robo task:
*
* - Make a new class that extends StackBasedTask
* - Give it a constructor that creates a new Frobinator
* - Override getDelegate(), and return the Frobinator instance
*
* Finally, add your new class to loadTasks.php as usual,
* and you are all done.
*
* If you need to add any methods to your task that should run
* immediately (e.g. to set parameters used at run() time), just
* implement them in your derived class.
*
* If you need additional methods that should run deferred, just
* define them as 'protected function _foo()'. Then, users may
* call $this->taskFrobinator()->foo() to get deferred execution
* of _foo().
*/
abstract class StackBasedTask extends BaseTask
{
protected $stack = [];
protected $stopOnFail = true;
public function stopOnFail($stop = true)
{
$this->stopOnFail = $stop;
return $this;
}
/**
* Derived classes should override the getDelegate() method, and
* return an instance of the API class being wrapped. When this
* is done, any method of the delegate is available as a method of
* this class. Calling one of the delegate's methods will defer
* execution until the run() method is called.
*/
protected function getDelegate()
{
return null;
}
/**
* Derived classes that have more than one delegate may override
* getCommandList to add as many delegate commands as desired to
* the list of potential functions that __call() tried to find.
*/
protected function getDelegateCommandList($function)
{
return [[$this, "_$function"], [$this->getDelegate(), $function]];
}
/**
* Print progress about the commands being executed
*/
protected function printTaskProgress($command, $action)
{
$this->printTaskInfo("{$command[1]} " . json_encode($action));
}
/**
* Derived classes can override processResult to add more
* logic to result handling from functions. By default, it
* is assumed that if a function returns in int, then
* 0 == success, and any other value is the error code.
*/
protected function processResult($function_result)
{
if (is_int($function_result)) {
if ($function_result) {
return Result::error($this, $function_result);
}
}
return Result::success($this);
}
/**
* Record a function to call later.
*/
protected function addToCommandStack($command, $args)
{
$this->stack[] = array_merge([$command], $args);
return $this;
}
/**
* Any API function provided by the delegate that executes immediately
* may be handled by __call automatically. These operations will all
* be deferred until this task's run() method is called.
*
* @throws \BadMethodCallException
*/
public function __call($function, $args)
{
foreach ($this->getDelegateCommandList($function) as $command) {
if (method_exists($command[0], $command[1])) {
// Otherwise, we'll defer calling this function
// until run(), and return $this.
$this->addToCommandStack($command, $args);
return $this;
}
}
$message = "Method $function does not exist.\n";
throw new \BadMethodCallException($message);
}
/**
* Run all of the queued objects on the stack
*/
public function run()
{
$result = Result::success($this);
foreach ($this->stack as $action) {
$command = array_shift($action);
$this->printTaskProgress($command, $action);
// TODO: merge data from the result on this call
// with data from the result on the previous call?
// For now, the result always comes from the last function.
$result = $this->callTaskMethod($command, $action);
if ($this->stopOnFail && $result && !$result->wasSuccessful()) {
break;
}
}
// todo: add timing information to the result
return $result;
}
/**
* Execute one task method
*/
protected function callTaskMethod($command, $action)
{
try {
$function_result = call_user_func_array($command, $action);
return $this->processResult($function_result);
} catch (Exception $e) {
$this->printTaskInfo("" . $e->getMessage() . "");
return Result::error($this, $e->getMessage());
}
}
}
PK CV$|e/ / # src/Task/Assets/CssPreprocessor.phpnu W+A files = $input;
$this->setDefaultCompiler();
}
protected function setDefaultCompiler()
{
if (isset($this->compilers[0])) {
//set first compiler as default
$this->compiler = $this->compilers[0];
}
}
/**
* Sets import directories
* Alias for setImportPaths
* @see CssPreprocessor::setImportPaths
*
* @param array|string $dirs
* @return $this
*/
public function importDir($dirs)
{
return $this->setImportPaths($dirs);
}
/**
* Adds import directory
*
* @param string $dir
*
* @return $this
*/
public function addImportPath($dir)
{
if (!isset($this->compilerOptions['importDirs'])) {
$this->compilerOptions['importDirs'] = [];
}
if (!in_array($dir, $this->compilerOptions['importDirs'], true)) {
$this->compilerOptions['importDirs'][] = $dir;
}
return $this;
}
/**
* Sets import directories
*
* @param array|string $dirs
* @return $this
*/
public function setImportPaths($dirs)
{
if (!is_array($dirs)) {
$dirs = [$dirs];
}
$this->compilerOptions['importDirs'] = $dirs;
return $this;
}
/**
* @param string $formatterName
*
* @return $this
*/
public function setFormatter($formatterName)
{
$this->compilerOptions['formatter'] = $formatterName;
return $this;
}
/**
* Sets the compiler.
*
* @param string $compiler
* @param array $options
* @return $this
*/
public function compiler($compiler, array $options = [])
{
$this->compiler = $compiler;
$this->compilerOptions = array_merge($this->compilerOptions, $options);
return $this;
}
/**
* Compiles file
* @param $file
*
* @return bool|mixed
*/
protected function compile($file)
{
if (is_callable($this->compiler)) {
return call_user_func($this->compiler, $file, $this->compilerOptions);
}
if (method_exists($this, $this->compiler)) {
return $this->{$this->compiler}($file);
}
return false;
}
/**
* Writes the result to destination.
*
* @return Result
*/
public function run()
{
if (!in_array($this->compiler, $this->compilers, true)
&& !is_callable($this->compiler)
) {
$message = sprintf('Invalid ' . static::FORMAT_NAME . ' compiler %s!', $this->compiler);
return Result::error($this, $message);
}
foreach ($this->files as $in => $out) {
if (!file_exists($in)) {
$message = sprintf('File %s not found.', $in);
return Result::error($this, $message);
}
if (file_exists($out) && !is_writable($out)) {
return Result::error($this, 'Destination already exists and cannot be overwritten.');
}
}
foreach ($this->files as $in => $out) {
$css = $this->compile($in);
if ($css instanceof Result) {
return $css;
} elseif (false === $css) {
$message = sprintf(
ucfirst(static::FORMAT_NAME) . ' compilation failed for %s.',
$in
);
return Result::error($this, $message);
}
$dst = $out . '.part';
$write_result = file_put_contents($dst, $css);
if (false === $write_result) {
$message = sprintf('File write failed: %s', $out);
@unlink($dst);
return Result::error($this, $message);
}
// Cannot be cross-volume: should always succeed
@rename($dst, $out);
$this->printTaskSuccess(
sprintf(
'Wrote CSS to %s',
$out
)
);
}
return Result::success($this, 'All ' . static::FORMAT_NAME . ' files compiled.');
}
}
PK CV
EJH H src/Task/Assets/Less.phpnu W+A taskLess([
* 'less/default.less' => 'css/default.css'
* ])
* ->run();
* ?>
* ```
*
* Use one of both less compilers in your project:
*
* ```
* "leafo/lessphp": "~0.5",
* "oyejorge/less.php": "~1.5"
* ```
*
* Specify directory (string or array) for less imports lookup:
*
* ```php
* taskLess([
* 'less/default.less' => 'css/default.css'
* ])
* ->importDir('less')
* ->compiler('lessphp')
* ->run();
* ?>
* ```
*
* You can implement additional compilers by extending this task and adding a
* method named after them and overloading the lessCompilers() method to
* inject the name there.
*/
class Less extends CssPreprocessor
{
const FORMAT_NAME = 'less';
protected $compilers = [
'less', // https://github.com/oyejorge/less.php
'lessphp', //https://github.com/leafo/lessphp
];
/**
* lessphp compiler
* @link https://github.com/leafo/lessphp
*
* @param string $file
* @return string
*/
protected function lessphp($file)
{
if (!class_exists('\lessc')) {
return Result::errorMissingPackage($this, 'lessc', 'leafo/lessphp');
}
$lessCode = file_get_contents($file);
$less = new \lessc();
if (isset($this->compilerOptions['importDirs'])) {
$less->setImportDir($this->compilerOptions['importDirs']);
}
return $less->compile($lessCode);
}
/**
* less compiler
* @link https://github.com/oyejorge/less.php
*
* @param string $file
* @return string
*/
protected function less($file)
{
if (!class_exists('\Less_Parser')) {
return Result::errorMissingPackage($this, 'Less_Parser', 'oyejorge/less.php');
}
$lessCode = file_get_contents($file);
$parser = new \Less_Parser();
$parser->SetOptions($this->compilerOptions);
if (isset($this->compilerOptions['importDirs'])) {
$importDirs = [];
foreach ($this->compilerOptions['importDirs'] as $dir) {
$importDirs[$dir] = $dir;
}
$parser->SetImportDirs($importDirs);
}
$parser->parse($lessCode);
return $parser->getCss();
}
}
PK CVsF; src/Task/Assets/Scss.phpnu W+A taskScss([
* 'scss/default.scss' => 'css/default.css'
* ])
* ->importDir('assets/styles')
* ->run();
* ?>
* ```
*
* Use the following scss compiler in your project:
*
* ```
* "leafo/scssphp": "~0.1",
* ```
*
* You can implement additional compilers by extending this task and adding a
* method named after them and overloading the scssCompilers() method to
* inject the name there.
*/
class Scss extends CssPreprocessor
{
const FORMAT_NAME = 'scss';
protected $compilers = [
'scssphp', // https://github.com/leafo/scssphp
];
/**
* scssphp compiler
* @link https://github.com/leafo/scssphp
*
* @param string $file
* @return string
*/
protected function scssphp($file)
{
if (!class_exists('\Leafo\ScssPhp\Compiler')) {
return Result::errorMissingPackage($this, 'scssphp', 'leafo/scssphp');
}
$scssCode = file_get_contents($file);
$scss = new \Leafo\ScssPhp\Compiler();
// set options for the scssphp compiler
if (isset($this->compilerOptions['importDirs'])) {
$scss->setImportPaths($this->compilerOptions['importDirs']);
}
if (isset($this->compilerOptions['formatter'])) {
$scss->setFormatter($this->compilerOptions['formatter']);
}
return $scss->compile($scssCode);
}
/**
* Sets the formatter for scssphp
*
* The method setFormatter($formatterName) sets the current formatter to $formatterName,
* the name of a class as a string that implements the formatting interface. See the source
* for Leafo\ScssPhp\Formatter\Expanded for an example.
*
* Five formatters are included with leafo/scssphp:
* - Leafo\ScssPhp\Formatter\Expanded
* - Leafo\ScssPhp\Formatter\Nested (default)
* - Leafo\ScssPhp\Formatter\Compressed
* - Leafo\ScssPhp\Formatter\Compact
* - Leafo\ScssPhp\Formatter\Crunched
*
* @link http://leafo.github.io/scssphp/docs/#output-formatting
* @param string $formatterName
* @return Scss
*/
public function setFormatter($formatterName)
{
return parent::setFormatter($formatterName);
}
}
PK CV+F src/Task/Assets/loadTasks.phpnu W+A taskMinify( 'web/assets/theme.css' )
* ->run()
* ?>
* ```
* Please install additional dependencies to use:
*
* ```
* "patchwork/jsqueeze": "~1.0",
* "natxet/CssMin": "~3.0"
* ```
*/
class Minify extends BaseTask
{
/** @var array $types */
protected $types = ['css', 'js'];
/** @var string $text */
protected $text;
/** @var string $dst */
protected $dst;
/** @var string $type css|js */
protected $type;
/** @var array $squeezeOptions */
protected $squeezeOptions = [
'singleLine' => true,
'keepImportantComments' => true,
'specialVarRx' => false,
];
/**
* Constructor. Accepts asset file path or string source.
*
* @param bool|string $input
*/
public function __construct($input)
{
if (file_exists($input)) {
return $this->fromFile($input);
}
return $this->fromText($input);
}
/**
* Sets destination. Tries to guess type from it.
*
* @param string $dst
*
* @return $this
*/
public function to($dst)
{
$this->dst = $dst;
if (!empty($this->dst) && empty($this->type)) {
$this->type($this->getExtension($this->dst));
}
return $this;
}
/**
* Sets type with validation.
*
* @param string $type css|js
*
* @return $this
*/
public function type($type)
{
$type = strtolower($type);
if (in_array($type, $this->types)) {
$this->type = $type;
}
return $this;
}
/**
* Sets text from string source.
*
* @param string $text
*
* @return $this
*/
protected function fromText($text)
{
$this->text = (string)$text;
unset($this->type);
return $this;
}
/**
* Sets text from asset file path. Tries to guess type and set default destination.
*
* @param string $path
*
* @return $this
*/
protected function fromFile($path)
{
$this->text = file_get_contents($path);
unset($this->type);
$this->type($this->getExtension($path));
if (empty($this->dst) && !empty($this->type)) {
$ext_length = strlen($this->type) + 1;
$this->dst = substr($path, 0, -$ext_length) . '.min.' . $this->type;
}
return $this;
}
/**
* Gets file extension from path.
*
* @param string $path
*
* @return string
*/
protected function getExtension($path)
{
return pathinfo($path, PATHINFO_EXTENSION);
}
/**
* Minifies and returns text.
*
* @return string|bool
*/
protected function getMinifiedText()
{
switch ($this->type) {
case 'css':
if (!class_exists('\CssMin')) {
return Result::errorMissingPackage($this, 'CssMin', 'natxet/CssMin');
}
return \CssMin::minify($this->text);
break;
case 'js':
if (!class_exists('\JSqueeze') && !class_exists('\Patchwork\JSqueeze')) {
return Result::errorMissingPackage($this, 'Patchwork\JSqueeze', 'patchwork/jsqueeze');
}
if (class_exists('\JSqueeze')) {
$jsqueeze = new \JSqueeze();
} else {
$jsqueeze = new \Patchwork\JSqueeze();
}
return $jsqueeze->squeeze(
$this->text,
$this->squeezeOptions['singleLine'],
$this->squeezeOptions['keepImportantComments'],
$this->squeezeOptions['specialVarRx']
);
break;
}
return false;
}
/**
* Single line option for the JS minimisation.
*
* @return $this;
*/
public function singleLine($singleLine)
{
$this->squeezeOptions['singleLine'] = (bool)$singleLine;
return $this;
}
/**
* keepImportantComments option for the JS minimisation.
*
* @return $this;
*/
public function keepImportantComments($keepImportantComments)
{
$this->squeezeOptions['keepImportantComments'] = (bool)$keepImportantComments;
return $this;
}
/**
* specialVarRx option for the JS minimisation.
*
* @return $this;
*/
public function specialVarRx($specialVarRx)
{
$this->squeezeOptions['specialVarRx'] = (bool)$specialVarRx;
return $this;
}
/**
* @return string
*/
public function __toString()
{
return $this->getMinifiedText();
}
/**
* Writes minified result to destination.
*
* @return Result
*/
public function run()
{
if (empty($this->type)) {
return Result::error($this, 'Unknown asset type.');
}
if (empty($this->dst)) {
return Result::error($this, 'Unknown file destination.');
}
if (file_exists($this->dst) && !is_writable($this->dst)) {
return Result::error($this, 'Destination already exists and cannot be overwritten.');
}
$size_before = strlen($this->text);
$minified = $this->getMinifiedText();
if ($minified instanceof Result) {
return $minified;
} elseif (false === $minified) {
return Result::error($this, 'Minification failed.');
}
$size_after = strlen($minified);
$dst = $this->dst . '.part';
$write_result = file_put_contents($dst, $minified);
if (false === $write_result) {
@unlink($dst);
return Result::error($this, 'File write failed.');
}
// Cannot be cross-volume; should always succeed.
@rename($dst, $this->dst);
if ($size_before === 0) {
$minified_percent = 0;
} else {
$minified_percent = number_format(100 - ($size_after / $size_before * 100), 1);
}
$this->printTaskSuccess(
sprintf(
'Wrote %s',
$this->dst
)
);
$this->printTaskSuccess(
sprintf(
'Wrote %s (reduced by %s / %s%%)',
$this->formatBytes($size_after),
$this->formatBytes(($size_before - $size_after)),
$minified_percent
)
);
return Result::success($this, 'Asset minified.');
}
}
PK CV]WK WK src/Task/Assets/ImageMinify.phpnu W+A taskImageMinify('assets/images/*')
* ->to('dist/images/')
* ->run();
* ```
*
* This will use the following minifiers:
*
* - PNG: optipng
* - GIF: gifsicle
* - JPG, JPEG: jpegtran
* - SVG: svgo
*
* When the minifier is specified the task will use that for all the input files. In that case
* it is useful to filter the files with the extension:
*
* ```php
* $this->taskImageMinify('assets/images/*.png')
* ->to('dist/images/')
* ->minifier('pngcrush');
* ->run();
* ```
*
* The task supports the following minifiers:
*
* - optipng
* - pngquant
* - advpng
* - pngout
* - zopflipng
* - pngcrush
* - gifsicle
* - jpegoptim
* - jpeg-recompress
* - jpegtran
* - svgo (only minification, no downloading)
*
* You can also specifiy extra options for the minifiers:
*
* ```php
* $this->taskImageMinify('assets/images/*.jpg')
* ->to('dist/images/')
* ->minifier('jpegtran', ['-progressive' => null, '-copy' => 'none'])
* ->run();
* ```
*
* This will execute as:
* `jpegtran -copy none -progressive -optimize -outfile "dist/images/test.jpg" "/var/www/test/assets/images/test.jpg"`
*/
class ImageMinify extends BaseTask
{
/**
* Destination directory for the minified images.
*
* @var string
*/
protected $to;
/**
* Array of the source files.
*
* @var array
*/
protected $dirs = [];
/**
* Symfony 2 filesystem.
*
* @var sfFilesystem
*/
protected $fs;
/**
* Target directory for the downloaded binary executables.
*
* @var string
*/
protected $executableTargetDir;
/**
* Array for the downloaded binary executables.
*
* @var array
*/
protected $executablePaths = [];
/**
* Array for the individual results of all the files.
*
* @var array
*/
protected $results = [];
/**
* Default minifier to use.
*
* @var string
*/
protected $minifier;
/**
* Array for minifier options.
*
* @var array
*/
protected $minifierOptions = [];
/**
* Supported minifiers.
*
* @var array
*/
protected $minifiers = [
// Default 4
'optipng',
'gifsicle',
'jpegtran',
'svgo',
// PNG
'pngquant',
'advpng',
'pngout',
'zopflipng',
'pngcrush',
// JPG
'jpegoptim',
'jpeg-recompress',
];
/**
* Binary repositories of Imagemin.
*
* @link https://github.com/imagemin
*
* @var array
*/
protected $imageminRepos = [
// PNG
'optipng' => 'https://github.com/imagemin/optipng-bin',
'pngquant' => 'https://github.com/imagemin/pngquant-bin',
'advpng' => 'https://github.com/imagemin/advpng-bin',
'pngout' => 'https://github.com/imagemin/pngout-bin',
'zopflipng' => 'https://github.com/imagemin/zopflipng-bin',
'pngcrush' => 'https://github.com/imagemin/pngcrush-bin',
// Gif
'gifsicle' => 'https://github.com/imagemin/gifsicle-bin',
// JPG
'jpegtran' => 'https://github.com/imagemin/jpegtran-bin',
'jpegoptim' => 'https://github.com/imagemin/jpegoptim-bin',
'cjpeg' => 'https://github.com/imagemin/mozjpeg-bin', // note: we do not support this minifier because it creates JPG from non-JPG files
'jpeg-recompress' => 'https://github.com/imagemin/jpeg-recompress-bin',
// WebP
'cwebp' => 'https://github.com/imagemin/cwebp-bin', // note: we do not support this minifier because it creates WebP from non-WebP files
];
public function __construct($dirs)
{
is_array($dirs)
? $this->dirs = $dirs
: $this->dirs[] = $dirs;
$this->fs = new sfFileSystem();
// guess the best path for the executables based on __DIR__
if (($pos = strpos(__DIR__, 'codegyre/robo')) !== false) {
// the executables should be stored in vendor/bin
$this->executableTargetDir = substr(__DIR__, 0, $pos).'bin';
}
// check if the executables are already available
foreach ($this->imageminRepos as $exec => $url) {
$path = $this->executableTargetDir.'/'.$exec;
// if this is Windows add a .exe extension
if (substr($this->getOS(), 0, 3) == 'win') {
$path .= '.exe';
}
if (is_file($path)) {
$this->executablePaths[$exec] = $path;
}
}
}
public function run()
{
// find the files
$files = $this->findFiles($this->dirs);
// minify the files
$result = $this->minify($files);
// check if there was an error
if ($result instanceof Result) {
return $result;
}
$amount = (count($files) == 1 ? 'image' : 'images');
$message = sprintf('Minified %d out of %d %s into %s', count($this->results['success']), count($files), $amount, $this->to);
if (count($this->results['success']) == count($files)) {
$this->printTaskSuccess($message);
return Result::success($this);
} else {
return Result::error($this, $message);
}
}
/**
* Sets the target directory where the files will be copied to.
*
* @param string $target
*
* @return $this
*/
public function to($target)
{
$this->to = rtrim($target, '/');
return $this;
}
/**
* Sets the minifier.
*
* @param string $minifier
* @param array $options
*
* @return $this
*/
public function minifier($minifier, array $options = [])
{
$this->minifier = $minifier;
$this->minifierOptions = array_merge($this->minifierOptions, $options);
return $this;
}
protected function findFiles($dirs)
{
$files = array();
// find the files
foreach ($dirs as $k => $v) {
// reset finder
$finder = new Finder();
$dir = $k;
$to = $v;
// check if target was given with the to() method instead of key/value pairs
if (is_int($k)) {
$dir = $v;
if (isset($this->to)) {
$to = $this->to;
} else {
throw new TaskException($this, 'target directory is not defined');
}
}
try {
$finder->files()->in($dir);
} catch (\InvalidArgumentException $e) {
// if finder cannot handle it, try with in()->name()
if (strpos($dir, '/') === false) {
$dir = './'.$dir;
}
$parts = explode('/', $dir);
$new_dir = implode('/', array_slice($parts, 0, -1));
try {
$finder->files()->in($new_dir)->name(array_pop($parts));
} catch (\InvalidArgumentException $e) {
return Result::error($this, $e->getMessage());
}
}
foreach ($finder as $file) {
// store the absolute path as key and target as value in the files array
$files[$file->getRealpath()] = $this->getTarget($file->getRealPath(), $to);
}
$amount = count($finder).(count($finder) == 1 ? ' file' : ' files');
$this->printTaskInfo('Found '.$amount.' in '.$dir.'');
}
return $files;
}
protected function getTarget($file, $to)
{
$target = $to.'/'.basename($file);
return $target;
}
protected function minify($files)
{
// store the individual results into the results array
$this->results = [
'success' => [],
'error' => [],
];
// loop through the files
foreach ($files as $from => $to) {
if (!isset($this->minifier)) {
// check filetype based on the extension
$extension = strtolower(pathinfo($from, PATHINFO_EXTENSION));
// set the default minifiers based on the extension
switch ($extension) {
case 'png':
$minifier = 'optipng';
break;
case 'jpg':
case 'jpeg':
$minifier = 'jpegtran';
break;
case 'gif':
$minifier = 'gifsicle';
break;
case 'svg':
$minifier = 'svgo';
break;
}
} else {
if (!in_array($this->minifier, $this->minifiers, true)
&& !is_callable(strtr($this->minifier, '-', '_'))
) {
$message = sprintf('Invalid minifier %s!', $this->minifier);
return Result::error($this, $message);
}
$minifier = $this->minifier;
}
// replace - with _ in minifier name (eg. jpeg-recompress)
$funcMinifier = strtr($minifier, '-', '_');
// call the minifier method which prepares the command
if (is_callable($funcMinifier)) {
$command = call_user_func($funcMinifier, $from, $to, $this->minifierOptions);
} elseif (method_exists($this, $funcMinifier)) {
$command = $this->{$funcMinifier}($from, $to);
} else {
$message = sprintf('Minifier method %s cannot be found!', $funcMinifier);
return Result::error($this, $message);
}
// launch the command
$this->printTaskInfo(sprintf('Minifying %s with %s', $from, $minifier));
$result = $this->executeCommand($command);
// check the return code
if ($result->getExitCode() == 127) {
$this->printTaskError(sprintf('The %s executable cannot be found', $minifier));
// try to install from imagemin repository
if (array_key_exists($minifier, $this->imageminRepos)) {
$result = $this->installFromImagemin($minifier);
if ($result instanceof Result) {
if ($result->wasSuccessful()) {
$this->printTaskSuccess($result->getMessage());
// retry the conversion with the downloaded executable
if (is_callable($minifier)) {
$command = call_user_func($minifier, $from, $to, $minifierOptions);
} elseif (method_exists($this, $minifier)) {
$command = $this->{$minifier}($from, $to);
}
// launch the command
$this->printTaskInfo(sprintf('Minifying %s with %s', $from, $minifier));
$result = $this->executeCommand($command);
} else {
$this->printTaskError($result->getMessage());
// the download was not successful
return $result;
}
}
} else {
return $result;
}
}
// check the success of the conversion
if ($result->getExitCode() !== 0) {
$this->results['error'][] = $from;
} else {
$this->results['success'][] = $from;
}
}
}
protected function getOS()
{
$os = php_uname('s');
$os .= '/'.php_uname('m');
// replace x86_64 to x64, because the imagemin repo uses that
$os = str_replace('x86_64', 'x64', $os);
// replace i386, i686, etc to x86, because of imagemin
$os = preg_replace('/i[0-9]86/', 'x86', $os);
// turn info to lowercase, because of imagemin
$os = strtolower($os);
return $os;
}
protected function executeCommand($command)
{
// insert the options into the command
$a = explode(' ', $command);
$executable = array_shift($a);
foreach ($this->minifierOptions as $key => $value) {
// first prepend the value
if (!empty($value)) {
array_unshift($a, $value);
}
// then add the key
if (!is_numeric($key)) {
array_unshift($a, $key);
}
}
// check if the executable can be replaced with the downloaded one
if (array_key_exists($executable, $this->executablePaths)) {
$executable = $this->executablePaths[$executable];
}
array_unshift($a, $executable);
$command = implode(' ', $a);
// execute the command
$exec = new Exec($command);
return $exec->printed(false)->run();
}
protected function installFromImagemin($executable)
{
// check if there is an url defined for the executable
if (!array_key_exists($executable, $this->imageminRepos)) {
$message = sprintf('The executable %s cannot be found in the defined imagemin repositories', $executable);
return Result::error($this, $message);
}
$this->printTaskInfo(sprintf('Downloading the %s executable from the imagemin repository', $executable));
$os = $this->getOS();
$url = $this->imageminRepos[$executable].'/blob/master/vendor/'.$os.'/'.$executable.'?raw=true';
if (substr($os, 0, 3) == 'win') {
// if it is win, add a .exe extension
$url = $this->imageminRepos[$executable].'/blob/master/vendor/'.$os.'/'.$executable.'.exe?raw=true';
}
$data = @file_get_contents($url, false, null);
if ($data === false) {
// there is something wrong with the url, try it without the version info
$url = preg_replace('/x[68][64]\//', '', $url);
$data = @file_get_contents($url, false, null);
if ($data === false) {
// there is still something wrong with the url if it is win, try with win32
if (substr($os, 0, 3) == 'win') {
$url = preg_replace('win/', 'win32/', $url);
$data = @file_get_contents($url, false, null);
if ($data === false) {
// there is nothing more we can do
$message = sprintf('Could not download the executable %s', $executable);
return Result::error($this, $message);
}
}
// if it is not windows there is nothing we can do
$message = sprintf('Could not download the executable %s', $executable);
return Result::error($this, $message);
}
}
// check if target directory exists
if (!is_dir($this->executableTargetDir)) {
mkdir($this->executableTargetDir);
}
// save the executable into the target dir
$path = $this->executableTargetDir.'/'.$executable;
if (substr($os, 0, 3) == 'win') {
// if it is win, add a .exe extension
$path = $this->executableTargetDir.'/'.$executable.'.exe';
}
$result = file_put_contents($path, $data);
if ($result === false) {
$message = sprintf('Could not copy the executable %s to %s', $executable, $target_dir);
return Result::error($this, $message);
}
// set the binary to executable
chmod($path, 0755);
// if everything successful, store the executable path
$this->executablePaths[$executable] = $this->executableTargetDir.'/'.$executable;
// if it is win, add a .exe extension
if (substr($os, 0, 3) == 'win') {
$this->executablePaths[$executable] .= '.exe';
}
$message = sprintf('Executable %s successfully downloaded', $executable);
return Result::success($this, $message);
}
protected function optipng($from, $to)
{
$command = sprintf('optipng -quiet -out "%s" -- "%s"', $to, $from);
if ($from != $to && is_file($to)) {
// earlier versions of optipng do not overwrite the target without a backup
// http://sourceforge.net/p/optipng/bugs/37/
unlink($to);
}
return $command;
}
protected function jpegtran($from, $to)
{
$command = sprintf('jpegtran -optimize -outfile "%s" "%s"', $to, $from);
return $command;
}
protected function gifsicle($from, $to)
{
$command = sprintf('gifsicle -o "%s" "%s"', $to, $from);
return $command;
}
protected function svgo($from, $to)
{
$command = sprintf('svgo "%s" "%s"', $from, $to);
return $command;
}
protected function pngquant($from, $to)
{
$command = sprintf('pngquant --force --output "%s" "%s"', $to, $from);
return $command;
}
protected function advpng($from, $to)
{
// advpng does not have any output parameters, copy the file and then compress the copy
$command = sprintf('advpng --recompress --quiet "%s"', $to);
$this->fs->copy($from, $to, true);
return $command;
}
protected function pngout($from, $to)
{
$command = sprintf('pngout -y -q "%s" "%s"', $from, $to);
return $command;
}
protected function zopflipng($from, $to)
{
$command = sprintf('zopflipng -y "%s" "%s"', $from, $to);
return $command;
}
protected function pngcrush($from, $to)
{
$command = sprintf('pngcrush -q -ow "%s" "%s"', $from, $to);
return $command;
}
protected function jpegoptim($from, $to)
{
// jpegoptim only takes the destination directory as an argument
$command = sprintf('jpegoptim --quiet -o --dest "%s" "%s"', dirname($to), $from);
return $command;
}
protected function jpeg_recompress($from, $to)
{
$command = sprintf('jpeg-recompress --quiet "%s" "%s"', $from, $to);
return $command;
}
}
PK CVgW W src/Task/Gulp/Base.phpnu W+A option('silent');
return $this;
}
/**
* adds `--no-color` option to gulp
*
* @return $this
*/
public function noColor()
{
$this->option('no-color');
return $this;
}
/**
* adds `--color` option to gulp
*
* @return $this
*/
public function color()
{
$this->option('color');
return $this;
}
/**
* adds `--tasks-simple` option to gulp
*
* @return $this
*/
public function simple()
{
$this->option('tasks-simple');
return $this;
}
public function __construct($task, $pathToGulp = null)
{
$this->task = $task;
if ($pathToGulp) {
$this->command = $pathToGulp;
} elseif (is_executable('/usr/bin/gulp')) {
$this->command = '/usr/bin/gulp';
} elseif (is_executable('/usr/local/bin/gulp')) {
$this->command = '/usr/local/bin/gulp';
} else {
throw new TaskException(__CLASS__, "Executable not found.");
}
}
public function getCommand()
{
return "{$this->command} " . ProcessUtils::escapeArgument($this->task) . "{$this->arguments}";
}
}PK CVA, src/Task/Gulp/loadTasks.phpnu W+A taskGulpRun()->run();
*
* // run task 'clean' with --silent option
* $this->taskGulpRun('clean')
* ->silent()
* ->run();
* ?>
* ```
*/
class Run extends Base implements CommandInterface
{
public function run()
{
if (strlen($this->arguments)) {
$this->printTaskInfo('Running Gulp task: ' . $this->task . ' with arguments: ' . $this->arguments);
} else {
$this->printTaskInfo('Running Gulp task: ' . $this->task . ' without arguments');
}
return $this->executeCommand($this->getCommand());
}
}PK CVKK src/Task/Bower/Base.phpnu W+A option('allow-root');
return $this;
}
/**
* adds `force-latest` option to bower
*
* @return $this
*/
public function forceLatest()
{
$this->option('force-latest');
return $this;
}
/**
* adds `production` option to bower
*
* @return $this
*/
public function noDev()
{
$this->option('production');
return $this;
}
/**
* adds `offline` option to bower
*
* @return $this
*/
public function offline()
{
$this->option('offline');
return $this;
}
public function __construct($pathToBower = null)
{
if ($pathToBower) {
$this->command = $pathToBower;
} elseif (is_executable('/usr/bin/bower')) {
$this->command = '/usr/bin/bower';
} elseif (is_executable('/usr/local/bin/bower')) {
$this->command = '/usr/local/bin/bower';
} else {
throw new TaskException(__CLASS__, "Executable not found.");
}
}
public function getCommand()
{
return "{$this->command} {$this->action}{$this->arguments}";
}
}PK CVվ5 src/Task/Bower/Update.phpnu W+A taskBowerUpdate->run();
*
* // prefer dist with custom path
* $this->taskBowerUpdate('path/to/my/bower')
* ->noDev()
* ->run();
* ?>
* ```
*/
class Update extends Base
{
protected $action = 'update';
public function run()
{
$this->printTaskInfo('Update Bower packages: ' . $this->arguments);
return $this->executeCommand($this->getCommand());
}
}PK CVxpsY Y src/Task/Bower/Install.phpnu W+A taskBowerInstall()->run();
*
* // prefer dist with custom path
* $this->taskBowerInstall('path/to/my/bower')
* ->noDev()
* ->run();
* ?>
* ```
*/
class Install extends Base implements CommandInterface
{
protected $action = 'install';
public function run()
{
$this->printTaskInfo('Install Bower packages: ' . $this->arguments);
return $this->executeCommand($this->getCommand());
}
}PK CV src/Task/Bower/loadTasks.phpnu W+A taskRsync()
* ->fromPath('src/')
* ->toHost('localhost')
* ->toUser('dev')
* ->toPath('/var/www/html/app/')
* ->remoteShell('ssh -i public_key')
* ->recursive()
* ->excludeVcs()
* ->checksum()
* ->wholeFile()
* ->verbose()
* ->progress()
* ->humanReadable()
* ->stats()
* ->run();
* ```
*
* You could also clone the task and do a dry-run first:
*
* ``` php
* $rsync = $this->taskRsync()
* ->fromPath('src/')
* ->toPath('example.com:/var/www/html/app/')
* ->archive()
* ->excludeVcs()
* ->progress()
* ->stats();
*
* $dryRun = clone $rsync;
* $dryRun->dryRun()->run();
* if ('y' === $this->ask('Do you want to run (y/n)')) {
* $rsync->run();
* }
* ```
*
* @method \Robo\Task\Remote\Rsync fromUser(string $user)
* @method \Robo\Task\Remote\Rsync fromHost(string $hostname)
* @method \Robo\Task\Remote\Rsync toUser(string $user)
* @method \Robo\Task\Remote\Rsync toHost(string $hostname)
*/
class Rsync extends BaseTask implements CommandInterface
{
use \Robo\Common\ExecOneCommand;
use \Robo\Common\DynamicParams;
protected $fromUser;
protected $fromHost;
protected $fromPath;
protected $toUser;
protected $toHost;
protected $toPath;
public static function init()
{
return new static();
}
public function __construct()
{
$this->command = 'rsync';
}
/**
* This can either be a full rsync path spec (user@host:path) or just a path.
* In case of the former do not specify host and user.
*
* @param string $path
* @return $this
*/
public function fromPath($path)
{
$this->fromPath = $path;
return $this;
}
/**
* This can either be a full rsync path spec (user@host:path) or just a path.
* In case of the former do not specify host and user.
*
* @param string $path
* @return $this
*/
public function toPath($path)
{
$this->toPath = $path;
return $this;
}
public function progress()
{
$this->option(__FUNCTION__);
return $this;
}
public function stats()
{
$this->option(__FUNCTION__);
return $this;
}
public function recursive()
{
$this->option(__FUNCTION__);
return $this;
}
public function verbose()
{
$this->option(__FUNCTION__);
return $this;
}
public function checksum()
{
$this->option(__FUNCTION__);
return $this;
}
public function archive()
{
$this->option(__FUNCTION__);
return $this;
}
public function compress()
{
$this->option(__FUNCTION__);
return $this;
}
public function owner()
{
$this->option(__FUNCTION__);
return $this;
}
public function group()
{
$this->option(__FUNCTION__);
return $this;
}
public function times()
{
$this->option(__FUNCTION__);
return $this;
}
public function delete()
{
$this->option(__FUNCTION__);
return $this;
}
public function timeout($seconds)
{
$this->option(__FUNCTION__, $seconds);
return $this;
}
public function humanReadable()
{
$this->option('human-readable');
return $this;
}
public function wholeFile()
{
$this->option('whole-file');
return $this;
}
public function dryRun()
{
$this->option('dry-run');
return $this;
}
public function itemizeChanges()
{
$this->option('itemize-changes');
return $this;
}
/**
* Excludes .git/, .svn/ and .hg/ folders.
*
* @return $this
*/
public function excludeVcs()
{
return $this->exclude(['.git/', '.svn/', '.hg/']);
}
public function exclude($pattern)
{
return $this->optionList(__FUNCTION__, $pattern);
}
public function excludeFrom($file)
{
if (!is_readable($file)) {
throw new TaskException($this, "Exclude file $file is not readable");
}
return $this->option('exclude-from', $file);
}
public function includeFilter($pattern)
{
return $this->optionList('include', $pattern);
}
public function filter($pattern)
{
return $this->optionList(__FUNCTION__, $pattern);
}
public function filesFrom($file)
{
if (!is_readable($file)) {
throw new TaskException($this, "Files-from file $file is not readable");
}
return $this->option('files-from', $file);
}
public function remoteShell($command)
{
$this->option('rsh', "'$command'");
return $this;
}
/**
* @return \Robo\Result
*/
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo("Running {$command}");
return $this->executeCommand($command);
}
/**
* Returns command that can be executed.
* This method is used to pass generated command from one task to another.
*
* @return string
*/
public function getCommand()
{
$this->option(null, escapeshellarg($this->getPathSpec('from')))
->option(null, escapeshellarg($this->getPathSpec('to')));
return $this->command . $this->arguments;
}
protected function getPathSpec($type)
{
if ($type !== 'from' && $type !== 'to') {
throw new TaskException($this, 'Type must be "from" or "to".');
}
foreach (['host', 'user', 'path'] as $part) {
$varName = $type . ucfirst($part);
$$part = $this->$varName;
}
$spec = isset($path) ? $path : '';
if (!empty($host)) {
$spec = "{$host}:{$spec}";
}
if (!empty($user)) {
$spec = "{$user}@{$spec}";
}
return $spec;
}
}
PK CV135 5 src/Task/Remote/Ssh.phpnu W+A taskSshExec('remote.example.com', 'user')
* ->remoteDir('/var/www/html')
* ->exec('ls -la')
* ->exec('chmod g+x logs')
* ->run();
*
* ```
*
* You can even exec other tasks (which implement CommandInterface):
*
* ```php
* $gitTask = $this->taskGitStack()
* ->checkout('master')
* ->pull();
*
* $this->taskSshExec('remote.example.com')
* ->remoteDir('/var/www/html/site')
* ->exec($gitTask)
* ->run();
* ```
*
* You can configure the remote directory for all future calls:
*
* ```php
* \Robo\Task\Remote\Ssh::configure('remoteDir', '/some-dir');
* ```
*
* @method $this stopOnFail(bool $stopOnFail) Whether or not to chain commands together with &&
* and stop the chain if one command fails
* @method $this remoteDir(string $remoteWorkingDirectory) Changes to the given directory before running commands
*/
class Ssh extends BaseTask implements CommandInterface
{
use \Robo\Common\DynamicParams;
use \Robo\Common\CommandReceiver;
use \Robo\Common\ExecOneCommand;
protected $hostname;
protected $user;
protected $stopOnFail = true;
protected $exec = [];
/**
* Changes to the given directory before running commands.
*
* @var string
*/
protected $remoteDir;
public function __construct($hostname = null, $user = null)
{
$this->hostname = $hostname;
$this->user = $user;
}
public function identityFile($filename)
{
$this->option('-i', $filename);
return $this;
}
public function port($port)
{
$this->option('-p', $port);
return $this;
}
public function forcePseudoTty()
{
$this->option('-t');
return $this;
}
public function quiet()
{
$this->option('-q');
return $this;
}
public function verbose()
{
$this->option('-v');
return $this;
}
/**
* @param string|CommandInterface $command
* @return $this
*/
public function exec($command)
{
if (is_array($command)) {
$command = implode(' ', array_filter($command));
}
$this->exec[] = $command;
return $this;
}
/**
* Returns command that can be executed.
* This method is used to pass generated command from one task to another.
*
* @return string
*/
public function getCommand()
{
$commands = [];
foreach ($this->exec as $command) {
$commands[] = $this->receiveCommand($command);
}
$remoteDir = $this->remoteDir ? $this->remoteDir : $this->getConfigValue('remoteDir');
if (!empty($remoteDir)) {
array_unshift($commands, sprintf('cd "%s"', $remoteDir));
}
$command = implode($this->stopOnFail ? ' && ' : ' ; ', $commands);
return $this->sshCommand($command);
}
/**
* @return \Robo\Result
*/
public function run()
{
$this->validateParameters();
$command = $this->getCommand();
$result = $this->executeCommand($command);
if (!$result->wasSuccessful()) {
return $result;
}
return Result::success($this);
}
protected function validateParameters()
{
if (empty($this->hostname)) {
throw new TaskException($this, 'Please set a hostname');
}
if (empty($this->exec)) {
throw new TaskException($this, 'Please add at least one command');
}
}
/**
* Returns an ssh command string running $command on the remote.
*
* @param string|CommandInterface $command
* @return string
*/
protected function sshCommand($command)
{
$command = $this->receiveCommand($command);
$sshOptions = $this->arguments;
$hostSpec = $this->hostname;
if ($this->user) {
$hostSpec = $this->user . '@' . $hostSpec;
}
return sprintf("ssh{$sshOptions} {$hostSpec} '{$command}'");
}
}
PK CV|| | src/Task/Remote/loadTasks.phpnu W+A checkout($url)->run();
}
/**
* @param $url
* @return \Robo\Result
*/
protected function _gitClone($url)
{
return (new GitStack())->cloneRepo($url)->run();
}
} PK CVG|
|
src/Task/Vcs/GitStack.phpnu W+A taskGitStack()
* ->stopOnFail()
* ->add('-A')
* ->commit('adding everything')
* ->push('origin','master')
* ->tag('0.6.0')
* ->push('origin','0.6.0')
* ->run()
*
* $this->taskGitStack()
* ->stopOnFail()
* ->add('doc/*')
* ->commit('doc updated')
* ->push()
* ->run();
* ?>
* ```
*/
class GitStack extends CommandStack
{
public function __construct($pathToGit = 'git')
{
$this->executable = $pathToGit;
}
/**
* Executes `git clone`
*
* @param $repo
* @param string $to
* @return $this
*/
public function cloneRepo($repo, $to = "")
{
return $this->exec(['clone', $repo, $to]);
}
/**
* Executes `git add` command with files to add pattern
*
* @param $pattern
* @return $this
*/
public function add($pattern)
{
return $this->exec([__FUNCTION__, $pattern]);
}
/**
* Executes `git commit` command with a message
*
* @param $message
* @param string $options
* @return $this
*/
public function commit($message, $options = "")
{
return $this->exec([__FUNCTION__, "-m '$message'", $options]);
}
/**
* Executes `git pull` command.
*
* @param string $origin
* @param string $branch
* @return $this
*/
public function pull($origin = '', $branch = '')
{
return $this->exec([__FUNCTION__, $origin, $branch]);
}
/**
* Executes `git push` command
*
* @param string $origin
* @param string $branch
* @return $this
*/
public function push($origin = '', $branch = '')
{
return $this->exec([__FUNCTION__, $origin, $branch]);
}
/**
* Performs git merge
*
* @param string $branch
* @return $this
*/
public function merge($branch)
{
return $this->exec([__FUNCTION__, $branch]);
}
/**
* Executes `git checkout` command
*
* @param $branch
* @return $this
*/
public function checkout($branch)
{
return $this->exec([__FUNCTION__, $branch]);
}
/**
* Executes `git tag` command
*
* @param $tag_name
* @return $this
*/
public function tag($tag_name)
{
return $this->exec([__FUNCTION__, $tag_name]);
}
public function run()
{
$this->printTaskInfo("Running git commands...");
return parent::run();
}
}
PK CV+V src/Task/Vcs/loadTasks.phpnu W+A src/Task/Vcs/SvnStack.phpnu W+A taskSvnStack()
* ->checkout('http://svn.collab.net/repos/svn/trunk')
* ->run()
*
* // alternatively
* $this->_svnCheckout('http://svn.collab.net/repos/svn/trunk');
*
* $this->taskSvnStack('username', 'password')
* ->stopOnFail()
* ->update()
* ->add('doc/*')
* ->commit('doc updated')
* ->run();
* ?>
* ```
*/
class SvnStack extends CommandStack implements CommandInterface
{
protected $stopOnFail = false;
protected $result;
public function __construct($username='', $password='', $pathToSvn = 'svn')
{
$this->executable = $pathToSvn;
if (!empty($username)) {
$this->executable .= " --username $username";
}
if (!empty($password)) {
$this->executable .= " --password $password";
}
$this->result = Result::success($this);
}
/**
* Updates `svn update` command
*
* @param string $path
* @return $this;
*/
public function update($path = '')
{
return $this->exec("update $path");
}
/**
* Executes `svn add` command with files to add pattern
*
* @param $pattern
* @return $this
*/
public function add($pattern='')
{
return $this->exec("add $pattern");
}
/**
* Executes `svn commit` command with a message
*
* @param $message
* @param string $options
* @return $this
*/
public function commit($message, $options = "")
{
return $this->exec("commit -m '$message' $options");
}
/**
* Executes `svn checkout` command
*
* @param $branch
* @return $this
*/
public function checkout($branch)
{
return $this->exec("checkout $branch");
}
}
PK CVlAm m src/Task/Testing/Codecept.phpnu W+A taskCodecept()
* ->suite('acceptance')
* ->env('chrome')
* ->group('admin')
* ->xml()
* ->html()
* ->run();
*
* ?>
* ```
*
*/
class Codecept extends BaseTask implements CommandInterface, PrintedInterface
{
use \Robo\Common\ExecOneCommand;
protected $suite = '';
protected $test = '';
protected $command;
public function __construct($pathToCodeception = '')
{
if ($pathToCodeception) {
$this->command = "$pathToCodeception run";
} elseif (file_exists('vendor/bin/codecept')) {
$this->command = 'vendor/bin/codecept run';
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->command = 'call ' . $this->command;
}
} elseif (file_exists('codecept.phar')) {
$this->command = 'php codecept.phar run';
} else {
throw new TaskException(__CLASS__, "Neither composer nor phar installation of Codeception found");
}
}
public function suite($suite)
{
$this->suite = $suite;
return $this;
}
public function test($testName)
{
$this->test = $testName;
return $this;
}
/**
* set group option. Can be called multiple times
*
* @param $group
* @return $this
*/
public function group($group)
{
$this->option("group", $group);
return $this;
}
public function excludeGroup($group)
{
$this->option("skip-group", $group);
return $this;
}
/**
* generate json report
*
* @param string $file
* @return $this
*/
public function json($file = null)
{
$this->option("json", $file);
return $this;
}
/**
* generate xml JUnit report
*
* @param string $file
* @return $this
*/
public function xml($file = null)
{
$this->option("xml", $file);
return $this;
}
/**
* Generate html report
*
* @param string $dir
* @return $this
*/
public function html($dir = null)
{
$this->option("html", $dir);
return $this;
}
/**
* generate tap report
*
* @param string $file
* @return $this
*/
public function tap($file = null)
{
$this->option("tap", $file);
return $this;
}
/**
* provides config file other then default `codeception.yml` with `-c` option
*
* @param $file
* @return $this
*/
public function configFile($file)
{
$this->option("-c", $file);
return $this;
}
/**
* collect codecoverage in raw format. You may pass name of cov file to save results
*
* @param string $cov
* @return $this
*/
public function coverage($cov = null)
{
$this->option("coverage", $cov);
return $this;
}
/**
* execute in silent mode
*
* @return $this
*/
public function silent()
{
$this->option("silent");
return $this;
}
/**
* collect code coverage in xml format. You may pass name of xml file to save results
*
* @param string $xml
* @return $this
*/
public function coverageXml($xml = null)
{
$this->option("coverage-xml", $xml);
return $this;
}
/**
* collect code coverage and generate html report. You may pass
*
* @param string $html
* @return $this
*/
public function coverageHtml($html = null)
{
$this->option("coverage-html", $html);
return $this;
}
public function env($env)
{
$this->option("env", $env);
return $this;
}
public function debug()
{
$this->option("debug");
return $this;
}
public function getCommand()
{
$this->option(null, $this->suite)
->option(null, $this->test);
return $this->command . $this->arguments;
}
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo('Executing ' . $command);
return $this->executeCommand($command);
}
}
PK CV src/Task/Testing/PHPUnit.phpnu W+A taskPHPUnit()
* ->group('core')
* ->bootstrap('test/bootstrap.php')
* ->run()
*
* ?>
* ```
*/
class PHPUnit extends BaseTask implements CommandInterface, PrintedInterface
{
use \Robo\Common\ExecOneCommand;
protected $command;
/**
* Test files to run, they're appended to the command and arguments.
*
* @var string
*/
protected $files = '';
public function __construct($pathToPhpUnit = null)
{
if ($pathToPhpUnit) {
$this->command = $pathToPhpUnit;
} elseif (file_exists('vendor/bin/phpunit')) {
$this->command = 'vendor/bin/phpunit';
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->command = 'call ' . $this->command;
}
} elseif (file_exists('phpunit.phar')) {
$this->command = 'php phpunit.phar';
} elseif (is_executable('/usr/bin/phpunit')) {
$this->command = '/usr/bin/phpunit';
} elseif (is_executable('~/.composer/vendor/bin/phpunit')) {
$this->command = '~/.composer/vendor/bin/phpunit';
} else {
throw new \Robo\Exception\TaskException(__CLASS__, "Neither local phpunit nor global composer installation not found");
}
}
public function filter($filter)
{
$this->option('filter', $filter);
return $this;
}
public function group($group)
{
$this->option("group", $group);
return $this;
}
public function excludeGroup($group)
{
$this->option("exclude-group", $group);
return $this;
}
/**
* adds `log-json` option to runner
*
* @param string $file
* @return $this
*/
public function json($file = null)
{
$this->option("log-json", $file);
return $this;
}
/**
* adds `log-junit` option
*
* @param string $file
* @return $this
*/
public function xml($file = null)
{
$this->option("log-junit", $file);
return $this;
}
public function tap($file = "")
{
$this->option("log-tap", $file);
return $this;
}
public function bootstrap($file)
{
$this->option("bootstrap", $file);
return $this;
}
public function configFile($file)
{
$this->option('-c', $file);
return $this;
}
public function debug()
{
$this->option("debug");
return $this;
}
/**
* Test files to run.
*
* @param string|array A single file or a list of files.
* @return $this
*/
public function files($files)
{
if (is_string($files)) {
$files = [$files];
}
$this->files = ' ' . implode(',', $files);
return $this;
}
public function getCommand()
{
return $this->command . $this->arguments . $this->files;
}
public function run()
{
$this->printTaskInfo('Running PHPUnit ' . $this->arguments);
return $this->executeCommand($this->getCommand());
}
}PK CVW~z
z
src/Task/Testing/Phpspec.phpnu W+A taskPhpspec()
* ->format('pretty')
* ->noInteraction()
* ->run();
* ?>
* ```
*
*/
class Phpspec extends BaseTask implements CommandInterface, PrintedInterface
{
use \Robo\Common\ExecOneCommand;
protected $command;
/**
* @var array $formaters available formaters for format option
*/
protected $formaters = ['progress', 'html', 'pretty', 'junit', 'dot', 'tap'];
/**
* @var array $verbose_levels available verbose levels
*/
protected $verbose_levels = ['v', 'vv', 'vvv'];
public function __construct($pathToPhpspec = null)
{
if ($pathToPhpspec) {
$this->command = "$pathToPhpspec";
$this->arg('run');
} elseif (file_exists('vendor/phpspec/phpspec/bin/phpspec')) {
$this->command = 'vendor/phpspec/phpspec/bin/phpspec run';
} else {
throw new \Robo\Exception\TaskException(__CLASS__, "Neither composer nor phar installation of Phpspec found");
}
}
public function stopOnFail()
{
$this->option('stop-on-failure');
return $this;
}
public function noCodeGeneration()
{
$this->option('no-code-generation');
return $this;
}
public function quiet()
{
$this->option('quiet');
return $this;
}
public function verbose($level = 'v')
{
if (!in_array($level, $this->verbose_levels)) {
throw new \InvalidArgumentException('expected ' . implode(',', $this->verbose_levels));
}
$this->option('-' . $level);
return $this;
}
public function noAnsi()
{
$this->option('no-ansi');
return $this;
}
public function noInteraction()
{
$this->option('no-interaction');
return $this;
}
public function config($config_file)
{
$this->option('config', $config_file);
return $this;
}
public function format($formater)
{
if (!in_array($formater, $this->formaters)) {
throw new \InvalidArgumentException('expected ' . implode(',', $this->formaters));
}
$this->option('format', $formater);
return $this;
}
public function getCommand()
{
return $this->command . $this->arguments;
}
public function run()
{
$this->printTaskInfo('Running phpspec ' . $this->arguments);
return $this->executeCommand($this->getCommand());
}
}
PK CVN { { src/Task/Testing/loadTasks.phpnu W+A taskPackPhar('package/codecept.phar')
* ->compress()
* ->stub('package/stub.php');
*
* $finder = Finder::create()
* ->name('*.php')
* ->in('src');
*
* foreach ($finder as $file) {
* $pharTask->addFile('src/'.$file->getRelativePathname(), $file->getRealPath());
* }
*
* $finder = Finder::create()->files()
* ->name('*.php')
* ->in('vendor');
*
* foreach ($finder as $file) {
* $pharTask->addStripped('vendor/'.$file->getRelativePathname(), $file->getRealPath());
* }
* $pharTask->run();
*
* // verify Phar is packed correctly
* $code = $this->_exec('php package/codecept.phar');
* ?>
* ```
*/
class PackPhar extends BaseTask implements PrintedInterface
{
use Timer;
/**
* @var \Phar
*/
protected $phar;
protected $compileDir = null;
protected $filename;
protected $compress = false;
protected $stub;
protected $bin;
protected $stubTemplate = <<filename = $filename;
if (file_exists($file->getRealPath())) {
@unlink($file->getRealPath());
}
$this->phar = new \Phar($file->getPathname(), 0, $file->getFilename());
}
/**
* @param boolean $compress
* @return $this
*/
public function compress($compress = true)
{
$this->compress = $compress;
return $this;
}
/**
* @param $stub
* @return $this
*/
public function stub($stub)
{
$this->phar->setStub(file_get_contents($stub));
return $this;
}
public function run()
{
$this->printTaskInfo("Creating {$this->filename}");
$this->phar->setSignatureAlgorithm(\Phar::SHA1);
$this->phar->startBuffering();
$this->printTaskInfo('Packing ' . count($this->files) . ' files into phar');
$progress = new ProgressBar($this->getOutput());
$progress->start(count($this->files));
$this->startTimer();
foreach ($this->files as $path => $content) {
$this->phar->addFromString($path, $content);
$progress->advance();
}
$this->phar->stopBuffering();
$progress->finish();
$this->getOutput()->writeln('');
if ($this->compress and in_array('GZ', \Phar::getSupportedCompression())) {
if (count($this->files) > 1000) {
$this->printTaskInfo("Too many files. Compression DISABLED");
} else {
$this->printTaskInfo($this->filename . " compressed");
$this->phar = $this->phar->compressFiles(\Phar::GZ);
}
}
$this->stopTimer();
$this->printTaskSuccess("{$this->filename} produced");
return Result::success($this, '', ['time' => $this->getExecutionTime()]);
}
public function addStripped($path, $file)
{
$this->files[$path] = $this->stripWhitespace(file_get_contents($file));
return $this;
}
public function addFile($path, $file)
{
$this->files[$path] = file_get_contents($file);
return $this;
}
public function executable($file)
{
$source = file_get_contents($file);
if (strpos($source, '#!/usr/bin/env php') === 0) {
$source = substr($source, strpos($source, 'phar->setStub(sprintf($this->stubTemplate, $source));
return $this;
}
/**
* Strips whitespace from source. Taken from composer
* @param $source
* @return string
*/
private function stripWhitespace($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
// $output .= $token[1];
$output .= str_repeat("\n", substr_count($token[1], "\n"));
} elseif (T_WHITESPACE === $token[0]) {
// reduce wide spaces
$whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
// normalize newlines to \n
$whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
// trim leading spaces
$whitespace = preg_replace('{\n +}', "\n", $whitespace);
$output .= $whitespace;
} else {
$output .= $token[1];
}
}
return $output;
}
}
PK CV_; ; , src/Task/Development/GenerateMarkdownDoc.phpnu W+A taskGenerateMarkdownDoc('models.md')
* ->docClass('Model\User') // take class Model\User
* ->docClass('Model\Post') // take class Model\Post
* ->filterMethods(function(\ReflectionMethod $r) {
* return $r->isPublic() or $r->isProtected(); // process public and protected methods
* })->processClass(function(\ReflectionClass $r, $text) {
* return "Class ".$r->getName()."\n\n$text\n\n###Methods\n";
* })->run();
* ```
*
* By default this task generates a documentation for each public method of a class.
* It combines method signature with a docblock. Both can be post-processed.
*
* ``` php
* taskGenerateMarkdownDoc('models.md')
* ->docClass('Model\User')
* ->processClassSignature(false) // false can be passed to not include class signature
* ->processClassDocBlock(function(\ReflectionClass $r, $text) {
* return "[This is part of application model]\n" . $text;
* })->processMethodSignature(function(\ReflectionMethod $r, $text) {
* return "#### {$r->name}()";
* })->processMethodDocBlock(function(\ReflectionMethod $r, $text) {
* return strpos($r->name, 'save')===0 ? "[Saves to the database]\n" . $text : $text;
* })->run();
* ```
*
* @method \Robo\Task\Development\GenerateMarkdownDoc docClass(string $classname) put a class you want to be documented
* @method \Robo\Task\Development\GenerateMarkdownDoc filterMethods(\Closure $func) using callback function filter out methods that won't be documented
* @method \Robo\Task\Development\GenerateMarkdownDoc filterClasses(\Closure $func) using callback function filter out classes that won't be documented
* @method \Robo\Task\Development\GenerateMarkdownDoc filterProperties(\Closure $func) using callback function filter out properties that won't be documented
* @method \Robo\Task\Development\GenerateMarkdownDoc processClass(\Closure $func) post-process class documentation
* @method \Robo\Task\Development\GenerateMarkdownDoc processClassSignature(\Closure $func) post-process class signature. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processClassDocBlock(\Closure $func) post-process class docblock contents. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processMethod(\Closure $func) post-process method documentation. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processMethodSignature(\Closure $func) post-process method signature. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processMethodDocBlock(\Closure $func) post-process method docblock contents. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processProperty(\Closure $func) post-process property documentation. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processPropertySignature(\Closure $func) post-process property signature. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc processPropertyDocBlock(\Closure $func) post-process property docblock contents. Provide *false* to skip.
* @method \Robo\Task\Development\GenerateMarkdownDoc reorder(\Closure $func) use a function to reorder classes
* @method \Robo\Task\Development\GenerateMarkdownDoc reorderMethods(\Closure $func) use a function to reorder methods in class
* @method \Robo\Task\Development\GenerateMarkdownDoc prepend($text) inserts text into beginning of markdown file
* @method \Robo\Task\Development\GenerateMarkdownDoc append($text) inserts text in the end of markdown file
*/
class GenerateMarkdownDoc extends BaseTask
{
use \Robo\Common\DynamicParams;
protected $docClass = [];
protected $filterMethods;
protected $filterClasses;
protected $filterProperties;
// process class
protected $processClass;
protected $processClassSignature;
protected $processClassDocBlock;
// process methods
protected $processMethod;
protected $processMethodSignature;
protected $processMethodDocBlock;
// process Properties
protected $processProperty;
protected $processPropertySignature;
protected $processPropertyDocBlock;
protected $reorder;
protected $reorderMethods;
protected $reorderProperties;
protected $filename;
protected $prepend = "";
protected $append = "";
protected $text;
protected $textForClass = [];
/**
* @param $filename
* @return static
*/
public static function init($filename)
{
return new static($filename);
}
function __construct($filename)
{
$this->filename = $filename;
}
public function run()
{
foreach ($this->docClass as $class) {
$this->printTaskInfo("Processing $class");
$this->textForClass[$class] = $this->documentClass($class);
}
if (is_callable($this->reorder)) {
$this->printTaskInfo("Applying reorder function");
call_user_func_array($this->reorder, [$this->textForClass]);
}
$this->text = implode("\n", $this->textForClass);
$result = (new Write($this->filename))
->line($this->prepend)
->text($this->text)
->line($this->append)
->run();
$this->printTaskSuccess("{$this->filename} created. ".count($this->docClass)." classes documented");
return new Result($this, $result->getExitCode(), $result->getMessage(), $this->textForClass);
}
protected function documentClass($class)
{
if (!class_exists($class)) {
return "";
}
$refl = new \ReflectionClass($class);
if (is_callable($this->filterClasses)) {
$ret = call_user_func($this->filterClasses, $refl);
if (!$ret) {
return;
}
}
$doc = $this->documentClassSignature($refl);
$doc .= "\n" . $this->documentClassDocBlock($refl);
$doc .= "\n";
if (is_callable($this->processClass)) {
$doc = call_user_func($this->processClass, $refl, $doc);
}
$properties = [];
foreach ($refl->getProperties() as $reflProperty) {
$properties[] = $this->documentProperty($reflProperty);
}
$properties = array_filter($properties);
$doc .= implode("\n", $properties);
$methods = [];
foreach ($refl->getMethods() as $reflMethod) {
$methods[$reflMethod->name] = $this->documentMethod($reflMethod);
}
if (is_callable($this->reorderMethods)) {
call_user_func_array($this->reorderMethods, [&$methods]);
}
$methods = array_filter($methods);
$doc .= implode("\n", $methods)."\n";
return $doc;
}
protected function documentClassSignature(\ReflectionClass $reflectionClass)
{
if ($this->processClassSignature === false) {
return "";
}
$signature = "## {$reflectionClass->name}\n\n";
if ($parent = $reflectionClass->getParentClass()) {
$signature .= "* *Extends* `{$parent->name}`";
}
$interfaces = $reflectionClass->getInterfaceNames();
if (count($interfaces)) {
$signature .= "\n* *Implements* `" . implode('`, `', $interfaces) . '`';
}
$traits = $reflectionClass->getTraitNames();
if (count($traits)) {
$signature .= "\n* *Uses* `" . implode('`, `', $traits) . '`';
}
if (is_callable($this->processClassSignature)) {
$signature = call_user_func($this->processClassSignature, $reflectionClass, $signature);
}
return $signature;
}
protected function documentClassDocBlock(\ReflectionClass $reflectionClass)
{
if ($this->processClassDocBlock === false) {
return "";
}
$doc = self::indentDoc($reflectionClass->getDocComment());
if (is_callable($this->processClassDocBlock)) {
$doc = call_user_func($this->processClassDocBlock, $reflectionClass, $doc);
}
return $doc;
}
protected function documentMethod(\ReflectionMethod $reflectedMethod)
{
if ($this->processMethod === false) {
return "";
}
if (is_callable($this->filterMethods)) {
$ret = call_user_func($this->filterMethods, $reflectedMethod);
if (!$ret) {
return "";
}
} else {
if (!$reflectedMethod->isPublic()) {
return "";
}
}
$signature = $this->documentMethodSignature($reflectedMethod);
$docblock = $this->documentMethodDocBlock($reflectedMethod);
$methodDoc = "$signature $docblock";
if (is_callable($this->processMethod)) {
$methodDoc = call_user_func($this->processMethod, $reflectedMethod, $methodDoc);
}
return $methodDoc;
}
protected function documentProperty(\ReflectionProperty $reflectedProperty)
{
if ($this->processProperty === false) {
return "";
}
if (is_callable($this->filterProperties)) {
$ret = call_user_func($this->filterProperties, $reflectedProperty);
if (!$ret) {
return "";
}
} else {
if (!$reflectedProperty->isPublic()) {
return "";
}
}
$signature = $this->documentPropertySignature($reflectedProperty);
$docblock = $this->documentPropertyDocBlock($reflectedProperty);
$propertyDoc = $signature . $docblock;
if (is_callable($this->processProperty)) {
$propertyDoc = call_user_func($this->processProperty, $reflectedProperty, $propertyDoc);
}
return $propertyDoc;
}
protected function documentPropertySignature(\ReflectionProperty $reflectedProperty)
{
if ($this->processPropertySignature === false) {
return "";
}
$modifiers = implode(' ', \Reflection::getModifierNames($reflectedProperty->getModifiers()));
$signature = "#### *$modifiers* {$reflectedProperty->name}";
if (is_callable($this->processPropertySignature)) {
$signature = call_user_func($this->processPropertySignature, $reflectedProperty, $signature);
}
return $signature;
}
protected function documentPropertyDocBlock(\ReflectionProperty $reflectedProperty)
{
if ($this->processPropertyDocBlock === false) {
return "";
}
$propertyDoc = $reflectedProperty->getDocComment();
// take from parent
if (!$propertyDoc) {
$parent = $reflectedProperty->getDeclaringClass();
while ($parent = $parent->getParentClass()) {
if ($parent->hasProperty($reflectedProperty->name)) {
$propertyDoc = $parent->getProperty($reflectedProperty->name)->getDocComment();
}
}
}
$propertyDoc = self::indentDoc($propertyDoc, 7);
$propertyDoc = preg_replace("~^@(.*?)([$\s])~", ' * `$1` $2', $propertyDoc); // format annotations
if (is_callable($this->processPropertyDocBlock)) {
$propertyDoc = call_user_func($this->processPropertyDocBlock, $reflectedProperty, $propertyDoc);
}
return trim($propertyDoc);
}
protected function documentParam(\ReflectionParameter $param)
{
$text = "";
if ($param->isArray()) {
$text .= 'array ';
}
if ($param->isCallable()) {
$text .= 'callable ';
}
$text .= '$' . $param->getName();
if ($param->isDefaultValueAvailable()) {
if ($param->allowsNull()) {
$text .= ' = null';
} else {
$text .= ' = ' . str_replace("\n", ' ', print_r($param->getDefaultValue(), true));
}
}
return $text;
}
public static function indentDoc($doc, $indent = 3)
{
if (!$doc) {
return $doc;
}
return implode(
"\n", array_map(
function ($line) use ($indent) {
return substr($line, $indent);
}, explode("\n", $doc)
)
);
}
protected function documentMethodSignature(\ReflectionMethod $reflectedMethod)
{
if ($this->processMethodSignature === false) {
return "";
}
$modifiers = implode(' ', \Reflection::getModifierNames($reflectedMethod->getModifiers()));
$params = implode(
', ', array_map(
function ($p) {
return $this->documentParam($p);
}, $reflectedMethod->getParameters()
)
);
$signature = "#### *$modifiers* {$reflectedMethod->name}($params)";
if (is_callable($this->processMethodSignature)) {
$signature = call_user_func($this->processMethodSignature, $reflectedMethod, $signature);
}
return $signature;
}
/**
* @param \ReflectionMethod $reflectedMethod
* @return mixed|string
*/
protected function documentMethodDocBlock(\ReflectionMethod $reflectedMethod)
{
if ($this->processMethodDocBlock === false) {
return "";
}
$methodDoc = $reflectedMethod->getDocComment();
// take from parent
if (!$methodDoc) {
$parent = $reflectedMethod->getDeclaringClass();
while ($parent = $parent->getParentClass()) {
if ($parent->hasMethod($reflectedMethod->name)) {
$methodDoc = $parent->getMethod($reflectedMethod->name)->getDocComment();
}
}
}
// take from interface
if (!$methodDoc) {
$interfaces = $reflectedMethod->getDeclaringClass()->getInterfaces();
foreach ($interfaces as $interface) {
$i = new \ReflectionClass($interface->name);
if ($i->hasMethod($reflectedMethod->name)) {
$methodDoc = $i->getMethod($reflectedMethod->name)->getDocComment();
break;
}
}
}
$methodDoc = self::indentDoc($methodDoc, 7);
$methodDoc = preg_replace("~^@(.*?) ([$\s])~m", ' * `$1` $2', $methodDoc); // format annotations
if (is_callable($this->processMethodDocBlock)) {
$methodDoc = call_user_func($this->processMethodDocBlock, $reflectedMethod, $methodDoc);
}
return $methodDoc;
}
}PK CVԯ- src/Task/Development/GitHub.phpnu W+A owner, $this->repo) = explode('/', $uri);
return $this;
}
protected function getUri()
{
return $this->owner . '/' . $this->repo;
}
public function askAuth()
{
self::$user = $this->ask('GitHub User');
self::$pass = $this->askHidden('Password');
return $this;
}
protected function sendRequest($uri, $params = [], $method = 'POST')
{
if (!$this->owner or !$this->repo) {
throw new TaskException($this, 'Repo URI is not set');
}
$ch = curl_init();
$url = sprintf('%s/repos/%s/%s', self::GITHUB_URL, $this->getUri(), $uri);
$this->printTaskInfo("$method $url");
if (!self::$user) {
$this->askAuth();
curl_setopt($ch, CURLOPT_USERPWD, self::$user . ':' . self::$pass);
}
curl_setopt_array(
$ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => $method != 'GET',
CURLOPT_POSTFIELDS => json_encode($params),
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_USERAGENT => self::$user ?: "Robo"
)
);
$output = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$response = json_decode($output);
$this->printTaskInfo($output);
return [$code, $response];
}
}PK CV $ src/Task/Development/OpenBrowser.phpnu W+A taskOpenBrowser('http://localhost')
* ->run();
*
* // open two browser windows
* $this->taskOpenBrowser([
* 'http://localhost/mysite',
* 'http://localhost/mysite2'
* ])
* ->run();
* ```
*/
class OpenBrowser extends BaseTask
{
protected $urls = [];
public function __construct($url)
{
$this->urls = (array) $url;
}
public function run()
{
$openCommand = $this->getOpenCommand();
if (empty($openCommand)) {
return Result::error($this, 'no suitable browser opening command found');
}
foreach ($this->urls as $url) {
passthru(sprintf($openCommand, ProcessUtils::escapeArgument($url)));
$this->printTaskInfo("Opened {$url}");
}
return Result::success($this);
}
private function getOpenCommand()
{
if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
return 'start "web" explorer "%s"';
}
passthru('which xdg-open', $linux);
passthru('which open', $osx);
if (0 === $linux) {
return 'xdg-open %s';
}
if (0 === $osx) {
return 'open %s';
}
}
}
PK CVE6}L L " src/Task/Development/PhpServer.phpnu W+A taskServer(8000)
* ->dir('public')
* ->run();
*
* // run with IP 0.0.0.0
* $this->taskServer(8000)
* ->host('0.0.0.0')
* ->run();
*
* // execute server in background
* $this->taskServer(8000)
* ->background()
* ->run();
* ?>
* ```
*/
class PhpServer extends Exec
{
protected $port;
protected $host = '127.0.0.1';
protected $command = 'php -S %s:%d ';
public function __construct($port)
{
$this->port = $port;
if (strtolower(PHP_OS) === 'linux') {
$this->command = 'exec php -S %s:%d ';
}
}
public function host($host)
{
$this->host = $host;
return $this;
}
public function dir($path)
{
$this->command .= "-t $path";
return $this;
}
public function getCommand()
{
return sprintf($this->command . $this->arguments, $this->host, $this->port);
}
}
PK CV! " src/Task/Development/loadTasks.phpnu W+A taskChangelog()
* ->version($version)
* ->change("released to github")
* ->run();
* ?>
* ```
*
* Changes can be asked from Console
*
* ``` php
* taskChangelog()
* ->version($version)
* ->askForChanges()
* ->run();
* ?>
* ```
*
* @method Development\Changelog filename(string $filename)
* @method Development\Changelog anchor(string $anchor)
* @method Development\Changelog version(string $version)
*/
class Changelog extends BaseTask
{
use \Robo\Common\DynamicParams;
protected $filename;
protected $log = [];
protected $anchor = "# Changelog";
protected $version = "";
/**
* @param string $filename
* @return \Robo\Task\Development\Changelog
*/
public static function init($filename = 'CHANGELOG.md')
{
return new Changelog($filename);
}
public function askForChanges()
{
while ($resp = $this->ask("Changed in this release: ")) {
$this->log[] = $resp;
};
return $this;
}
public function __construct($filename)
{
$this->filename = $filename;
}
public function changes(array $data)
{
$this->log = array_merge($this->log, $data);
return $this;
}
public function change($change)
{
$this->log[] = $change;
return $this;
}
public function getChanges()
{
return $this->log;
}
public function run()
{
if (empty($this->log)) {
return Result::error($this, "Changelog is empty");
}
$text = implode(
"\n", array_map(
function ($i) {
return "* $i *" . date('Y-m-d') . "*";
}, $this->log
)
) . "\n";
$ver = "#### {$this->version}\n\n";
$text = $ver . $text;
if (!file_exists($this->filename)) {
$this->printTaskInfo("Creating {$this->filename}");
$res = file_put_contents($this->filename, $this->anchor);
if ($res === false) {
return Result::error($this, "File {$this->filename} cant be created");
}
}
// trying to append to changelog for today
$result = (new Replace($this->filename))
->from($ver)
->to($text)
->run();
if (!$result->getData()['replaced']) {
$result = (new Replace($this->filename))
->from($this->anchor)
->to($this->anchor . "\n\n" . $text)
->run();
}
return new Result($this, $result->getExitCode(), $result->getMessage(), $this->log);
}
}PK CV@ &