├── .coveralls.yml ├── .editorconfig ├── .gitignore ├── .php_cs ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── doc ├── Command.md ├── Domain.md ├── Event.md └── EventLog.md ├── phpunit.xml.dist ├── src ├── Command │ ├── AbstractCommand.php │ ├── CommandDispatcher.php │ ├── CommandDispatcherInterface.php │ └── CommandHandlerInterface.php ├── Domain │ ├── AbstractAggregateRoot.php │ └── CommandEventDispatcher.php ├── Event │ ├── AbstractEvent.php │ ├── EventDispatcher.php │ ├── EventDispatcherInterface.php │ ├── EventListener.php │ └── EventSubscriberInterface.php ├── EventLog │ ├── EventLog.php │ ├── EventSubscriber │ │ └── LogAllEvents.php │ └── Repository │ │ └── EventLogRepositoryInterface.php ├── Exception │ ├── BadMethodCallException.php │ ├── CommandDispatcherException.php │ ├── ExceptionInterface.php │ └── InvalidArgumentException.php └── User │ ├── Author.php │ ├── AuthorInterface.php │ └── Robot.php └── tests ├── Command ├── AbstractCommandTest.php ├── CommandDispatcherTest.php └── Sample │ ├── TestCommandHandlerDefiningSameCommandTwice.php │ ├── TestCommandHandlerOne.php │ ├── TestCommandHandlerTwo.php │ ├── TestCommandInterface.php │ ├── TestCommandOne.php │ └── TestCommandTwo.php ├── Domain ├── AbstractAggregateRootTest.php ├── CommandEventDispatcherTest.php └── Sample │ └── TestAggregateRoot.php ├── Event ├── AbstractEventTest.php ├── EventDispatcherTest.php ├── EventListenerTest.php └── Sample │ ├── TestEvent.php │ ├── TestEventSubscriber.php │ └── TestOtherEvent.php ├── EventLog ├── EventLogTest.php └── EventSubscriber │ └── LogAllEventsTest.php └── User ├── AuthorTest.php └── RobotTest.php /.coveralls.yml: -------------------------------------------------------------------------------- 1 | # for php-coveralls 2 | service_name: travis-ci 3 | src_dir: src 4 | coverage_clover: build/logs/clover.xml 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 4 7 | trim_trailing_whitespace = true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Composer 2 | /composer.phar 3 | /vendor/ 4 | 5 | ### Php-cs 6 | /.php_cs.cache 7 | 8 | ### Phpunit 9 | /build/ 10 | /phpunit.xml 11 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | level(Symfony\CS\FixerInterface::SYMFONY_LEVEL) 5 | // and extra fixers: 6 | ->fixers( 7 | [ 8 | '-psr0', 9 | '-phpdoc_no_empty_return', 10 | '-unalign_equals', 11 | 'align_double_arrow', 12 | 'align_equals', 13 | 'multiline_spaces_before_semicolon', 14 | 'ordered_use', 15 | 'strict', 16 | 'strict_param', 17 | ] 18 | ) 19 | ->finder( 20 | Symfony\CS\Finder\DefaultFinder::create()->in([__DIR__ . '/src', __DIR__ . '/tests']) 21 | ) 22 | ->setUsingCache(true); 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: php 4 | 5 | php: 6 | - 5.5 7 | - 5.6 8 | 9 | cache: 10 | directories: 11 | - vendor 12 | - $HOME/.composer/cache 13 | 14 | before_install: 15 | - composer selfupdate 16 | - composer config github-oauth.github.com $GITHUB_OAUTH_TOKEN 17 | 18 | install: 19 | - composer install --no-interaction --prefer-dist 20 | 21 | script: 22 | - phpunit 23 | - mkdir -p .autoload/composer && mv -v ./vendor/autoload.php ./.autoload/autoload.php && mv -v ./vendor/composer/!(installed.json) ./.autoload/composer 24 | 25 | after_script: 26 | - mv -v ./.autoload/autoload.php ./vendor/autoload.php && mv -v ./.autoload/composer/* ./vendor/composer && rm -r .autoload 27 | - php vendor/bin/coveralls -v 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v0.6.0](https://github.com/php-ddd/domain-driven-design/tree/v0.6.0) (2015-08-15) 4 | 5 | [Full Changelog](https://github.com/php-ddd/domain-driven-design/compare/v0.5.0...v0.6.0) 6 | 7 | **Implemented enhancements:** 8 | 9 | - Add event logging capability [\#11](https://github.com/php-ddd/domain-driven-design/issues/11) 10 | 11 | **Merged pull requests:** 12 | 13 | - Event logging capability [\#12](https://github.com/php-ddd/domain-driven-design/pull/12) ([juliendufresne](https://github.com/juliendufresne)) 14 | 15 | ## [v0.5.0](https://github.com/php-ddd/domain-driven-design/tree/v0.5.0) (2015-08-12) 16 | [Full Changelog](https://github.com/php-ddd/domain-driven-design/compare/v0.4.0...v0.5.0) 17 | 18 | **Implemented enhancements:** 19 | 20 | - Link between Command and Event [\#9](https://github.com/php-ddd/domain-driven-design/issues/9) 21 | - Command system [\#7](https://github.com/php-ddd/domain-driven-design/issues/7) 22 | 23 | **Merged pull requests:** 24 | 25 | - Add link between Command and Event systems [\#10](https://github.com/php-ddd/domain-driven-design/pull/10) ([juliendufresne](https://github.com/juliendufresne)) 26 | 27 | ## [v0.4.0](https://github.com/php-ddd/domain-driven-design/tree/v0.4.0) (2015-08-12) 28 | [Full Changelog](https://github.com/php-ddd/domain-driven-design/compare/v0.3.0...v0.4.0) 29 | 30 | **Merged pull requests:** 31 | 32 | - implementation of the command system [\#8](https://github.com/php-ddd/domain-driven-design/pull/8) ([juliendufresne](https://github.com/juliendufresne)) 33 | 34 | ## [v0.3.0](https://github.com/php-ddd/domain-driven-design/tree/v0.3.0) (2015-08-11) 35 | [Full Changelog](https://github.com/php-ddd/domain-driven-design/compare/v0.2.0...v0.3.0) 36 | 37 | **Implemented enhancements:** 38 | 39 | - Add more coninuous integration tools [\#5](https://github.com/php-ddd/domain-driven-design/issues/5) 40 | 41 | **Merged pull requests:** 42 | 43 | - Adds Continuous integration tools [\#6](https://github.com/php-ddd/domain-driven-design/pull/6) ([juliendufresne](https://github.com/juliendufresne)) 44 | 45 | ## [v0.2.0](https://github.com/php-ddd/domain-driven-design/tree/v0.2.0) (2015-08-10) 46 | [Full Changelog](https://github.com/php-ddd/domain-driven-design/compare/v0.1.0...v0.2.0) 47 | 48 | **Implemented enhancements:** 49 | 50 | - Basic tools to create Domain objects [\#3](https://github.com/php-ddd/domain-driven-design/issues/3) 51 | 52 | **Merged pull requests:** 53 | 54 | - Basic tools to create Domain objects [\#4](https://github.com/php-ddd/domain-driven-design/pull/4) ([juliendufresne](https://github.com/juliendufresne)) 55 | 56 | ## [v0.1.0](https://github.com/php-ddd/domain-driven-design/tree/v0.1.0) (2015-08-10) 57 | **Implemented enhancements:** 58 | 59 | - Event System [\#1](https://github.com/php-ddd/domain-driven-design/issues/1) 60 | 61 | **Merged pull requests:** 62 | 63 | - Adds an event system [\#2](https://github.com/php-ddd/domain-driven-design/pull/2) ([juliendufresne](https://github.com/juliendufresne)) 64 | 65 | 66 | 67 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* 68 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributions are welcome! 2 | 3 | ## Quick guide: 4 | 5 | * Fork the repo. 6 | * Install dependencies: `composer install`. 7 | * Create branch, e.g. feature-foo or bugfix-bar. 8 | * Make changes. 9 | * If you are adding functionality or fixing a bug - add a test! 10 | * Fix project coding style: `php vendor/bin/php-cs-fixer fix`. 11 | * Check if tests pass: `php vendor/bin/phpunit`. 12 | 13 | ## Opening a pull request 14 | 15 | You can do some things to increase the chance that your pull request is accepted the first time: 16 | 17 | * Submit one pull request per fix or feature. 18 | * If your changes are not up to date - rebase your branch on master. 19 | * Follow the conventions used in the project. 20 | * Remember about tests and documentation. 21 | * Don't bump version. 22 | 23 | ## Project's standards: 24 | 25 | * [PSR-0: Autoloading Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) 26 | * [PSR-1: Basic Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) 27 | * [PSR-2: Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) 28 | * [Symfony Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html) 29 | * Keep the order of class elements: static properties, instance properties, constructor (or setUp for PHPUnit), destructor (or tearDown for PHPUnit), static methods, instance methods, magic static methods, magic instance methods. 30 | 31 | ## Scrutinizer settings 32 | 33 | ```yml 34 | filter: 35 | excluded_paths: 36 | - tests/* 37 | - vendor/* 38 | 39 | tools: 40 | php_analyzer: true 41 | php_changetracking: true 42 | php_code_sniffer: 43 | config: 44 | standard: "PSR2" 45 | php_cs_fixer: 46 | config: { level: psr2 } 47 | php_loc: 48 | enabled: true 49 | excluded_dirs: [vendor, tests] 50 | php_mess_detector: true 51 | php_pdepend: true 52 | php_sim: true 53 | sensiolabs_security_checker: true 54 | 55 | checks: 56 | php: 57 | align_assignments: true 58 | argument_type_checks: true 59 | assignment_of_null_return: true 60 | avoid_aliased_php_functions: true 61 | avoid_conflicting_incrementers: true 62 | avoid_corrupting_byteorder_marks: true 63 | avoid_duplicate_types: true 64 | avoid_entity_manager_injection: true 65 | avoid_fixme_comments: true 66 | avoid_length_functions_in_loops: true 67 | avoid_multiple_statements_on_same_line: true 68 | avoid_perl_style_comments: true 69 | avoid_tab_indentation: true 70 | avoid_todo_comments: true 71 | avoid_unnecessary_concatenation: true 72 | avoid_usage_of_logical_operators: true 73 | avoid_useless_overridden_methods: true 74 | blank_line_after_namespace_declaration: true 75 | catch_class_exists: true 76 | classes_in_camel_caps: true 77 | closure_use_modifiable: true 78 | closure_use_not_conflicting: true 79 | code_rating: true 80 | deprecated_code_usage: true 81 | duplication: true 82 | encourage_postdec_operator: true 83 | encourage_shallow_comparison: true 84 | encourage_single_quotes: true 85 | ensure_lower_case_builtin_functions: true 86 | fix_doc_comments: true 87 | fix_identation_4spaces: true 88 | fix_line_ending: true 89 | fix_linefeed: true 90 | fix_php_opening_tag: true 91 | fix_use_statements: 92 | remove_unused: true 93 | preserve_multiple: false 94 | preserve_blanklines: false 95 | order_alphabetically: true 96 | foreach_traversable: true 97 | foreach_usable_as_reference: true 98 | function_body_start_on_new_line: true 99 | function_in_camel_caps: true 100 | instanceof_class_exists: true 101 | lowercase_basic_constants: true 102 | lowercase_php_keywords: true 103 | method_calls_on_non_object: true 104 | missing_arguments: true 105 | more_specific_types_in_doc_comments: true 106 | naming_conventions: 107 | local_variable: '^[a-z][a-zA-Z0-9]*$' 108 | abstract_class_name: ^Abstract|Factory$ 109 | utility_class_name: 'Utils?$' 110 | constant_name: '^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$' 111 | property_name: '^[a-z][a-zA-Z0-9]*$' 112 | method_name: '^(?:[a-z]|__)[a-zA-Z0-9]*$' 113 | parameter_name: '^[a-z][a-zA-Z0-9]*$' 114 | interface_name: '^[A-Z][a-zA-Z0-9]*Interface$' 115 | type_name: '^[A-Z][a-zA-Z0-9]*$' 116 | exception_name: '^[A-Z][a-zA-Z0-9]*Exception$' 117 | isser_method_name: '^(?:is|has|should|may|supports)' 118 | newline_at_end_of_file: true 119 | no_commented_out_code: true 120 | no_debug_code: true 121 | no_duplicate_arguments: true 122 | no_else_if_statements: true 123 | no_empty_statements: true 124 | no_eval: true 125 | no_exit: true 126 | no_global_keyword: true 127 | no_goto: true 128 | no_non_implemented_abstract_methods: true 129 | no_property_on_interface: true 130 | no_short_method_names: 131 | minimum: '3' 132 | no_short_variable_names: 133 | minimum: '3' 134 | no_short_open_tag: true 135 | no_space_around_object_operator: true 136 | no_space_before_semicolon: true 137 | no_space_inside_cast_operator: true 138 | no_trailing_whitespace: true 139 | no_underscore_prefix_in_methods: true 140 | no_underscore_prefix_in_properties: true 141 | no_unnecessary_final_modifier: true 142 | no_unnecessary_function_call_in_for_loop: true 143 | no_unnecessary_if: true 144 | non_commented_empty_catch_block: true 145 | one_class_per_file: true 146 | optional_parameters_at_the_end: true 147 | overriding_private_members: true 148 | param_doc_comment_if_not_inferrable: true 149 | parameter_doc_comments: true 150 | parameter_non_unique: true 151 | parameters_in_camelcaps: true 152 | php5_style_constructor: true 153 | precedence_in_conditions: true 154 | precedence_mistakes: true 155 | prefer_sapi_constant: true 156 | prefer_unix_line_ending: true 157 | prefer_while_loop_over_for_loop: true 158 | properties_in_camelcaps: true 159 | psr2_class_declaration: true 160 | psr2_control_structure_declaration: true 161 | psr2_switch_declaration: true 162 | remove_extra_empty_lines: true 163 | remove_php_closing_tag: true 164 | remove_trailing_whitespace: true 165 | require_braces_around_control_structures: true 166 | require_php_tag_first: true 167 | require_scope_for_methods: true 168 | require_scope_for_properties: true 169 | return_doc_comment_if_not_inferrable: true 170 | return_doc_comments: true 171 | scope_indentation: 172 | spaces_per_level: '4' 173 | security_vulnerabilities: true 174 | simplify_boolean_return: true 175 | single_namespace_per_use: true 176 | space_after_cast: true 177 | spacing_around_conditional_operators: true 178 | spacing_around_non_conditional_operators: true 179 | spacing_of_function_arguments: true 180 | sql_injection_vulnerabilities: true 181 | switch_fallthrough_commented: true 182 | symfony_request_injection: true 183 | too_many_arguments: true 184 | unreachable_code: true 185 | unused_methods: true 186 | unused_parameters: true 187 | unused_properties: true 188 | unused_variables: true 189 | uppercase_constants: true 190 | use_self_instead_of_fqcn: true 191 | use_statement_alias_conflict: true 192 | useless_calls: true 193 | variable_existence: true 194 | verify_access_scope_valid: true 195 | verify_argument_usable_as_reference: true 196 | verify_property_names: true 197 | 198 | coding_style: 199 | php: 200 | spaces: 201 | before_left_brace: 202 | class: false 203 | function: false 204 | braces: 205 | classes_functions: 206 | class: new-line 207 | function: new-line 208 | closure: end-of-line 209 | if: 210 | opening: end-of-line 211 | for: 212 | opening: end-of-line 213 | while: 214 | opening: end-of-line 215 | do_while: 216 | opening: end-of-line 217 | switch: 218 | opening: end-of-line 219 | try: 220 | opening: end-of-line 221 | upper_lower_casing: 222 | keywords: 223 | general: lower 224 | constants: 225 | true_false_null: lower 226 | ``` 227 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Julien Dufresne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Domain Driven Design 2 | 3 | Tools/framework to ease the implementation of Eric Evans Domain Driven Design in PHP. 4 | 5 | To ease its development, this framework requires PHP5.5+ 6 | 7 | ## What's inside 8 | 9 | - Command System - [doc](doc/Command.md) 10 | - Basic domain tools - [doc](doc/Domain.md) 11 | - Event system (EventDispatcher, Listener) - [doc](doc/Event.md) 12 | - Event Log - [doc](doc/EventLog.md) 13 | 14 | ## Project Status 15 | 16 | | Project Version | Build | Code coverage | Code Quality | 17 | |:-------------------:|:---------------------------------------:|:----------------------------------------------:|:---------------------------------------------:| 18 | | [Master][100] | [![Travis][110]][111] | [![coveralls][120]][121] | [![scrutinizer][130]][131] [![Insight][1]][2] | 19 | | [v0.6][v0.6-github] | [![Travis][v0.6-travis-i]][v0.6-travis] | [![coveralls][v0.6-coverall-i]][v0.6-coverall] | | 20 | | [v0.5][v0.5-github] | [![Travis][v0.5-travis-i]][v0.5-travis] | [![coveralls][v0.5-coverall-i]][v0.5-coverall] | | 21 | 22 | [1]: https://insight.sensiolabs.com/projects/c16e3843-2ea5-491c-8410-e7467ced168b/mini.png 23 | [2]: https://insight.sensiolabs.com/projects/c16e3843-2ea5-491c-8410-e7467ced168b 24 | 25 | [100]: https://github.com/php-ddd/domain-driven-design 26 | [110]: https://travis-ci.org/php-ddd/domain-driven-design.svg?branch=master 27 | [111]: https://travis-ci.org/php-ddd/domain-driven-design 28 | [120]: https://coveralls.io/repos/php-ddd/domain-driven-design/badge.svg?service=github&branch=master 29 | [121]: https://coveralls.io/github/php-ddd/domain-driven-design?branch=master 30 | [130]: https://scrutinizer-ci.com/g/php-ddd/domain-driven-design/badges/quality-score.png?b=master 31 | [131]: https://scrutinizer-ci.com/g/php-ddd/domain-driven-design/?branch=master 32 | 33 | [v0.5-github]: https://github.com/php-ddd/domain-driven-design/tree/v0.5.0 34 | [v0.5-travis-i]: https://travis-ci.org/php-ddd/domain-driven-design.svg?branch=v0.5.0 35 | [v0.5-travis]: https://travis-ci.org/php-ddd/domain-driven-design/builds/75346518 36 | [v0.5-coverall-i]: https://coveralls.io/repos/php-ddd/domain-driven-design/badge.svg?service=github&branch=v0.5.0 37 | [v0.5-coverall]: https://coveralls.io/github/php-ddd/domain-driven-design?branch=v0.5.0 38 | 39 | [v0.6-github]: https://github.com/php-ddd/domain-driven-design/tree/v0.6.0 40 | [v0.6-travis-i]: https://travis-ci.org/php-ddd/domain-driven-design.svg?branch=v0.6.0 41 | [v0.6-travis]: https://travis-ci.org/php-ddd/domain-driven-design/builds/75761186 42 | [v0.6-coverall-i]: https://coveralls.io/repos/php-ddd/domain-driven-design/badge.svg?service=github&branch=v0.6.0 43 | [v0.6-coverall]: https://coveralls.io/github/php-ddd/domain-driven-design?branch=v0.6.0 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-ddd/domain-driven-design", 3 | "description": "Tools/framework to ease the implementation of Eric Evans Domain Driven Design in PHP.", 4 | "minimum-stability": "stable", 5 | "license": "MIT", 6 | "autoload": { 7 | "psr-4": {"PhpDDD\\DomainDrivenDesign\\": "src"} 8 | }, 9 | "autoload-dev": { 10 | "psr-4": {"PhpDDD\\DomainDrivenDesign\\Tests\\": "tests"} 11 | }, 12 | "authors": [ 13 | { 14 | "name": "Julien Dufresne" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.5", 19 | "php-ddd/utils": "^1.2.0" 20 | }, 21 | "require-dev": { 22 | "fabpot/php-cs-fixer": "^1.8.1", 23 | "phpmd/phpmd": "^2.2.3", 24 | "phpunit/phpunit": "^4.7.0", 25 | "phploc/phploc": "^2.1.3", 26 | "satooshi/php-coveralls": "dev-master" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /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": "db944b31bfc6a9be134c4b6148d6b05a", 8 | "packages": [ 9 | { 10 | "name": "php-ddd/utils", 11 | "version": "v1.2.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/php-ddd/utils.git", 15 | "reference": "85f1f5ac4dda32a0a2550d4f5c59af2ef00ee997" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/php-ddd/utils/zipball/85f1f5ac4dda32a0a2550d4f5c59af2ef00ee997", 20 | "reference": "85f1f5ac4dda32a0a2550d4f5c59af2ef00ee997", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.5" 25 | }, 26 | "require-dev": { 27 | "fabpot/php-cs-fixer": "^1.8.1", 28 | "phploc/phploc": "^2.1.3", 29 | "phpmd/phpmd": "^2.2.3", 30 | "phpunit/phpunit": "^4.7.0", 31 | "satooshi/php-coveralls": "dev-master" 32 | }, 33 | "type": "library", 34 | "autoload": { 35 | "psr-4": { 36 | "PhpDDD\\Utils\\": "src/" 37 | } 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Julien Dufresne" 46 | } 47 | ], 48 | "description": "Provide some utility classes that could not belong to a specific repository.", 49 | "time": "2015-08-15 20:01:27" 50 | } 51 | ], 52 | "packages-dev": [ 53 | { 54 | "name": "doctrine/instantiator", 55 | "version": "1.0.5", 56 | "source": { 57 | "type": "git", 58 | "url": "https://github.com/doctrine/instantiator.git", 59 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 60 | }, 61 | "dist": { 62 | "type": "zip", 63 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 64 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 65 | "shasum": "" 66 | }, 67 | "require": { 68 | "php": ">=5.3,<8.0-DEV" 69 | }, 70 | "require-dev": { 71 | "athletic/athletic": "~0.1.8", 72 | "ext-pdo": "*", 73 | "ext-phar": "*", 74 | "phpunit/phpunit": "~4.0", 75 | "squizlabs/php_codesniffer": "~2.0" 76 | }, 77 | "type": "library", 78 | "extra": { 79 | "branch-alias": { 80 | "dev-master": "1.0.x-dev" 81 | } 82 | }, 83 | "autoload": { 84 | "psr-4": { 85 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 86 | } 87 | }, 88 | "notification-url": "https://packagist.org/downloads/", 89 | "license": [ 90 | "MIT" 91 | ], 92 | "authors": [ 93 | { 94 | "name": "Marco Pivetta", 95 | "email": "ocramius@gmail.com", 96 | "homepage": "http://ocramius.github.com/" 97 | } 98 | ], 99 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 100 | "homepage": "https://github.com/doctrine/instantiator", 101 | "keywords": [ 102 | "constructor", 103 | "instantiate" 104 | ], 105 | "time": "2015-06-14 21:17:01" 106 | }, 107 | { 108 | "name": "fabpot/php-cs-fixer", 109 | "version": "v1.10", 110 | "source": { 111 | "type": "git", 112 | "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", 113 | "reference": "8e21b4fb32c4618a425817d9f0daf3d57a9808d1" 114 | }, 115 | "dist": { 116 | "type": "zip", 117 | "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/8e21b4fb32c4618a425817d9f0daf3d57a9808d1", 118 | "reference": "8e21b4fb32c4618a425817d9f0daf3d57a9808d1", 119 | "shasum": "" 120 | }, 121 | "require": { 122 | "ext-tokenizer": "*", 123 | "php": ">=5.3.6", 124 | "sebastian/diff": "~1.1", 125 | "symfony/console": "~2.3", 126 | "symfony/event-dispatcher": "~2.1", 127 | "symfony/filesystem": "~2.1", 128 | "symfony/finder": "~2.1", 129 | "symfony/process": "~2.3", 130 | "symfony/stopwatch": "~2.5" 131 | }, 132 | "require-dev": { 133 | "satooshi/php-coveralls": "0.7.*@dev" 134 | }, 135 | "bin": [ 136 | "php-cs-fixer" 137 | ], 138 | "type": "application", 139 | "autoload": { 140 | "psr-4": { 141 | "Symfony\\CS\\": "Symfony/CS/" 142 | } 143 | }, 144 | "notification-url": "https://packagist.org/downloads/", 145 | "license": [ 146 | "MIT" 147 | ], 148 | "authors": [ 149 | { 150 | "name": "Dariusz Rumiński", 151 | "email": "dariusz.ruminski@gmail.com" 152 | }, 153 | { 154 | "name": "Fabien Potencier", 155 | "email": "fabien@symfony.com" 156 | } 157 | ], 158 | "description": "A tool to automatically fix PHP code style", 159 | "time": "2015-07-27 20:56:10" 160 | }, 161 | { 162 | "name": "guzzle/guzzle", 163 | "version": "v3.9.3", 164 | "source": { 165 | "type": "git", 166 | "url": "https://github.com/guzzle/guzzle3.git", 167 | "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" 168 | }, 169 | "dist": { 170 | "type": "zip", 171 | "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", 172 | "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", 173 | "shasum": "" 174 | }, 175 | "require": { 176 | "ext-curl": "*", 177 | "php": ">=5.3.3", 178 | "symfony/event-dispatcher": "~2.1" 179 | }, 180 | "replace": { 181 | "guzzle/batch": "self.version", 182 | "guzzle/cache": "self.version", 183 | "guzzle/common": "self.version", 184 | "guzzle/http": "self.version", 185 | "guzzle/inflection": "self.version", 186 | "guzzle/iterator": "self.version", 187 | "guzzle/log": "self.version", 188 | "guzzle/parser": "self.version", 189 | "guzzle/plugin": "self.version", 190 | "guzzle/plugin-async": "self.version", 191 | "guzzle/plugin-backoff": "self.version", 192 | "guzzle/plugin-cache": "self.version", 193 | "guzzle/plugin-cookie": "self.version", 194 | "guzzle/plugin-curlauth": "self.version", 195 | "guzzle/plugin-error-response": "self.version", 196 | "guzzle/plugin-history": "self.version", 197 | "guzzle/plugin-log": "self.version", 198 | "guzzle/plugin-md5": "self.version", 199 | "guzzle/plugin-mock": "self.version", 200 | "guzzle/plugin-oauth": "self.version", 201 | "guzzle/service": "self.version", 202 | "guzzle/stream": "self.version" 203 | }, 204 | "require-dev": { 205 | "doctrine/cache": "~1.3", 206 | "monolog/monolog": "~1.0", 207 | "phpunit/phpunit": "3.7.*", 208 | "psr/log": "~1.0", 209 | "symfony/class-loader": "~2.1", 210 | "zendframework/zend-cache": "2.*,<2.3", 211 | "zendframework/zend-log": "2.*,<2.3" 212 | }, 213 | "suggest": { 214 | "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." 215 | }, 216 | "type": "library", 217 | "extra": { 218 | "branch-alias": { 219 | "dev-master": "3.9-dev" 220 | } 221 | }, 222 | "autoload": { 223 | "psr-0": { 224 | "Guzzle": "src/", 225 | "Guzzle\\Tests": "tests/" 226 | } 227 | }, 228 | "notification-url": "https://packagist.org/downloads/", 229 | "license": [ 230 | "MIT" 231 | ], 232 | "authors": [ 233 | { 234 | "name": "Michael Dowling", 235 | "email": "mtdowling@gmail.com", 236 | "homepage": "https://github.com/mtdowling" 237 | }, 238 | { 239 | "name": "Guzzle Community", 240 | "homepage": "https://github.com/guzzle/guzzle/contributors" 241 | } 242 | ], 243 | "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", 244 | "homepage": "http://guzzlephp.org/", 245 | "keywords": [ 246 | "client", 247 | "curl", 248 | "framework", 249 | "http", 250 | "http client", 251 | "rest", 252 | "web service" 253 | ], 254 | "time": "2015-03-18 18:23:50" 255 | }, 256 | { 257 | "name": "pdepend/pdepend", 258 | "version": "2.1.0", 259 | "source": { 260 | "type": "git", 261 | "url": "https://github.com/pdepend/pdepend.git", 262 | "reference": "f58902a774449f73f1a1d9cd1a07aeac8fbee367" 263 | }, 264 | "dist": { 265 | "type": "zip", 266 | "url": "https://api.github.com/repos/pdepend/pdepend/zipball/f58902a774449f73f1a1d9cd1a07aeac8fbee367", 267 | "reference": "f58902a774449f73f1a1d9cd1a07aeac8fbee367", 268 | "shasum": "" 269 | }, 270 | "require": { 271 | "symfony/config": ">=2.4", 272 | "symfony/dependency-injection": ">=2.4", 273 | "symfony/filesystem": ">=2.4" 274 | }, 275 | "require-dev": { 276 | "phpunit/phpunit": "4.*@stable", 277 | "squizlabs/php_codesniffer": "@stable" 278 | }, 279 | "bin": [ 280 | "src/bin/pdepend" 281 | ], 282 | "type": "library", 283 | "autoload": { 284 | "psr-0": { 285 | "PDepend\\": "src/main/php/" 286 | } 287 | }, 288 | "notification-url": "https://packagist.org/downloads/", 289 | "license": [ 290 | "BSD-3-Clause" 291 | ], 292 | "description": "Official version of pdepend to be handled with Composer", 293 | "time": "2015-05-21 18:09:06" 294 | }, 295 | { 296 | "name": "phpdocumentor/reflection-docblock", 297 | "version": "2.0.4", 298 | "source": { 299 | "type": "git", 300 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 301 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 302 | }, 303 | "dist": { 304 | "type": "zip", 305 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 306 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 307 | "shasum": "" 308 | }, 309 | "require": { 310 | "php": ">=5.3.3" 311 | }, 312 | "require-dev": { 313 | "phpunit/phpunit": "~4.0" 314 | }, 315 | "suggest": { 316 | "dflydev/markdown": "~1.0", 317 | "erusev/parsedown": "~1.0" 318 | }, 319 | "type": "library", 320 | "extra": { 321 | "branch-alias": { 322 | "dev-master": "2.0.x-dev" 323 | } 324 | }, 325 | "autoload": { 326 | "psr-0": { 327 | "phpDocumentor": [ 328 | "src/" 329 | ] 330 | } 331 | }, 332 | "notification-url": "https://packagist.org/downloads/", 333 | "license": [ 334 | "MIT" 335 | ], 336 | "authors": [ 337 | { 338 | "name": "Mike van Riel", 339 | "email": "mike.vanriel@naenius.com" 340 | } 341 | ], 342 | "time": "2015-02-03 12:10:50" 343 | }, 344 | { 345 | "name": "phploc/phploc", 346 | "version": "2.1.4", 347 | "source": { 348 | "type": "git", 349 | "url": "https://github.com/sebastianbergmann/phploc.git", 350 | "reference": "6cdf01336c06d20825831fe8cee70764fe373585" 351 | }, 352 | "dist": { 353 | "type": "zip", 354 | "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/6cdf01336c06d20825831fe8cee70764fe373585", 355 | "reference": "6cdf01336c06d20825831fe8cee70764fe373585", 356 | "shasum": "" 357 | }, 358 | "require": { 359 | "php": ">=5.3.3", 360 | "sebastian/finder-facade": "~1.1", 361 | "sebastian/git": "~2.0", 362 | "sebastian/version": "~1.0.3", 363 | "symfony/console": "~2.5" 364 | }, 365 | "require-dev": { 366 | "phpunit/phpunit": "~4" 367 | }, 368 | "bin": [ 369 | "phploc" 370 | ], 371 | "type": "library", 372 | "extra": { 373 | "branch-alias": { 374 | "dev-master": "2.1-dev" 375 | } 376 | }, 377 | "autoload": { 378 | "classmap": [ 379 | "src/" 380 | ] 381 | }, 382 | "notification-url": "https://packagist.org/downloads/", 383 | "license": [ 384 | "BSD-3-Clause" 385 | ], 386 | "authors": [ 387 | { 388 | "name": "Sebastian Bergmann", 389 | "email": "sebastian@phpunit.de", 390 | "role": "lead" 391 | } 392 | ], 393 | "description": "A tool for quickly measuring the size of a PHP project.", 394 | "homepage": "https://github.com/sebastianbergmann/phploc", 395 | "time": "2015-08-04 07:41:00" 396 | }, 397 | { 398 | "name": "phpmd/phpmd", 399 | "version": "2.2.3", 400 | "source": { 401 | "type": "git", 402 | "url": "https://github.com/phpmd/phpmd.git", 403 | "reference": "5eeb5a4d39c8304910b33ae49f8813905346cc35" 404 | }, 405 | "dist": { 406 | "type": "zip", 407 | "url": "https://api.github.com/repos/phpmd/phpmd/zipball/5eeb5a4d39c8304910b33ae49f8813905346cc35", 408 | "reference": "5eeb5a4d39c8304910b33ae49f8813905346cc35", 409 | "shasum": "" 410 | }, 411 | "require": { 412 | "pdepend/pdepend": "~2.0", 413 | "php": ">=5.3.0", 414 | "symfony/config": ">=2.4", 415 | "symfony/dependency-injection": ">=2.4", 416 | "symfony/filesystem": ">=2.4" 417 | }, 418 | "require-dev": { 419 | "phpunit/phpunit": "*", 420 | "squizlabs/php_codesniffer": "*" 421 | }, 422 | "bin": [ 423 | "src/bin/phpmd" 424 | ], 425 | "type": "project", 426 | "autoload": { 427 | "psr-0": { 428 | "PHPMD\\": "src/main/php" 429 | } 430 | }, 431 | "notification-url": "https://packagist.org/downloads/", 432 | "license": [ 433 | "BSD-3-Clause" 434 | ], 435 | "authors": [ 436 | { 437 | "name": "Manuel Pichler", 438 | "email": "github@manuel-pichler.de", 439 | "homepage": "https://github.com/manuelpichler", 440 | "role": "Project founder" 441 | }, 442 | { 443 | "name": "Other contributors", 444 | "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", 445 | "role": "Contributors" 446 | } 447 | ], 448 | "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", 449 | "homepage": "http://phpmd.org/", 450 | "keywords": [ 451 | "mess detection", 452 | "mess detector", 453 | "pdepend", 454 | "phpmd", 455 | "pmd" 456 | ], 457 | "time": "2015-05-27 18:16:57" 458 | }, 459 | { 460 | "name": "phpspec/prophecy", 461 | "version": "v1.5.0", 462 | "source": { 463 | "type": "git", 464 | "url": "https://github.com/phpspec/prophecy.git", 465 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" 466 | }, 467 | "dist": { 468 | "type": "zip", 469 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", 470 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", 471 | "shasum": "" 472 | }, 473 | "require": { 474 | "doctrine/instantiator": "^1.0.2", 475 | "phpdocumentor/reflection-docblock": "~2.0", 476 | "sebastian/comparator": "~1.1" 477 | }, 478 | "require-dev": { 479 | "phpspec/phpspec": "~2.0" 480 | }, 481 | "type": "library", 482 | "extra": { 483 | "branch-alias": { 484 | "dev-master": "1.4.x-dev" 485 | } 486 | }, 487 | "autoload": { 488 | "psr-0": { 489 | "Prophecy\\": "src/" 490 | } 491 | }, 492 | "notification-url": "https://packagist.org/downloads/", 493 | "license": [ 494 | "MIT" 495 | ], 496 | "authors": [ 497 | { 498 | "name": "Konstantin Kudryashov", 499 | "email": "ever.zet@gmail.com", 500 | "homepage": "http://everzet.com" 501 | }, 502 | { 503 | "name": "Marcello Duarte", 504 | "email": "marcello.duarte@gmail.com" 505 | } 506 | ], 507 | "description": "Highly opinionated mocking framework for PHP 5.3+", 508 | "homepage": "https://github.com/phpspec/prophecy", 509 | "keywords": [ 510 | "Double", 511 | "Dummy", 512 | "fake", 513 | "mock", 514 | "spy", 515 | "stub" 516 | ], 517 | "time": "2015-08-13 10:07:40" 518 | }, 519 | { 520 | "name": "phpunit/php-code-coverage", 521 | "version": "2.2.2", 522 | "source": { 523 | "type": "git", 524 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 525 | "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c" 526 | }, 527 | "dist": { 528 | "type": "zip", 529 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c", 530 | "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c", 531 | "shasum": "" 532 | }, 533 | "require": { 534 | "php": ">=5.3.3", 535 | "phpunit/php-file-iterator": "~1.3", 536 | "phpunit/php-text-template": "~1.2", 537 | "phpunit/php-token-stream": "~1.3", 538 | "sebastian/environment": "^1.3.2", 539 | "sebastian/version": "~1.0" 540 | }, 541 | "require-dev": { 542 | "ext-xdebug": ">=2.1.4", 543 | "phpunit/phpunit": "~4" 544 | }, 545 | "suggest": { 546 | "ext-dom": "*", 547 | "ext-xdebug": ">=2.2.1", 548 | "ext-xmlwriter": "*" 549 | }, 550 | "type": "library", 551 | "extra": { 552 | "branch-alias": { 553 | "dev-master": "2.2.x-dev" 554 | } 555 | }, 556 | "autoload": { 557 | "classmap": [ 558 | "src/" 559 | ] 560 | }, 561 | "notification-url": "https://packagist.org/downloads/", 562 | "license": [ 563 | "BSD-3-Clause" 564 | ], 565 | "authors": [ 566 | { 567 | "name": "Sebastian Bergmann", 568 | "email": "sb@sebastian-bergmann.de", 569 | "role": "lead" 570 | } 571 | ], 572 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 573 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 574 | "keywords": [ 575 | "coverage", 576 | "testing", 577 | "xunit" 578 | ], 579 | "time": "2015-08-04 03:42:39" 580 | }, 581 | { 582 | "name": "phpunit/php-file-iterator", 583 | "version": "1.4.1", 584 | "source": { 585 | "type": "git", 586 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 587 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 588 | }, 589 | "dist": { 590 | "type": "zip", 591 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 592 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 593 | "shasum": "" 594 | }, 595 | "require": { 596 | "php": ">=5.3.3" 597 | }, 598 | "type": "library", 599 | "extra": { 600 | "branch-alias": { 601 | "dev-master": "1.4.x-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": "sb@sebastian-bergmann.de", 617 | "role": "lead" 618 | } 619 | ], 620 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 621 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 622 | "keywords": [ 623 | "filesystem", 624 | "iterator" 625 | ], 626 | "time": "2015-06-21 13:08:43" 627 | }, 628 | { 629 | "name": "phpunit/php-text-template", 630 | "version": "1.2.1", 631 | "source": { 632 | "type": "git", 633 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 634 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 635 | }, 636 | "dist": { 637 | "type": "zip", 638 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 639 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 640 | "shasum": "" 641 | }, 642 | "require": { 643 | "php": ">=5.3.3" 644 | }, 645 | "type": "library", 646 | "autoload": { 647 | "classmap": [ 648 | "src/" 649 | ] 650 | }, 651 | "notification-url": "https://packagist.org/downloads/", 652 | "license": [ 653 | "BSD-3-Clause" 654 | ], 655 | "authors": [ 656 | { 657 | "name": "Sebastian Bergmann", 658 | "email": "sebastian@phpunit.de", 659 | "role": "lead" 660 | } 661 | ], 662 | "description": "Simple template engine.", 663 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 664 | "keywords": [ 665 | "template" 666 | ], 667 | "time": "2015-06-21 13:50:34" 668 | }, 669 | { 670 | "name": "phpunit/php-timer", 671 | "version": "1.0.7", 672 | "source": { 673 | "type": "git", 674 | "url": "https://github.com/sebastianbergmann/php-timer.git", 675 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" 676 | }, 677 | "dist": { 678 | "type": "zip", 679 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 680 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 681 | "shasum": "" 682 | }, 683 | "require": { 684 | "php": ">=5.3.3" 685 | }, 686 | "type": "library", 687 | "autoload": { 688 | "classmap": [ 689 | "src/" 690 | ] 691 | }, 692 | "notification-url": "https://packagist.org/downloads/", 693 | "license": [ 694 | "BSD-3-Clause" 695 | ], 696 | "authors": [ 697 | { 698 | "name": "Sebastian Bergmann", 699 | "email": "sb@sebastian-bergmann.de", 700 | "role": "lead" 701 | } 702 | ], 703 | "description": "Utility class for timing", 704 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 705 | "keywords": [ 706 | "timer" 707 | ], 708 | "time": "2015-06-21 08:01:12" 709 | }, 710 | { 711 | "name": "phpunit/php-token-stream", 712 | "version": "1.4.5", 713 | "source": { 714 | "type": "git", 715 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 716 | "reference": "09fc125d65c344c53a7c7ad8f261e3f3af9f76c5" 717 | }, 718 | "dist": { 719 | "type": "zip", 720 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/09fc125d65c344c53a7c7ad8f261e3f3af9f76c5", 721 | "reference": "09fc125d65c344c53a7c7ad8f261e3f3af9f76c5", 722 | "shasum": "" 723 | }, 724 | "require": { 725 | "ext-tokenizer": "*", 726 | "php": ">=5.3.3" 727 | }, 728 | "require-dev": { 729 | "phpunit/phpunit": "~4.2" 730 | }, 731 | "type": "library", 732 | "extra": { 733 | "branch-alias": { 734 | "dev-master": "1.4-dev" 735 | } 736 | }, 737 | "autoload": { 738 | "classmap": [ 739 | "src/" 740 | ] 741 | }, 742 | "notification-url": "https://packagist.org/downloads/", 743 | "license": [ 744 | "BSD-3-Clause" 745 | ], 746 | "authors": [ 747 | { 748 | "name": "Sebastian Bergmann", 749 | "email": "sebastian@phpunit.de" 750 | } 751 | ], 752 | "description": "Wrapper around PHP's tokenizer extension.", 753 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 754 | "keywords": [ 755 | "tokenizer" 756 | ], 757 | "time": "2015-08-13 14:23:08" 758 | }, 759 | { 760 | "name": "phpunit/phpunit", 761 | "version": "4.8.4", 762 | "source": { 763 | "type": "git", 764 | "url": "https://github.com/sebastianbergmann/phpunit.git", 765 | "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7" 766 | }, 767 | "dist": { 768 | "type": "zip", 769 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", 770 | "reference": "55bf1d6092b0e13a1f26bd5eaffeef3d8ad85ea7", 771 | "shasum": "" 772 | }, 773 | "require": { 774 | "ext-dom": "*", 775 | "ext-json": "*", 776 | "ext-pcre": "*", 777 | "ext-reflection": "*", 778 | "ext-spl": "*", 779 | "php": ">=5.3.3", 780 | "phpspec/prophecy": "^1.3.1", 781 | "phpunit/php-code-coverage": "~2.1", 782 | "phpunit/php-file-iterator": "~1.4", 783 | "phpunit/php-text-template": "~1.2", 784 | "phpunit/php-timer": ">=1.0.6", 785 | "phpunit/phpunit-mock-objects": "~2.3", 786 | "sebastian/comparator": "~1.1", 787 | "sebastian/diff": "~1.2", 788 | "sebastian/environment": "~1.3", 789 | "sebastian/exporter": "~1.2", 790 | "sebastian/global-state": "~1.0", 791 | "sebastian/version": "~1.0", 792 | "symfony/yaml": "~2.1|~3.0" 793 | }, 794 | "suggest": { 795 | "phpunit/php-invoker": "~1.1" 796 | }, 797 | "bin": [ 798 | "phpunit" 799 | ], 800 | "type": "library", 801 | "extra": { 802 | "branch-alias": { 803 | "dev-master": "4.8.x-dev" 804 | } 805 | }, 806 | "autoload": { 807 | "classmap": [ 808 | "src/" 809 | ] 810 | }, 811 | "notification-url": "https://packagist.org/downloads/", 812 | "license": [ 813 | "BSD-3-Clause" 814 | ], 815 | "authors": [ 816 | { 817 | "name": "Sebastian Bergmann", 818 | "email": "sebastian@phpunit.de", 819 | "role": "lead" 820 | } 821 | ], 822 | "description": "The PHP Unit Testing framework.", 823 | "homepage": "https://phpunit.de/", 824 | "keywords": [ 825 | "phpunit", 826 | "testing", 827 | "xunit" 828 | ], 829 | "time": "2015-08-15 04:21:23" 830 | }, 831 | { 832 | "name": "phpunit/phpunit-mock-objects", 833 | "version": "2.3.6", 834 | "source": { 835 | "type": "git", 836 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 837 | "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42" 838 | }, 839 | "dist": { 840 | "type": "zip", 841 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/18dfbcb81d05e2296c0bcddd4db96cade75e6f42", 842 | "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42", 843 | "shasum": "" 844 | }, 845 | "require": { 846 | "doctrine/instantiator": "~1.0,>=1.0.2", 847 | "php": ">=5.3.3", 848 | "phpunit/php-text-template": "~1.2", 849 | "sebastian/exporter": "~1.2" 850 | }, 851 | "require-dev": { 852 | "phpunit/phpunit": "~4.4" 853 | }, 854 | "suggest": { 855 | "ext-soap": "*" 856 | }, 857 | "type": "library", 858 | "extra": { 859 | "branch-alias": { 860 | "dev-master": "2.3.x-dev" 861 | } 862 | }, 863 | "autoload": { 864 | "classmap": [ 865 | "src/" 866 | ] 867 | }, 868 | "notification-url": "https://packagist.org/downloads/", 869 | "license": [ 870 | "BSD-3-Clause" 871 | ], 872 | "authors": [ 873 | { 874 | "name": "Sebastian Bergmann", 875 | "email": "sb@sebastian-bergmann.de", 876 | "role": "lead" 877 | } 878 | ], 879 | "description": "Mock Object library for PHPUnit", 880 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 881 | "keywords": [ 882 | "mock", 883 | "xunit" 884 | ], 885 | "time": "2015-07-10 06:54:24" 886 | }, 887 | { 888 | "name": "psr/log", 889 | "version": "1.0.0", 890 | "source": { 891 | "type": "git", 892 | "url": "https://github.com/php-fig/log.git", 893 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 894 | }, 895 | "dist": { 896 | "type": "zip", 897 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 898 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 899 | "shasum": "" 900 | }, 901 | "type": "library", 902 | "autoload": { 903 | "psr-0": { 904 | "Psr\\Log\\": "" 905 | } 906 | }, 907 | "notification-url": "https://packagist.org/downloads/", 908 | "license": [ 909 | "MIT" 910 | ], 911 | "authors": [ 912 | { 913 | "name": "PHP-FIG", 914 | "homepage": "http://www.php-fig.org/" 915 | } 916 | ], 917 | "description": "Common interface for logging libraries", 918 | "keywords": [ 919 | "log", 920 | "psr", 921 | "psr-3" 922 | ], 923 | "time": "2012-12-21 11:40:51" 924 | }, 925 | { 926 | "name": "satooshi/php-coveralls", 927 | "version": "dev-master", 928 | "source": { 929 | "type": "git", 930 | "url": "https://github.com/satooshi/php-coveralls.git", 931 | "reference": "2fbf803803d179ab1082807308a67bbd5a760c70" 932 | }, 933 | "dist": { 934 | "type": "zip", 935 | "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/2fbf803803d179ab1082807308a67bbd5a760c70", 936 | "reference": "2fbf803803d179ab1082807308a67bbd5a760c70", 937 | "shasum": "" 938 | }, 939 | "require": { 940 | "ext-json": "*", 941 | "ext-simplexml": "*", 942 | "guzzle/guzzle": ">=2.7", 943 | "php": ">=5.3", 944 | "psr/log": "1.0.0", 945 | "symfony/config": ">=2.0", 946 | "symfony/console": ">=2.0", 947 | "symfony/stopwatch": ">=2.2", 948 | "symfony/yaml": ">=2.0" 949 | }, 950 | "require-dev": { 951 | "apigen/apigen": "2.8.*@stable", 952 | "pdepend/pdepend": "dev-master as 2.0.0", 953 | "phpmd/phpmd": "dev-master", 954 | "phpunit/php-invoker": ">=1.1.0,<1.2.0", 955 | "phpunit/phpunit": "3.7.*@stable", 956 | "sebastian/finder-facade": "dev-master", 957 | "sebastian/phpcpd": "1.4.*@stable", 958 | "squizlabs/php_codesniffer": "1.4.*@stable", 959 | "theseer/fdomdocument": "dev-master" 960 | }, 961 | "suggest": { 962 | "symfony/http-kernel": "Allows Symfony integration" 963 | }, 964 | "bin": [ 965 | "composer/bin/coveralls" 966 | ], 967 | "type": "library", 968 | "extra": { 969 | "branch-alias": { 970 | "dev-master": "0.7-dev" 971 | } 972 | }, 973 | "autoload": { 974 | "psr-0": { 975 | "Satooshi\\Component": "src/", 976 | "Satooshi\\Bundle": "src/" 977 | } 978 | }, 979 | "notification-url": "https://packagist.org/downloads/", 980 | "license": [ 981 | "MIT" 982 | ], 983 | "authors": [ 984 | { 985 | "name": "Kitamura Satoshi", 986 | "email": "with.no.parachute@gmail.com", 987 | "homepage": "https://www.facebook.com/satooshi.jp" 988 | } 989 | ], 990 | "description": "PHP client library for Coveralls API", 991 | "homepage": "https://github.com/satooshi/php-coveralls", 992 | "keywords": [ 993 | "ci", 994 | "coverage", 995 | "github", 996 | "test" 997 | ], 998 | "time": "2014-11-11 15:35:34" 999 | }, 1000 | { 1001 | "name": "sebastian/comparator", 1002 | "version": "1.2.0", 1003 | "source": { 1004 | "type": "git", 1005 | "url": "https://github.com/sebastianbergmann/comparator.git", 1006 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 1007 | }, 1008 | "dist": { 1009 | "type": "zip", 1010 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 1011 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 1012 | "shasum": "" 1013 | }, 1014 | "require": { 1015 | "php": ">=5.3.3", 1016 | "sebastian/diff": "~1.2", 1017 | "sebastian/exporter": "~1.2" 1018 | }, 1019 | "require-dev": { 1020 | "phpunit/phpunit": "~4.4" 1021 | }, 1022 | "type": "library", 1023 | "extra": { 1024 | "branch-alias": { 1025 | "dev-master": "1.2.x-dev" 1026 | } 1027 | }, 1028 | "autoload": { 1029 | "classmap": [ 1030 | "src/" 1031 | ] 1032 | }, 1033 | "notification-url": "https://packagist.org/downloads/", 1034 | "license": [ 1035 | "BSD-3-Clause" 1036 | ], 1037 | "authors": [ 1038 | { 1039 | "name": "Jeff Welch", 1040 | "email": "whatthejeff@gmail.com" 1041 | }, 1042 | { 1043 | "name": "Volker Dusch", 1044 | "email": "github@wallbash.com" 1045 | }, 1046 | { 1047 | "name": "Bernhard Schussek", 1048 | "email": "bschussek@2bepublished.at" 1049 | }, 1050 | { 1051 | "name": "Sebastian Bergmann", 1052 | "email": "sebastian@phpunit.de" 1053 | } 1054 | ], 1055 | "description": "Provides the functionality to compare PHP values for equality", 1056 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1057 | "keywords": [ 1058 | "comparator", 1059 | "compare", 1060 | "equality" 1061 | ], 1062 | "time": "2015-07-26 15:48:44" 1063 | }, 1064 | { 1065 | "name": "sebastian/diff", 1066 | "version": "1.3.0", 1067 | "source": { 1068 | "type": "git", 1069 | "url": "https://github.com/sebastianbergmann/diff.git", 1070 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" 1071 | }, 1072 | "dist": { 1073 | "type": "zip", 1074 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", 1075 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", 1076 | "shasum": "" 1077 | }, 1078 | "require": { 1079 | "php": ">=5.3.3" 1080 | }, 1081 | "require-dev": { 1082 | "phpunit/phpunit": "~4.2" 1083 | }, 1084 | "type": "library", 1085 | "extra": { 1086 | "branch-alias": { 1087 | "dev-master": "1.3-dev" 1088 | } 1089 | }, 1090 | "autoload": { 1091 | "classmap": [ 1092 | "src/" 1093 | ] 1094 | }, 1095 | "notification-url": "https://packagist.org/downloads/", 1096 | "license": [ 1097 | "BSD-3-Clause" 1098 | ], 1099 | "authors": [ 1100 | { 1101 | "name": "Kore Nordmann", 1102 | "email": "mail@kore-nordmann.de" 1103 | }, 1104 | { 1105 | "name": "Sebastian Bergmann", 1106 | "email": "sebastian@phpunit.de" 1107 | } 1108 | ], 1109 | "description": "Diff implementation", 1110 | "homepage": "http://www.github.com/sebastianbergmann/diff", 1111 | "keywords": [ 1112 | "diff" 1113 | ], 1114 | "time": "2015-02-22 15:13:53" 1115 | }, 1116 | { 1117 | "name": "sebastian/environment", 1118 | "version": "1.3.2", 1119 | "source": { 1120 | "type": "git", 1121 | "url": "https://github.com/sebastianbergmann/environment.git", 1122 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" 1123 | }, 1124 | "dist": { 1125 | "type": "zip", 1126 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", 1127 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", 1128 | "shasum": "" 1129 | }, 1130 | "require": { 1131 | "php": ">=5.3.3" 1132 | }, 1133 | "require-dev": { 1134 | "phpunit/phpunit": "~4.4" 1135 | }, 1136 | "type": "library", 1137 | "extra": { 1138 | "branch-alias": { 1139 | "dev-master": "1.3.x-dev" 1140 | } 1141 | }, 1142 | "autoload": { 1143 | "classmap": [ 1144 | "src/" 1145 | ] 1146 | }, 1147 | "notification-url": "https://packagist.org/downloads/", 1148 | "license": [ 1149 | "BSD-3-Clause" 1150 | ], 1151 | "authors": [ 1152 | { 1153 | "name": "Sebastian Bergmann", 1154 | "email": "sebastian@phpunit.de" 1155 | } 1156 | ], 1157 | "description": "Provides functionality to handle HHVM/PHP environments", 1158 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1159 | "keywords": [ 1160 | "Xdebug", 1161 | "environment", 1162 | "hhvm" 1163 | ], 1164 | "time": "2015-08-03 06:14:51" 1165 | }, 1166 | { 1167 | "name": "sebastian/exporter", 1168 | "version": "1.2.1", 1169 | "source": { 1170 | "type": "git", 1171 | "url": "https://github.com/sebastianbergmann/exporter.git", 1172 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e" 1173 | }, 1174 | "dist": { 1175 | "type": "zip", 1176 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", 1177 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e", 1178 | "shasum": "" 1179 | }, 1180 | "require": { 1181 | "php": ">=5.3.3", 1182 | "sebastian/recursion-context": "~1.0" 1183 | }, 1184 | "require-dev": { 1185 | "phpunit/phpunit": "~4.4" 1186 | }, 1187 | "type": "library", 1188 | "extra": { 1189 | "branch-alias": { 1190 | "dev-master": "1.2.x-dev" 1191 | } 1192 | }, 1193 | "autoload": { 1194 | "classmap": [ 1195 | "src/" 1196 | ] 1197 | }, 1198 | "notification-url": "https://packagist.org/downloads/", 1199 | "license": [ 1200 | "BSD-3-Clause" 1201 | ], 1202 | "authors": [ 1203 | { 1204 | "name": "Jeff Welch", 1205 | "email": "whatthejeff@gmail.com" 1206 | }, 1207 | { 1208 | "name": "Volker Dusch", 1209 | "email": "github@wallbash.com" 1210 | }, 1211 | { 1212 | "name": "Bernhard Schussek", 1213 | "email": "bschussek@2bepublished.at" 1214 | }, 1215 | { 1216 | "name": "Sebastian Bergmann", 1217 | "email": "sebastian@phpunit.de" 1218 | }, 1219 | { 1220 | "name": "Adam Harvey", 1221 | "email": "aharvey@php.net" 1222 | } 1223 | ], 1224 | "description": "Provides the functionality to export PHP variables for visualization", 1225 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1226 | "keywords": [ 1227 | "export", 1228 | "exporter" 1229 | ], 1230 | "time": "2015-06-21 07:55:53" 1231 | }, 1232 | { 1233 | "name": "sebastian/finder-facade", 1234 | "version": "1.2.0", 1235 | "source": { 1236 | "type": "git", 1237 | "url": "https://github.com/sebastianbergmann/finder-facade.git", 1238 | "reference": "a520dcc3dd39160eea480daa3426f4fd419a327b" 1239 | }, 1240 | "dist": { 1241 | "type": "zip", 1242 | "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/a520dcc3dd39160eea480daa3426f4fd419a327b", 1243 | "reference": "a520dcc3dd39160eea480daa3426f4fd419a327b", 1244 | "shasum": "" 1245 | }, 1246 | "require": { 1247 | "symfony/finder": "~2.3", 1248 | "theseer/fdomdocument": "~1.3" 1249 | }, 1250 | "type": "library", 1251 | "autoload": { 1252 | "classmap": [ 1253 | "src/" 1254 | ] 1255 | }, 1256 | "notification-url": "https://packagist.org/downloads/", 1257 | "license": [ 1258 | "BSD-3-Clause" 1259 | ], 1260 | "authors": [ 1261 | { 1262 | "name": "Sebastian Bergmann", 1263 | "email": "sebastian@phpunit.de", 1264 | "role": "lead" 1265 | } 1266 | ], 1267 | "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", 1268 | "homepage": "https://github.com/sebastianbergmann/finder-facade", 1269 | "time": "2015-06-04 08:11:58" 1270 | }, 1271 | { 1272 | "name": "sebastian/git", 1273 | "version": "2.0.1", 1274 | "source": { 1275 | "type": "git", 1276 | "url": "https://github.com/sebastianbergmann/git.git", 1277 | "reference": "2d5c139d0eedcb9e67e0e9ca08023be6e9b7b47b" 1278 | }, 1279 | "dist": { 1280 | "type": "zip", 1281 | "url": "https://api.github.com/repos/sebastianbergmann/git/zipball/2d5c139d0eedcb9e67e0e9ca08023be6e9b7b47b", 1282 | "reference": "2d5c139d0eedcb9e67e0e9ca08023be6e9b7b47b", 1283 | "shasum": "" 1284 | }, 1285 | "require": { 1286 | "php": ">=5.3.3" 1287 | }, 1288 | "type": "library", 1289 | "extra": { 1290 | "branch-alias": { 1291 | "dev-master": "2.0-dev" 1292 | } 1293 | }, 1294 | "autoload": { 1295 | "classmap": [ 1296 | "src/" 1297 | ] 1298 | }, 1299 | "notification-url": "https://packagist.org/downloads/", 1300 | "license": [ 1301 | "BSD-3-Clause" 1302 | ], 1303 | "authors": [ 1304 | { 1305 | "name": "Sebastian Bergmann", 1306 | "email": "sebastian@phpunit.de" 1307 | } 1308 | ], 1309 | "description": "Simple wrapper for Git", 1310 | "homepage": "http://www.github.com/sebastianbergmann/git", 1311 | "keywords": [ 1312 | "git" 1313 | ], 1314 | "time": "2015-04-06 16:23:43" 1315 | }, 1316 | { 1317 | "name": "sebastian/global-state", 1318 | "version": "1.0.0", 1319 | "source": { 1320 | "type": "git", 1321 | "url": "https://github.com/sebastianbergmann/global-state.git", 1322 | "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" 1323 | }, 1324 | "dist": { 1325 | "type": "zip", 1326 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", 1327 | "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", 1328 | "shasum": "" 1329 | }, 1330 | "require": { 1331 | "php": ">=5.3.3" 1332 | }, 1333 | "require-dev": { 1334 | "phpunit/phpunit": "~4.2" 1335 | }, 1336 | "suggest": { 1337 | "ext-uopz": "*" 1338 | }, 1339 | "type": "library", 1340 | "extra": { 1341 | "branch-alias": { 1342 | "dev-master": "1.0-dev" 1343 | } 1344 | }, 1345 | "autoload": { 1346 | "classmap": [ 1347 | "src/" 1348 | ] 1349 | }, 1350 | "notification-url": "https://packagist.org/downloads/", 1351 | "license": [ 1352 | "BSD-3-Clause" 1353 | ], 1354 | "authors": [ 1355 | { 1356 | "name": "Sebastian Bergmann", 1357 | "email": "sebastian@phpunit.de" 1358 | } 1359 | ], 1360 | "description": "Snapshotting of global state", 1361 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1362 | "keywords": [ 1363 | "global state" 1364 | ], 1365 | "time": "2014-10-06 09:23:50" 1366 | }, 1367 | { 1368 | "name": "sebastian/recursion-context", 1369 | "version": "1.0.1", 1370 | "source": { 1371 | "type": "git", 1372 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1373 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba" 1374 | }, 1375 | "dist": { 1376 | "type": "zip", 1377 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba", 1378 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba", 1379 | "shasum": "" 1380 | }, 1381 | "require": { 1382 | "php": ">=5.3.3" 1383 | }, 1384 | "require-dev": { 1385 | "phpunit/phpunit": "~4.4" 1386 | }, 1387 | "type": "library", 1388 | "extra": { 1389 | "branch-alias": { 1390 | "dev-master": "1.0.x-dev" 1391 | } 1392 | }, 1393 | "autoload": { 1394 | "classmap": [ 1395 | "src/" 1396 | ] 1397 | }, 1398 | "notification-url": "https://packagist.org/downloads/", 1399 | "license": [ 1400 | "BSD-3-Clause" 1401 | ], 1402 | "authors": [ 1403 | { 1404 | "name": "Jeff Welch", 1405 | "email": "whatthejeff@gmail.com" 1406 | }, 1407 | { 1408 | "name": "Sebastian Bergmann", 1409 | "email": "sebastian@phpunit.de" 1410 | }, 1411 | { 1412 | "name": "Adam Harvey", 1413 | "email": "aharvey@php.net" 1414 | } 1415 | ], 1416 | "description": "Provides functionality to recursively process PHP variables", 1417 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1418 | "time": "2015-06-21 08:04:50" 1419 | }, 1420 | { 1421 | "name": "sebastian/version", 1422 | "version": "1.0.6", 1423 | "source": { 1424 | "type": "git", 1425 | "url": "https://github.com/sebastianbergmann/version.git", 1426 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1427 | }, 1428 | "dist": { 1429 | "type": "zip", 1430 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1431 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1432 | "shasum": "" 1433 | }, 1434 | "type": "library", 1435 | "autoload": { 1436 | "classmap": [ 1437 | "src/" 1438 | ] 1439 | }, 1440 | "notification-url": "https://packagist.org/downloads/", 1441 | "license": [ 1442 | "BSD-3-Clause" 1443 | ], 1444 | "authors": [ 1445 | { 1446 | "name": "Sebastian Bergmann", 1447 | "email": "sebastian@phpunit.de", 1448 | "role": "lead" 1449 | } 1450 | ], 1451 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1452 | "homepage": "https://github.com/sebastianbergmann/version", 1453 | "time": "2015-06-21 13:59:46" 1454 | }, 1455 | { 1456 | "name": "symfony/config", 1457 | "version": "v2.7.3", 1458 | "source": { 1459 | "type": "git", 1460 | "url": "https://github.com/symfony/Config.git", 1461 | "reference": "6c905bbed1e728226de656e4c07d620dfe9e80d9" 1462 | }, 1463 | "dist": { 1464 | "type": "zip", 1465 | "url": "https://api.github.com/repos/symfony/Config/zipball/6c905bbed1e728226de656e4c07d620dfe9e80d9", 1466 | "reference": "6c905bbed1e728226de656e4c07d620dfe9e80d9", 1467 | "shasum": "" 1468 | }, 1469 | "require": { 1470 | "php": ">=5.3.9", 1471 | "symfony/filesystem": "~2.3" 1472 | }, 1473 | "require-dev": { 1474 | "symfony/phpunit-bridge": "~2.7" 1475 | }, 1476 | "type": "library", 1477 | "extra": { 1478 | "branch-alias": { 1479 | "dev-master": "2.7-dev" 1480 | } 1481 | }, 1482 | "autoload": { 1483 | "psr-4": { 1484 | "Symfony\\Component\\Config\\": "" 1485 | } 1486 | }, 1487 | "notification-url": "https://packagist.org/downloads/", 1488 | "license": [ 1489 | "MIT" 1490 | ], 1491 | "authors": [ 1492 | { 1493 | "name": "Fabien Potencier", 1494 | "email": "fabien@symfony.com" 1495 | }, 1496 | { 1497 | "name": "Symfony Community", 1498 | "homepage": "https://symfony.com/contributors" 1499 | } 1500 | ], 1501 | "description": "Symfony Config Component", 1502 | "homepage": "https://symfony.com", 1503 | "time": "2015-07-09 16:07:40" 1504 | }, 1505 | { 1506 | "name": "symfony/console", 1507 | "version": "v2.7.3", 1508 | "source": { 1509 | "type": "git", 1510 | "url": "https://github.com/symfony/Console.git", 1511 | "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e" 1512 | }, 1513 | "dist": { 1514 | "type": "zip", 1515 | "url": "https://api.github.com/repos/symfony/Console/zipball/d6cf02fe73634c96677e428f840704bfbcaec29e", 1516 | "reference": "d6cf02fe73634c96677e428f840704bfbcaec29e", 1517 | "shasum": "" 1518 | }, 1519 | "require": { 1520 | "php": ">=5.3.9" 1521 | }, 1522 | "require-dev": { 1523 | "psr/log": "~1.0", 1524 | "symfony/event-dispatcher": "~2.1", 1525 | "symfony/phpunit-bridge": "~2.7", 1526 | "symfony/process": "~2.1" 1527 | }, 1528 | "suggest": { 1529 | "psr/log": "For using the console logger", 1530 | "symfony/event-dispatcher": "", 1531 | "symfony/process": "" 1532 | }, 1533 | "type": "library", 1534 | "extra": { 1535 | "branch-alias": { 1536 | "dev-master": "2.7-dev" 1537 | } 1538 | }, 1539 | "autoload": { 1540 | "psr-4": { 1541 | "Symfony\\Component\\Console\\": "" 1542 | } 1543 | }, 1544 | "notification-url": "https://packagist.org/downloads/", 1545 | "license": [ 1546 | "MIT" 1547 | ], 1548 | "authors": [ 1549 | { 1550 | "name": "Fabien Potencier", 1551 | "email": "fabien@symfony.com" 1552 | }, 1553 | { 1554 | "name": "Symfony Community", 1555 | "homepage": "https://symfony.com/contributors" 1556 | } 1557 | ], 1558 | "description": "Symfony Console Component", 1559 | "homepage": "https://symfony.com", 1560 | "time": "2015-07-28 15:18:12" 1561 | }, 1562 | { 1563 | "name": "symfony/dependency-injection", 1564 | "version": "v2.7.3", 1565 | "source": { 1566 | "type": "git", 1567 | "url": "https://github.com/symfony/DependencyInjection.git", 1568 | "reference": "851e3ffe8a366b1590bdaf3df2c1395f2d27d8a6" 1569 | }, 1570 | "dist": { 1571 | "type": "zip", 1572 | "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/851e3ffe8a366b1590bdaf3df2c1395f2d27d8a6", 1573 | "reference": "851e3ffe8a366b1590bdaf3df2c1395f2d27d8a6", 1574 | "shasum": "" 1575 | }, 1576 | "require": { 1577 | "php": ">=5.3.9" 1578 | }, 1579 | "conflict": { 1580 | "symfony/expression-language": "<2.6" 1581 | }, 1582 | "require-dev": { 1583 | "symfony/config": "~2.2", 1584 | "symfony/expression-language": "~2.6", 1585 | "symfony/phpunit-bridge": "~2.7", 1586 | "symfony/yaml": "~2.1" 1587 | }, 1588 | "suggest": { 1589 | "symfony/config": "", 1590 | "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", 1591 | "symfony/yaml": "" 1592 | }, 1593 | "type": "library", 1594 | "extra": { 1595 | "branch-alias": { 1596 | "dev-master": "2.7-dev" 1597 | } 1598 | }, 1599 | "autoload": { 1600 | "psr-4": { 1601 | "Symfony\\Component\\DependencyInjection\\": "" 1602 | } 1603 | }, 1604 | "notification-url": "https://packagist.org/downloads/", 1605 | "license": [ 1606 | "MIT" 1607 | ], 1608 | "authors": [ 1609 | { 1610 | "name": "Fabien Potencier", 1611 | "email": "fabien@symfony.com" 1612 | }, 1613 | { 1614 | "name": "Symfony Community", 1615 | "homepage": "https://symfony.com/contributors" 1616 | } 1617 | ], 1618 | "description": "Symfony DependencyInjection Component", 1619 | "homepage": "https://symfony.com", 1620 | "time": "2015-07-28 14:07:07" 1621 | }, 1622 | { 1623 | "name": "symfony/event-dispatcher", 1624 | "version": "v2.7.3", 1625 | "source": { 1626 | "type": "git", 1627 | "url": "https://github.com/symfony/EventDispatcher.git", 1628 | "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3" 1629 | }, 1630 | "dist": { 1631 | "type": "zip", 1632 | "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", 1633 | "reference": "9310b5f9a87ec2ea75d20fec0b0017c77c66dac3", 1634 | "shasum": "" 1635 | }, 1636 | "require": { 1637 | "php": ">=5.3.9" 1638 | }, 1639 | "require-dev": { 1640 | "psr/log": "~1.0", 1641 | "symfony/config": "~2.0,>=2.0.5", 1642 | "symfony/dependency-injection": "~2.6", 1643 | "symfony/expression-language": "~2.6", 1644 | "symfony/phpunit-bridge": "~2.7", 1645 | "symfony/stopwatch": "~2.3" 1646 | }, 1647 | "suggest": { 1648 | "symfony/dependency-injection": "", 1649 | "symfony/http-kernel": "" 1650 | }, 1651 | "type": "library", 1652 | "extra": { 1653 | "branch-alias": { 1654 | "dev-master": "2.7-dev" 1655 | } 1656 | }, 1657 | "autoload": { 1658 | "psr-4": { 1659 | "Symfony\\Component\\EventDispatcher\\": "" 1660 | } 1661 | }, 1662 | "notification-url": "https://packagist.org/downloads/", 1663 | "license": [ 1664 | "MIT" 1665 | ], 1666 | "authors": [ 1667 | { 1668 | "name": "Fabien Potencier", 1669 | "email": "fabien@symfony.com" 1670 | }, 1671 | { 1672 | "name": "Symfony Community", 1673 | "homepage": "https://symfony.com/contributors" 1674 | } 1675 | ], 1676 | "description": "Symfony EventDispatcher Component", 1677 | "homepage": "https://symfony.com", 1678 | "time": "2015-06-18 19:21:56" 1679 | }, 1680 | { 1681 | "name": "symfony/filesystem", 1682 | "version": "v2.7.3", 1683 | "source": { 1684 | "type": "git", 1685 | "url": "https://github.com/symfony/Filesystem.git", 1686 | "reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8" 1687 | }, 1688 | "dist": { 1689 | "type": "zip", 1690 | "url": "https://api.github.com/repos/symfony/Filesystem/zipball/2d7b2ddaf3f548f4292df49a99d19c853d43f0b8", 1691 | "reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8", 1692 | "shasum": "" 1693 | }, 1694 | "require": { 1695 | "php": ">=5.3.9" 1696 | }, 1697 | "require-dev": { 1698 | "symfony/phpunit-bridge": "~2.7" 1699 | }, 1700 | "type": "library", 1701 | "extra": { 1702 | "branch-alias": { 1703 | "dev-master": "2.7-dev" 1704 | } 1705 | }, 1706 | "autoload": { 1707 | "psr-4": { 1708 | "Symfony\\Component\\Filesystem\\": "" 1709 | } 1710 | }, 1711 | "notification-url": "https://packagist.org/downloads/", 1712 | "license": [ 1713 | "MIT" 1714 | ], 1715 | "authors": [ 1716 | { 1717 | "name": "Fabien Potencier", 1718 | "email": "fabien@symfony.com" 1719 | }, 1720 | { 1721 | "name": "Symfony Community", 1722 | "homepage": "https://symfony.com/contributors" 1723 | } 1724 | ], 1725 | "description": "Symfony Filesystem Component", 1726 | "homepage": "https://symfony.com", 1727 | "time": "2015-07-09 16:07:40" 1728 | }, 1729 | { 1730 | "name": "symfony/finder", 1731 | "version": "v2.7.3", 1732 | "source": { 1733 | "type": "git", 1734 | "url": "https://github.com/symfony/Finder.git", 1735 | "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4" 1736 | }, 1737 | "dist": { 1738 | "type": "zip", 1739 | "url": "https://api.github.com/repos/symfony/Finder/zipball/ae0f363277485094edc04c9f3cbe595b183b78e4", 1740 | "reference": "ae0f363277485094edc04c9f3cbe595b183b78e4", 1741 | "shasum": "" 1742 | }, 1743 | "require": { 1744 | "php": ">=5.3.9" 1745 | }, 1746 | "require-dev": { 1747 | "symfony/phpunit-bridge": "~2.7" 1748 | }, 1749 | "type": "library", 1750 | "extra": { 1751 | "branch-alias": { 1752 | "dev-master": "2.7-dev" 1753 | } 1754 | }, 1755 | "autoload": { 1756 | "psr-4": { 1757 | "Symfony\\Component\\Finder\\": "" 1758 | } 1759 | }, 1760 | "notification-url": "https://packagist.org/downloads/", 1761 | "license": [ 1762 | "MIT" 1763 | ], 1764 | "authors": [ 1765 | { 1766 | "name": "Fabien Potencier", 1767 | "email": "fabien@symfony.com" 1768 | }, 1769 | { 1770 | "name": "Symfony Community", 1771 | "homepage": "https://symfony.com/contributors" 1772 | } 1773 | ], 1774 | "description": "Symfony Finder Component", 1775 | "homepage": "https://symfony.com", 1776 | "time": "2015-07-09 16:07:40" 1777 | }, 1778 | { 1779 | "name": "symfony/process", 1780 | "version": "v2.7.3", 1781 | "source": { 1782 | "type": "git", 1783 | "url": "https://github.com/symfony/Process.git", 1784 | "reference": "48aeb0e48600321c272955132d7606ab0a49adb3" 1785 | }, 1786 | "dist": { 1787 | "type": "zip", 1788 | "url": "https://api.github.com/repos/symfony/Process/zipball/48aeb0e48600321c272955132d7606ab0a49adb3", 1789 | "reference": "48aeb0e48600321c272955132d7606ab0a49adb3", 1790 | "shasum": "" 1791 | }, 1792 | "require": { 1793 | "php": ">=5.3.9" 1794 | }, 1795 | "require-dev": { 1796 | "symfony/phpunit-bridge": "~2.7" 1797 | }, 1798 | "type": "library", 1799 | "extra": { 1800 | "branch-alias": { 1801 | "dev-master": "2.7-dev" 1802 | } 1803 | }, 1804 | "autoload": { 1805 | "psr-4": { 1806 | "Symfony\\Component\\Process\\": "" 1807 | } 1808 | }, 1809 | "notification-url": "https://packagist.org/downloads/", 1810 | "license": [ 1811 | "MIT" 1812 | ], 1813 | "authors": [ 1814 | { 1815 | "name": "Fabien Potencier", 1816 | "email": "fabien@symfony.com" 1817 | }, 1818 | { 1819 | "name": "Symfony Community", 1820 | "homepage": "https://symfony.com/contributors" 1821 | } 1822 | ], 1823 | "description": "Symfony Process Component", 1824 | "homepage": "https://symfony.com", 1825 | "time": "2015-07-01 11:25:50" 1826 | }, 1827 | { 1828 | "name": "symfony/stopwatch", 1829 | "version": "v2.7.3", 1830 | "source": { 1831 | "type": "git", 1832 | "url": "https://github.com/symfony/Stopwatch.git", 1833 | "reference": "b07a866719bbac5294c67773340f97b871733310" 1834 | }, 1835 | "dist": { 1836 | "type": "zip", 1837 | "url": "https://api.github.com/repos/symfony/Stopwatch/zipball/b07a866719bbac5294c67773340f97b871733310", 1838 | "reference": "b07a866719bbac5294c67773340f97b871733310", 1839 | "shasum": "" 1840 | }, 1841 | "require": { 1842 | "php": ">=5.3.9" 1843 | }, 1844 | "require-dev": { 1845 | "symfony/phpunit-bridge": "~2.7" 1846 | }, 1847 | "type": "library", 1848 | "extra": { 1849 | "branch-alias": { 1850 | "dev-master": "2.7-dev" 1851 | } 1852 | }, 1853 | "autoload": { 1854 | "psr-4": { 1855 | "Symfony\\Component\\Stopwatch\\": "" 1856 | } 1857 | }, 1858 | "notification-url": "https://packagist.org/downloads/", 1859 | "license": [ 1860 | "MIT" 1861 | ], 1862 | "authors": [ 1863 | { 1864 | "name": "Fabien Potencier", 1865 | "email": "fabien@symfony.com" 1866 | }, 1867 | { 1868 | "name": "Symfony Community", 1869 | "homepage": "https://symfony.com/contributors" 1870 | } 1871 | ], 1872 | "description": "Symfony Stopwatch Component", 1873 | "homepage": "https://symfony.com", 1874 | "time": "2015-07-01 18:23:16" 1875 | }, 1876 | { 1877 | "name": "symfony/yaml", 1878 | "version": "v2.7.3", 1879 | "source": { 1880 | "type": "git", 1881 | "url": "https://github.com/symfony/Yaml.git", 1882 | "reference": "71340e996171474a53f3d29111d046be4ad8a0ff" 1883 | }, 1884 | "dist": { 1885 | "type": "zip", 1886 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/71340e996171474a53f3d29111d046be4ad8a0ff", 1887 | "reference": "71340e996171474a53f3d29111d046be4ad8a0ff", 1888 | "shasum": "" 1889 | }, 1890 | "require": { 1891 | "php": ">=5.3.9" 1892 | }, 1893 | "require-dev": { 1894 | "symfony/phpunit-bridge": "~2.7" 1895 | }, 1896 | "type": "library", 1897 | "extra": { 1898 | "branch-alias": { 1899 | "dev-master": "2.7-dev" 1900 | } 1901 | }, 1902 | "autoload": { 1903 | "psr-4": { 1904 | "Symfony\\Component\\Yaml\\": "" 1905 | } 1906 | }, 1907 | "notification-url": "https://packagist.org/downloads/", 1908 | "license": [ 1909 | "MIT" 1910 | ], 1911 | "authors": [ 1912 | { 1913 | "name": "Fabien Potencier", 1914 | "email": "fabien@symfony.com" 1915 | }, 1916 | { 1917 | "name": "Symfony Community", 1918 | "homepage": "https://symfony.com/contributors" 1919 | } 1920 | ], 1921 | "description": "Symfony Yaml Component", 1922 | "homepage": "https://symfony.com", 1923 | "time": "2015-07-28 14:07:07" 1924 | }, 1925 | { 1926 | "name": "theseer/fdomdocument", 1927 | "version": "1.6.1", 1928 | "source": { 1929 | "type": "git", 1930 | "url": "https://github.com/theseer/fDOMDocument.git", 1931 | "reference": "d9ad139d6c2e8edf5e313ffbe37ff13344cf0684" 1932 | }, 1933 | "dist": { 1934 | "type": "zip", 1935 | "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/d9ad139d6c2e8edf5e313ffbe37ff13344cf0684", 1936 | "reference": "d9ad139d6c2e8edf5e313ffbe37ff13344cf0684", 1937 | "shasum": "" 1938 | }, 1939 | "require": { 1940 | "ext-dom": "*", 1941 | "lib-libxml": "*", 1942 | "php": ">=5.3.3" 1943 | }, 1944 | "type": "library", 1945 | "autoload": { 1946 | "classmap": [ 1947 | "src/" 1948 | ] 1949 | }, 1950 | "notification-url": "https://packagist.org/downloads/", 1951 | "license": [ 1952 | "BSD-3-Clause" 1953 | ], 1954 | "authors": [ 1955 | { 1956 | "name": "Arne Blankerts", 1957 | "email": "arne@blankerts.de", 1958 | "role": "lead" 1959 | } 1960 | ], 1961 | "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", 1962 | "homepage": "https://github.com/theseer/fDOMDocument", 1963 | "time": "2015-05-27 22:58:02" 1964 | } 1965 | ], 1966 | "aliases": [], 1967 | "minimum-stability": "stable", 1968 | "stability-flags": { 1969 | "satooshi/php-coveralls": 20 1970 | }, 1971 | "prefer-stable": false, 1972 | "prefer-lowest": false, 1973 | "platform": { 1974 | "php": ">=5.5" 1975 | }, 1976 | "platform-dev": [] 1977 | } 1978 | -------------------------------------------------------------------------------- /doc/Command.md: -------------------------------------------------------------------------------- 1 | # Command System 2 | 3 | When it comes to change the state of an aggregate whether it's an addition, a deletion or a modification, 4 | it's probably a better idea not to have the same flow as when we read it. 5 | In fact, read access should be as fast as possible. And what is often the slowest part of an application is the data access. 6 | In read process we want to avoid heavy things like join queries. 7 | 8 | But let's get back to the purpose of the Command System. 9 | 10 | The Command system we suggest you to use is the C part of the Command Query Responsibility Segregation (CQRS) design pattern. 11 | It's the write part of your application. 12 | 13 | Requests from your user is transformed into a Plain Old PHP Object, a Command then dispatched through a CommandDispatcher to a CommandHandler that will know how to process it. 14 | 15 | Let's decompose this a bit. We have: 16 | - A command with some properties and no logic. In fact Commands contains only public properties (why bother having private with getters and setters if we know what we are doing ?). 17 | - A CommandHandler is a simple class that knows how to handle a Command. 18 | - A CommandDispatcher links Command and CommandHandler so that when a Command appears, the right CommandHandler is called. 19 | 20 | ## Inside the CommandHandler 21 | 22 | What's not in this framework is your domain logic so let's clarify what you should do in a CommandHandler. 23 | First, get the right aggregate root according to the Command data. If the command is about letting one user making some 24 | changes in his settings, then the command should probably contain an identification of this user. 25 | Once you get the aggregate root, then you'll be able to apply the corresponding action by calling the right method in 26 | the user with the parameters coming from the command. 27 | Now that the aggregate changed, your CommandHandler is able to save the new states of the user. 28 | 29 | Note: if you use the Event mechanism, you might also wants to do one of the following: 30 | - add the aggregate roots back to the Command so you will be able to pull the events from outside of the Command world 31 | - or pull events directly from the aggregate root and publish them. 32 | 33 | For some example on how to declare a command handler, you might want to have a look at our [tests classes](/tests/Command/Sample). 34 | -------------------------------------------------------------------------------- /doc/Domain.md: -------------------------------------------------------------------------------- 1 | # Domain 2 | 3 | The domain model is the heart of your application. It contains your Aggregates (Aggregate roots, Value Objects and Entities). 4 | 5 | This part include an abstract AggregateRoot object that helps you integrate integrate easily with the event system. 6 | 7 | See an example of how it works [here](/tests/Domain/Sample/TestAggregateRoot.php) 8 | -------------------------------------------------------------------------------- /doc/Event.md: -------------------------------------------------------------------------------- 1 | # Event System 2 | 3 | As Greg Young (and many others) says: "An event is something that happened, it's in the past". 4 | 5 | The Event system allows you to act on what happened on your application. 6 | It is thrown by the Domain (Aggregate roots). 7 | 8 | It has many uses: 9 | * send an email to a user when he subscribes to your website 10 | * log every things that happened to your application 11 | * do something on other aggregate roots when another BoundedContext changes its states 12 | * ... 13 | 14 | The main advantages over classic development is that everything is bound to something inside your Domain application. 15 | 16 | 17 | ## How it works 18 | 19 | 1. Configuring the EventDispatcher 20 | 1. Create an Event class (extending [`PhpDDD\DomainDrivenDesign\Event\AbstractEvent`](/src/Event/AbstractEvent.php)) - [example](/tests/Event/Sample/TestEvent.php) 21 | 2. Create an EventSubscriber (implementing [`PhpDDD\DomainDrivenDesign\Event\EventSubscriberInterface`](/src/Event/EventSubscriberInterface.php)) - [example](/tests/Event/Sample/TestEventSubscriber.php) 22 | 3. Attach your EventSubscriber to the EventDispatcher (you may need to instantiate it) 23 | 2. Publishing 24 | 1. instantiate your Event 25 | 2. publish it (`$eventDispatcher->publish(new Event());`) 26 | 27 | -------------------------------------------------------------------------------- /doc/EventLog.md: -------------------------------------------------------------------------------- 1 | # Event Log 2 | 3 | The Event Log use the tools we already defined (Domain, Event) to store all the events triggered in the application. 4 | You can see it as a partial example of how to implement a Bounded Context. 5 | 6 | ## What's included 7 | * an aggregate ([EventLog](/src/EventLog/EventLog.php)) 8 | * an event subscriber ([LogAllEvents](/src/EventLog/EventSubscriber/LogAllEvents.php)) 9 | * a repository ([EventLogRepositoryInterface](/src/EventLog/Repository/EventLogRepositoryInterface.php)) 10 | 11 | ## How to use it ? 12 | 13 | First implement the repository. 14 | Ex: 15 | 16 | ```php 17 | 18 | use PhpDDD\DomainDrivenDesign\EventLog\EventLog; 19 | use PhpDDD\DomainDrivenDesign\EventLog\Repository\EventLogRepository; 20 | 21 | class EventLogRepository implements EventLogRepositoryInterface 22 | { 23 | const FILE = '/tmp/events.log'; 24 | 25 | public function save(EventLog $eventLog) 26 | { 27 | // here we choose to save it in a file 28 | file_put_contents(self::FILE, serialize($eventLog), FILE_APPEND); 29 | } 30 | 31 | /** 32 | * You might want to add your own method. 33 | * For instance, the load method is not used in the domain but you will probably need it. 34 | */ 35 | public function load($eventLogId) 36 | { 37 | if (!file_exists(self::FILE)) { 38 | throw new Exception('There is no event log with the id given'); 39 | } 40 | 41 | $eventLogs = file(self::FILE); 42 | 43 | if (count($eventLogs) < $eventLogId) { 44 | throw new Exception('There is no event log with the id given'); 45 | } 46 | 47 | return unserialize($eventLogs[$eventLogId - 1]); 48 | } 49 | } 50 | ``` 51 | 52 | Then add the LogAllEvents subscriber to the EventDispatcher 53 | 54 | ```php 55 | $eventDispatcher = /* ... */; 56 | 57 | $repository = new EventLogRepository(); 58 | $eventDispatcher->subscribe(new LogAllEvents($repository)); 59 | 60 | // Now each time an event is published in the event dispatcher, it will be saved in the file /tmp/events.log 61 | ``` 62 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | tests 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Command/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | 'value', ...] 46 | */ 47 | public function __construct(array $data = []) 48 | { 49 | $data['requester'] = isset($data['requester']) ? $data['requester'] : Author::robot(); 50 | $this->populate($data); 51 | } 52 | 53 | /** 54 | * @param AbstractAggregateRoot $abstractAggregateRoot 55 | */ 56 | public function addAggregateRoot(AbstractAggregateRoot $abstractAggregateRoot) 57 | { 58 | $this->aggregateRoots[] = $abstractAggregateRoot; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Command/CommandDispatcher.php: -------------------------------------------------------------------------------- 1 | getHandlingMethodForCommand($command); 40 | $nbMethods = count($handlingMethods); 41 | if (0 === $nbMethods) { 42 | throw CommandDispatcherException::commandHandlerNotFound($command); 43 | } elseif (1 !== $nbMethods) { 44 | throw CommandDispatcherException::tooManyCommandHandler($command, $this->getHandlerForCommand($command)); 45 | } 46 | $method = current($handlingMethods); 47 | 48 | call_user_func($method, $command); 49 | 50 | return $command; 51 | } 52 | 53 | /** 54 | * @param CommandHandlerInterface $handler 55 | */ 56 | public function register(CommandHandlerInterface $handler) 57 | { 58 | foreach ($handler->getHandlingMethods() as $commandClassName => $callable) { 59 | if (isset($this->handlers[$commandClassName])) { 60 | throw CommandDispatcherException::commandHandlerAlreadyDefinedForCommand( 61 | $commandClassName, 62 | $this->handlers[$commandClassName], 63 | $handler 64 | ); 65 | } 66 | 67 | // if callable is a string, then it should be a method name of the command handler 68 | if (!is_callable($callable)) { 69 | $callable = [$handler, $callable]; 70 | } 71 | 72 | $this->handlingMethods[$commandClassName] = $callable; 73 | $this->handlers[$commandClassName] = $handler; 74 | } 75 | } 76 | 77 | /** 78 | * @return CommandHandlerInterface[] 79 | */ 80 | public function getHandlers() 81 | { 82 | return $this->handlers; 83 | } 84 | 85 | /** 86 | * @param AbstractCommand $command 87 | * 88 | * @return callable[] 89 | */ 90 | private function getHandlingMethodForCommand(AbstractCommand $command) 91 | { 92 | return $this->filterForCommand($this->handlingMethods, $command); 93 | } 94 | 95 | /** 96 | * @param AbstractCommand $command 97 | * 98 | * @return CommandHandlerInterface[] 99 | */ 100 | private function getHandlerForCommand(AbstractCommand $command) 101 | { 102 | return $this->filterForCommand($this->handlers, $command); 103 | } 104 | 105 | /** 106 | * @param array $property 107 | * @param AbstractCommand $command 108 | * 109 | * @return array 110 | */ 111 | private function filterForCommand(array $property, AbstractCommand $command) 112 | { 113 | $filtered = []; 114 | foreach ($property as $commandClassName => $handler) { 115 | if ($command instanceof $commandClassName) { 116 | // After that, we still need to check if there is no other handler defined for this command. 117 | // This is because we allow command handler to listen to parent class or interface. 118 | $filtered[] = $handler; 119 | } 120 | } 121 | 122 | return $filtered; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Command/CommandDispatcherInterface.php: -------------------------------------------------------------------------------- 1 | 'myMethod', 21 | * MyCommandInterface::class => [self::class, 'myStaticMethod'], 22 | * ] 23 | * 24 | * @return callable[]|string[] 25 | */ 26 | public static function getHandlingMethods(); 27 | } 28 | -------------------------------------------------------------------------------- /src/Domain/AbstractAggregateRoot.php: -------------------------------------------------------------------------------- 1 | events; 28 | $this->events = []; 29 | 30 | return $events; 31 | } 32 | 33 | /** 34 | * @param AbstractEvent $event 35 | */ 36 | protected function apply(AbstractEvent $event) 37 | { 38 | $event->aggregateRoot = $this; 39 | $this->executeEvent($event); 40 | $this->events[] = $event; 41 | } 42 | 43 | /** 44 | * @param AbstractEvent $event 45 | * 46 | * @throws BadMethodCallException 47 | */ 48 | private function executeEvent(AbstractEvent $event) 49 | { 50 | $eventName = ClassUtils::getShortName($event); 51 | $method = sprintf('apply%s', (string) $eventName); 52 | 53 | if (!method_exists($this, $method)) { 54 | throw new BadMethodCallException( 55 | sprintf( 56 | 'You must define the %s::%s(%s $event) method in order to apply event named "%s".', 57 | get_class($this), 58 | $method, 59 | get_class($event), 60 | $eventName 61 | ) 62 | ); 63 | } 64 | 65 | $this->$method($event); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Domain/CommandEventDispatcher.php: -------------------------------------------------------------------------------- 1 | commandDispatcher = $commandDispatcher; 35 | $this->eventDispatcher = $eventDispatcher; 36 | } 37 | 38 | /** 39 | * Try to handle the command given in argument. 40 | * It will look over every handlers registered and find the one that knows how handle it. 41 | * 42 | * @param AbstractCommand $command 43 | * 44 | * @return AbstractCommand 45 | */ 46 | public function handle(AbstractCommand $command) 47 | { 48 | $this->commandDispatcher->handle($command); 49 | // Now that the command is handled, let's found if there is some events to publish 50 | $events = $this->extractEventsFromCommand($command); 51 | foreach ($events as $event) { 52 | // we will only publish event to synchronous listeners 53 | $this->publish($event); 54 | } 55 | 56 | return $command; 57 | } 58 | 59 | /** 60 | * Register a CommandHandler for one or more Command. 61 | * 62 | * @param CommandHandlerInterface $handler 63 | * 64 | * @return void 65 | */ 66 | public function register(CommandHandlerInterface $handler) 67 | { 68 | $this->commandDispatcher->register($handler); 69 | } 70 | 71 | /** 72 | * @param AbstractEvent $event 73 | * @param bool $asynchronous 74 | * 75 | * @return AbstractEvent 76 | */ 77 | public function publish(AbstractEvent $event, $asynchronous = false) 78 | { 79 | return $this->eventDispatcher->publish($event, $asynchronous); 80 | } 81 | 82 | /** 83 | * @param EventSubscriberInterface $subscriber 84 | */ 85 | public function subscribe(EventSubscriberInterface $subscriber) 86 | { 87 | $this->eventDispatcher->subscribe($subscriber); 88 | } 89 | 90 | /** 91 | * @param AbstractCommand $command 92 | * 93 | * @return AbstractEvent[] 94 | */ 95 | private function extractEventsFromCommand(AbstractCommand $command) 96 | { 97 | $events = []; 98 | foreach ($command->aggregateRoots as $aggregateRoot) { 99 | $events = array_merge($events, $aggregateRoot->pullEvents()); 100 | } 101 | 102 | return $events; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Event/AbstractEvent.php: -------------------------------------------------------------------------------- 1 | populate($data); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Event/EventDispatcher.php: -------------------------------------------------------------------------------- 1 | doPublish($this->getListeners($event, $asynchronous), $eventName, $event); 25 | 26 | return $event; 27 | } 28 | 29 | /** 30 | * @param EventSubscriberInterface $subscriber 31 | */ 32 | public function subscribe(EventSubscriberInterface $subscriber) 33 | { 34 | foreach ($subscriber->getSubscribedEvents() as $subscribedEvent) { 35 | $subscribedEvent->setSubscriber($subscriber); 36 | $this->listeners[] = $subscribedEvent; 37 | } 38 | } 39 | 40 | /** 41 | * @param AbstractEvent $event 42 | * @param bool $asynchronous 43 | * 44 | * @return EventListener[] 45 | */ 46 | public function getListeners(AbstractEvent $event, $asynchronous = false) 47 | { 48 | return array_filter( 49 | $this->listeners, 50 | function (EventListener $eventSubscriber) use ($asynchronous, $event) { 51 | $supportedEvent = $eventSubscriber->getEventClassName(); 52 | 53 | if ($eventSubscriber->isAsynchronous() !== $asynchronous) { 54 | return false; 55 | } 56 | if (null === $supportedEvent) { 57 | return true; 58 | } 59 | 60 | return $event instanceof $supportedEvent; 61 | } 62 | ); 63 | } 64 | 65 | /** 66 | * Triggers the listeners of an event. 67 | * 68 | * This method can be overridden to add functionality that is executed 69 | * for each listener. 70 | * 71 | * @param EventListener[] $listeners The event listeners. 72 | * @param string $eventName The name of the event to dispatch. 73 | * @param AbstractEvent $event The event object to pass to the event handlers/listeners. 74 | */ 75 | private function doPublish(array $listeners, $eventName, AbstractEvent $event) 76 | { 77 | foreach ($listeners as $listener) { 78 | call_user_func($listener->getMethod(), $event, $eventName, $this); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Event/EventDispatcherInterface.php: -------------------------------------------------------------------------------- 1 | eventClassName = $eventClassName; 34 | $this->method = $method; 35 | $this->asynchronous = (bool) $asynchronous; 36 | } 37 | 38 | /** 39 | * @param bool $asynchronous 40 | */ 41 | public static function validateAsynchronous($asynchronous) 42 | { 43 | if (!is_bool($asynchronous)) { 44 | throw InvalidArgumentException::wrongType('asynchronous', $asynchronous, 'boolean'); 45 | } 46 | } 47 | 48 | /** 49 | * @param string|null $eventClassName 50 | */ 51 | public static function validateEventClassName($eventClassName) 52 | { 53 | if (null !== $eventClassName && !is_string($eventClassName)) { 54 | throw InvalidArgumentException::wrongType('eventClassName', $eventClassName, 'null or sting'); 55 | } 56 | } 57 | 58 | /** 59 | * @param string|callable $method 60 | */ 61 | public static function validateMethod($method) 62 | { 63 | if (!is_string($method) && !is_callable($method)) { 64 | throw InvalidArgumentException::wrongType('method', $method, 'callable or sting'); 65 | } 66 | } 67 | 68 | /** 69 | * @return bool 70 | */ 71 | public function isAsynchronous() 72 | { 73 | return $this->asynchronous; 74 | } 75 | 76 | /** 77 | * @param EventSubscriberInterface $subscriber 78 | */ 79 | public function setSubscriber(EventSubscriberInterface $subscriber) 80 | { 81 | if (!is_callable($this->method)) { 82 | $this->method = [$subscriber, $this->method]; 83 | } 84 | } 85 | 86 | /** 87 | * @return string 88 | */ 89 | public function getEventClassName() 90 | { 91 | return $this->eventClassName; 92 | } 93 | 94 | /** 95 | * @return callable|string 96 | */ 97 | public function getMethod() 98 | { 99 | return $this->method; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Event/EventSubscriberInterface.php: -------------------------------------------------------------------------------- 1 | apply(new LogEntryAdded(...); 78 | // But doing so will probably create an infinite loop when you want to subscribe to all events 79 | // Instead, we make an exception so you don't need to subscribe to every events except this one 80 | $this->aggregateRootId = $aggregateRootId; 81 | $this->aggregateRootType = $aggregateRootType; 82 | $this->author = $author; 83 | $this->eventName = $eventName; 84 | $this->occurredAt = $occurredAt; 85 | $this->metadata = $metadata; 86 | } 87 | 88 | /** 89 | * @param AbstractEvent $event 90 | * 91 | * @return EventLog 92 | */ 93 | public static function addEventEntry(AbstractEvent $event) 94 | { 95 | return new self( 96 | null !== $event->aggregateRoot ? $event->aggregateRoot->getId() : null, 97 | null !== $event->aggregateRoot ? get_class($event->aggregateRoot) : null, 98 | $event->author, 99 | get_class($event), 100 | $event->date, 101 | serialize($event) 102 | ); 103 | } 104 | 105 | /** 106 | * Get the unique identifier of this aggregate root. 107 | * 108 | * @return mixed 109 | */ 110 | public function getId() 111 | { 112 | return $this->id; 113 | } 114 | 115 | /** 116 | * @return mixed 117 | */ 118 | public function getAggregateRootId() 119 | { 120 | return $this->aggregateRootId; 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getAggregateRootType() 127 | { 128 | return $this->aggregateRootType; 129 | } 130 | 131 | /** 132 | * @return Author 133 | */ 134 | public function getAuthor() 135 | { 136 | return $this->author; 137 | } 138 | 139 | /** 140 | * @return string 141 | */ 142 | public function getEventName() 143 | { 144 | return $this->eventName; 145 | } 146 | 147 | /** 148 | * @return string 149 | */ 150 | public function getMetadata() 151 | { 152 | return $this->metadata; 153 | } 154 | 155 | /** 156 | * @return DateTime 157 | */ 158 | public function getOccurredAt() 159 | { 160 | return $this->occurredAt; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/EventLog/EventSubscriber/LogAllEvents.php: -------------------------------------------------------------------------------- 1 | repository = $repository; 37 | } 38 | 39 | /** 40 | * @param AbstractEvent $event 41 | * 42 | * @return AbstractEvent 43 | */ 44 | public function logEvent(AbstractEvent $event) 45 | { 46 | $eventLog = EventLog::addEventEntry($event); 47 | 48 | return $this->repository->save($eventLog); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/EventLog/Repository/EventLogRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | identifier = $identifier; 28 | $this->type = $type; 29 | } 30 | 31 | /** 32 | * @param AuthorInterface $author 33 | * 34 | * @return Author 35 | */ 36 | public static function fromObject(AuthorInterface $author) 37 | { 38 | return new self($author->getId(), get_class($author)); 39 | } 40 | 41 | /** 42 | * @return Author 43 | */ 44 | public static function robot() 45 | { 46 | return self::fromObject(new Robot()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/User/AuthorInterface.php: -------------------------------------------------------------------------------- 1 | getMockForAbstractCommand([]); 21 | $this->assertEquals(Author::robot(), $mock->requester); 22 | } 23 | 24 | public function testConstructorWithAuthor() 25 | { 26 | $requester = Author::robot(); 27 | $parameters = ['requester' => $requester]; 28 | 29 | $mock = $this->getMockForAbstractCommand($parameters); 30 | $this->assertEquals($requester, $mock->requester); 31 | } 32 | 33 | /** 34 | * @expectedException \InvalidArgumentException 35 | */ 36 | public function testConstructorWithUnknownProperty() 37 | { 38 | $this->getMockForAbstractCommand(['unknownProperty' => 'value']); 39 | } 40 | 41 | /** 42 | * 43 | */ 44 | public function testAddAggregateRoot() 45 | { 46 | $mock = $this->getMockForAbstractCommand([]); 47 | $this->assertEquals([], $mock->aggregateRoots); 48 | $mock->addAggregateRoot($this->getMockForAggregateRoot()); 49 | 50 | $this->assertCount(1, $mock->aggregateRoots); 51 | } 52 | 53 | /** 54 | * @param array $constructorArgs 55 | * 56 | * @return PHPUnit_Framework_MockObject_MockObject|AbstractCommand 57 | */ 58 | private function getMockForAbstractCommand(array $constructorArgs) 59 | { 60 | return $this->getMockBuilder(self::TESTED_CLASS) 61 | ->setConstructorArgs([$constructorArgs]) 62 | ->getMockForAbstractClass(); 63 | } 64 | 65 | /** 66 | * @return PHPUnit_Framework_MockObject_MockObject|AbstractAggregateRoot 67 | */ 68 | private function getMockForAggregateRoot() 69 | { 70 | return $this->getMockForAbstractClass(AbstractAggregateRoot::class); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Command/CommandDispatcherTest.php: -------------------------------------------------------------------------------- 1 | commandHandler = new TestCommandHandlerOne(); 34 | $this->commandDispatcher = new CommandDispatcher(); 35 | $this->commandDispatcher->register($this->commandHandler); 36 | } 37 | 38 | /** 39 | * 40 | */ 41 | protected function tearDown() 42 | { 43 | $this->commandDispatcher = null; 44 | } 45 | 46 | /** 47 | * 48 | */ 49 | public function testRegister() 50 | { 51 | $commandHandler = new TestCommandHandlerOne(); 52 | $commandDispatcher = new CommandDispatcher(); 53 | 54 | $commandDispatcher->register($commandHandler); 55 | 56 | $this->assertCount(2, $commandDispatcher->getHandlers()); 57 | } 58 | 59 | /** 60 | * @expectedException \PhpDDD\DomainDrivenDesign\Exception\CommandDispatcherException 61 | */ 62 | public function testRegisterCommandMoreThanOnce() 63 | { 64 | $commandHandler = new TestCommandHandlerOne(); 65 | $commandDispatcher = new CommandDispatcher(); 66 | 67 | $commandDispatcher->register($commandHandler); 68 | $commandDispatcher->register($commandHandler); 69 | } 70 | 71 | /** 72 | */ 73 | public function testRegisterSameCommandByDifferentHandler() 74 | { 75 | $commandDispatcher = new CommandDispatcher(); 76 | $commandDispatcher->register(new TestCommandHandlerOne()); 77 | 78 | try { 79 | $commandDispatcher->register(new TestCommandHandlerTwo()); 80 | $this->assertTrue(false, 'An exception of type CommandDispatcherException should have been thrown.'); 81 | } catch (CommandDispatcherException $exception) { 82 | $this->assertTrue(true, 'The exception has been thrown.'); 83 | } 84 | } 85 | 86 | public function testHandleWithCallable() 87 | { 88 | $this->commandDispatcher->handle(new TestCommandOne()); 89 | 90 | $this->assertTrue(TestCommandHandlerOne::$firstCommandCalled); 91 | $this->assertFalse($this->commandHandler->secondCommandCalled); 92 | } 93 | 94 | public function testHandleWithMethodName() 95 | { 96 | $this->commandDispatcher->handle(new TestCommandTwo()); 97 | 98 | $this->assertFalse(TestCommandHandlerOne::$firstCommandCalled); 99 | $this->assertTrue($this->commandHandler->secondCommandCalled); 100 | } 101 | 102 | /** 103 | * @expectedException \PhpDDD\DomainDrivenDesign\Exception\CommandDispatcherException 104 | */ 105 | public function testHandleUnknownCommand() 106 | { 107 | $commandDispatcher = new CommandDispatcher(); 108 | $commandDispatcher->handle(new TestCommandOne()); 109 | } 110 | 111 | /** 112 | */ 113 | public function testHandleSameCommandBySameHandler() 114 | { 115 | $commandDispatcher = new CommandDispatcher(); 116 | $commandDispatcher->register(new TestCommandHandlerDefiningSameCommandTwice()); 117 | 118 | try { 119 | $commandDispatcher->handle(new TestCommandOne()); 120 | $this->assertTrue(false, 'An exception of type CommandDispatcherException should have been thrown.'); 121 | } catch (CommandDispatcherException $exception) { 122 | $this->assertTrue(true, 'The exception has been thrown.'); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/Command/Sample/TestCommandHandlerDefiningSameCommandTwice.php: -------------------------------------------------------------------------------- 1 | function (TestCommandOne $command) { }, 20 | TestCommandInterface::class => function (TestCommandInterface $command) { }, 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Command/Sample/TestCommandHandlerOne.php: -------------------------------------------------------------------------------- 1 | 'myMethod', 32 | * MyCommandInterface::class => [self::class, 'myStaticMethod'], 33 | * ] 34 | * 35 | * @return callable[]|string[] 36 | */ 37 | public static function getHandlingMethods() 38 | { 39 | return [ 40 | TestCommandOne::class => [self::class, 'testFirstCommand'], 41 | TestCommandTwo::class => 'testSecondCommand', 42 | ]; 43 | } 44 | 45 | /** 46 | * 47 | */ 48 | public function __construct() 49 | { 50 | self::$firstCommandCalled = false; 51 | } 52 | 53 | /** 54 | * @param TestCommandOne $command 55 | * 56 | * @return TestCommandOne 57 | */ 58 | public static function testFirstCommand(TestCommandOne $command) 59 | { 60 | self::$firstCommandCalled = true; 61 | $aggregate = new TestAggregateRoot(); 62 | $aggregate->run(); 63 | 64 | $command->addAggregateRoot($aggregate); 65 | 66 | return $command; 67 | } 68 | 69 | /** 70 | * 71 | */ 72 | public function testSecondCommand() 73 | { 74 | $this->secondCommandCalled = true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Command/Sample/TestCommandHandlerTwo.php: -------------------------------------------------------------------------------- 1 | 'myMethod', 24 | * MyCommandInterface::class => [self::class, 'myStaticMethod'], 25 | * ] 26 | * 27 | * @return callable[]|string[] 28 | */ 29 | public static function getHandlingMethods() 30 | { 31 | return [ 32 | TestCommandOne::class => function (TestCommandOne $command) { self::$closureCalled = true; }, 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Command/Sample/TestCommandInterface.php: -------------------------------------------------------------------------------- 1 | pullEvents(); 19 | $this->assertCount(0, $events, 'Should not contain any event initially.'); 20 | 21 | $aggregate->run(); 22 | 23 | $events = $aggregate->pullEvents(); 24 | $this->assertCount(1, $events); 25 | $event = array_shift($events); 26 | $this->assertEquals(TestEvent::class, get_class($event)); 27 | $this->assertEmpty($aggregate->pullEvents()); 28 | } 29 | 30 | /** 31 | * @expectedException \PhpDDD\DomainDrivenDesign\Exception\BadMethodCallException 32 | */ 33 | public function testApplyWrongEvent() 34 | { 35 | $aggregate = new TestAggregateRoot(); 36 | $aggregate->testWithoutApplyMethod(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Domain/CommandEventDispatcherTest.php: -------------------------------------------------------------------------------- 1 | commandDispatcher = $this->getMockForCommandDispatcher(); 38 | $this->eventDispatcher = $this->getMockForEventDispatcher(); 39 | $this->commandEventDispatcher = new CommandEventDispatcher($this->commandDispatcher, $this->eventDispatcher); 40 | } 41 | 42 | protected function tearDown() 43 | { 44 | $this->commandDispatcher = null; 45 | $this->eventDispatcher = null; 46 | $this->commandEventDispatcher = null; 47 | } 48 | 49 | public function testHandle() 50 | { 51 | $commandDispatcher = new CommandDispatcher(); 52 | $eventDispatcher = $this->getMockForEventDispatcher(); 53 | $commandEventDispatcher = new CommandEventDispatcher($commandDispatcher, $eventDispatcher); 54 | $commandEventDispatcher->register(new TestCommandHandlerOne()); 55 | $commandEventDispatcher->subscribe(new TestEventSubscriber()); 56 | 57 | // the TestEvent is added to the aggregate 58 | $event = new TestEvent(); 59 | $eventDispatcher 60 | ->expects($this->once()) 61 | ->method('publish') 62 | ->willReturn($event); 63 | 64 | $command = new TestCommandOne(); 65 | $result = $commandEventDispatcher->handle($command); 66 | $this->assertEquals($command, $result); 67 | } 68 | 69 | public function testRegister() 70 | { 71 | $this->commandDispatcher->expects($this->once()) 72 | ->method('register') 73 | ->willReturn(null); 74 | 75 | $this->commandEventDispatcher->register(new TestCommandHandlerOne()); 76 | } 77 | 78 | public function testSubscribe() 79 | { 80 | $this->eventDispatcher->expects($this->once()) 81 | ->method('subscribe') 82 | ->willReturn(null); 83 | 84 | $this->commandEventDispatcher->subscribe(new TestEventSubscriber()); 85 | } 86 | 87 | public function testPublish() 88 | { 89 | $this->commandEventDispatcher->subscribe(new TestEventSubscriber()); 90 | $event = new TestEvent(); 91 | $this->eventDispatcher->expects($this->once()) 92 | ->method('publish') 93 | ->willReturn($event); 94 | 95 | $return = $this->commandEventDispatcher->publish($event); 96 | 97 | $this->assertEquals($event, $return); 98 | } 99 | 100 | /** 101 | * @return PHPUnit_Framework_MockObject_MockObject|CommandHandlerInterface 102 | */ 103 | private function getMockForCommandDispatcher() 104 | { 105 | return $this 106 | ->getMockBuilder(CommandDispatcherInterface::class) 107 | ->setMethods(['register', 'handle']) 108 | ->getMockForAbstractClass(); 109 | } 110 | 111 | /** 112 | * @return PHPUnit_Framework_MockObject_MockObject|EventDispatcherInterface 113 | */ 114 | private function getMockForEventDispatcher() 115 | { 116 | return $this 117 | ->getMockBuilder(EventDispatcherInterface::class) 118 | ->setMethods(['subscribe', 'publish']) 119 | ->getMockForAbstractClass(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /tests/Domain/Sample/TestAggregateRoot.php: -------------------------------------------------------------------------------- 1 | editable = true; 22 | } 23 | 24 | /** 25 | * This method is an entry point for your aggregate. It tries to alter your object. 26 | * 27 | * Inside, you should validate all the inputs, check that the aggregate current state allows this kind of behaviour 28 | * and if so, apply an Event. 29 | * If the aggregate current state does not allow such behaviour, you should probably throw an exception extending 30 | * the \DomainException. You will then be able to catch this kind of exception and display a nice message to the 31 | * user. 32 | */ 33 | public function run() 34 | { 35 | if (false === $this->editable) { 36 | throw new \DomainException('You can not edit the TestAggregateRoot.'); 37 | } 38 | // If we arrive here, we succeed. We can update the object 39 | // Rather than modify the Aggregate directly we apply an event. 40 | // Internally, this will call the applyTestEvent method 41 | $this->apply(new TestEvent()); 42 | } 43 | 44 | /** 45 | * This method will throw a BadMethodCallException because there is no applyTestOtherEvent method defined. 46 | */ 47 | public function testWithoutApplyMethod() 48 | { 49 | $this->apply(new TestOtherEvent()); 50 | } 51 | 52 | /** 53 | * An event occurs. Since it's an event, there is nothing we can do to prevent the aggregate to be modified. 54 | * We should never add some test nor throw any exception inside this method. 55 | * 56 | * @param TestEvent $event 57 | * 58 | * @return TestEvent 59 | */ 60 | protected function applyTestEvent(TestEvent $event) 61 | { 62 | $this->editable = false; 63 | 64 | return $event; 65 | } 66 | 67 | /** 68 | * Get the unique identifier of this aggregate root. 69 | * 70 | * @return mixed 71 | */ 72 | public function getId() 73 | { 74 | return mt_rand(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Event/AbstractEventTest.php: -------------------------------------------------------------------------------- 1 | getMockForAbstractEvent([]); 20 | $this->assertEquals(Author::robot(), $mock->author); 21 | } 22 | 23 | public function testConstructorWithAuthor() 24 | { 25 | $author = Author::robot(); 26 | $parameters = ['author' => $author]; 27 | 28 | $mock = $this->getMockForAbstractEvent($parameters); 29 | $this->assertEquals($author, $mock->author); 30 | } 31 | 32 | /** 33 | * @expectedException \InvalidArgumentException 34 | */ 35 | public function testConstructorWithUnknownProperty() 36 | { 37 | $this->getMockForAbstractEvent(['unknownProperty' => 'value']); 38 | } 39 | 40 | /** 41 | * @param $constructorArgs 42 | * 43 | * @return PHPUnit_Framework_MockObject_MockObject|AbstractEvent 44 | */ 45 | private function getMockForAbstractEvent($constructorArgs) 46 | { 47 | return $this->getMockBuilder(self::TESTED_CLASS) 48 | ->setConstructorArgs([$constructorArgs]) 49 | ->getMockForAbstractClass(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/Event/EventDispatcherTest.php: -------------------------------------------------------------------------------- 1 | dispatcher = new EventDispatcher(); 31 | $this->event = new TestEvent(); 32 | } 33 | 34 | /** 35 | * 36 | */ 37 | protected function tearDown() 38 | { 39 | $this->dispatcher = null; 40 | $this->event = null; 41 | } 42 | 43 | public function testInitialState() 44 | { 45 | $this->assertEquals([], $this->dispatcher->getListeners($this->event)); 46 | $this->assertEquals([], $this->dispatcher->getListeners($this->event, true)); 47 | } 48 | 49 | public function testAddSubscriber() 50 | { 51 | $this->dispatcher->subscribe(new TestEventSubscriber()); 52 | $this->assertCount(2, $this->dispatcher->getListeners($this->event)); 53 | $this->assertCount(2, $this->dispatcher->getListeners($this->event, true)); 54 | } 55 | 56 | public function testPublish() 57 | { 58 | $subscriber = new TestEventSubscriber(); 59 | $this->dispatcher->subscribe($subscriber); 60 | 61 | $this->dispatcher->publish($this->event); 62 | $this->assertEquals(2, $subscriber->nbPreFooCalled); 63 | } 64 | 65 | public function testAsynchronousPublish() 66 | { 67 | $subscriber = new TestEventSubscriber(); 68 | $this->dispatcher->subscribe($subscriber); 69 | 70 | $this->dispatcher->publish($this->event, true); 71 | $this->assertEquals(1, $subscriber->nbPreFooCalled); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Event/EventListenerTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('myMethod', $eventListener->getMethod()); 19 | $this->assertEquals(TestEvent::class, $eventListener->getEventClassName()); 20 | $this->assertFalse($eventListener->isAsynchronous()); 21 | } 22 | 23 | /** 24 | * @dataProvider wrongConstructorProvider 25 | * @expectedException \PhpDDD\DomainDrivenDesign\Exception\InvalidArgumentException 26 | * 27 | * @param mixed $param1 28 | * @param mixed $param2 29 | * @param mixed $param3 30 | */ 31 | public function testConstructorWithWrongArguments($param1, $param2, $param3) 32 | { 33 | new EventListener($param1, $param2, $param3); 34 | } 35 | 36 | public function testSetSubscriber() 37 | { 38 | $eventListener = new EventListener('preFoo', TestEvent::class); 39 | 40 | $this->assertTrue(is_string($eventListener->getMethod())); 41 | $eventListener->setSubscriber(new TestEventSubscriber()); 42 | $this->assertTrue(is_callable($eventListener->getMethod())); 43 | } 44 | 45 | /** 46 | * @return array 47 | */ 48 | public function wrongConstructorProvider() 49 | { 50 | return [ 51 | [5, 'all', true], 52 | ['callable', 5, true], 53 | ['callable', 'all', 'true'], 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Event/Sample/TestEvent.php: -------------------------------------------------------------------------------- 1 | nbPreFooCalled; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Event/Sample/TestOtherEvent.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(EventLog::class, $eventLog); 24 | $this->assertNull($eventLog->getId()); 25 | if (null === $event->aggregateRoot) { 26 | $this->assertNull($eventLog->getAggregateRootId()); 27 | $this->assertNull($eventLog->getAggregateRootType()); 28 | } else { 29 | $this->assertEquals($event->aggregateRoot->getId(), $eventLog->getAggregateRootId()); 30 | $this->assertEquals(get_class($event->aggregateRoot), $eventLog->getAggregateRootType()); 31 | } 32 | $this->assertEquals($event->author, $eventLog->getAuthor()); 33 | $this->assertEquals(get_class($event), $eventLog->getEventName()); 34 | $this->assertEquals($event->date, $eventLog->getOccurredAt()); 35 | $this->assertEquals(serialize($event), $eventLog->getMetadata()); 36 | } 37 | 38 | /** 39 | * @return array 40 | */ 41 | public function addEventEntryProvider() 42 | { 43 | return [ 44 | [new TestEvent()], 45 | [new TestEvent(['myProperty' => 'test',])], 46 | ]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/EventLog/EventSubscriber/LogAllEventsTest.php: -------------------------------------------------------------------------------- 1 | assertCount(1, $eventListeners, 'LogAllEvents should only use one callable method.'); 21 | /** 22 | * @var EventListener $eventListener 23 | */ 24 | $eventListener = array_shift($eventListeners); 25 | $this->assertInstanceOf(EventListener::class, $eventListener); 26 | $this->assertFalse($eventListener->isAsynchronous()); 27 | $this->assertNull($eventListener->getEventClassName()); 28 | } 29 | 30 | public function testLogEvent() 31 | { 32 | $repositoryStub = $this->getMockBuilder(EventLogRepositoryInterface::class) 33 | ->setMethods(['save']) 34 | ->getMockForAbstractClass(); 35 | 36 | $repositoryStub->expects($this->once()) 37 | ->method('save') 38 | ->willReturnArgument(0); 39 | 40 | $logAllEvents = new LogAllEvents($repositoryStub); 41 | $logAllEvents->logEvent(new TestEvent()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/User/AuthorTest.php: -------------------------------------------------------------------------------- 1 | getMock(AuthorInterface::class); 18 | $author = Author::fromObject($mock); 19 | 20 | $this->assertNull($author->identifier); 21 | $this->assertEquals(get_class($mock), $author->type); 22 | } 23 | 24 | public function testRobot() 25 | { 26 | $author = Author::robot(); 27 | $this->assertNull($author->identifier); 28 | $this->assertEquals(Robot::class, $author->type); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/User/RobotTest.php: -------------------------------------------------------------------------------- 1 | assertNull($robot->getId(), 'A robot should not have any identifier.'); 18 | } 19 | } 20 | --------------------------------------------------------------------------------