├── .gitignore ├── .travis.yml ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml ├── src └── PasswordPolicy │ ├── Policy.php │ ├── PolicyBuilder.php │ ├── PolicyManager.php │ ├── Providers │ └── Laravel │ │ ├── Facade.php │ │ ├── PasswordPolicyServiceProvider.php │ │ └── PasswordValidator.php │ ├── Rule.php │ ├── Rules │ ├── AbstractSequenceRule.php │ ├── CaseRule.php │ ├── ContainRule.php │ ├── DigitRule.php │ ├── LengthRule.php │ ├── MinPassingRulesRule.php │ ├── NumberSequenceRule.php │ └── SpecialCharacterRule.php │ └── Validator.php └── tests ├── Integration └── Providers │ └── Laravel │ └── PasswordValidatorTest.php └── Unit ├── ExampleTest.php ├── PolicyBuilderTest.php ├── PolicyManagerTest.php ├── Providers └── Laravel │ └── PasswordValidatorTest.php ├── Rules ├── CaseRuleTest.php ├── ContainRuleTest.php ├── DigitRuleTest.php ├── LengthRuleTest.php ├── MinPassingRulesRuleTest.php └── SpecialCharacterRuleTest.php └── ValidatorTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '5.6' 4 | - '7.0' 5 | - nightly 6 | install: composer install 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://api.travis-ci.org/joshralph93/password-policy.svg?branch=master)](https://travis-ci.org/joshralph93/password-policy) 2 | 3 | A fluent password policy builder library. The package can be used stand-alone or easily added to Laravel. 4 | 5 | # Table of Contents 6 | - [Install](#install) 7 | - [Stand Alone Usage](#policy-builder) 8 | - [Laravel](#larave) 9 | - [Install](#install-package) 10 | - [Define Policies](#define-policies) 11 | - [Setup Validation](#setup-validation) 12 | 13 | ## Install 14 | ``` 15 | $ composer require joshralph/password-policy 16 | ``` 17 | 18 | ## Usage 19 | 20 | ### Policy Builder 21 | 22 | ```php 23 | $builder = new \PasswordPolicy\PolicyBuilder(new \PasswordPolicy\Policy); 24 | $builder->minLength(6) 25 | ->upperCase(); 26 | ``` 27 | 28 | Any of the following methods may be chained on the builder class to build your password policy. 29 | 30 | #### minLength(length) 31 | 32 | ##### length 33 | Type: int 34 | 35 | Minimum number of characters the password must contain. 36 | 37 | #### maxLength(length) 38 | 39 | ##### length 40 | Type: int 41 | 42 | Maximum number of characters the password must contain. 43 | 44 | #### upperCase([min]) 45 | 46 | ##### min 47 | Type: int 48 | 49 | Minimum number of upper case characters the password must contain. 50 | 51 | #### lowerCase([min]) 52 | 53 | ##### min 54 | Type: int 55 | 56 | Minimum number of lower case characters the password must contain. 57 | 58 | #### digits([min]) 59 | 60 | ##### min 61 | Type: int 62 | 63 | Minimum number of numeric characters the password must contain. 64 | 65 | #### specialCharacters([min]) 66 | 67 | ##### min 68 | Type: int 69 | 70 | Minimum number of special characters the password must contain. 71 | 72 | #### doesNotContain(phrases [,phrases]) 73 | 74 | ##### phrases 75 | Type: string|array 76 | 77 | Phrases that the password should not contain 78 | 79 | *Example* 80 | 81 | ```php 82 | ->doesNotContain('password', $firstName, $lastName) 83 | ``` 84 | 85 | #### minPassingRules(passesRequired, ruleSet) 86 | 87 | ##### passesRequired 88 | Type: int 89 | 90 | The minimum number of rules in the *ruleSet* that need to pass, in order for this rule to pass 91 | 92 | ##### ruleSet 93 | Type: \Closure 94 | 95 | A closure which is given a new PolicyBuilder instance. 96 | 97 | *Example* 98 | 99 | ```php 100 | // One of these rules must pass 101 | ->minPassingRules(1, function (PolicyBuilder $builder) { 102 | $builder->doesNotContain('password') 103 | ->minLength(10); 104 | }) 105 | ``` 106 | 107 | ### Laravel 108 | 109 | If you are a Laravel user, this package can seamlessly integrate with your validators. 110 | 111 | #### Install Package 112 | 113 | Begin by adding the below service provider. 114 | ```php 115 | // config/app.php 116 | 117 | 'providers' => [ 118 | // ... 119 | \PasswordPolicy\Providers\Laravel\PasswordPolicyServiceProvider::class, 120 | ], 121 | ``` 122 | 123 | #### Define Policies 124 | 125 | Within an app service provider (e.g. AppServiceProvider.php) you can start defining password policies. 126 | 127 | ```php 128 | // App/Providers/AppServiceProvider.php 129 | 130 | // use PasswordPolicy\PolicyBuilder; 131 | 132 | 133 | /** 134 | * Bootstrap any application services. 135 | * 136 | * @return void 137 | */ 138 | public function boot() 139 | { 140 | \PasswordPolicy::define('default', function (PolicyBuilder $builder) { 141 | $builder->minLength(8) 142 | ->upperCase(3); 143 | // ... 144 | }); 145 | } 146 | ``` 147 | 148 | You can define as many policies as you require, however it's recommended to stick with 'default' when possible. 149 | 150 | #### Setup Validation 151 | 152 | Once you're policies have been defined, you're ready to start using the policies. A new 'password' validation rule is now available to use. 153 | 154 | ```php 155 | // Request class 156 | 157 | /** 158 | * Declare validation rules 159 | * 160 | * @return array 161 | */ 162 | public function rules() 163 | { 164 | return [ 165 | // ... 166 | 'password' => 'required|password' 167 | ]; 168 | } 169 | 170 | ``` 171 | 172 | The validator will use the 'default' policy by default. To use an alternative policy, add an additional parameter: 173 | ```php 174 | 175 | 'password' => 'required|password:admin' 176 | 177 | ``` 178 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joshralph/password-policy", 3 | "description": "A library to intuitively create password policies and validate a subject against them.", 4 | "autoload": { 5 | "psr-4": { 6 | "PasswordPolicy\\Tests\\": "tests/", 7 | "PasswordPolicy\\": "src/PasswordPolicy/" 8 | } 9 | }, 10 | "require": { 11 | "php": ">=5.5" 12 | }, 13 | "require-dev": { 14 | "phpunit/phpunit": "^4.8", 15 | "mockery/mockery": "^0.9.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "d99081759e5cb898634ad50044ede4f6", 8 | "content-hash": "eee58088916d628a375238a180aa8cd4", 9 | "packages": [], 10 | "packages-dev": [ 11 | { 12 | "name": "doctrine/instantiator", 13 | "version": "1.0.5", 14 | "source": { 15 | "type": "git", 16 | "url": "https://github.com/doctrine/instantiator.git", 17 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 18 | }, 19 | "dist": { 20 | "type": "zip", 21 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 22 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 23 | "shasum": "" 24 | }, 25 | "require": { 26 | "php": ">=5.3,<8.0-DEV" 27 | }, 28 | "require-dev": { 29 | "athletic/athletic": "~0.1.8", 30 | "ext-pdo": "*", 31 | "ext-phar": "*", 32 | "phpunit/phpunit": "~4.0", 33 | "squizlabs/php_codesniffer": "~2.0" 34 | }, 35 | "type": "library", 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.0.x-dev" 39 | } 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 44 | } 45 | }, 46 | "notification-url": "https://packagist.org/downloads/", 47 | "license": [ 48 | "MIT" 49 | ], 50 | "authors": [ 51 | { 52 | "name": "Marco Pivetta", 53 | "email": "ocramius@gmail.com", 54 | "homepage": "http://ocramius.github.com/" 55 | } 56 | ], 57 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 58 | "homepage": "https://github.com/doctrine/instantiator", 59 | "keywords": [ 60 | "constructor", 61 | "instantiate" 62 | ], 63 | "time": "2015-06-14 21:17:01" 64 | }, 65 | { 66 | "name": "hamcrest/hamcrest-php", 67 | "version": "v1.2.2", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/hamcrest/hamcrest-php.git", 71 | "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", 76 | "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": ">=5.3.2" 81 | }, 82 | "replace": { 83 | "cordoval/hamcrest-php": "*", 84 | "davedevelopment/hamcrest-php": "*", 85 | "kodova/hamcrest-php": "*" 86 | }, 87 | "require-dev": { 88 | "phpunit/php-file-iterator": "1.3.3", 89 | "satooshi/php-coveralls": "dev-master" 90 | }, 91 | "type": "library", 92 | "autoload": { 93 | "classmap": [ 94 | "hamcrest" 95 | ], 96 | "files": [ 97 | "hamcrest/Hamcrest.php" 98 | ] 99 | }, 100 | "notification-url": "https://packagist.org/downloads/", 101 | "license": [ 102 | "BSD" 103 | ], 104 | "description": "This is the PHP port of Hamcrest Matchers", 105 | "keywords": [ 106 | "test" 107 | ], 108 | "time": "2015-05-11 14:41:42" 109 | }, 110 | { 111 | "name": "mockery/mockery", 112 | "version": "0.9.5", 113 | "source": { 114 | "type": "git", 115 | "url": "https://github.com/padraic/mockery.git", 116 | "reference": "4db079511a283e5aba1b3c2fb19037c645e70fc2" 117 | }, 118 | "dist": { 119 | "type": "zip", 120 | "url": "https://api.github.com/repos/padraic/mockery/zipball/4db079511a283e5aba1b3c2fb19037c645e70fc2", 121 | "reference": "4db079511a283e5aba1b3c2fb19037c645e70fc2", 122 | "shasum": "" 123 | }, 124 | "require": { 125 | "hamcrest/hamcrest-php": "~1.1", 126 | "lib-pcre": ">=7.0", 127 | "php": ">=5.3.2" 128 | }, 129 | "require-dev": { 130 | "phpunit/phpunit": "~4.0" 131 | }, 132 | "type": "library", 133 | "extra": { 134 | "branch-alias": { 135 | "dev-master": "0.9.x-dev" 136 | } 137 | }, 138 | "autoload": { 139 | "psr-0": { 140 | "Mockery": "library/" 141 | } 142 | }, 143 | "notification-url": "https://packagist.org/downloads/", 144 | "license": [ 145 | "BSD-3-Clause" 146 | ], 147 | "authors": [ 148 | { 149 | "name": "Pádraic Brady", 150 | "email": "padraic.brady@gmail.com", 151 | "homepage": "http://blog.astrumfutura.com" 152 | }, 153 | { 154 | "name": "Dave Marshall", 155 | "email": "dave.marshall@atstsolutions.co.uk", 156 | "homepage": "http://davedevelopment.co.uk" 157 | } 158 | ], 159 | "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", 160 | "homepage": "http://github.com/padraic/mockery", 161 | "keywords": [ 162 | "BDD", 163 | "TDD", 164 | "library", 165 | "mock", 166 | "mock objects", 167 | "mockery", 168 | "stub", 169 | "test", 170 | "test double", 171 | "testing" 172 | ], 173 | "time": "2016-05-22 21:52:33" 174 | }, 175 | { 176 | "name": "phpdocumentor/reflection-common", 177 | "version": "1.0", 178 | "source": { 179 | "type": "git", 180 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 181 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" 182 | }, 183 | "dist": { 184 | "type": "zip", 185 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 186 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 187 | "shasum": "" 188 | }, 189 | "require": { 190 | "php": ">=5.5" 191 | }, 192 | "require-dev": { 193 | "phpunit/phpunit": "^4.6" 194 | }, 195 | "type": "library", 196 | "extra": { 197 | "branch-alias": { 198 | "dev-master": "1.0.x-dev" 199 | } 200 | }, 201 | "autoload": { 202 | "psr-4": { 203 | "phpDocumentor\\Reflection\\": [ 204 | "src" 205 | ] 206 | } 207 | }, 208 | "notification-url": "https://packagist.org/downloads/", 209 | "license": [ 210 | "MIT" 211 | ], 212 | "authors": [ 213 | { 214 | "name": "Jaap van Otterdijk", 215 | "email": "opensource@ijaap.nl" 216 | } 217 | ], 218 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 219 | "homepage": "http://www.phpdoc.org", 220 | "keywords": [ 221 | "FQSEN", 222 | "phpDocumentor", 223 | "phpdoc", 224 | "reflection", 225 | "static analysis" 226 | ], 227 | "time": "2015-12-27 11:43:31" 228 | }, 229 | { 230 | "name": "phpdocumentor/reflection-docblock", 231 | "version": "3.1.0", 232 | "source": { 233 | "type": "git", 234 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 235 | "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" 236 | }, 237 | "dist": { 238 | "type": "zip", 239 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", 240 | "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", 241 | "shasum": "" 242 | }, 243 | "require": { 244 | "php": ">=5.5", 245 | "phpdocumentor/reflection-common": "^1.0@dev", 246 | "phpdocumentor/type-resolver": "^0.2.0", 247 | "webmozart/assert": "^1.0" 248 | }, 249 | "require-dev": { 250 | "mockery/mockery": "^0.9.4", 251 | "phpunit/phpunit": "^4.4" 252 | }, 253 | "type": "library", 254 | "autoload": { 255 | "psr-4": { 256 | "phpDocumentor\\Reflection\\": [ 257 | "src/" 258 | ] 259 | } 260 | }, 261 | "notification-url": "https://packagist.org/downloads/", 262 | "license": [ 263 | "MIT" 264 | ], 265 | "authors": [ 266 | { 267 | "name": "Mike van Riel", 268 | "email": "me@mikevanriel.com" 269 | } 270 | ], 271 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 272 | "time": "2016-06-10 09:48:41" 273 | }, 274 | { 275 | "name": "phpdocumentor/type-resolver", 276 | "version": "0.2", 277 | "source": { 278 | "type": "git", 279 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 280 | "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" 281 | }, 282 | "dist": { 283 | "type": "zip", 284 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", 285 | "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", 286 | "shasum": "" 287 | }, 288 | "require": { 289 | "php": ">=5.5", 290 | "phpdocumentor/reflection-common": "^1.0" 291 | }, 292 | "require-dev": { 293 | "mockery/mockery": "^0.9.4", 294 | "phpunit/phpunit": "^5.2||^4.8.24" 295 | }, 296 | "type": "library", 297 | "extra": { 298 | "branch-alias": { 299 | "dev-master": "1.0.x-dev" 300 | } 301 | }, 302 | "autoload": { 303 | "psr-4": { 304 | "phpDocumentor\\Reflection\\": [ 305 | "src/" 306 | ] 307 | } 308 | }, 309 | "notification-url": "https://packagist.org/downloads/", 310 | "license": [ 311 | "MIT" 312 | ], 313 | "authors": [ 314 | { 315 | "name": "Mike van Riel", 316 | "email": "me@mikevanriel.com" 317 | } 318 | ], 319 | "time": "2016-06-10 07:14:17" 320 | }, 321 | { 322 | "name": "phpspec/prophecy", 323 | "version": "v1.6.1", 324 | "source": { 325 | "type": "git", 326 | "url": "https://github.com/phpspec/prophecy.git", 327 | "reference": "58a8137754bc24b25740d4281399a4a3596058e0" 328 | }, 329 | "dist": { 330 | "type": "zip", 331 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", 332 | "reference": "58a8137754bc24b25740d4281399a4a3596058e0", 333 | "shasum": "" 334 | }, 335 | "require": { 336 | "doctrine/instantiator": "^1.0.2", 337 | "php": "^5.3|^7.0", 338 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 339 | "sebastian/comparator": "^1.1", 340 | "sebastian/recursion-context": "^1.0" 341 | }, 342 | "require-dev": { 343 | "phpspec/phpspec": "^2.0" 344 | }, 345 | "type": "library", 346 | "extra": { 347 | "branch-alias": { 348 | "dev-master": "1.6.x-dev" 349 | } 350 | }, 351 | "autoload": { 352 | "psr-0": { 353 | "Prophecy\\": "src/" 354 | } 355 | }, 356 | "notification-url": "https://packagist.org/downloads/", 357 | "license": [ 358 | "MIT" 359 | ], 360 | "authors": [ 361 | { 362 | "name": "Konstantin Kudryashov", 363 | "email": "ever.zet@gmail.com", 364 | "homepage": "http://everzet.com" 365 | }, 366 | { 367 | "name": "Marcello Duarte", 368 | "email": "marcello.duarte@gmail.com" 369 | } 370 | ], 371 | "description": "Highly opinionated mocking framework for PHP 5.3+", 372 | "homepage": "https://github.com/phpspec/prophecy", 373 | "keywords": [ 374 | "Double", 375 | "Dummy", 376 | "fake", 377 | "mock", 378 | "spy", 379 | "stub" 380 | ], 381 | "time": "2016-06-07 08:13:47" 382 | }, 383 | { 384 | "name": "phpunit/php-code-coverage", 385 | "version": "2.2.4", 386 | "source": { 387 | "type": "git", 388 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 389 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 390 | }, 391 | "dist": { 392 | "type": "zip", 393 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 394 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 395 | "shasum": "" 396 | }, 397 | "require": { 398 | "php": ">=5.3.3", 399 | "phpunit/php-file-iterator": "~1.3", 400 | "phpunit/php-text-template": "~1.2", 401 | "phpunit/php-token-stream": "~1.3", 402 | "sebastian/environment": "^1.3.2", 403 | "sebastian/version": "~1.0" 404 | }, 405 | "require-dev": { 406 | "ext-xdebug": ">=2.1.4", 407 | "phpunit/phpunit": "~4" 408 | }, 409 | "suggest": { 410 | "ext-dom": "*", 411 | "ext-xdebug": ">=2.2.1", 412 | "ext-xmlwriter": "*" 413 | }, 414 | "type": "library", 415 | "extra": { 416 | "branch-alias": { 417 | "dev-master": "2.2.x-dev" 418 | } 419 | }, 420 | "autoload": { 421 | "classmap": [ 422 | "src/" 423 | ] 424 | }, 425 | "notification-url": "https://packagist.org/downloads/", 426 | "license": [ 427 | "BSD-3-Clause" 428 | ], 429 | "authors": [ 430 | { 431 | "name": "Sebastian Bergmann", 432 | "email": "sb@sebastian-bergmann.de", 433 | "role": "lead" 434 | } 435 | ], 436 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 437 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 438 | "keywords": [ 439 | "coverage", 440 | "testing", 441 | "xunit" 442 | ], 443 | "time": "2015-10-06 15:47:00" 444 | }, 445 | { 446 | "name": "phpunit/php-file-iterator", 447 | "version": "1.4.1", 448 | "source": { 449 | "type": "git", 450 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 451 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 452 | }, 453 | "dist": { 454 | "type": "zip", 455 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 456 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 457 | "shasum": "" 458 | }, 459 | "require": { 460 | "php": ">=5.3.3" 461 | }, 462 | "type": "library", 463 | "extra": { 464 | "branch-alias": { 465 | "dev-master": "1.4.x-dev" 466 | } 467 | }, 468 | "autoload": { 469 | "classmap": [ 470 | "src/" 471 | ] 472 | }, 473 | "notification-url": "https://packagist.org/downloads/", 474 | "license": [ 475 | "BSD-3-Clause" 476 | ], 477 | "authors": [ 478 | { 479 | "name": "Sebastian Bergmann", 480 | "email": "sb@sebastian-bergmann.de", 481 | "role": "lead" 482 | } 483 | ], 484 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 485 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 486 | "keywords": [ 487 | "filesystem", 488 | "iterator" 489 | ], 490 | "time": "2015-06-21 13:08:43" 491 | }, 492 | { 493 | "name": "phpunit/php-text-template", 494 | "version": "1.2.1", 495 | "source": { 496 | "type": "git", 497 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 498 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 499 | }, 500 | "dist": { 501 | "type": "zip", 502 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 503 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 504 | "shasum": "" 505 | }, 506 | "require": { 507 | "php": ">=5.3.3" 508 | }, 509 | "type": "library", 510 | "autoload": { 511 | "classmap": [ 512 | "src/" 513 | ] 514 | }, 515 | "notification-url": "https://packagist.org/downloads/", 516 | "license": [ 517 | "BSD-3-Clause" 518 | ], 519 | "authors": [ 520 | { 521 | "name": "Sebastian Bergmann", 522 | "email": "sebastian@phpunit.de", 523 | "role": "lead" 524 | } 525 | ], 526 | "description": "Simple template engine.", 527 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 528 | "keywords": [ 529 | "template" 530 | ], 531 | "time": "2015-06-21 13:50:34" 532 | }, 533 | { 534 | "name": "phpunit/php-timer", 535 | "version": "1.0.8", 536 | "source": { 537 | "type": "git", 538 | "url": "https://github.com/sebastianbergmann/php-timer.git", 539 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" 540 | }, 541 | "dist": { 542 | "type": "zip", 543 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", 544 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", 545 | "shasum": "" 546 | }, 547 | "require": { 548 | "php": ">=5.3.3" 549 | }, 550 | "require-dev": { 551 | "phpunit/phpunit": "~4|~5" 552 | }, 553 | "type": "library", 554 | "autoload": { 555 | "classmap": [ 556 | "src/" 557 | ] 558 | }, 559 | "notification-url": "https://packagist.org/downloads/", 560 | "license": [ 561 | "BSD-3-Clause" 562 | ], 563 | "authors": [ 564 | { 565 | "name": "Sebastian Bergmann", 566 | "email": "sb@sebastian-bergmann.de", 567 | "role": "lead" 568 | } 569 | ], 570 | "description": "Utility class for timing", 571 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 572 | "keywords": [ 573 | "timer" 574 | ], 575 | "time": "2016-05-12 18:03:57" 576 | }, 577 | { 578 | "name": "phpunit/php-token-stream", 579 | "version": "1.4.8", 580 | "source": { 581 | "type": "git", 582 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 583 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 584 | }, 585 | "dist": { 586 | "type": "zip", 587 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 588 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 589 | "shasum": "" 590 | }, 591 | "require": { 592 | "ext-tokenizer": "*", 593 | "php": ">=5.3.3" 594 | }, 595 | "require-dev": { 596 | "phpunit/phpunit": "~4.2" 597 | }, 598 | "type": "library", 599 | "extra": { 600 | "branch-alias": { 601 | "dev-master": "1.4-dev" 602 | } 603 | }, 604 | "autoload": { 605 | "classmap": [ 606 | "src/" 607 | ] 608 | }, 609 | "notification-url": "https://packagist.org/downloads/", 610 | "license": [ 611 | "BSD-3-Clause" 612 | ], 613 | "authors": [ 614 | { 615 | "name": "Sebastian Bergmann", 616 | "email": "sebastian@phpunit.de" 617 | } 618 | ], 619 | "description": "Wrapper around PHP's tokenizer extension.", 620 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 621 | "keywords": [ 622 | "tokenizer" 623 | ], 624 | "time": "2015-09-15 10:49:45" 625 | }, 626 | { 627 | "name": "phpunit/phpunit", 628 | "version": "4.8.27", 629 | "source": { 630 | "type": "git", 631 | "url": "https://github.com/sebastianbergmann/phpunit.git", 632 | "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90" 633 | }, 634 | "dist": { 635 | "type": "zip", 636 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c062dddcb68e44b563f66ee319ddae2b5a322a90", 637 | "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90", 638 | "shasum": "" 639 | }, 640 | "require": { 641 | "ext-dom": "*", 642 | "ext-json": "*", 643 | "ext-pcre": "*", 644 | "ext-reflection": "*", 645 | "ext-spl": "*", 646 | "php": ">=5.3.3", 647 | "phpspec/prophecy": "^1.3.1", 648 | "phpunit/php-code-coverage": "~2.1", 649 | "phpunit/php-file-iterator": "~1.4", 650 | "phpunit/php-text-template": "~1.2", 651 | "phpunit/php-timer": "^1.0.6", 652 | "phpunit/phpunit-mock-objects": "~2.3", 653 | "sebastian/comparator": "~1.1", 654 | "sebastian/diff": "~1.2", 655 | "sebastian/environment": "~1.3", 656 | "sebastian/exporter": "~1.2", 657 | "sebastian/global-state": "~1.0", 658 | "sebastian/version": "~1.0", 659 | "symfony/yaml": "~2.1|~3.0" 660 | }, 661 | "suggest": { 662 | "phpunit/php-invoker": "~1.1" 663 | }, 664 | "bin": [ 665 | "phpunit" 666 | ], 667 | "type": "library", 668 | "extra": { 669 | "branch-alias": { 670 | "dev-master": "4.8.x-dev" 671 | } 672 | }, 673 | "autoload": { 674 | "classmap": [ 675 | "src/" 676 | ] 677 | }, 678 | "notification-url": "https://packagist.org/downloads/", 679 | "license": [ 680 | "BSD-3-Clause" 681 | ], 682 | "authors": [ 683 | { 684 | "name": "Sebastian Bergmann", 685 | "email": "sebastian@phpunit.de", 686 | "role": "lead" 687 | } 688 | ], 689 | "description": "The PHP Unit Testing framework.", 690 | "homepage": "https://phpunit.de/", 691 | "keywords": [ 692 | "phpunit", 693 | "testing", 694 | "xunit" 695 | ], 696 | "time": "2016-07-21 06:48:14" 697 | }, 698 | { 699 | "name": "phpunit/phpunit-mock-objects", 700 | "version": "2.3.8", 701 | "source": { 702 | "type": "git", 703 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 704 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 705 | }, 706 | "dist": { 707 | "type": "zip", 708 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 709 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 710 | "shasum": "" 711 | }, 712 | "require": { 713 | "doctrine/instantiator": "^1.0.2", 714 | "php": ">=5.3.3", 715 | "phpunit/php-text-template": "~1.2", 716 | "sebastian/exporter": "~1.2" 717 | }, 718 | "require-dev": { 719 | "phpunit/phpunit": "~4.4" 720 | }, 721 | "suggest": { 722 | "ext-soap": "*" 723 | }, 724 | "type": "library", 725 | "extra": { 726 | "branch-alias": { 727 | "dev-master": "2.3.x-dev" 728 | } 729 | }, 730 | "autoload": { 731 | "classmap": [ 732 | "src/" 733 | ] 734 | }, 735 | "notification-url": "https://packagist.org/downloads/", 736 | "license": [ 737 | "BSD-3-Clause" 738 | ], 739 | "authors": [ 740 | { 741 | "name": "Sebastian Bergmann", 742 | "email": "sb@sebastian-bergmann.de", 743 | "role": "lead" 744 | } 745 | ], 746 | "description": "Mock Object library for PHPUnit", 747 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 748 | "keywords": [ 749 | "mock", 750 | "xunit" 751 | ], 752 | "time": "2015-10-02 06:51:40" 753 | }, 754 | { 755 | "name": "sebastian/comparator", 756 | "version": "1.2.0", 757 | "source": { 758 | "type": "git", 759 | "url": "https://github.com/sebastianbergmann/comparator.git", 760 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 761 | }, 762 | "dist": { 763 | "type": "zip", 764 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 765 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 766 | "shasum": "" 767 | }, 768 | "require": { 769 | "php": ">=5.3.3", 770 | "sebastian/diff": "~1.2", 771 | "sebastian/exporter": "~1.2" 772 | }, 773 | "require-dev": { 774 | "phpunit/phpunit": "~4.4" 775 | }, 776 | "type": "library", 777 | "extra": { 778 | "branch-alias": { 779 | "dev-master": "1.2.x-dev" 780 | } 781 | }, 782 | "autoload": { 783 | "classmap": [ 784 | "src/" 785 | ] 786 | }, 787 | "notification-url": "https://packagist.org/downloads/", 788 | "license": [ 789 | "BSD-3-Clause" 790 | ], 791 | "authors": [ 792 | { 793 | "name": "Jeff Welch", 794 | "email": "whatthejeff@gmail.com" 795 | }, 796 | { 797 | "name": "Volker Dusch", 798 | "email": "github@wallbash.com" 799 | }, 800 | { 801 | "name": "Bernhard Schussek", 802 | "email": "bschussek@2bepublished.at" 803 | }, 804 | { 805 | "name": "Sebastian Bergmann", 806 | "email": "sebastian@phpunit.de" 807 | } 808 | ], 809 | "description": "Provides the functionality to compare PHP values for equality", 810 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 811 | "keywords": [ 812 | "comparator", 813 | "compare", 814 | "equality" 815 | ], 816 | "time": "2015-07-26 15:48:44" 817 | }, 818 | { 819 | "name": "sebastian/diff", 820 | "version": "1.4.1", 821 | "source": { 822 | "type": "git", 823 | "url": "https://github.com/sebastianbergmann/diff.git", 824 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 825 | }, 826 | "dist": { 827 | "type": "zip", 828 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 829 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 830 | "shasum": "" 831 | }, 832 | "require": { 833 | "php": ">=5.3.3" 834 | }, 835 | "require-dev": { 836 | "phpunit/phpunit": "~4.8" 837 | }, 838 | "type": "library", 839 | "extra": { 840 | "branch-alias": { 841 | "dev-master": "1.4-dev" 842 | } 843 | }, 844 | "autoload": { 845 | "classmap": [ 846 | "src/" 847 | ] 848 | }, 849 | "notification-url": "https://packagist.org/downloads/", 850 | "license": [ 851 | "BSD-3-Clause" 852 | ], 853 | "authors": [ 854 | { 855 | "name": "Kore Nordmann", 856 | "email": "mail@kore-nordmann.de" 857 | }, 858 | { 859 | "name": "Sebastian Bergmann", 860 | "email": "sebastian@phpunit.de" 861 | } 862 | ], 863 | "description": "Diff implementation", 864 | "homepage": "https://github.com/sebastianbergmann/diff", 865 | "keywords": [ 866 | "diff" 867 | ], 868 | "time": "2015-12-08 07:14:41" 869 | }, 870 | { 871 | "name": "sebastian/environment", 872 | "version": "1.3.7", 873 | "source": { 874 | "type": "git", 875 | "url": "https://github.com/sebastianbergmann/environment.git", 876 | "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" 877 | }, 878 | "dist": { 879 | "type": "zip", 880 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", 881 | "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", 882 | "shasum": "" 883 | }, 884 | "require": { 885 | "php": ">=5.3.3" 886 | }, 887 | "require-dev": { 888 | "phpunit/phpunit": "~4.4" 889 | }, 890 | "type": "library", 891 | "extra": { 892 | "branch-alias": { 893 | "dev-master": "1.3.x-dev" 894 | } 895 | }, 896 | "autoload": { 897 | "classmap": [ 898 | "src/" 899 | ] 900 | }, 901 | "notification-url": "https://packagist.org/downloads/", 902 | "license": [ 903 | "BSD-3-Clause" 904 | ], 905 | "authors": [ 906 | { 907 | "name": "Sebastian Bergmann", 908 | "email": "sebastian@phpunit.de" 909 | } 910 | ], 911 | "description": "Provides functionality to handle HHVM/PHP environments", 912 | "homepage": "http://www.github.com/sebastianbergmann/environment", 913 | "keywords": [ 914 | "Xdebug", 915 | "environment", 916 | "hhvm" 917 | ], 918 | "time": "2016-05-17 03:18:57" 919 | }, 920 | { 921 | "name": "sebastian/exporter", 922 | "version": "1.2.2", 923 | "source": { 924 | "type": "git", 925 | "url": "https://github.com/sebastianbergmann/exporter.git", 926 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" 927 | }, 928 | "dist": { 929 | "type": "zip", 930 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", 931 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", 932 | "shasum": "" 933 | }, 934 | "require": { 935 | "php": ">=5.3.3", 936 | "sebastian/recursion-context": "~1.0" 937 | }, 938 | "require-dev": { 939 | "ext-mbstring": "*", 940 | "phpunit/phpunit": "~4.4" 941 | }, 942 | "type": "library", 943 | "extra": { 944 | "branch-alias": { 945 | "dev-master": "1.3.x-dev" 946 | } 947 | }, 948 | "autoload": { 949 | "classmap": [ 950 | "src/" 951 | ] 952 | }, 953 | "notification-url": "https://packagist.org/downloads/", 954 | "license": [ 955 | "BSD-3-Clause" 956 | ], 957 | "authors": [ 958 | { 959 | "name": "Jeff Welch", 960 | "email": "whatthejeff@gmail.com" 961 | }, 962 | { 963 | "name": "Volker Dusch", 964 | "email": "github@wallbash.com" 965 | }, 966 | { 967 | "name": "Bernhard Schussek", 968 | "email": "bschussek@2bepublished.at" 969 | }, 970 | { 971 | "name": "Sebastian Bergmann", 972 | "email": "sebastian@phpunit.de" 973 | }, 974 | { 975 | "name": "Adam Harvey", 976 | "email": "aharvey@php.net" 977 | } 978 | ], 979 | "description": "Provides the functionality to export PHP variables for visualization", 980 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 981 | "keywords": [ 982 | "export", 983 | "exporter" 984 | ], 985 | "time": "2016-06-17 09:04:28" 986 | }, 987 | { 988 | "name": "sebastian/global-state", 989 | "version": "1.1.1", 990 | "source": { 991 | "type": "git", 992 | "url": "https://github.com/sebastianbergmann/global-state.git", 993 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 994 | }, 995 | "dist": { 996 | "type": "zip", 997 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 998 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 999 | "shasum": "" 1000 | }, 1001 | "require": { 1002 | "php": ">=5.3.3" 1003 | }, 1004 | "require-dev": { 1005 | "phpunit/phpunit": "~4.2" 1006 | }, 1007 | "suggest": { 1008 | "ext-uopz": "*" 1009 | }, 1010 | "type": "library", 1011 | "extra": { 1012 | "branch-alias": { 1013 | "dev-master": "1.0-dev" 1014 | } 1015 | }, 1016 | "autoload": { 1017 | "classmap": [ 1018 | "src/" 1019 | ] 1020 | }, 1021 | "notification-url": "https://packagist.org/downloads/", 1022 | "license": [ 1023 | "BSD-3-Clause" 1024 | ], 1025 | "authors": [ 1026 | { 1027 | "name": "Sebastian Bergmann", 1028 | "email": "sebastian@phpunit.de" 1029 | } 1030 | ], 1031 | "description": "Snapshotting of global state", 1032 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1033 | "keywords": [ 1034 | "global state" 1035 | ], 1036 | "time": "2015-10-12 03:26:01" 1037 | }, 1038 | { 1039 | "name": "sebastian/recursion-context", 1040 | "version": "1.0.2", 1041 | "source": { 1042 | "type": "git", 1043 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1044 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791" 1045 | }, 1046 | "dist": { 1047 | "type": "zip", 1048 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", 1049 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791", 1050 | "shasum": "" 1051 | }, 1052 | "require": { 1053 | "php": ">=5.3.3" 1054 | }, 1055 | "require-dev": { 1056 | "phpunit/phpunit": "~4.4" 1057 | }, 1058 | "type": "library", 1059 | "extra": { 1060 | "branch-alias": { 1061 | "dev-master": "1.0.x-dev" 1062 | } 1063 | }, 1064 | "autoload": { 1065 | "classmap": [ 1066 | "src/" 1067 | ] 1068 | }, 1069 | "notification-url": "https://packagist.org/downloads/", 1070 | "license": [ 1071 | "BSD-3-Clause" 1072 | ], 1073 | "authors": [ 1074 | { 1075 | "name": "Jeff Welch", 1076 | "email": "whatthejeff@gmail.com" 1077 | }, 1078 | { 1079 | "name": "Sebastian Bergmann", 1080 | "email": "sebastian@phpunit.de" 1081 | }, 1082 | { 1083 | "name": "Adam Harvey", 1084 | "email": "aharvey@php.net" 1085 | } 1086 | ], 1087 | "description": "Provides functionality to recursively process PHP variables", 1088 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1089 | "time": "2015-11-11 19:50:13" 1090 | }, 1091 | { 1092 | "name": "sebastian/version", 1093 | "version": "1.0.6", 1094 | "source": { 1095 | "type": "git", 1096 | "url": "https://github.com/sebastianbergmann/version.git", 1097 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1098 | }, 1099 | "dist": { 1100 | "type": "zip", 1101 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1102 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1103 | "shasum": "" 1104 | }, 1105 | "type": "library", 1106 | "autoload": { 1107 | "classmap": [ 1108 | "src/" 1109 | ] 1110 | }, 1111 | "notification-url": "https://packagist.org/downloads/", 1112 | "license": [ 1113 | "BSD-3-Clause" 1114 | ], 1115 | "authors": [ 1116 | { 1117 | "name": "Sebastian Bergmann", 1118 | "email": "sebastian@phpunit.de", 1119 | "role": "lead" 1120 | } 1121 | ], 1122 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1123 | "homepage": "https://github.com/sebastianbergmann/version", 1124 | "time": "2015-06-21 13:59:46" 1125 | }, 1126 | { 1127 | "name": "symfony/yaml", 1128 | "version": "v3.1.2", 1129 | "source": { 1130 | "type": "git", 1131 | "url": "https://github.com/symfony/yaml.git", 1132 | "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de" 1133 | }, 1134 | "dist": { 1135 | "type": "zip", 1136 | "url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de", 1137 | "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de", 1138 | "shasum": "" 1139 | }, 1140 | "require": { 1141 | "php": ">=5.5.9" 1142 | }, 1143 | "type": "library", 1144 | "extra": { 1145 | "branch-alias": { 1146 | "dev-master": "3.1-dev" 1147 | } 1148 | }, 1149 | "autoload": { 1150 | "psr-4": { 1151 | "Symfony\\Component\\Yaml\\": "" 1152 | }, 1153 | "exclude-from-classmap": [ 1154 | "/Tests/" 1155 | ] 1156 | }, 1157 | "notification-url": "https://packagist.org/downloads/", 1158 | "license": [ 1159 | "MIT" 1160 | ], 1161 | "authors": [ 1162 | { 1163 | "name": "Fabien Potencier", 1164 | "email": "fabien@symfony.com" 1165 | }, 1166 | { 1167 | "name": "Symfony Community", 1168 | "homepage": "https://symfony.com/contributors" 1169 | } 1170 | ], 1171 | "description": "Symfony Yaml Component", 1172 | "homepage": "https://symfony.com", 1173 | "time": "2016-06-29 05:41:56" 1174 | }, 1175 | { 1176 | "name": "webmozart/assert", 1177 | "version": "1.0.2", 1178 | "source": { 1179 | "type": "git", 1180 | "url": "https://github.com/webmozart/assert.git", 1181 | "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde" 1182 | }, 1183 | "dist": { 1184 | "type": "zip", 1185 | "url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", 1186 | "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", 1187 | "shasum": "" 1188 | }, 1189 | "require": { 1190 | "php": ">=5.3.3" 1191 | }, 1192 | "require-dev": { 1193 | "phpunit/phpunit": "^4.6" 1194 | }, 1195 | "type": "library", 1196 | "extra": { 1197 | "branch-alias": { 1198 | "dev-master": "1.0-dev" 1199 | } 1200 | }, 1201 | "autoload": { 1202 | "psr-4": { 1203 | "Webmozart\\Assert\\": "src/" 1204 | } 1205 | }, 1206 | "notification-url": "https://packagist.org/downloads/", 1207 | "license": [ 1208 | "MIT" 1209 | ], 1210 | "authors": [ 1211 | { 1212 | "name": "Bernhard Schussek", 1213 | "email": "bschussek@gmail.com" 1214 | } 1215 | ], 1216 | "description": "Assertions to validate method input/output with nice error messages.", 1217 | "keywords": [ 1218 | "assert", 1219 | "check", 1220 | "validate" 1221 | ], 1222 | "time": "2015-08-24 13:29:44" 1223 | } 1224 | ], 1225 | "aliases": [], 1226 | "minimum-stability": "stable", 1227 | "stability-flags": [], 1228 | "prefer-stable": false, 1229 | "prefer-lowest": false, 1230 | "platform": [], 1231 | "platform-dev": [] 1232 | } 1233 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Policy.php: -------------------------------------------------------------------------------- 1 | rules[] = $rule; 10 | } 11 | 12 | /** 13 | * @return Rule[] 14 | */ 15 | public function rules() 16 | { 17 | return $this->rules; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/PasswordPolicy/PolicyBuilder.php: -------------------------------------------------------------------------------- 1 | policy = $policy; 34 | } 35 | 36 | /** 37 | * Add a min length rule 38 | * 39 | * @param $length int 40 | * 41 | * @return $this 42 | */ 43 | public function minLength($length) 44 | { 45 | $this->policy->addRule( 46 | (new LengthRule)->min($length) 47 | ); 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Add a max length rule 54 | * 55 | * @param $length int 56 | * 57 | * @return $this 58 | */ 59 | public function maxLength($length) 60 | { 61 | $this->policy->addRule( 62 | (new LengthRule)->max($length) 63 | ); 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Add an upper case rule 70 | * 71 | * @param int $min min number of upper case characters 72 | * 73 | * @return $this 74 | */ 75 | public function upperCase($min = 1) 76 | { 77 | $this->policy->addRule( 78 | (new CaseRule)->upper($min) 79 | ); 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Add an lower case rule 86 | * 87 | * @param int $min min number of lower case characters 88 | * 89 | * @return $this 90 | */ 91 | public function lowerCase($min = 1) 92 | { 93 | $this->policy->addRule( 94 | (new CaseRule)->lower($min) 95 | ); 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Add a digit rule 102 | * 103 | * @param int $min min number of digits 104 | * 105 | * @return $this 106 | */ 107 | public function digits($min = 1) 108 | { 109 | $this->policy->addRule( 110 | (new DigitRule)->min($min) 111 | ); 112 | 113 | return $this; 114 | } 115 | 116 | /** 117 | * Add a does not complain rule based on the given phrases 118 | * 119 | * @param $phrases string|array 120 | * @param $caseSensitive bool 121 | * 122 | * @return $this 123 | */ 124 | public function doesNotContain($phrases, $caseSensitive = true) 125 | { 126 | $phrases = is_array($phrases) ? $phrases : func_get_args(); 127 | 128 | $this->policy->addRule( 129 | (new ContainRule($caseSensitive))->phrase($phrases)->doesnt() 130 | ); 131 | 132 | return $this; 133 | } 134 | 135 | /** 136 | * Add a contains rule based on the given phrases 137 | * 138 | * @param $phrases string|array 139 | * @param $caseSensitive bool 140 | * 141 | * @return $this 142 | */ 143 | public function contains($phrases, $caseSensitive = true) 144 | { 145 | $phrases = is_array($phrases) ? $phrases : func_get_args(); 146 | 147 | $this->policy->addRule( 148 | (new ContainRule($caseSensitive))->phrase($phrases) 149 | ); 150 | 151 | return $this; 152 | } 153 | 154 | /** 155 | * Add a nested set of minimum passing rules 156 | * 157 | * @param $passesRequired 158 | * @param Closure $ruleSet 159 | * 160 | * @return $this 161 | */ 162 | public function minPassingRules($passesRequired, Closure $ruleSet) 163 | { 164 | $this->policy->addRule( 165 | (new MinPassingRulesRule($passesRequired))->using($ruleSet) 166 | ); 167 | 168 | return $this; 169 | } 170 | 171 | /** 172 | * Special characters 173 | * 174 | * @param int $min 175 | * @return $this 176 | */ 177 | public function specialCharacters($min = 1) 178 | { 179 | $this->policy->addRule( 180 | (new SpecialCharacterRule)->min($min) 181 | ); 182 | 183 | return $this; 184 | } 185 | 186 | 187 | /** 188 | * Get the policy instance 189 | * 190 | * @return Policy 191 | */ 192 | public function getPolicy() 193 | { 194 | return $this->policy; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/PasswordPolicy/PolicyManager.php: -------------------------------------------------------------------------------- 1 | defaultPolicy = $name; 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * Get the name of the default policy 44 | * 45 | * @return string 46 | */ 47 | public function getDefaultName() 48 | { 49 | return $this->defaultPolicy; 50 | } 51 | 52 | /** 53 | * Define a new policy by name 54 | * 55 | * @param $name string 56 | * @param $policy Policy|PolicyBuilder|Closure 57 | * 58 | * @return $this 59 | */ 60 | public function define($name, $policy) 61 | { 62 | $this->policies[$name] = $this->parsePolicy($policy); 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * Parse policy 69 | * 70 | * @param $policy 71 | * 72 | * @return Policy 73 | * @throws InvalidArgumentException 74 | */ 75 | protected function parsePolicy($policy) 76 | { 77 | if ($policy instanceof Policy) { 78 | return $policy; 79 | } 80 | 81 | if ($policy instanceof PolicyBuilder) { 82 | return $policy->getPolicy(); 83 | } 84 | 85 | if ($policy instanceof Closure) { 86 | return $this->parseClosure($policy); 87 | } 88 | 89 | throw new InvalidArgumentException("Invalid policy declaration."); 90 | } 91 | 92 | /** 93 | * Parse closure definition 94 | * 95 | * @param Closure $closure 96 | * 97 | * @return Policy 98 | */ 99 | protected function parseClosure(Closure $closure) 100 | { 101 | call_user_func($closure, $builder = $this->newBuilder()); 102 | 103 | return $builder->getPolicy(); 104 | } 105 | 106 | /** 107 | * Get a new builder instance 108 | * 109 | * @return PolicyBuilder 110 | */ 111 | public function newBuilder() 112 | { 113 | return new PolicyBuilder(new Policy); 114 | } 115 | 116 | /** 117 | * Get a new validator instance for the given policy name 118 | * 119 | * @param $policy string 120 | * 121 | * @return Validator 122 | */ 123 | public function validator($policy) 124 | { 125 | return new Validator($this->resolve($policy)); 126 | } 127 | 128 | /** 129 | * Resolve a policy 130 | * 131 | * @param $policy Policy|string 132 | * 133 | * @throws InvalidArgumentException 134 | * @return Policy 135 | */ 136 | protected function resolve($policy) 137 | { 138 | if ($policy instanceof Policy) { 139 | return $policy; 140 | } 141 | 142 | return $this->getPolicy($policy); 143 | } 144 | 145 | /** 146 | * Check whether a given policy exists 147 | * 148 | * @param $name string 149 | * 150 | * @return bool 151 | */ 152 | public function policyExists($name) 153 | { 154 | return isset($this->policies[$name]); 155 | } 156 | 157 | /** 158 | * Get a policy by name. Throws an exception if policy is not found. 159 | * 160 | * @param $name string 161 | * 162 | * @throws InvalidArgumentException 163 | * @return Policy 164 | */ 165 | public function getPolicy($name) 166 | { 167 | if ($this->policyExists($name)) { 168 | return $this->policies[$name]; 169 | } 170 | 171 | throw new InvalidArgumentException("Password policy [{$name}] does not exist."); 172 | } 173 | } -------------------------------------------------------------------------------- /src/PasswordPolicy/Providers/Laravel/Facade.php: -------------------------------------------------------------------------------- 1 | registerManager(); 24 | $this->registerBuilder(); 25 | $this->registerFacade(); 26 | $this->defineDefaultPolicy(); 27 | } 28 | 29 | /** 30 | * Boot the service provider 31 | * 32 | * @return void 33 | */ 34 | public function boot() 35 | { 36 | $this->configureValidationRule(); 37 | } 38 | 39 | /** 40 | * Register the policy manager within the Laravel container 41 | * 42 | * @return void 43 | */ 44 | protected function registerManager() 45 | { 46 | $this->app->singleton(PolicyManager::class); 47 | } 48 | 49 | /** 50 | * Register policy builder 51 | * 52 | * @return void 53 | */ 54 | protected function registerBuilder() 55 | { 56 | $this->app->bind(PolicyBuilder::class); 57 | } 58 | 59 | /** 60 | * Configure custom Laravel validation rule 61 | * 62 | * @return void 63 | */ 64 | protected function configureValidationRule() 65 | { 66 | $this->app['validator']->extend('password', PasswordValidator::class . '@validate'); 67 | } 68 | 69 | /** 70 | * Register password policy facade 71 | * 72 | * @return void 73 | */ 74 | protected function registerFacade() 75 | { 76 | $loader = AliasLoader::getInstance(); 77 | $loader->alias('PasswordPolicy', Facade::class); 78 | } 79 | 80 | /** 81 | * Define the default password policy 82 | * 83 | * @return void 84 | */ 85 | protected function defineDefaultPolicy() 86 | { 87 | $defaultPolicy = $this->defaultPolicy($this->app->make(PolicyBuilder::class)); 88 | 89 | if ($defaultPolicy instanceof PolicyBuilder) { 90 | $defaultPolicy = $defaultPolicy->getPolicy(); 91 | } 92 | 93 | $this->app->make(PolicyManager::class)->define('default', $defaultPolicy); 94 | } 95 | 96 | /** 97 | * Build the default policy instance 98 | * 99 | * @param PolicyBuilder $builder 100 | * 101 | * @return \PasswordPolicy\Policy 102 | */ 103 | protected function defaultPolicy(PolicyBuilder $builder) 104 | { 105 | return $builder->getPolicy(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Providers/Laravel/PasswordValidator.php: -------------------------------------------------------------------------------- 1 | manager = $manager; 28 | } 29 | 30 | /** 31 | * Validate the given value 32 | * 33 | * @param $attribute 34 | * @param $value 35 | * @param $parameters 36 | * @param $validator 37 | * 38 | * @return bool 39 | */ 40 | public function validate($attribute, $value, $parameters, $validator) 41 | { 42 | // Use the default policy if the user has not specified one. 43 | $policy = isset($parameters[0]) ? $parameters[0] : $this->manager->getDefaultName(); 44 | 45 | return $this->manager 46 | ->validator($policy) 47 | ->attempt($value); 48 | } 49 | } -------------------------------------------------------------------------------- /src/PasswordPolicy/Rule.php: -------------------------------------------------------------------------------- 1 | lower = $min; 36 | 37 | return $this; 38 | } 39 | 40 | /** 41 | * Set the minimum number of upper case characters 42 | * 43 | * @param int $min 44 | * 45 | * @return $this 46 | */ 47 | public function upper($min = 1) 48 | { 49 | $this->upper = $min; 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * Test a rule 56 | * 57 | * @param $subject 58 | * 59 | * @return bool 60 | */ 61 | public function test($subject) 62 | { 63 | // Lower case 64 | if (!$this->check($subject, '/[^a-z]/', $this->lower)) { 65 | return false; 66 | } 67 | 68 | // Upper case 69 | if (!$this->check($subject, '/[^A-Z]/', $this->upper)) { 70 | return false; 71 | } 72 | 73 | return true; 74 | } 75 | 76 | /** 77 | * Check the given subject against a pattern for minimum occurrence 78 | * 79 | * @param $subject 80 | * @param $pattern 81 | * @param $min 82 | * 83 | * @return bool 84 | */ 85 | private function check($subject, $pattern, $min) 86 | { 87 | $matchingCharacters = preg_replace($pattern, '', $subject); 88 | 89 | if (strlen($matchingCharacters) < $min) { 90 | return false; 91 | } 92 | 93 | return true; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Rules/ContainRule.php: -------------------------------------------------------------------------------- 1 | caseSensitive = $caseSensitive; 36 | } 37 | 38 | /** 39 | * Set phrase(s) 40 | * 41 | * @param $phrases 42 | * 43 | * @return $this 44 | */ 45 | public function phrase($phrases) 46 | { 47 | $phrases = is_array($phrases) ? $phrases : func_get_args(); 48 | 49 | $this->phrases = array_merge($this->phrases, $phrases); 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * Toggle doesn't contain 56 | * 57 | * @return $this 58 | */ 59 | public function doesnt() 60 | { 61 | $this->does = false; 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * Toggle does contain 68 | * 69 | * @return $this 70 | */ 71 | public function does() 72 | { 73 | $this->does = true; 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Test a rule 80 | * 81 | * @param $subject 82 | * 83 | * @return bool 84 | */ 85 | public function test($subject) 86 | { 87 | if ($this->does) { 88 | foreach ($this->phrases as $phrase) { 89 | if ($this->containsPhrase($subject, $phrase)) { 90 | return true; 91 | } 92 | } 93 | 94 | return false; 95 | } else { 96 | foreach ($this->phrases as $phrase) { 97 | if ($this->containsPhrase($subject, $phrase)) { 98 | return false; 99 | } 100 | } 101 | 102 | return true; 103 | } 104 | } 105 | 106 | /** 107 | * Check if a subject contains a phrase 108 | * 109 | * @param $subject 110 | * @param $phrase 111 | * 112 | * @return bool 113 | */ 114 | private function containsPhrase($subject, $phrase) 115 | { 116 | if ($this->caseSensitive) { 117 | return strpos($subject, $phrase) !== false; 118 | } 119 | 120 | return mb_stripos($subject, $phrase) !== false; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Rules/DigitRule.php: -------------------------------------------------------------------------------- 1 | min = $min; 29 | 30 | return $this; 31 | } 32 | 33 | /** 34 | * Test a rule 35 | * 36 | * @param $subject 37 | * 38 | * @return bool 39 | */ 40 | public function test($subject) 41 | { 42 | return $this->check($subject, '/[^0-9]/', $this->min); 43 | } 44 | 45 | /** 46 | * Check the given subject against a pattern for minimum occurrence 47 | * 48 | * @param $subject 49 | * @param $pattern 50 | * @param $min 51 | * 52 | * @return bool 53 | */ 54 | private function check($subject, $pattern, $min) 55 | { 56 | $matchingCharacters = preg_replace($pattern, '', $subject); 57 | 58 | if (strlen($matchingCharacters) < $min) { 59 | return false; 60 | } 61 | 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Rules/LengthRule.php: -------------------------------------------------------------------------------- 1 | min = $min; 25 | 26 | return $this; 27 | } 28 | 29 | public function max($max) 30 | { 31 | $this->max = $max; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Test a rule 38 | * 39 | * @param $subject 40 | * 41 | * @return bool 42 | */ 43 | public function test($subject) 44 | { 45 | $length = strlen($subject); 46 | 47 | if ($this->min !== null && $length < $this->min) { 48 | return false; 49 | } 50 | 51 | if ($this->max !== null && $length > $this->max) { 52 | return false; 53 | } 54 | 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Rules/MinPassingRulesRule.php: -------------------------------------------------------------------------------- 1 | passesRequired = $passesRequired; 35 | } 36 | 37 | /** 38 | * Set of rules to apply the passes required check to 39 | * 40 | * @param Closure $test 41 | * 42 | * @return $this 43 | */ 44 | public function using(Closure $test) 45 | { 46 | $this->test = $test; 47 | 48 | return $this; 49 | } 50 | 51 | /** 52 | * Test a rule 53 | * 54 | * @param $subject 55 | * 56 | * @return bool 57 | */ 58 | public function test($subject) 59 | { 60 | call_user_func($this->test, $builder = $this->createBuilder()); 61 | 62 | $passedRules = 0; 63 | 64 | /** @var Rule $rule */ 65 | foreach ($builder->getPolicy()->rules() as $rule) { 66 | if ($rule->test($subject) === true) { 67 | $passedRules++; 68 | } 69 | } 70 | 71 | return $passedRules >= $this->passesRequired; 72 | } 73 | 74 | /** 75 | * Create the policy builder to nest 76 | * 77 | * @return PolicyBuilder 78 | */ 79 | private function createBuilder() 80 | { 81 | return new PolicyBuilder(new Policy); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Rules/NumberSequenceRule.php: -------------------------------------------------------------------------------- 1 | min = $min; 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * Test a rule 34 | * 35 | * @param $subject 36 | * 37 | * @return bool 38 | */ 39 | public function test($subject) 40 | { 41 | return $this->check($subject, '/[\w\d\s]/', $this->min); 42 | } 43 | 44 | /** 45 | * Check the given subject against a pattern for minimum occurrence 46 | * 47 | * @param $subject 48 | * @param $pattern 49 | * @param $min 50 | * 51 | * @return bool 52 | */ 53 | private function check($subject, $pattern, $min) 54 | { 55 | $matchingCharacters = preg_replace($pattern, '', $subject); 56 | 57 | if (mb_strlen($matchingCharacters) < $min) { 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/PasswordPolicy/Validator.php: -------------------------------------------------------------------------------- 1 | policy = $policy; 13 | } 14 | 15 | /** 16 | * @param Policy $policy 17 | * 18 | * @return $this 19 | */ 20 | public function setPolicy(Policy $policy) 21 | { 22 | $this->policy = $policy; 23 | 24 | return $this; 25 | } 26 | 27 | public function getPolicy() 28 | { 29 | return $this->policy; 30 | } 31 | 32 | public function attempt($subject) 33 | { 34 | /** @var Rule $rule */ 35 | foreach ($this->policy->rules() as $rule) { 36 | if (!$rule->test($subject)) { 37 | return false; 38 | } 39 | } 40 | 41 | return true; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Integration/Providers/Laravel/PasswordValidatorTest.php: -------------------------------------------------------------------------------- 1 | policy() 17 | ->specialCharacters(); 18 | 19 | $this->assertFails('foo'); 20 | $this->assertPasses('foo@'); 21 | } 22 | 23 | /** @test */ 24 | public function a_given_number_of_special_characters_are_required() 25 | { 26 | $this->policy() 27 | ->specialCharacters(3); 28 | 29 | $this->assertFails('foo@@'); 30 | $this->assertPasses('foo@@@'); 31 | } 32 | 33 | protected function policy() 34 | { 35 | return $this->policy ?: $this->policy = new PolicyBuilder(new Policy); 36 | } 37 | 38 | protected function attempt($subject) 39 | { 40 | return (new Validator($this->policy->getPolicy()))->attempt($subject); 41 | } 42 | 43 | protected function assertPasses($subject) 44 | { 45 | return $this->assertTrue($this->attempt($subject)); 46 | } 47 | 48 | protected function assertFails($subject) 49 | { 50 | return $this->assertFalse($this->attempt($subject)); 51 | } 52 | } -------------------------------------------------------------------------------- /tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | minLength(8) 13 | ->upperCase(3) 14 | ->lowerCase(3) 15 | ->digits(3) 16 | ->doesNotContain('BAZ'); 17 | 18 | $validator = new Validator($builder->getPolicy()); 19 | 20 | $this->assertFalse($validator->attempt('')); 21 | $this->assertFalse($validator->attempt('foobar')); 22 | $this->assertFalse($validator->attempt('fooBAR')); 23 | $this->assertFalse($validator->attempt('fooBAZ123')); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Unit/PolicyBuilderTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('addRule') 16 | ->with(\Mockery::type(LengthRule::class)) 17 | ->once(); 18 | 19 | $builder = new PolicyBuilder($mock->getMock()); 20 | $builder->minLength(2); 21 | 22 | $this->assertTrue(true); 23 | } 24 | 25 | protected function tearDown() 26 | { 27 | \Mockery::close(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Unit/PolicyManagerTest.php: -------------------------------------------------------------------------------- 1 | setDefaultName('foobar'); 15 | $this->assertEquals('foobar', $manager->getDefaultName()); 16 | } 17 | 18 | /** @test */ 19 | public function it_throws_an_exception_when_an_invalid_policy_is_requested() 20 | { 21 | $name = 'foobar'; 22 | $manager = new PolicyManager; 23 | 24 | $this->setExpectedException('InvalidArgumentException', "Password policy [{$name}] does not exist."); 25 | $manager->getPolicy($name); 26 | } 27 | 28 | /** @test */ 29 | public function it_successfully_registers_a_new_policy() 30 | { 31 | $manager = new PolicyManager; 32 | 33 | $this->assertSame($manager, $manager->define('admin', $policy = new Policy)); 34 | $this->assertSame($policy, $manager->getPolicy('admin')); 35 | } 36 | 37 | /** @test */ 38 | public function it_successfully_registers_a_new_policy_using_a_builder_instance() 39 | { 40 | $manager = new PolicyManager; 41 | $builder = new PolicyBuilder($policy = new Policy); 42 | 43 | $this->assertSame($manager, $manager->define('admin', $builder)); 44 | $this->assertSame($policy, $manager->getPolicy('admin')); 45 | } 46 | 47 | /** @test */ 48 | public function it_successfully_registers_a_new_policy_using_a_closure() 49 | { 50 | $manager = new PolicyManager; 51 | 52 | $this->assertSame($manager, $manager->define('admin', function ($builder) { 53 | $_SERVER['__test.policy'] = $builder->getPolicy(); 54 | })); 55 | 56 | $this->assertSame($_SERVER['__test.policy'], $manager->getPolicy('admin')); 57 | } 58 | 59 | /** @test */ 60 | public function it_throws_an_exception_when_parsing_an_invalid_policy_definition() 61 | { 62 | $manager = new PolicyManager; 63 | 64 | $this->setExpectedException(\InvalidArgumentException::class); 65 | $manager->define('foobar', null); 66 | } 67 | 68 | /** @test */ 69 | public function it_returns_a_new_builder_instance() 70 | { 71 | $manager = new PolicyManager; 72 | $builder = $manager->newBuilder(); 73 | $builder2 = $manager->newBuilder(); 74 | 75 | $this->assertInstanceOf(PolicyBuilder::class, $builder); 76 | $this->assertNotSame($builder, $builder2); 77 | } 78 | 79 | /** @test */ 80 | public function it_returns_a_validator_instance_for_a_valid_named_policy() 81 | { 82 | $manager = new PolicyManager; 83 | $manager->define('foo', $policy = new Policy); 84 | 85 | $validator = $manager->validator('foo'); 86 | 87 | $this->assertInstanceOf(Validator::class, $validator); 88 | $this->assertSame($policy, $validator->getPolicy()); 89 | } 90 | 91 | /** @test */ 92 | public function it_throws_an_exception_when_a_validator_is_requested_for_an_invalid_policy_name() 93 | { 94 | $manager = new PolicyManager; 95 | 96 | $this->setExpectedException('InvalidArgumentException', "Password policy [bar] does not exist."); 97 | 98 | $manager->validator('bar'); 99 | } 100 | 101 | /** @test */ 102 | public function it_correctly_checks_if_a_policy_exists() 103 | { 104 | $manager = new PolicyManager; 105 | $manager->define('foo', $policy = new Policy); 106 | 107 | $this->assertTrue($manager->policyExists('foo')); 108 | $this->assertFalse($manager->policyExists('bar')); 109 | } 110 | } -------------------------------------------------------------------------------- /tests/Unit/Providers/Laravel/PasswordValidatorTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('getDefaultName') 17 | // ->shouldReceive('validator') 18 | // ->once() 19 | // ->andReturn('default'); 20 | // 21 | // $manager = $managerMock->getMock(); 22 | // $manager->define('default', new Policy); 23 | // 24 | // $validatorMock = m::mock(PasswordValidator::class, [$manager]) 25 | // ->shouldReceive('validate') 26 | // ->once() 27 | // ->with('password', 'password', [], null) 28 | // ->andReturn(true); 29 | // 30 | // $validator = $validatorMock->getMock(); 31 | // $this->assertTrue(($validator->validate('password', 'password', [], null))); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/Unit/Rules/CaseRuleTest.php: -------------------------------------------------------------------------------- 1 | lower(3); 12 | 13 | $this->assertFalse($rule->test('BAR')); 14 | $this->assertFalse($rule->test('BaR')); 15 | $this->assertFalse($rule->test('ba')); 16 | $this->assertTrue($rule->test('bar')); 17 | $this->assertTrue($rule->test('fOoBaR')); 18 | } 19 | 20 | /** @test */ 21 | function it_tests_a_string_contains_a_given_number_of_upper_case_characters() 22 | { 23 | $rule = new CaseRule; 24 | $rule->upper(3); 25 | 26 | $this->assertFalse($rule->test('bar')); 27 | $this->assertFalse($rule->test('bAr')); 28 | $this->assertFalse($rule->test('BA')); 29 | $this->assertTrue($rule->test('BAR')); 30 | $this->assertTrue($rule->test('fOoBaR')); 31 | } 32 | 33 | /** @test */ 34 | function it_tests_a_string_contains_a_given_number_of_upper_and_lower_case_characters() 35 | { 36 | $rule = new CaseRule; 37 | $rule->lower(3)->upper(3); 38 | 39 | $this->assertFalse($rule->test('bar')); 40 | $this->assertFalse($rule->test('BAR')); 41 | $this->assertFalse($rule->test('foBA')); 42 | $this->assertTrue($rule->test('fOoBaR')); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Unit/Rules/ContainRuleTest.php: -------------------------------------------------------------------------------- 1 | phrase('foo'); 12 | 13 | $this->assertFalse($rule->test('bar')); 14 | $this->assertTrue($rule->test('foobar')); 15 | } 16 | 17 | /** @test */ 18 | function it_tests_a_string_contains_a_string_insensitive() 19 | { 20 | $rule = new ContainRule(false); 21 | $rule->phrase('föo'); 22 | 23 | $this->assertFalse($rule->test('bar')); 24 | $this->assertTrue($rule->test('föobar')); 25 | $this->assertFalse($rule->test('BAR')); 26 | $this->assertTrue($rule->test('FÖOBAR')); 27 | } 28 | 29 | /** @test */ 30 | function it_tests_a_string_contains_multiple_strings() 31 | { 32 | $rule = new ContainRule(); 33 | $rule->phrase(['foo', 'bar']) 34 | ->phrase('bax', 'baz'); 35 | 36 | $this->assertFalse($rule->test('hello')); 37 | $this->assertTrue($rule->test('foo')); 38 | $this->assertTrue($rule->test('bax')); 39 | $this->assertTrue($rule->test('baz')); 40 | } 41 | 42 | /** @test */ 43 | function it_tests_a_string_contains_multiple_strings_insensitive() 44 | { 45 | $rule = new ContainRule(false); 46 | $rule->phrase(['foo', 'bar']) 47 | ->phrase('bax', 'băz'); 48 | 49 | $this->assertFalse($rule->test('hello')); 50 | $this->assertFalse($rule->test('HELLO')); 51 | $this->assertTrue($rule->test('foo')); 52 | $this->assertTrue($rule->test('bax')); 53 | $this->assertTrue($rule->test('băz')); 54 | $this->assertTrue($rule->test('FOO')); 55 | $this->assertTrue($rule->test('BaX')); 56 | $this->assertTrue($rule->test('bĂz')); 57 | } 58 | 59 | /** @test */ 60 | function it_tests_a_string_does_not_contain_a_string() 61 | { 62 | $rule = new ContainRule(); 63 | $rule->phrase('foo')->doesnt(); 64 | 65 | $this->assertTrue($rule->test('bar')); 66 | $this->assertFalse($rule->test('foobar')); 67 | $this->assertTrue($rule->test('FOObar')); 68 | } 69 | 70 | /** @test */ 71 | function it_tests_a_string_does_not_contain_a_string_insensitive() 72 | { 73 | $rule = new ContainRule(false); 74 | $rule->phrase('föo')->doesnt(); 75 | 76 | $this->assertTrue($rule->test('bar')); 77 | $this->assertTrue($rule->test('BAR')); 78 | $this->assertFalse($rule->test('föobar')); 79 | $this->assertFalse($rule->test('FÖObar')); 80 | } 81 | 82 | /** @test */ 83 | function it_tests_a_string_does_not_contain_multiple_strings() 84 | { 85 | $rule = new ContainRule(); 86 | $rule->phrase(['foo', 'bar']) 87 | ->phrase('bax', 'baz') 88 | ->doesnt(); 89 | 90 | $this->assertTrue($rule->test('hello')); 91 | $this->assertTrue($rule->test('HELLO')); 92 | $this->assertFalse($rule->test('foo')); 93 | $this->assertFalse($rule->test('bax')); 94 | $this->assertFalse($rule->test('baz')); 95 | $this->assertTrue($rule->test('Foo')); 96 | $this->assertTrue($rule->test('BAX')); 97 | } 98 | 99 | /** @test */ 100 | function it_tests_a_string_does_not_contain_multiple_strings_insensitive() 101 | { 102 | $rule = new ContainRule(false); 103 | $rule->phrase(['foo', 'bar']) 104 | ->phrase('bax', 'bâz') 105 | ->doesnt(); 106 | 107 | $this->assertTrue($rule->test('hello')); 108 | $this->assertTrue($rule->test('HELLO')); 109 | $this->assertFalse($rule->test('foo')); 110 | $this->assertFalse($rule->test('bax')); 111 | $this->assertFalse($rule->test('bâz')); 112 | $this->assertFalse($rule->test('FOO')); 113 | $this->assertFalse($rule->test('Bax')); 114 | $this->assertFalse($rule->test('bÂz')); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tests/Unit/Rules/DigitRuleTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($rule->test('foobar')); 13 | $this->assertTrue($rule->test('foobar1')); 14 | } 15 | 16 | /** @test */ 17 | function it_tests_a_string_contains_a_given_number_of_digits() 18 | { 19 | $rule = new DigitRule(); 20 | $rule->min(3); 21 | 22 | $this->assertFalse($rule->test('foobar')); 23 | $this->assertFalse($rule->test('foobar1')); 24 | $this->assertFalse($rule->test('12')); 25 | $this->assertTrue($rule->test('123')); 26 | $this->assertTrue($rule->test('1foo2bar3')); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Unit/Rules/LengthRuleTest.php: -------------------------------------------------------------------------------- 1 | min(10); 13 | 14 | $this->assertFalse($rule->test('')); 15 | $this->assertFalse($rule->test('hello')); 16 | $this->assertTrue($rule->test('helloworld')); 17 | $this->assertTrue($rule->test('hello world')); 18 | } 19 | 20 | /** @test */ 21 | function it_tests_max_length() 22 | { 23 | $rule = new LengthRule(); 24 | $rule->max(10); 25 | 26 | $this->assertTrue($rule->test('hello')); 27 | $this->assertTrue($rule->test('helloworld')); 28 | $this->assertFalse($rule->test('hello world')); 29 | } 30 | 31 | /** @test */ 32 | function it_tests_min_and_max_length() 33 | { 34 | $rule = new LengthRule(); 35 | $rule->min(3)->max(6); 36 | 37 | $this->assertFalse($rule->test('ab')); 38 | $this->assertTrue($rule->test('abc')); 39 | $this->assertTrue($rule->test('abcde')); 40 | $this->assertTrue($rule->test('abcdef')); 41 | $this->assertFalse($rule->test('abcdefg')); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Unit/Rules/MinPassingRulesRuleTest.php: -------------------------------------------------------------------------------- 1 | mockRule(true); 15 | 16 | $rule = (new MinPassingRulesRule(1))->using(function (PolicyBuilder $builder) use ($passing) { 17 | $builder->getPolicy()->addRule($passing); 18 | }); 19 | 20 | $this->assertTrue($rule->test('password')); 21 | } 22 | 23 | /** @test */ 24 | function failing_rules_fail() 25 | { 26 | $failing = $this->mockRule(false); 27 | 28 | $rule = (new MinPassingRulesRule(1))->using(function (PolicyBuilder $builder) use ($failing) { 29 | $builder->getPolicy()->addRule($failing); 30 | }); 31 | 32 | $this->assertFalse($rule->test('password')); 33 | } 34 | 35 | /** @test */ 36 | function if_more_than_the_min_required_passing_rules_pass_the_whole_policy_passes() 37 | { 38 | $passing = $this->mockRule(true); 39 | $failing = $this->mockRule(false); 40 | 41 | $rule = (new MinPassingRulesRule(1))->using(function (PolicyBuilder $builder) use ($passing, $failing) { 42 | $builder->getPolicy()->addRule($passing); 43 | $builder->getPolicy()->addRule($failing); 44 | }); 45 | 46 | $this->assertTrue($rule->test('password')); 47 | } 48 | 49 | /** @test */ 50 | function if_less_than_the_min_required_passing_rules_pass_the_whole_policy_fails() 51 | { 52 | $failing = $this->mockRule('false'); 53 | 54 | $rule = (new MinPassingRulesRule(1))->using(function (PolicyBuilder $builder) use ($failing) { 55 | $builder->getPolicy()->addRule($failing); 56 | }); 57 | 58 | $this->assertFalse($rule->test('password')); 59 | } 60 | 61 | /** 62 | * Create a mock rule, which passes based on the given result 63 | * 64 | * @param $result 65 | * 66 | * @return \Mockery\MockInterface 67 | */ 68 | protected function mockRule($result) 69 | { 70 | return \Mockery::mock(Rule::class) 71 | ->shouldReceive('test') 72 | ->andReturn($result) 73 | ->getMock(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Unit/Rules/SpecialCharacterRuleTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($rule->test('foobar')); 13 | $this->assertTrue($rule->test('foob@r')); 14 | } 15 | 16 | /** @test */ 17 | public function spaces_are_not_counted_as_special_characters() 18 | { 19 | $rule = new SpecialCharacterRule; 20 | 21 | $this->assertFalse($rule->test('foo bar')); 22 | } 23 | 24 | /** @test */ 25 | public function a_minimum_can_be_applied() 26 | { 27 | $rule = new SpecialCharacterRule; 28 | $rule->min(3); 29 | 30 | $this->assertFalse($rule->test('foob@r')); 31 | $this->assertFalse($rule->test('foo£@r')); 32 | $this->assertTrue($rule->test('(oo£@r')); 33 | } 34 | } -------------------------------------------------------------------------------- /tests/Unit/ValidatorTest.php: -------------------------------------------------------------------------------- 1 | assertSame($policy1, $validator->getPolicy()); 16 | 17 | $validator->setPolicy($policy2 = new Policy); 18 | $this->assertSame($policy2, $validator->getPolicy()); 19 | } 20 | 21 | /** @test */ 22 | function it_calls_the_test_method_on_rules() 23 | { 24 | $subject = 'foo bar'; 25 | 26 | $mock = m::mock(Rule::class) 27 | ->shouldReceive('test') 28 | ->with($subject) 29 | ->andReturnValues([true]); 30 | 31 | $policy = new Policy; 32 | $policy->addRule($mock->getMock()); 33 | 34 | $validator = new Validator($policy); 35 | 36 | $this->assertTrue($validator->attempt($subject)); 37 | } 38 | } 39 | --------------------------------------------------------------------------------