diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..797b003
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,70 @@
+name: Testing
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Validate composer.json and composer.lock
+ run: make lint-composer
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v3
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+
+ - name: Install dependencies
+ run: make composer
+
+ - name: Pack and upload the build
+ uses: actions/upload-artifact@v2
+ with:
+ name: website
+ path: .
+
+ test:
+ if: ${{ success() }}
+ needs: build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download site build
+ uses: actions/download-artifact@v2
+ with:
+ name: website
+ path: .
+
+ - name: Cache Composer packages
+ uses: actions/cache@v2
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+ - name: Lint PHP files
+ run: make lint-php
+
+ - name: Check PHP styles
+ run: make phpcs
+
+ - name: Static Analysis
+ run: make stan
+
+ - name: Unit Tests
+ run: make tests
+
diff --git a/.gitignore b/.gitignore
index c921004..41308e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,6 @@
vendor/*
coverage/*
+bin/*
+
+!bin/.gitkeep
+.phpunit.result.cache
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 1a9ef04..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: php
-php:
- - '7.0'
-install: make all
diff --git a/Makefile b/Makefile
index 97448df..aac01e5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,32 +1,72 @@
COMPOSER ?= composer
-PROJECT = "Tic-Tac-Toe"
+DOCKER_COMPOSE = docker-compose
+PROJECT = "TicTacToe."
+COMPOSE_PROJECT_NAME ?= $(notdir $(shell pwd))
+PHP_SERVICE = php
+PHP_CMD = php
-all: clear lint-composer lint-php composer phpcs test coverage
+ifeq ($(RUNNER), travis)
+ CMD :=
+else
+ CMD := docker-compose exec $(PHP_SERVICE)
+endif
+
+all: container-up clear composer lint-composer lint-php phpcs tests
lint-composer:
@echo "\n==> Validating composer.json and composer.lock:"
- $(COMPOSER) validate --strict
+ $(CMD) $(COMPOSER) validate --strict
lint-php:
@echo "\n==> Validating all php files:"
- @find src -type f -name \*.php | while read file; do php -l "$$file" || exit 1; done
+ $(CMD) find src tests -type f -iname '*php' -exec $(PHP_CMD) -l {} \;
composer:
- $(COMPOSER) install
+ @echo "\n==> Running composer install, runner $(RUNNER)"
+ $(CMD) $(COMPOSER) install
+
+lint: lint-composer lint-php
clear:
- rm -rf vendor
+ $(CMD) rm -rf vendor
+ $(CMD) rm -rf bin/php*
phpcs:
- php vendor/bin/phpcs . -np
+ @echo "\n==> Checking style guidelines"
+ $(CMD) bin/phpcs --standard=phpcs.xml -p
phpcbf:
- vendor/bin/phpcbf src/*
-
-test:
- vendor/bin/phpunit
+ $(CMD) bin/phpcbf
coverage:
- vendor/bin/phpunit --coverage-html coverage
+ @echo "\n==> Generating coverage report"
+ $(CMD) bin/phpunit --coverage-html coverage
+
+tests:
+ @echo "\n==> Running tests"
+ $(CMD) bin/phpunit
+
+stan:
+ @echo "\n==> Running stan for analysis"
+ $(CMD) bin/phpstan analyse --memory-limit=-1 src
+
+container-stop:
+ @echo "\n==> Stopping docker container"
+ $(DOCKER_COMPOSE) stop
+
+container-down:
+ @echo "\n==> Removing docker container"
+ $(DOCKER_COMPOSE) down
+
+container-remove:
+ @echo "\n==> Removing docker container(s)"
+ $(DOCKER_COMPOSE) rm
+
+container-up:
+ @echo "\n==> Docker container building and starting ..."
+ $(DOCKER_COMPOSE) up --build -d
+
+tear-down: clear container-stop container-down container-remove
+
-.PHONY: lint-php lint-composer phpcs phpcbf test coverage composer clear
+.PHONY: lint-php lint-composer phpcs phpcbf composer clear tests coverage container-up container-stop container-down container-remove
diff --git a/README.md b/README.md
index 0a82a35..b9805b2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-# tic-tac-toe-sami
-
-[![Build Status](https://travis-ci.org/mnsami/tic-tac-toe.svg?branch=master)](https://travis-ci.org/mnsami/tic-tac-toe)
+# tic-tac-toe
### Description
@@ -8,7 +6,8 @@ A php-cli Tic-Tac-Toe simple two players game.
### Requirements
-1. `php >= 7.0` (required for `phpunit v6.1`)
+1. `php >= 8`
+2. `Docker`
### Installation
@@ -18,11 +17,11 @@ Run `make all` will do all the magic for you.
### Start playing
-To start playing the game, you need to run `php main.php`
+TODO, refactoring has to be finished first.
### Unit tests
-To generate tests report, run `make test`.
+To generate tests report, run `make tests`.
### Coverage
diff --git a/bin/.gitkeep b/bin/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/composer.json b/composer.json
index 203e710..c2e7fb4 100644
--- a/composer.json
+++ b/composer.json
@@ -5,19 +5,34 @@
"type": "project",
"authors": [
{
- "name": "Mina Nabil Sami"
+ "name": "Mina Nabil Sami",
+ "email": "mina.nsami@gmail.com"
}
],
"require": {
- "php": ">=7",
- "squizlabs/php_codesniffer": "2.*"
- },
- "require-dev": {
- "phpunit/phpunit": "6.1.*"
+ "php": ">=8",
+ "ramsey/uuid": "^3.8"
},
"autoload": {
"psr-0": {
- "TicTacToe": "src/"
+ "TicTacToe": "./src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "unit\\TicTacToe\\": "./tests/unit"
}
+ },
+ "config": {
+ "bin-dir": "bin",
+ "platform": {
+ "php": "8"
+ },
+ "sort-packages": true
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8",
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "^3.7"
}
}
diff --git a/composer.lock b/composer.lock
index e3f446e..88e0397 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,121 +1,278 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "bb919364e2ff349c52686ba89992197f",
+ "content-hash": "d14ecad59044ee066b708801ee5e9c2c",
"packages": [
{
- "name": "squizlabs/php_codesniffer",
- "version": "2.8.1",
+ "name": "paragonie/random_compat",
+ "version": "v9.99.100",
"source": {
"type": "git",
- "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d"
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d",
- "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
- "ext-simplexml": "*",
- "ext-tokenizer": "*",
- "ext-xmlwriter": "*",
- "php": ">=5.1.2"
+ "php": ">= 7"
},
"require-dev": {
- "phpunit/phpunit": "~4.0"
+ "phpunit/phpunit": "4.*|5.*",
+ "vimeo/psalm": "^1"
},
- "bin": [
- "scripts/phpcs",
- "scripts/phpcbf"
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "polyfill",
+ "pseudorandom",
+ "random"
+ ],
+ "support": {
+ "email": "info@paragonie.com",
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "source": "https://github.com/paragonie/random_compat"
+ },
+ "time": "2020-10-15T08:29:30+00:00"
+ },
+ {
+ "name": "ramsey/uuid",
+ "version": "3.9.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ramsey/uuid.git",
+ "reference": "ffa80ab953edd85d5b6c004f96181a538aad35a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/ffa80ab953edd85d5b6c004f96181a538aad35a3",
+ "reference": "ffa80ab953edd85d5b6c004f96181a538aad35a3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "paragonie/random_compat": "^1 | ^2 | ^9.99.99",
+ "php": "^5.4 | ^7.0 | ^8.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "replace": {
+ "rhumsaa/uuid": "self.version"
+ },
+ "require-dev": {
+ "codeception/aspect-mock": "^1 | ^2",
+ "doctrine/annotations": "^1.2",
+ "goaop/framework": "1.0.0-alpha.2 | ^1 | >=2.1.0 <=2.3.2",
+ "mockery/mockery": "^0.9.11 | ^1",
+ "moontoast/math": "^1.1",
+ "nikic/php-parser": "<=4.5.0",
+ "paragonie/random-lib": "^2",
+ "php-mock/php-mock-phpunit": "^0.3 | ^1.1 | ^2.6",
+ "php-parallel-lint/php-parallel-lint": "^1.3",
+ "phpunit/phpunit": ">=4.8.36 <9.0.0 | >=9.3.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "suggest": {
+ "ext-ctype": "Provides support for PHP Ctype functions",
+ "ext-libsodium": "Provides the PECL libsodium extension for use with the SodiumRandomGenerator",
+ "ext-openssl": "Provides the OpenSSL extension for use with the OpenSslGenerator",
+ "ext-uuid": "Provides the PECL UUID extension for use with the PeclUuidTimeGenerator and PeclUuidRandomGenerator",
+ "moontoast/math": "Provides support for converting UUID to 128-bit integer (in string form).",
+ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
+ "ramsey/uuid-console": "A console application for generating UUIDs with ramsey/uuid",
+ "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
+ },
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.x-dev"
+ "dev-master": "3.x-dev"
}
},
"autoload": {
- "classmap": [
- "CodeSniffer.php",
- "CodeSniffer/CLI.php",
- "CodeSniffer/Exception.php",
- "CodeSniffer/File.php",
- "CodeSniffer/Fixer.php",
- "CodeSniffer/Report.php",
- "CodeSniffer/Reporting.php",
- "CodeSniffer/Sniff.php",
- "CodeSniffer/Tokens.php",
- "CodeSniffer/Reports/",
- "CodeSniffer/Tokenizers/",
- "CodeSniffer/DocGenerators/",
- "CodeSniffer/Standards/AbstractPatternSniff.php",
- "CodeSniffer/Standards/AbstractScopeSniff.php",
- "CodeSniffer/Standards/AbstractVariableSniff.php",
- "CodeSniffer/Standards/IncorrectPatternException.php",
- "CodeSniffer/Standards/Generic/Sniffs/",
- "CodeSniffer/Standards/MySource/Sniffs/",
- "CodeSniffer/Standards/PEAR/Sniffs/",
- "CodeSniffer/Standards/PSR1/Sniffs/",
- "CodeSniffer/Standards/PSR2/Sniffs/",
- "CodeSniffer/Standards/Squiz/Sniffs/",
- "CodeSniffer/Standards/Zend/Sniffs/"
- ]
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
+ "MIT"
],
"authors": [
{
- "name": "Greg Sherwood",
- "role": "lead"
+ "name": "Ben Ramsey",
+ "email": "ben@benramsey.com",
+ "homepage": "https://benramsey.com"
+ },
+ {
+ "name": "Marijn Huizendveld",
+ "email": "marijn.huizendveld@gmail.com"
+ },
+ {
+ "name": "Thibaud Fabre",
+ "email": "thibaud@aztech.io"
}
],
- "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
- "homepage": "http://www.squizlabs.com/php-codesniffer",
+ "description": "Formerly rhumsaa/uuid. A PHP 5.4+ library for generating RFC 4122 version 1, 3, 4, and 5 universally unique identifiers (UUID).",
+ "homepage": "https://github.com/ramsey/uuid",
"keywords": [
- "phpcs",
- "standards"
+ "guid",
+ "identifier",
+ "uuid"
],
- "time": "2017-03-01T22:17:45+00:00"
+ "support": {
+ "issues": "https://github.com/ramsey/uuid/issues",
+ "rss": "https://github.com/ramsey/uuid/releases.atom",
+ "source": "https://github.com/ramsey/uuid",
+ "wiki": "https://github.com/ramsey/uuid/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ramsey",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-09-25T23:07:42+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.26.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
+ "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.26-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-24T11:49:31+00:00"
}
],
"packages-dev": [
{
"name": "doctrine/instantiator",
- "version": "1.0.5",
+ "version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
- "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
- "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
"shasum": ""
},
"require": {
- "php": ">=5.3,<8.0-DEV"
+ "php": "^7.1 || ^8.0"
},
"require-dev": {
- "athletic/athletic": "~0.1.8",
+ "doctrine/coding-standard": "^9",
"ext-pdo": "*",
"ext-phar": "*",
- "phpunit/phpunit": "~4.0",
- "squizlabs/php_codesniffer": "~2.0"
+ "phpbench/phpbench": "^0.16 || ^1",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "vimeo/psalm": "^4.22"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
@@ -129,40 +286,66 @@
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
- "homepage": "http://ocramius.github.com/"
+ "homepage": "https://ocramius.github.io/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
- "homepage": "https://github.com/doctrine/instantiator",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
"keywords": [
"constructor",
"instantiate"
],
- "time": "2015-06-14T21:17:01+00:00"
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-03T08:28:38+00:00"
},
{
"name": "myclabs/deep-copy",
- "version": "1.6.1",
+ "version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102"
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102",
- "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
"shasum": ""
},
"require": {
- "php": ">=5.4.0"
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3,<3.2.2"
},
"require-dev": {
- "doctrine/collections": "1.*",
- "phpunit/phpunit": "~4.1"
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
},
"type": "library",
"autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
}
@@ -172,7 +355,6 @@
"MIT"
],
"description": "Create deep copies (clones) of your objects",
- "homepage": "https://github.com/myclabs/DeepCopy",
"keywords": [
"clone",
"copy",
@@ -180,32 +362,99 @@
"object",
"object graph"
],
- "time": "2017-04-12T18:52:22+00:00"
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-03-03T13:19:32+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.15.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1"
+ },
+ "time": "2022-09-04T07:30:47+00:00"
},
{
"name": "phar-io/manifest",
- "version": "1.0.1",
+ "version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
- "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
- "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-phar": "*",
- "phar-io/version": "^1.0.1",
- "php": "^5.6 || ^7.0"
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "2.0.x-dev"
}
},
"autoload": {
@@ -235,24 +484,28 @@
}
],
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
- "time": "2017-03-05T18:14:27+00:00"
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+ },
+ "time": "2021-07-20T11:28:43+00:00"
},
{
"name": "phar-io/version",
- "version": "1.0.1",
+ "version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/phar-io/version.git",
- "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
- "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": "^7.2 || ^8.0"
},
"type": "library",
"autoload": {
@@ -282,253 +535,236 @@
}
],
"description": "Library for handling version information and constraints",
- "time": "2017-03-05T17:38:23+00:00"
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
},
{
- "name": "phpdocumentor/reflection-common",
- "version": "1.0",
+ "name": "phpstan/phpstan",
+ "version": "1.8.6",
"source": {
"type": "git",
- "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
- "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
- "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c386ab2741e64cc9e21729f891b28b2b10fe6618",
+ "reference": "c386ab2741e64cc9e21729f891b28b2b10fe6618",
"shasum": ""
},
"require": {
- "php": ">=5.5"
+ "php": "^7.2|^8.0"
},
- "require-dev": {
- "phpunit/phpunit": "^4.6"
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
},
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
"autoload": {
- "psr-4": {
- "phpDocumentor\\Reflection\\": [
- "src"
- ]
- }
+ "files": [
+ "bootstrap.php"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "authors": [
- {
- "name": "Jaap van Otterdijk",
- "email": "opensource@ijaap.nl"
- }
- ],
- "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
- "homepage": "http://www.phpdoc.org",
+ "description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
- "FQSEN",
- "phpDocumentor",
- "phpdoc",
- "reflection",
+ "dev",
"static analysis"
],
- "time": "2015-12-27T11:43:31+00:00"
- },
- {
- "name": "phpdocumentor/reflection-docblock",
- "version": "3.1.1",
- "source": {
- "type": "git",
- "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e",
- "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e",
- "shasum": ""
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "source": "https://github.com/phpstan/phpstan/tree/1.8.6"
},
- "require": {
- "php": ">=5.5",
- "phpdocumentor/reflection-common": "^1.0@dev",
- "phpdocumentor/type-resolver": "^0.2.0",
- "webmozart/assert": "^1.0"
- },
- "require-dev": {
- "mockery/mockery": "^0.9.4",
- "phpunit/phpunit": "^4.4"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "phpDocumentor\\Reflection\\": [
- "src/"
- ]
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ },
{
- "name": "Mike van Riel",
- "email": "me@mikevanriel.com"
+ "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
+ "type": "tidelift"
}
],
- "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
- "time": "2016-09-30T07:12:33+00:00"
+ "time": "2022-09-23T09:54:39+00:00"
},
{
- "name": "phpdocumentor/type-resolver",
- "version": "0.2.1",
+ "name": "phpunit/php-code-coverage",
+ "version": "9.2.17",
"source": {
"type": "git",
- "url": "https://github.com/phpDocumentor/TypeResolver.git",
- "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb"
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
- "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8",
+ "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8",
"shasum": ""
},
"require": {
- "php": ">=5.5",
- "phpdocumentor/reflection-common": "^1.0"
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.14",
+ "php": ">=7.3",
+ "phpunit/php-file-iterator": "^3.0.3",
+ "phpunit/php-text-template": "^2.0.2",
+ "sebastian/code-unit-reverse-lookup": "^2.0.2",
+ "sebastian/complexity": "^2.0",
+ "sebastian/environment": "^5.1.2",
+ "sebastian/lines-of-code": "^1.0.3",
+ "sebastian/version": "^3.0.1",
+ "theseer/tokenizer": "^1.2.0"
},
"require-dev": {
- "mockery/mockery": "^0.9.4",
- "phpunit/phpunit": "^5.2||^4.8.24"
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcov": "*",
+ "ext-xdebug": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "9.2-dev"
}
},
"autoload": {
- "psr-4": {
- "phpDocumentor\\Reflection\\": [
- "src/"
- ]
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Mike van Riel",
- "email": "me@mikevanriel.com"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "time": "2016-11-25T06:54:22+00:00"
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-08-30T12:24:04+00:00"
},
{
- "name": "phpspec/prophecy",
- "version": "v1.7.0",
+ "name": "phpunit/php-file-iterator",
+ "version": "3.0.6",
"source": {
"type": "git",
- "url": "https://github.com/phpspec/prophecy.git",
- "reference": "93d39f1f7f9326d746203c7c056f300f7f126073"
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073",
- "reference": "93d39f1f7f9326d746203c7c056f300f7f126073",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
- "doctrine/instantiator": "^1.0.2",
- "php": "^5.3|^7.0",
- "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
- "sebastian/comparator": "^1.1|^2.0",
- "sebastian/recursion-context": "^1.0|^2.0|^3.0"
+ "php": ">=7.3"
},
"require-dev": {
- "phpspec/phpspec": "^2.5|^3.2",
- "phpunit/phpunit": "^4.8 || ^5.6.5"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.6.x-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Prophecy\\": "src/"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Konstantin Kudryashov",
- "email": "ever.zet@gmail.com",
- "homepage": "http://everzet.com"
- },
- {
- "name": "Marcello Duarte",
- "email": "marcello.duarte@gmail.com"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Highly opinionated mocking framework for PHP 5.3+",
- "homepage": "https://github.com/phpspec/prophecy",
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
- "Double",
- "Dummy",
- "fake",
- "mock",
- "spy",
- "stub"
- ],
- "time": "2017-03-02T20:05:34+00:00"
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-02T12:48:52+00:00"
},
{
- "name": "phpunit/php-code-coverage",
- "version": "5.1.1",
+ "name": "phpunit/php-invoker",
+ "version": "3.1.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "bc433b7af27e0ab9b6b4c6d8ec918a493875f6bc"
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bc433b7af27e0ab9b6b4c6d8ec918a493875f6bc",
- "reference": "bc433b7af27e0ab9b6b4c6d8ec918a493875f6bc",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-xmlwriter": "*",
- "php": "^7.0",
- "phpunit/php-file-iterator": "^1.3",
- "phpunit/php-text-template": "^1.2",
- "phpunit/php-token-stream": "^1.4.11 || ^2.0",
- "sebastian/code-unit-reverse-lookup": "^1.0",
- "sebastian/environment": "^2.0",
- "sebastian/version": "^2.0"
+ "php": ">=7.3"
},
"require-dev": {
- "ext-xdebug": "^2.5",
- "phpunit/phpunit": "^6.0"
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^9.3"
},
"suggest": {
- "ext-xdebug": "^2.5.1"
+ "ext-pcntl": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "5.1.x-dev"
+ "dev-master": "3.1-dev"
}
},
"autoload": {
@@ -543,40 +779,51 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
- "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
- "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
"keywords": [
- "coverage",
- "testing",
- "xunit"
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2017-04-12T07:59:32+00:00"
+ "time": "2020-09-28T05:58:55+00:00"
},
{
- "name": "phpunit/php-file-iterator",
- "version": "1.4.2",
+ "name": "phpunit/php-text-template",
+ "version": "2.0.4",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4.x-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -591,36 +838,53 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
- "description": "FilterIterator implementation that filters files based on a list of suffixes.",
- "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
- "filesystem",
- "iterator"
+ "template"
],
- "time": "2016-10-03T07:40:28+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T05:33:50+00:00"
},
{
- "name": "phpunit/php-text-template",
- "version": "1.2.1",
+ "name": "phpunit/php-timer",
+ "version": "5.0.3",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
- "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
"autoload": {
"classmap": [
"src/"
@@ -637,40 +901,83 @@
"role": "lead"
}
],
- "description": "Simple template engine.",
- "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
- "template"
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2015-06-21T13:50:34+00:00"
+ "time": "2020-10-26T13:16:10+00:00"
},
{
- "name": "phpunit/php-timer",
- "version": "1.0.9",
+ "name": "phpunit/phpunit",
+ "version": "9.5.25",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
- "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
+ "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "doctrine/instantiator": "^1.3.1",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.10.1",
+ "phar-io/manifest": "^2.0.3",
+ "phar-io/version": "^3.0.2",
+ "php": ">=7.3",
+ "phpunit/php-code-coverage": "^9.2.13",
+ "phpunit/php-file-iterator": "^3.0.5",
+ "phpunit/php-invoker": "^3.1.1",
+ "phpunit/php-text-template": "^2.0.3",
+ "phpunit/php-timer": "^5.0.2",
+ "sebastian/cli-parser": "^1.0.1",
+ "sebastian/code-unit": "^1.0.6",
+ "sebastian/comparator": "^4.0.8",
+ "sebastian/diff": "^4.0.3",
+ "sebastian/environment": "^5.1.3",
+ "sebastian/exporter": "^4.0.5",
+ "sebastian/global-state": "^5.0.1",
+ "sebastian/object-enumerator": "^4.0.3",
+ "sebastian/resource-operations": "^3.0.3",
+ "sebastian/type": "^3.2",
+ "sebastian/version": "^3.0.2"
},
- "require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+ "suggest": {
+ "ext-soap": "*",
+ "ext-xdebug": "*"
},
+ "bin": [
+ "phpunit"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0-dev"
+ "dev-master": "9.5-dev"
}
},
"autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
"classmap": [
"src/"
]
@@ -682,42 +989,61 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
- "description": "Utility class for timing",
- "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
"keywords": [
- "timer"
+ "phpunit",
+ "testing",
+ "xunit"
],
- "time": "2017-02-26T11:10:40+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.25"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-09-25T03:44:45+00:00"
},
{
- "name": "phpunit/php-token-stream",
- "version": "1.4.11",
+ "name": "sebastian/cli-parser",
+ "version": "1.0.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7"
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7",
- "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
"shasum": ""
},
"require": {
- "ext-tokenizer": "*",
- "php": ">=5.3.3"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "~4.2"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -732,73 +1058,48 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Wrapper around PHP's tokenizer extension.",
- "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
- "keywords": [
- "tokenizer"
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2017-02-27T10:12:30+00:00"
+ "time": "2020-09-28T06:08:49+00:00"
},
{
- "name": "phpunit/phpunit",
- "version": "6.1.0",
+ "name": "sebastian/code-unit",
+ "version": "1.0.8",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "2aa57c530381662b01c2cf705b03e8c12e918f1d"
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2aa57c530381662b01c2cf705b03e8c12e918f1d",
- "reference": "2aa57c530381662b01c2cf705b03e8c12e918f1d",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-json": "*",
- "ext-libxml": "*",
- "ext-mbstring": "*",
- "ext-xml": "*",
- "myclabs/deep-copy": "^1.3",
- "phar-io/manifest": "^1.0.1",
- "phar-io/version": "^1.0",
- "php": "^7.0",
- "phpspec/prophecy": "^1.7",
- "phpunit/php-code-coverage": "^5.0",
- "phpunit/php-file-iterator": "^1.4",
- "phpunit/php-text-template": "^1.2",
- "phpunit/php-timer": "^1.0.6",
- "phpunit/phpunit-mock-objects": "^4.0",
- "sebastian/comparator": "^2.0",
- "sebastian/diff": "^1.2",
- "sebastian/environment": "^2.0",
- "sebastian/exporter": "^3.1",
- "sebastian/global-state": "^1.1 || ^2.0",
- "sebastian/object-enumerator": "^3.0.2",
- "sebastian/resource-operations": "^1.0",
- "sebastian/version": "^2.0"
- },
- "conflict": {
- "phpdocumentor/reflection-docblock": "3.0.2",
- "phpunit/dbunit": "<3.0"
+ "php": ">=7.3"
},
"require-dev": {
- "ext-pdo": "*"
+ "phpunit/phpunit": "^9.3"
},
- "suggest": {
- "ext-xdebug": "*",
- "phpunit/php-invoker": "^1.1"
- },
- "bin": [
- "phpunit"
- ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "6.1.x-dev"
+ "dev-master": "1.0-dev"
}
},
"autoload": {
@@ -817,48 +1118,44 @@
"role": "lead"
}
],
- "description": "The PHP Unit Testing framework.",
- "homepage": "https://phpunit.de/",
- "keywords": [
- "phpunit",
- "testing",
- "xunit"
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2017-04-07T04:45:38+00:00"
+ "time": "2020-10-26T13:08:54+00:00"
},
{
- "name": "phpunit/phpunit-mock-objects",
- "version": "4.0.1",
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "2.0.3",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
- "reference": "eabce450df194817a7d7e27e19013569a903a2bf"
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/eabce450df194817a7d7e27e19013569a903a2bf",
- "reference": "eabce450df194817a7d7e27e19013569a903a2bf",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
"shasum": ""
},
"require": {
- "doctrine/instantiator": "^1.0.2",
- "php": "^7.0",
- "phpunit/php-text-template": "^1.2",
- "sebastian/exporter": "^3.0"
- },
- "conflict": {
- "phpunit/phpunit": "<6.0"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
- },
- "suggest": {
- "ext-soap": "*"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.0.x-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -873,42 +1170,49 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
}
],
- "description": "Mock Object library for PHPUnit",
- "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
- "keywords": [
- "mock",
- "xunit"
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2017-03-03T06:30:20+00:00"
+ "time": "2020-09-28T05:30:19+00:00"
},
{
- "name": "sebastian/code-unit-reverse-lookup",
- "version": "1.0.1",
+ "name": "sebastian/comparator",
+ "version": "4.0.8",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
- "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
- "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
+ "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": ">=7.3",
+ "sebastian/diff": "^4.0",
+ "sebastian/exporter": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "^5.7 || ^6.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -920,42 +1224,68 @@
"license": [
"BSD-3-Clause"
],
- "authors": [
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
+ },
+ "funding": [
{
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Looks up which function or method a line of code belongs to",
- "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
- "time": "2017-03-04T06:30:41+00:00"
+ "time": "2022-09-14T12:41:17+00:00"
},
{
- "name": "sebastian/comparator",
- "version": "2.0.0",
+ "name": "sebastian/complexity",
+ "version": "2.0.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "20f84f468cb67efee293246e6a09619b891f55f0"
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/20f84f468cb67efee293246e6a09619b891f55f0",
- "reference": "20f84f468cb67efee293246e6a09619b891f55f0",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
"shasum": ""
},
"require": {
- "php": "^7.0",
- "sebastian/diff": "^1.2",
- "sebastian/exporter": "^3.0"
+ "nikic/php-parser": "^4.7",
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -968,56 +1298,51 @@
"BSD-3-Clause"
],
"authors": [
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
- },
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
- },
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Provides the functionality to compare PHP values for equality",
- "homepage": "http://www.github.com/sebastianbergmann/comparator",
- "keywords": [
- "comparator",
- "compare",
- "equality"
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2017-03-03T06:26:08+00:00"
+ "time": "2020-10-26T15:52:27+00:00"
},
{
"name": "sebastian/diff",
- "version": "1.4.1",
+ "version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
- "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "~4.8"
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -1030,46 +1355,62 @@
"BSD-3-Clause"
],
"authors": [
- {
- "name": "Kore Nordmann",
- "email": "mail@kore-nordmann.de"
- },
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
}
],
"description": "Diff implementation",
"homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
- "diff"
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2015-12-08T07:14:41+00:00"
+ "time": "2020-10-26T13:10:38+00:00"
},
{
"name": "sebastian/environment",
- "version": "2.0.0",
+ "version": "5.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac"
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
- "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
+ "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^5.0"
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "5.1-dev"
}
},
"autoload": {
@@ -1094,34 +1435,44 @@
"environment",
"hhvm"
],
- "time": "2016-11-26T07:53:53+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-04-03T09:37:03+00:00"
},
{
"name": "sebastian/exporter",
- "version": "3.1.0",
+ "version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
+ "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
- "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
+ "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
"shasum": ""
},
"require": {
- "php": "^7.0",
- "sebastian/recursion-context": "^3.0"
+ "php": ">=7.3",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
"ext-mbstring": "*",
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.1.x-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -1134,6 +1485,10 @@
"BSD-3-Clause"
],
"authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
@@ -1142,46 +1497,55 @@
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
- },
- {
- "name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de"
- },
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
- "time": "2017-04-03T13:19:02+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-09-14T06:03:37+00:00"
},
{
"name": "sebastian/global-state",
- "version": "1.1.1",
+ "version": "5.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
- "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.2"
+ "ext-dom": "*",
+ "phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-uopz": "*"
@@ -1189,7 +1553,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0-dev"
+ "dev-master": "5.0-dev"
}
},
"autoload": {
@@ -1212,34 +1576,101 @@
"keywords": [
"global state"
],
- "time": "2015-10-12T03:26:01+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-02-14T08:28:10+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.6",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-28T06:42:11+00:00"
},
{
"name": "sebastian/object-enumerator",
- "version": "3.0.2",
+ "version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
- "reference": "31dd3379d16446c5d86dec32ab1ad1f378581ad8"
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/31dd3379d16446c5d86dec32ab1ad1f378581ad8",
- "reference": "31dd3379d16446c5d86dec32ab1ad1f378581ad8",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
"shasum": ""
},
"require": {
- "php": "^7.0",
- "sebastian/object-reflector": "^1.0",
- "sebastian/recursion-context": "^3.0"
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.0.x-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -1259,32 +1690,42 @@
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
- "time": "2017-03-12T15:17:29+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:12:34+00:00"
},
{
"name": "sebastian/object-reflector",
- "version": "1.1.1",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
- "reference": "773f97c67f28de00d397be301821b06708fca0be"
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
- "reference": "773f97c67f28de00d397be301821b06708fca0be",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -1304,32 +1745,42 @@
],
"description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
- "time": "2017-03-29T09:07:27+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:14:26+00:00"
},
{
"name": "sebastian/recursion-context",
- "version": "3.0.0",
+ "version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
- "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": ">=7.3"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.0.x-dev"
+ "dev-master": "4.0-dev"
}
},
"autoload": {
@@ -1342,14 +1793,14 @@
"BSD-3-Clause"
],
"authors": [
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
@@ -1357,29 +1808,42 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
- "time": "2017-03-03T06:23:57+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:17:30+00:00"
},
{
"name": "sebastian/resource-operations",
- "version": "1.0.0",
+ "version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
- "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
- "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
"shasum": ""
},
"require": {
- "php": ">=5.6.0"
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -1399,29 +1863,95 @@
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
- "time": "2015-07-28T20:34:47+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:45:17+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
+ "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2022-09-12T14:47:03+00:00"
},
{
"name": "sebastian/version",
- "version": "2.0.1",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
- "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
- "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
"shasum": ""
},
"require": {
- "php": ">=5.6"
+ "php": ">=7.3"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -1442,57 +1972,123 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
- "time": "2016-10-03T07:35:21+00:00"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:39:44+00:00"
},
{
- "name": "webmozart/assert",
- "version": "1.2.0",
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.7.1",
"source": {
"type": "git",
- "url": "https://github.com/webmozart/assert.git",
- "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f"
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f",
- "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619",
+ "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.6",
- "sebastian/version": "^1.0.1"
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.3-dev"
+ "dev-master": "3.x-dev"
}
},
- "autoload": {
- "psr-4": {
- "Webmozart\\Assert\\": "src/"
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
}
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "support": {
+ "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
+ "source": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
+ },
+ "time": "2022-06-18T07:21:10+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Bernhard Schussek",
- "email": "bschussek@gmail.com"
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
}
],
- "description": "Assertions to validate method input/output with nice error messages.",
- "keywords": [
- "assert",
- "check",
- "validate"
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
],
- "time": "2016-11-23T20:04:58+00:00"
+ "time": "2021-07-28T10:34:58+00:00"
}
],
"aliases": [],
@@ -1501,7 +2097,11 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=7"
+ "php": ">=8"
+ },
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "8"
},
- "platform-dev": []
+ "plugin-api-version": "2.3.0"
}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..0719bd6
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3.7'
+
+services:
+ php:
+ build: docker/php-cli
+ tty: true
+ working_dir: /code
+ restart: on-failure
+ volumes:
+ - ./:/code
+ - ./docker/php-cli/php.ini:/usr/local/etc/php/php.ini:ro
diff --git a/docker/php-cli/Dockerfile b/docker/php-cli/Dockerfile
new file mode 100644
index 0000000..f603d61
--- /dev/null
+++ b/docker/php-cli/Dockerfile
@@ -0,0 +1,25 @@
+FROM php:8-cli-alpine
+
+RUN apk update \
+ && apk add git \
+ curl \
+ zip \
+ unzip \
+ vim
+
+RUN apk add --no-cache $PHPIZE_DEPS \
+ && pecl install -f xdebug \
+ && docker-php-ext-enable xdebug \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install Composer
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
+ && composer --version
+
+# Set timezone
+ENV TIMEZONE=Etc/UCT
+RUN ln -snf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime && echo ${TIMEZONE} > /etc/timezone \
+ && printf '[PHP]\ndate.timezone = "%s"\n', ${TIMEZONE} > /usr/local/etc/php/conf.d/tzone.ini \
+ && "date"
+
+WORKDIR /code
diff --git a/docker/php-cli/php.ini b/docker/php-cli/php.ini
new file mode 100644
index 0000000..4cc5da2
--- /dev/null
+++ b/docker/php-cli/php.ini
@@ -0,0 +1,12 @@
+date.timezone = ${TIMEZONE}
+short_open_tag = Off
+log_errors = On
+error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
+display_errors = Off
+error_log = /proc/self/fd/2
+memory_limit = -1
+
+; Optimizations for Symfony, as documented on http://symfony.com/doc/current/performance.html
+opcache.max_accelerated_files = 20000
+realpath_cache_size = 4096K
+realpath_cache_ttl = 600
diff --git a/main.php b/main.php
index cee99de..a6cb138 100644
--- a/main.php
+++ b/main.php
@@ -3,7 +3,6 @@
require "./vendor/autoload.php";
use TicTacToe\Application;
-use TicTacToe\IO\IOHandler;
-$game = new Application(new IOHandler());
+$game = new Application();
$game->run();
diff --git a/phpcs.xml b/phpcs.xml
index dbf467b..288bd4c 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -5,6 +5,7 @@
+ tests/*
*/Test/*
*/vendor/*
*/coverage/*
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..c2b7eeb
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,6 @@
+parameters:
+ level: 7
+ excludePaths:
+ # only because of the refactoring
+ - src/TicTacToe/Application.php
+ - src/TicTacToe/Board.php
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index e15153a..868e457 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,19 +1,16 @@
-
-
-
- ./src/TicTacToe/Test/
-
-
-
-
- ./src/TicTacToe/
-
- ./src/TicTacToe/Test/
-
-
-
- ./vendor/
-
-
+
+
+
+ ./src/TicTacToe/
+
+
+ ./vendor/
+
+
+
+
+ ./tests/unit
+
+
diff --git a/src/TicTacToe/Application.php b/src/TicTacToe/Application.php
index c79859e..a7aeebc 100644
--- a/src/TicTacToe/Application.php
+++ b/src/TicTacToe/Application.php
@@ -2,68 +2,34 @@
namespace TicTacToe;
-use TicTacToe\Board;
-use TicTacToe\IO\IOHandler;
-use TicTacToe\Player\HumanPlayer;
+use TicTacToe\Engine\Infrastructure\CommandBus\GameCommandHandlerFactory;
+use TicTacToe\Engine\Infrastructure\Persistence\InMemory\InMemoryEventStore;
+use TicTacToe\Engine\Presentation\Console\ConsoleInput;
+use TicTacToe\Engine\Presentation\Console\ConsoleOutput;
+use TicTacToe\Engine\Presentation\Input;
+use TicTacToe\Engine\Presentation\Output;
+use TicTacToe\Shared\Infrastructure\CommandBus\CommandBusFactory;
+use TicTacToe\Shared\Infrastructure\EventStore;
class Application
{
- /**
- * Maximum players for this game
- *
- * @const integer
- */
- const MAX_PLAYERS = 2;
-
- /**
- * String padding lenght used to show
- * test in cli
- *
- * @const integer
- */
- const STRING_PADDING_LENGTH = 64;
-
- /**
- * Board instance
- *
- * @var Board
- */
- protected $board = null;
-
- /**
- * IOHandler for handling
- * input from STDIN
- * and output to STDOUT
- *
- * @var IOHandler
- */
- protected $ioHandler = null;
+ private CommandBusFactory $commandBusFactory;
- /**
- * Array of players
- *
- * @var array
- */
- protected $players;
+ private EventStore $eventStore;
- /**
- * Boolean if players choose to start game
- *
- * @var bool
- */
- protected $isStarted = false;
+ private Output $output;
- protected $playerTurn = 0;
- protected $moves = 0;
+ private Input $input;
/**
* Constructor
*/
- public function __construct(IOHandler $ioHandler)
+ public function __construct()
{
- $this->board = new Board();
- $this->players = array();
- $this->ioHandler = $ioHandler;
+ $this->eventStore = new InMemoryEventStore();
+ $this->commandBusFactory = new CommandBusFactory(new GameCommandHandlerFactory(), $this->eventStore);
+ $this->input = new ConsoleInput();
+ $this->output = new ConsoleOutput();
}
/**
@@ -71,14 +37,14 @@ public function __construct(IOHandler $ioHandler)
*
* @return void
*/
- protected function processInput()
+ protected function processInput(): void
{
- $keyPressed = $this->ioHandler->readString(true);
+ $keyPressed = $this->input->readString();
switch ($keyPressed) {
case 'd':
case 'D':
- $this->ioHandler->writeLine($this->board->getBoard());
+ $this->output->info("D pressed");
break;
case '1':
case '2':
@@ -89,42 +55,18 @@ protected function processInput()
case '7':
case '8':
case '9':
- return $keyPressed;
+ $this->output->warning('number pressed pressed');
break;
case 's':
case 'S':
- // check in players
- if (!$this->isStarted) {
- $this->checkInPlayers();
- }
+ $this->output->success('S pressed');
break;
default:
- $this->ioHandler->writeLine("Unidentified input.", IOHandler::WARNING);
+ $this->output->error("Unidentified input.");
break;
}
}
- /**
- * Is game started
- *
- * @return bool
- */
- public function isGameStarted()
- {
- return $this->isStarted;
- }
-
- public function getMovesCount()
- {
- return $this->moves;
- }
-
-
- public function getCellValue($location)
- {
- return $this->board->getCellValue($location);
- }
-
/**
* Run the game
*
@@ -134,237 +76,8 @@ public function getCellValue($location)
*/
public function run()
{
- $this->showWelcome();
-
- while (($winner = $this->board->checkForWinner()) == null) {
- if (!$this->isStarted) {
- $this->processInput();
- } else {
- try {
- $this->nextStep();
-
- if ($this->isGameDraw()) {
- $this->gameIsDraw();
- break;
- }
- } catch (\Exception $e) {
- $this->ioHandler->writeLine($e->getMessage(), IOHandler::ERROR);
- continue;
- }
- }
- }
-
- if (!empty($winner)) {
- $this->gameHasWinner($winner);
- }
- }
-
- /**
- * Check if game is draw between players
- *
- * @return void
- */
- protected function isGameDraw()
- {
- return ((true == $this->board->areAllCellsFilled()) && (null == $this->board->checkForWinner()));
- }
-
- /**
- * Show winner
- *
- * @param string $marker Winner marker
- *
- * @return void
- */
- protected function gameHasWinner($marker)
- {
- $winner = $this->getPlayerByMarker($marker);
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("#", "#"), IOHandler::SUCCESS);
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "), IOHandler::SUCCESS);
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("Hoorrraaayyy !"), IOHandler::SUCCESS);
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding($winner->getName() . " won!"), IOHandler::SUCCESS);
- foreach ($this->board->getBoard() as $boardRow) {
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding($boardRow), IOHandler::SUCCESS);
- }
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("Thank you for playing Tic-Tac-Toe"), IOHandler::SUCCESS);
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "), IOHandler::SUCCESS);
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("#", "#"), IOHandler::SUCCESS);
- }
-
- /**
- * Game is ended with draw
- *
- * @return void
- */
- protected function gameIsDraw()
- {
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("#", "#"));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("It is a DRAW!"));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("Thank you for playing Tic-Tac-Toe."));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("#", "#"));
- }
-
- /**
- * Play
- *
- * @return void
- */
- private function nextStep()
- {
- $this->playerTurn = $this->moves % count($this->players);
- $player = $this->players[$this->playerTurn];
-
- $this->ioHandler->write("{$player->getName()}'s turn, please enter the location: ");
- $location = $this->processInput();
-
- if ($location) {
- $this->board->setBoardCell($location, $player->getMarker());
- $this->moves++;
- }
- }
-
- /**
- * Show the welcome message
- *
- * @return void
- */
- protected function showWelcome()
- {
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("#", "#"));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("Tic-Tac-Toe Game"));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("#", "#"));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("Welcome to Tic-Tac-Toe"));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("This is a two players tic-tac-toe game."));
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- foreach ($this->board->demoBoard() as $boardRow) {
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding($boardRow));
+ while (true) {
+ $this->processInput();
}
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine("| To start playing you need to press 's/S', then enter players |");
- $this->ioHandler->writeLine("| details (name and marker). If at anytime you want to draw the |");
- $this->ioHandler->writeLine("| board press 'd/D'. |");
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding(" ", " "));
- $this->ioHandler->writeLine("| Enjoy! |");
- $this->ioHandler->writeLine($this->getBorderedStringWithPadding("_", "_"));
- $this->ioHandler->writeLine();
- $this->ioHandler->write("Press 's/S' to start the game... ", IOHandler::LIGHT_CYAN);
- }
-
- /**
- * Prompt to make players enter their name and
- * choose their markers.
- *
- * @return void
- */
- public function checkInPlayers()
- {
- $this->isStarted = true;
- $playersCheckedIn = 0;
- $player = array(
- 'name' => null,
- 'marker' => null
- );
- while ($playersCheckedIn < self::MAX_PLAYERS) {
- try {
- if (!isset($player['name'])) {
- $this->ioHandler->write("What is the name of Player " . ($playersCheckedIn + 1) . ": ");
- $player['name'] = $this->ioHandler->readString();
- }
-
- if (!isset($player['marker'])) {
- $this->ioHandler->write("Please choose your Tic-Tac-Toe marker i.e. 'x', 'o': ");
- $marker = $this->ioHandler->readString(true);
- if (!$this->isMarkerTaken($marker)) {
- $this->players[] = new HumanPlayer($marker, $player['name']);
- $player['marker'] = $marker;
- }
- }
- } catch (\Exception $e) {
- $this->ioHandler->writeLine($e->getMessage(), IOHandler::ERROR);
- continue;
- }
-
- $playersCheckedIn++;
- unset($player);
- }
- }
-
- /**
- * Get players
- *
- * @return array
- */
- public function getPlayers()
- {
- return $this->players;
- }
-
- /**
- * Get Player by marker
- *
- * @return TicTacToe\Player\HumanPlayer
- */
- protected function getPlayerByMarker($marker)
- {
- foreach ($this->players as $player) {
- if ($marker === $player->getMarker()) {
- return $player;
- }
- }
-
- return null;
- }
-
- /**
- * Check if marker is already chosen
- * by another player.
- *
- * @param string $marker marker to check
- *
- * @throws \RuntimeException in case of marker already taken
- * @return true if not taken, false otherwise
- */
- private function isMarkerTaken($marker)
- {
- foreach ($this->players as $player) {
- if ($marker === $player->getMarker()) {
- throw new \RuntimeException("Sorry! '{$marker}' is already taken.");
- }
- }
-
- return false;
- }
-
- /**
- * Get a RIGHT and LEFT padded string with
- * left and right border.
- *
- * @param string $string String to output
- * @param string $padding Optional. Specifies the string to use for padding. Default is whitespace
- *
- * @return string
- */
- private function getBorderedStringWithPadding($string, $padding = " ")
- {
- return "|" . $this->getPaddedStringForOutput($string, $padding) . "|";
- }
-
- /**
- * Get a RIGHT and LEFT padded string used
- * in showing Welcome, Game draw and Game win
- *
- * @param string $string String to output
- * @param string $padding Optional. Specifies the string to use for padding. Default is whitespace
- *
- * @return string
- */
- private function getPaddedStringForOutput($string, $padding = " ")
- {
- return str_pad($string, self::STRING_PADDING_LENGTH, $padding, STR_PAD_BOTH);
}
}
diff --git a/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameCommand.php b/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameCommand.php
new file mode 100644
index 0000000..db80400
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameCommand.php
@@ -0,0 +1,39 @@
+ */
+ private array $playerIds;
+
+ private int $boardSize;
+
+ /**
+ * @param array $playerIds
+ * @param int $boardSize
+ */
+ public function __construct(array $playerIds, int $boardSize)
+ {
+ $this->playerIds = $playerIds;
+ $this->boardSize = $boardSize;
+ }
+
+ /**
+ * @return array
+ */
+ public function playerIds(): array
+ {
+ return $this->playerIds;
+ }
+
+ public function boardSize(): int
+ {
+ return $this->boardSize;
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameHandler.php b/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameHandler.php
new file mode 100644
index 0000000..015630f
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameHandler.php
@@ -0,0 +1,60 @@
+gameRepository = $gameRepository;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handles(): string
+ {
+ return CreateNewGameCommand::class;
+ }
+
+ /**
+ * @param CreateNewGameCommand $command
+ * @inheritDoc
+ * @throws SorryBoardSizeIsNotValid
+ */
+ public function handle(Command $command): DataTransformer
+ {
+ if (!$command instanceof CreateNewGameCommand) {
+ throw new SorryWrongCommand();
+ }
+
+ $playerIds = array_map(function (string $playerId) {
+ return new PlayerId($playerId);
+ }, $command->playerIds());
+
+ $game = Game::start(
+ new GameId(),
+ new Board($command->boardSize()),
+ ...$playerIds
+ );
+
+ $this->gameRepository->add($game);
+
+ return new CreateNewGameResponseDto($game);
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameResponseDto.php b/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameResponseDto.php
new file mode 100644
index 0000000..15938f0
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/CreateNewGame/CreateNewGameResponseDto.php
@@ -0,0 +1,65 @@
+gameId = (string) $game->id();
+ $this->boardSize = $game->board()->size();
+ $this->playerIds = $game->playerIds()->toArray();
+ $this->createdAt = $game->createdAt();
+ }
+
+ public function gameId(): string
+ {
+ return $this->gameId;
+ }
+
+ public function boardSize(): int
+ {
+ return $this->boardSize;
+ }
+
+ /**
+ * @return PlayerId[]
+ */
+ public function playerIds(): array
+ {
+ return $this->playerIds;
+ }
+
+ public function createdAt(): \DateTimeImmutable
+ {
+ return $this->createdAt;
+ }
+
+ /**
+ * @return array|int|string>
+ */
+ public function toArray(): array
+ {
+ return [
+ 'id' => $this->gameId,
+ 'boardSize' => $this->boardSize,
+ 'playerIds' => $this->playerIds,
+ 'createdAt' => $this->createdAt->format(\DateTimeImmutable::ATOM)
+ ];
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerCommand.php b/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerCommand.php
new file mode 100644
index 0000000..a0c80c1
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerCommand.php
@@ -0,0 +1,30 @@
+name = $name;
+ $this->token = $token;
+ }
+
+ public function name(): string
+ {
+ return $this->name;
+ }
+
+ public function token(): string
+ {
+ return $this->token;
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerHandler.php b/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerHandler.php
new file mode 100644
index 0000000..d3d86b6
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerHandler.php
@@ -0,0 +1,46 @@
+playerRepository = $playerRepository;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handles(): string
+ {
+ return CreateNewPlayerCommand::class;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handle(Command $command): DataTransformer
+ {
+ if (!$command instanceof CreateNewPlayerCommand) {
+ throw new SorryWrongCommand();
+ }
+ $player = Player::createPlayerWithToken($command->name(), $command->token());
+
+ $this->playerRepository->add($player);
+
+ return new CreateNewPlayerResponseDto($player);
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerResponseDto.php b/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerResponseDto.php
new file mode 100644
index 0000000..bd35b80
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/CreateNewPlayer/CreateNewPlayerResponseDto.php
@@ -0,0 +1,60 @@
+playerId = (string) $player->id();
+ $this->playerToken = (string) $player->playingToken();
+ $this->playerName = $player->name();
+ $this->createdAt = $player->createdAt();
+ }
+
+ public function playerId(): string
+ {
+ return $this->playerId;
+ }
+
+ public function playerName(): string
+ {
+ return $this->playerName;
+ }
+
+ public function playerToken(): string
+ {
+ return $this->playerToken;
+ }
+
+ public function createdAt(): \DateTimeImmutable
+ {
+ return $this->createdAt;
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return [
+ 'id' => $this->playerId,
+ 'name' => $this->playerName,
+ 'token' => $this->playerToken,
+ 'createdAt' => $this->createdAt->format(\DateTimeImmutable::ATOM)
+ ];
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/PlayNewTurn/PlayNewTurnCommand.php b/src/TicTacToe/Engine/Application/PlayNewTurn/PlayNewTurnCommand.php
new file mode 100644
index 0000000..a138ed2
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/PlayNewTurn/PlayNewTurnCommand.php
@@ -0,0 +1,43 @@
+playerId = $playerId;
+ $this->position = $position;
+ $this->gameId = $gameId;
+ }
+
+ public function gameId(): string
+ {
+ return $this->gameId;
+ }
+
+ public function playerId(): string
+ {
+ return $this->playerId;
+ }
+
+ public function position(): int
+ {
+ return $this->position;
+ }
+}
diff --git a/src/TicTacToe/Engine/Application/PlayNewTurn/PlayNewTurnHandler.php b/src/TicTacToe/Engine/Application/PlayNewTurn/PlayNewTurnHandler.php
new file mode 100644
index 0000000..9f17559
--- /dev/null
+++ b/src/TicTacToe/Engine/Application/PlayNewTurn/PlayNewTurnHandler.php
@@ -0,0 +1,77 @@
+turnRepository = $turnRepository;
+ $this->playerRepository = $playerRepository;
+ $this->gameRepository = $gameRepository;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handles(): string
+ {
+ return PlayNewTurnCommand::class;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handle(Command $command): DataTransformer
+ {
+ if (!$command instanceof PlayNewTurnCommand) {
+ throw new SorryWrongCommand();
+ }
+
+ $playerId = $command->playerId();
+ $player = $this->playerRepository->ofId(new PlayerId($playerId));
+ if ($player === null) {
+ throw new SorryPlayerNotFound("Player of Id {$playerId} not found.");
+ }
+
+ $gameId = $command->gameId();
+ $game = $this->gameRepository->ofId(new GameId($gameId));
+ if ($game === null) {
+ throw new SorryGameNotFound("Game of Id {$gameId} not found.");
+ }
+
+ $turn = $player->play($game->id(), new Position($command->position()));
+
+ $game->setBoardPiece(
+ $turn
+ );
+
+ $this->turnRepository->add($turn);
+
+ return new EmptyResponseDto();
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Board/Board.php b/src/TicTacToe/Engine/Domain/Model/Board/Board.php
new file mode 100644
index 0000000..34e0b4f
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Board/Board.php
@@ -0,0 +1,68 @@
+ */
+ private array $cells;
+
+ private int $size;
+
+ /**
+ * @throws SorryBoardSizeIsNotValid
+ */
+ public function __construct(int $size)
+ {
+ if ($size != self::BOARD_SIZE_3) {
+ throw new SorryBoardSizeIsNotValid("Only allowed size for board is: " . self::BOARD_SIZE_3);
+ }
+
+ $this->cells = [];
+ $this->size = $size;
+ $this->initBoard();
+ }
+
+ public function size(): int
+ {
+ return $this->size;
+ }
+
+ private function initBoard(): void
+ {
+ /** @var int $position */
+ foreach (Position::positions() as $position) {
+ $this->cells[$position] = Cell::empty();
+ }
+ }
+
+ public function isFull(): bool
+ {
+ /** @var Cell $cell */
+ foreach ($this->cells as $cell) {
+ if ($cell->isEmpty()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function setCell(Position $position, Cell $cell): void
+ {
+ $this->cells[$position->position()] = $cell;
+ }
+
+ public static function create3By3Board(): Board
+ {
+ return new self(
+ self::BOARD_SIZE_3
+ );
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Board/Cell.php b/src/TicTacToe/Engine/Domain/Model/Board/Cell.php
new file mode 100644
index 0000000..f29db51
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Board/Cell.php
@@ -0,0 +1,37 @@
+value = $value;
+ }
+
+ public function __toString(): string
+ {
+ return $this->value;
+ }
+
+ public function isEmpty(): bool
+ {
+ return empty($this->value);
+ }
+
+ public static function empty(): Cell
+ {
+ return new Cell();
+ }
+
+ public static function createFromPlayerToken(PlayerToken $playerToken): Cell
+ {
+ return new Cell((string) $playerToken);
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Board/Exception/SorryBoardSizeIsNotValid.php b/src/TicTacToe/Engine/Domain/Model/Board/Exception/SorryBoardSizeIsNotValid.php
new file mode 100644
index 0000000..7effd9f
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Board/Exception/SorryBoardSizeIsNotValid.php
@@ -0,0 +1,9 @@
+position = $position;
+ }
+
+ public function position(): int
+ {
+ return $this->position;
+ }
+
+ /**
+ * @return array
+ */
+ public static function positions(): array
+ {
+ return self::VALID_POSITIONS;
+ }
+
+ public static function isValid(int $position): bool
+ {
+ if (!in_array($position, self::VALID_POSITIONS, true)) {
+ throw new SorryInvalidPosition('Out of boundary position.');
+ }
+
+ return true;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/Event/GameCreated.php b/src/TicTacToe/Engine/Domain/Model/Game/Event/GameCreated.php
new file mode 100644
index 0000000..a4ca3b0
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/Event/GameCreated.php
@@ -0,0 +1,45 @@
+game = $game;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function occurredAt(): \DateTimeImmutable
+ {
+ return $this->game->createdAt();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'gameId' => (string) $this->game->id(),
+ 'createdAt' => $this->game->createdAt()->format(\DateTimeImmutable::ATOM)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function name(): string
+ {
+ return GameCreated::class;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/Event/PlayerTurnPlayed.php b/src/TicTacToe/Engine/Domain/Model/Game/Event/PlayerTurnPlayed.php
new file mode 100644
index 0000000..abdecc0
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/Event/PlayerTurnPlayed.php
@@ -0,0 +1,46 @@
+turn = $turn;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function occurredAt(): \DateTimeImmutable
+ {
+ return $this->turn->createdAt();
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return [
+ 'turnId' => (string) $this->turn->moveId(),
+ 'madeBy' => (string) $this->turn->madeBy()->id(),
+ 'position' => $this->turn->position()->position(),
+ 'createdAt' => $this->turn->createdAt()->format(\DateTimeImmutable::ATOM)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function name(): string
+ {
+ return PlayerTurnPlayed::class;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/Exception/SorryGameNotFound.php b/src/TicTacToe/Engine/Domain/Model/Game/Exception/SorryGameNotFound.php
new file mode 100644
index 0000000..8e40a01
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/Exception/SorryGameNotFound.php
@@ -0,0 +1,8 @@
+ self::MAX_PLAYERS) {
+ throw new SorryTooManyPlayers("Only " . self::MAX_PLAYERS . " players allowed.");
+ }
+
+ $this->id = $gameId;
+ $this->board = $board;
+ $this->playerIds = new PlayerIdSet(...$playerIds);
+ $this->createdAt = new \DateTimeImmutable();
+ }
+
+ public static function start(GameId $gameId, Board $board, PlayerId ...$playerIds): self
+ {
+ $game = new self($gameId, $board, ...$playerIds);
+
+ $game->record(
+ new GameCreated($game)
+ );
+
+ return $game;
+ }
+
+ public function setBoardPiece(Turn $turn): void
+ {
+ $this->board->setCell($turn->position(), $turn->cell());
+ }
+
+ public function id(): GameId
+ {
+ return $this->id;
+ }
+
+ public function playerIds(): PlayerIdSet
+ {
+ return $this->playerIds;
+ }
+
+ public function board(): Board
+ {
+ return $this->board;
+ }
+
+ public function createdAt(): \DateTimeImmutable
+ {
+ return $this->createdAt;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/GameId.php b/src/TicTacToe/Engine/Domain/Model/Game/GameId.php
new file mode 100644
index 0000000..3470948
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/GameId.php
@@ -0,0 +1,27 @@
+id = null === $id ? Uuid::uuid4()->toString() : $id;
+ }
+
+ public function equals(GameId $gameId): bool
+ {
+ return $this->id === (string) $gameId;
+ }
+
+ public function __toString(): string
+ {
+ return $this->id;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/GameRepository.php b/src/TicTacToe/Engine/Domain/Model/Game/GameRepository.php
new file mode 100644
index 0000000..9b352d9
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/GameRepository.php
@@ -0,0 +1,30 @@
+
+ */
+ public function games(): array;
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/Turn.php b/src/TicTacToe/Engine/Domain/Model/Game/Turn.php
new file mode 100644
index 0000000..8f61149
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/Turn.php
@@ -0,0 +1,68 @@
+gameId = $gameId;
+ $this->moveId = $moveId;
+ $this->cell = $cell;
+ $this->madeBy = $madeBy;
+ $this->position = $position;
+ $this->createdAt = new \DateTimeImmutable();
+ }
+
+ public function gameId(): GameId
+ {
+ return $this->gameId;
+ }
+
+ public function moveId(): TurnId
+ {
+ return $this->moveId;
+ }
+
+ public function madeBy(): Player
+ {
+ return $this->madeBy;
+ }
+
+ public function cell(): Cell
+ {
+ return $this->cell;
+ }
+
+ public function position(): Position
+ {
+ return $this->position;
+ }
+
+ public function createdAt(): \DateTimeImmutable
+ {
+ return $this->createdAt;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/TurnId.php b/src/TicTacToe/Engine/Domain/Model/Game/TurnId.php
new file mode 100644
index 0000000..25fe803
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/TurnId.php
@@ -0,0 +1,26 @@
+id = null === $id ? Uuid::uuid4()->toString() : $id;
+ }
+
+ public function equals(TurnId $moveId): bool
+ {
+ return $this->id === (string)$moveId;
+ }
+
+ public function __toString(): string
+ {
+ return $this->id;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Game/TurnRepository.php b/src/TicTacToe/Engine/Domain/Model/Game/TurnRepository.php
new file mode 100644
index 0000000..94a3efc
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Game/TurnRepository.php
@@ -0,0 +1,28 @@
+
+ */
+ public function turns(): array;
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Player/Event/PlayerCreated.php b/src/TicTacToe/Engine/Domain/Model/Player/Event/PlayerCreated.php
new file mode 100644
index 0000000..adc26d2
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Player/Event/PlayerCreated.php
@@ -0,0 +1,47 @@
+player = $player;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function occurredAt(): \DateTimeImmutable
+ {
+ return $this->player->createdAt();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function toArray(): array
+ {
+ return [
+ 'playerId' => (string) $this->player->id(),
+ 'playerName' => $this->player->name(),
+ 'playerToken' => (string) $this->player->playingToken(),
+ 'createdAt' => $this->player->createdAt()->format(\DateTimeImmutable::ATOM)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function name(): string
+ {
+ return PlayerCreated::class;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Player/Exception/SorryInvalidPlayerToken.php b/src/TicTacToe/Engine/Domain/Model/Player/Exception/SorryInvalidPlayerToken.php
new file mode 100644
index 0000000..a4cc849
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Player/Exception/SorryInvalidPlayerToken.php
@@ -0,0 +1,9 @@
+id = $id;
+ $this->name = $name;
+ $this->playingToken = $token;
+ $this->createdAt = new \DateTimeImmutable();
+ }
+
+ public static function createPlayerWithTokenX(string $name): Player
+ {
+ $player = new self(
+ new PlayerId(),
+ new PlayerName($name),
+ PlayerToken::createGameTokenX()
+ );
+
+ $player->record(
+ new PlayerCreated($player)
+ );
+
+ return $player;
+ }
+
+ public static function createPlayerWithTokenY(string $name): Player
+ {
+ $player = new self(
+ new PlayerId(),
+ new PlayerName($name),
+ PlayerToken::createGameTokenY()
+ );
+
+ $player->record(
+ new PlayerCreated($player)
+ );
+
+ return $player;
+ }
+
+ /**
+ * @throws SorryPlayerNameIsTooLong
+ * @throws SorryPlayerNameIsTooShort
+ * @throws SorryInvalidPlayerToken
+ */
+ public static function createPlayerWithToken(string $name, string $token): Player
+ {
+ $player = new self(
+ new PlayerId(),
+ new PlayerName($name),
+ new PlayerToken($token)
+ );
+
+ $player->record(
+ new PlayerCreated($player)
+ );
+
+ return $player;
+ }
+
+ public function name(): string
+ {
+ return (string) $this->name;
+ }
+
+ public function playingToken(): PlayerToken
+ {
+ return $this->playingToken;
+ }
+
+ public function id(): PlayerId
+ {
+ return $this->id;
+ }
+
+ public function createdAt(): \DateTimeImmutable
+ {
+ return $this->createdAt;
+ }
+
+ public function play(GameId $gameId, Position $position): Turn
+ {
+ $turn = new Turn(
+ $gameId,
+ new TurnId(),
+ Cell::createFromPlayerToken($this->playingToken),
+ $position,
+ $this
+ );
+
+ $this->record(
+ new PlayerTurnPlayed($turn)
+ );
+
+ return $turn;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Player/PlayerId.php b/src/TicTacToe/Engine/Domain/Model/Player/PlayerId.php
new file mode 100644
index 0000000..a88813f
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Player/PlayerId.php
@@ -0,0 +1,27 @@
+id = null === $id ? Uuid::uuid4()->toString() : $id;
+ }
+
+ public function equals(PlayerId $playerId): bool
+ {
+ return $this->id === (string) $playerId;
+ }
+
+ public function __toString(): string
+ {
+ return $this->id;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Player/PlayerIdSet.php b/src/TicTacToe/Engine/Domain/Model/Player/PlayerIdSet.php
new file mode 100644
index 0000000..1f2eae5
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Player/PlayerIdSet.php
@@ -0,0 +1,46 @@
+
+ */
+class PlayerIdSet implements \Countable, \IteratorAggregate
+{
+ /** @var PlayerId[] */
+ private array $playerIds;
+
+ public function __construct(PlayerId ...$playerIds)
+ {
+ if (empty($playerIds)) {
+ throw new \InvalidArgumentException('Player Id set cannot be empty');
+ }
+
+ $this->playerIds = $playerIds;
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return $this->playerIds;
+ }
+
+ /**
+ * @return Iterator
+ */
+ public function getIterator(): Iterator
+ {
+ return new \ArrayIterator($this->toArray());
+ }
+
+ public function count(): int
+ {
+ return count($this->playerIds);
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Player/PlayerName.php b/src/TicTacToe/Engine/Domain/Model/Player/PlayerName.php
new file mode 100644
index 0000000..ee6c8e6
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Player/PlayerName.php
@@ -0,0 +1,38 @@
+ self::MAX_LENGTH) {
+ throw new SorryPlayerNameIsTooLong("Player name is too long, maximum " . self::MAX_LENGTH);
+ }
+
+ $this->name = $name;
+ }
+
+ public function __toString(): string
+ {
+ return $this->name;
+ }
+}
diff --git a/src/TicTacToe/Engine/Domain/Model/Player/PlayerRepository.php b/src/TicTacToe/Engine/Domain/Model/Player/PlayerRepository.php
new file mode 100644
index 0000000..8e44f83
--- /dev/null
+++ b/src/TicTacToe/Engine/Domain/Model/Player/PlayerRepository.php
@@ -0,0 +1,31 @@
+token = $token;
+ }
+
+ public static function createGameTokenX(): PlayerToken
+ {
+ return new self(self::X_TOKEN);
+ }
+
+ public static function createGameTokenY(): PlayerToken
+ {
+ return new self(self::Y_TOKEN);
+ }
+
+ public function __toString(): string
+ {
+ return $this->token;
+ }
+}
diff --git a/src/TicTacToe/Engine/Infrastructure/CommandBus/GameCommandHandlerFactory.php b/src/TicTacToe/Engine/Infrastructure/CommandBus/GameCommandHandlerFactory.php
new file mode 100644
index 0000000..132d0ec
--- /dev/null
+++ b/src/TicTacToe/Engine/Infrastructure/CommandBus/GameCommandHandlerFactory.php
@@ -0,0 +1,34 @@
+ function () use ($gameRepository) {
+ return new CreateNewGameHandler($gameRepository);
+ },
+ CreateNewPlayerCommand::class => function () use ($playerRepository) {
+ return new CreateNewPlayerHandler($playerRepository);
+ }
+ ];
+ }
+}
diff --git a/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryEventStore.php b/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryEventStore.php
new file mode 100644
index 0000000..1cf6777
--- /dev/null
+++ b/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryEventStore.php
@@ -0,0 +1,32 @@
+events = $events;
+ }
+
+ public function store(Event $event): void
+ {
+ $this->events[] = unserialize(serialize($event));
+ }
+
+ /**
+ * @return Event[]
+ */
+ public function events(): array
+ {
+ return $this->events;
+ }
+}
diff --git a/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryGameRepository.php b/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryGameRepository.php
new file mode 100644
index 0000000..3436ac4
--- /dev/null
+++ b/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryGameRepository.php
@@ -0,0 +1,52 @@
+games[(string) $game->id()] = $game;
+ }
+
+ /**
+ * @param GameId $gameId
+ * @return Game|null
+ */
+ public function ofId(GameId $gameId): ?Game
+ {
+ if (!isset($this->games[(string) $gameId])) {
+ return null;
+ }
+
+ return $this->games[(string) $gameId];
+ }
+
+ /**
+ * @return GameId
+ */
+ public function nextIdentity(): GameId
+ {
+ return new GameId();
+ }
+
+ /**
+ * @return array
+ */
+ public function games(): array
+ {
+ return $this->games;
+ }
+}
diff --git a/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryPlayerRepository.php b/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryPlayerRepository.php
new file mode 100644
index 0000000..1018245
--- /dev/null
+++ b/src/TicTacToe/Engine/Infrastructure/Persistence/InMemory/InMemoryPlayerRepository.php
@@ -0,0 +1,42 @@
+ */
+ private array $players = [];
+
+ public function add(Player $player): void
+ {
+ $this->players[(string)$player->id()] = $player;
+ }
+
+ public function ofId(PlayerId $playerId): ?Player
+ {
+ if (!isset($this->players[(string) $playerId])) {
+ return null;
+ }
+
+ return $this->players[(string) $playerId];
+ }
+
+ public function nextIdentity(): PlayerId
+ {
+ return new PlayerId();
+ }
+
+ /**
+ * @return array
+ */
+ public function players(): array
+ {
+ return $this->players;
+ }
+}
diff --git a/src/TicTacToe/Engine/Presentation/Console/ConsoleInput.php b/src/TicTacToe/Engine/Presentation/Console/ConsoleInput.php
new file mode 100644
index 0000000..17b77af
--- /dev/null
+++ b/src/TicTacToe/Engine/Presentation/Console/ConsoleInput.php
@@ -0,0 +1,42 @@
+readStringFromStream();
+ if (empty($input) && !$allowEmpty) {
+ throw new \InvalidArgumentException("Input cannot be empty.");
+ }
+
+ return trim($input);
+ }
+
+ public function readInteger(): int
+ {
+ $input = $this->readIntegerFromStream();
+ if (!empty($input)) {
+ throw new \InvalidArgumentException("Input cannot be empty.");
+ }
+
+ return $input;
+ }
+
+ protected function readStringFromStream(): string
+ {
+ $stream = fgets(STDIN);
+ return trim($stream !== false ? $stream : '');
+ }
+
+ protected function readIntegerFromStream(): int
+ {
+ fscanf(STDIN, "%d\n", $number);
+ return $number;
+ }
+}
diff --git a/src/TicTacToe/Engine/Presentation/Console/ConsoleOutput.php b/src/TicTacToe/Engine/Presentation/Console/ConsoleOutput.php
new file mode 100644
index 0000000..4479c96
--- /dev/null
+++ b/src/TicTacToe/Engine/Presentation/Console/ConsoleOutput.php
@@ -0,0 +1,52 @@
+getBorderedStringWithPadding($message);
+ }
+
+ private function getBorderedStringWithPadding(string $string, string $padding = " "): string
+ {
+ return "|" . $this->getPaddedStringForOutput($string, $padding) . "|";
+ }
+
+ private function getPaddedStringForOutput(string $string, string $padding = " "): string
+ {
+ return str_pad($string, self::STRING_PADDING_LENGTH, $padding, STR_PAD_BOTH);
+ }
+}
diff --git a/src/TicTacToe/Engine/Presentation/Input.php b/src/TicTacToe/Engine/Presentation/Input.php
new file mode 100644
index 0000000..a28a7fa
--- /dev/null
+++ b/src/TicTacToe/Engine/Presentation/Input.php
@@ -0,0 +1,12 @@
+write($line . PHP_EOL, $type);
- }
- } elseif (!is_array($output)) {
- $this->write($output . PHP_EOL, $type);
- }
- }
-
- public function readInteger()
- {
- $input = $this->readIntegerFromStream();
- if (!empty($input)) {
- if (!is_integer($input)) {
- throw new \InvalidArgumentException("Input is not a valid 'integer'.");
- }
- }
-
- $input = intval($input);
- return $input;
- }
-
- public function readString($allowEmpty = false)
- {
- $input = $this->readStringFromStream();
- if (empty($input) && !$allowEmpty) {
- throw new \InvalidArgumentException("Input cannot be empty.");
- }
- if (!is_string($input)) {
- throw new \InvalidArgumentException("Input is not a valid 'string'.");
- }
-
- return trim($input);
- }
-}
diff --git a/src/TicTacToe/Player/HumanPlayer.php b/src/TicTacToe/Player/HumanPlayer.php
deleted file mode 100644
index 9cd89e7..0000000
--- a/src/TicTacToe/Player/HumanPlayer.php
+++ /dev/null
@@ -1,9 +0,0 @@
-isAllowedMarker($marker)) {
- $this->marker = $marker;
- }
-
- if (empty($name)) {
- $this->name = "Player_" . $marker;
- } else {
- $this->name = $name;
- }
-
- $this->marker = $marker;
- }
-
- /**
- * Checks if the player choose 'x' or 'o' as
- * his/her marker.
- *
- * @param char $marker Makrer player chose
- *
- * @return true if yes false otherwise
- */
- protected function isAllowedMarker($marker)
- {
- if (in_array(strtolower($marker), $this->allowedMarkers)) {
- return true;
- }
-
- throw new \InvalidArgumentException("Only 'x' or 'y' are allowed as markers.");
- }
-
- public function getName()
- {
- return $this->name;
- }
-
- public function getMarker()
- {
- return $this->marker;
- }
-}
diff --git a/src/TicTacToe/Player/PlayerInterface.php b/src/TicTacToe/Player/PlayerInterface.php
deleted file mode 100644
index b1c3c63..0000000
--- a/src/TicTacToe/Player/PlayerInterface.php
+++ /dev/null
@@ -1,20 +0,0 @@
-
+ */
+ public function toArray(): array;
+}
diff --git a/src/TicTacToe/Shared/Application/EmptyResponseDto.php b/src/TicTacToe/Shared/Application/EmptyResponseDto.php
new file mode 100644
index 0000000..8983c51
--- /dev/null
+++ b/src/TicTacToe/Shared/Application/EmptyResponseDto.php
@@ -0,0 +1,13 @@
+supports($command)) {
+ throw new SorryCommandHandlerNotFound($commandName);
+ }
+
+ $callBack = $handlers[$commandName];
+ return $callBack();
+ }
+
+ public function supports(Command $command): bool
+ {
+ $handlers = $this::commandHandlers();
+ $commandName = get_class($command);
+
+ return array_key_exists($commandName, $handlers);
+ }
+
+ /**
+ * @return \Closure[]
+ */
+ abstract protected static function commandHandlers(): array;
+}
diff --git a/src/TicTacToe/Shared/Domain/Model/AggregateRoot.php b/src/TicTacToe/Shared/Domain/Model/AggregateRoot.php
new file mode 100644
index 0000000..04faae3
--- /dev/null
+++ b/src/TicTacToe/Shared/Domain/Model/AggregateRoot.php
@@ -0,0 +1,34 @@
+events = [];
+ }
+
+ /**
+ * @return Event[]
+ */
+ public function getRecordedEvents(): array
+ {
+ return $this->events;
+ }
+
+ public function resetRecordedEvents(): void
+ {
+ $this->events = [];
+ }
+
+ protected function record(Event $event): void
+ {
+ $this->events[] = $event;
+ }
+}
diff --git a/src/TicTacToe/Shared/Domain/Model/Event.php b/src/TicTacToe/Shared/Domain/Model/Event.php
new file mode 100644
index 0000000..ad45c6a
--- /dev/null
+++ b/src/TicTacToe/Shared/Domain/Model/Event.php
@@ -0,0 +1,29 @@
+
+ */
+ public function toArray(): array;
+
+ /**
+ * Get event name
+ *
+ * @return string
+ */
+ public function name(): string;
+}
diff --git a/src/TicTacToe/Shared/Infrastructure/CommandBus/CommandBus.php b/src/TicTacToe/Shared/Infrastructure/CommandBus/CommandBus.php
new file mode 100644
index 0000000..4ee066c
--- /dev/null
+++ b/src/TicTacToe/Shared/Infrastructure/CommandBus/CommandBus.php
@@ -0,0 +1,23 @@
+commandHandlerFactory = $commandHandlerFactory;
+ $this->eventStore = $eventStore;
+ }
+
+ /**
+ * @param Command $command
+ * @return DataTransformer
+ */
+ public function handle(Command $command): DataTransformer
+ {
+ $handler = $this->commandHandlerFactory->create($command);
+ return $handler->handle($command);
+ }
+
+ public function events(): array
+ {
+ return $this->eventStore->events();
+ }
+}
diff --git a/src/TicTacToe/Shared/Infrastructure/CommandBus/Exception/SorryCommandHandlerNotFound.php b/src/TicTacToe/Shared/Infrastructure/CommandBus/Exception/SorryCommandHandlerNotFound.php
new file mode 100644
index 0000000..edecc97
--- /dev/null
+++ b/src/TicTacToe/Shared/Infrastructure/CommandBus/Exception/SorryCommandHandlerNotFound.php
@@ -0,0 +1,17 @@
+createMock(IOHandler::class);
-
- $ioHandlerStub->expects($this->exactly(4))
- ->method('readstring')
- ->will($this->onConsecutiveCalls('foo','x','baar','o'));
-
-
- $application = new Application($ioHandlerStub);
- $application->checkInPlayers();
-
- $players = $application->getPlayers();
-
- $this->assertEquals(2, count($players));
-
- $this->assertEquals('foo', $players[0]->getName());
- $this->assertEquals('x', $players[0]->getMarker());
- $this->assertEquals('baar', $players[1]->getName());
- $this->assertEquals('o', $players[1]->getMarker());
- }
-
- public function testNextStepSuccess()
- {
- $ioHandlerStub = $this->createMock(IOHandler::class);
-
- $ioHandlerStub->expects($this->exactly(5))
- ->method('readstring')
- ->will($this->onConsecutiveCalls('foo','x','baar','o', '1'));
-
-
- $application = new Application($ioHandlerStub);
- $application->checkInPlayers();
-
- $players = $application->getPlayers();
-
- $this->assertEquals(0, $application->getMovesCount());
- $this->assertEquals(2, count($players));
-
- $this->assertEquals('foo', $players[0]->getName());
- $this->assertEquals('x', $players[0]->getMarker());
- $this->assertEquals('baar', $players[1]->getName());
- $this->assertEquals('o', $players[1]->getMarker());
-
- $this->invokeMethod($application, 'nextStep');
- $this->assertEquals(1, $application->getMovesCount());
- $this->assertNotNull($application->getCellValue(1));
- }
-
- public function testCheckInPlayersFailureAndRetrial()
- {
- $ioHandlerStub = $this->createMock(IOHandler::class);
-
- $ioHandlerStub->expects($this->any())
- ->method('readString')
- ->will($this->onConsecutiveCalls('foo','x','baar','x', 'o'));
-
- $application = new Application($ioHandlerStub);
-
- $application->checkInPlayers();
- $players = $application->getPlayers();
-
- $this->assertEquals(2, count($players));
-
- $this->assertEquals('foo', $players[0]->getName());
- $this->assertEquals('x', $players[0]->getMarker());
- $this->assertEquals('baar', $players[1]->getName());
- $this->assertEquals('o', $players[1]->getMarker());
- }
-
- public function testGetPlayerByMarkerSuccess()
- {
- $ioHandlerStub = $this->createMock(IOHandler::class);
-
- $ioHandlerStub->expects($this->any())
- ->method('readString')
- ->will($this->onConsecutiveCalls('foo','x','baar','x', 'o'));
-
- $application = new Application($ioHandlerStub);
-
- $application->checkInPlayers();
- $player = $this->invokeMethod($application, 'getPlayerByMarker', array('x'));
-
- $this->assertEquals('foo', $player->getName());
- $this->assertEquals('x', $player->getMarker());
- }
-
- public function testGetPlayerByMarkerReturnNull()
- {
- $ioHandlerStub = $this->createMock(IOHandler::class);
-
- $ioHandlerStub->expects($this->any())
- ->method('readString')
- ->will($this->onConsecutiveCalls('foo','x','baar','o'));
-
- $application = new Application($ioHandlerStub);
-
- $application->checkInPlayers();
- $player = $this->invokeMethod($application, 'getPlayerByMarker', array('i'));
-
- $this->assertNull($player);
- }
-
- public function testGetBorderedStringWithPadding()
- {
- $expected = "|############################foobaar#############################|";
-
- $ioHandler = new IOHandler();
- $application = new Application($ioHandler);
- $result = $this->invokeMethod($application, 'getBorderedStringWithPadding', array('foobaar', '#'));
-
- $this->assertEquals($expected, $result);
- $this->assertEquals(66, strlen($result));
- }
-
- public function testGetPaddedStringForOutput()
- {
- $expected = "############################foobaar#############################";
-
- $ioHandler = new IOHandler();
- $application = new Application($ioHandler);
- $result = $this->invokeMethod($application, 'getPaddedStringForOutput', array('foobaar', '#'));
-
- $this->assertEquals($expected, $result);
- $this->assertEquals(64, strlen($result));
- }
-
- public function testShowWelcome()
- {
- $expected = sprintf(IOHandler::INFO, "|################################################################|" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| Tic-Tac-Toe Game |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "|################################################################|" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| Welcome to Tic-Tac-Toe |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| This is a two players tic-tac-toe game. |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| 1 | 2 | 3 |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| ---|---|--- |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| 4 | 5 | 6 |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| ---|---|--- |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| 7 | 8 | 9 |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| To start playing you need to press 's/S', then enter players |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| details (name and marker). If at anytime you want to draw the |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| board press 'd/D'. |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "| Enjoy! |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "|________________________________________________________________|" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, PHP_EOL);
- $expected .= sprintf(IOHandler::LIGHT_CYAN, "Press 's/S' to start the game... ");
-
- $ioHandler = new IOHandler();
- $application = new Application($ioHandler);
-
- $this->expectOutputString($expected);
- $this->invokeMethod($application, 'showWelcome');
- }
-
- public function testIsGameDrawFalse()
- {
- $ioHandler = new IOHandler();
- $application = new Application($ioHandler);
-
- $result = $this->invokeMethod($application, 'isGameDraw');
-
- $this->assertFalse($result);
- }
-
- public function testProcessKeyPressShowBoard()
- {
- $ioHandlerStub = $this->getMockBuilder(IOHandler::class)
- ->setMethods(['readString'])
- ->getMock();
-
- $ioHandlerStub->expects($this->any())
- ->method('readString')
- ->willReturn('d');
-
- $application = new Application($ioHandlerStub);
-
- $expected = "";
- $expected .= sprintf(IOHandler::INFO, " | | " . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "---|---|---" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, " | | " . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, "---|---|---" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO, " | | " . PHP_EOL);
-
- $this->expectOutputString($expected);
- $this->invokeMethod($application, 'processInput');
- }
-
- public function testProcessKeyPressUnidentified()
- {
- $ioHandlerStub = $this->getMockBuilder(IOHandler::class)
- ->setMethods(['readString'])
- ->getMock();
-
- $ioHandlerStub->expects($this->any())
- ->method('readString')
- ->willReturn('q');
-
- $application = new Application($ioHandlerStub);
-
- $expected = "";
- $expected .= sprintf(IOHandler::WARNING, "Unidentified input." . PHP_EOL);
-
- $this->expectOutputString($expected);
- $this->invokeMethod($application, 'processInput');
- }
-
- public function testProcessInputEnterLocation()
- {
- $ioHandlerStub = $this->getMockBuilder(IOHandler::class)
- ->setMethods(['readString'])
- ->getMock();
-
- $ioHandlerStub->expects($this->any())
- ->method('readString')
- ->willReturn('1');
-
- $application = new Application($ioHandlerStub);
-
- $result = $this->invokeMethod($application, 'processInput');
- $this->assertEquals('1', $result);
- }
-
- public function testProcessKeyPressGameStart()
- {
- $ioHandlerStub = $this->getMockBuilder(IOHandler::class)
- ->setMethods(['readString'])
- ->getMock();
-
- $ioHandlerStub->expects($this->exactly(5))
- ->method('readString')
- ->will($this->onConsecutiveCalls('s','foo','x','baar','o'));
-
- $application = new Application($ioHandlerStub);
-
- $this->invokeMethod($application, 'processInput');
- $this->assertTrue($application->isGameStarted());
- }
-
- public function testGameIsDraw()
- {
- $expected = "";
- $expected .= sprintf(IOHandler::INFO,"|################################################################|" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO,"| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO,"| It is a DRAW! |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO,"| Thank you for playing Tic-Tac-Toe. |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO,"| |" . PHP_EOL);
- $expected .= sprintf(IOHandler::INFO,"|################################################################|" . PHP_EOL);
-
- $ioHandler = new IOHandler();
- $application = new Application($ioHandler);
- $this->expectOutputString($expected);
- $this->invokeMethod($application, 'gameIsDraw');
- }
-}
diff --git a/src/TicTacToe/Test/BaseTest.php b/src/TicTacToe/Test/BaseTest.php
deleted file mode 100644
index 06c79da..0000000
--- a/src/TicTacToe/Test/BaseTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-getMethod($methodName);
- $method->setAccessible(true);
-
- return $method->invokeArgs($object, $parameters);
- }
-}
diff --git a/src/TicTacToe/Test/BoardTest.php b/src/TicTacToe/Test/BoardTest.php
deleted file mode 100644
index 3b300bf..0000000
--- a/src/TicTacToe/Test/BoardTest.php
+++ /dev/null
@@ -1,170 +0,0 @@
-assertTrue($board->isValidMove(1));
- }
-
- public function testSetBoardInvalidMove()
- {
- $board = new Board();
-
- try {
- $board->setBoardCell(1, 'x');
- $board->setBoardCell(1, 'o');
- } catch (\Exception $e) {
- $this->assertInstanceOf(\LogicException::class, $e);
- }
- }
-
- public function testSetBoardThrowsOutOfRangeException()
- {
- $board = new Board();
-
- try {
- $board->setBoardCell(10, 'x');
- } catch (\Exception $e) {
- $this->assertInstanceOf(\OutOfRangeException::class, $e);
- }
- }
-
- public function testCellHasValueTrue()
- {
- $board = new Board();
- $board->setBoardCell(1, 'x');
- $this->assertTrue($board->cellHasValue(1));
- }
-
- public function testCheckForWinnerTrue()
- {
- $board = new Board();
-
- $board->setBoardCell(1, 'x');
- $board->setBoardCell(2, 'x');
- $board->setBoardCell(3, 'x');
-
- $this->assertEquals('x', $board->checkForWinner());
- }
-
- public function testCheckForWinnerNoWinner()
- {
- $board = new Board();
-
- $board->setBoardCell(1, 'x');
- $board->setBoardCell(2, 'x');
- $board->setBoardCell(9, 'x');
-
- $this->assertEquals(null, $board->checkForWinner());
- }
-
- public function testGetBoardSize()
- {
- $board = new Board();
-
- $this->assertEquals(3, $board->getBoardSize());
- }
-
- public function testGetBoardWithEmptyCells()
- {
- $board = new Board();
- $boardPlan = $board->getBoard();
-
- $expected = array(
- " | | ",
- "---|---|---",
- " | | ",
- "---|---|---",
- " | | ",
- );
- $this->assertEquals($expected, $boardPlan);
- }
-
- public function testGetDemoBoard()
- {
- $board = new Board();
- $boardPlan = $board->demoBoard();
-
- $expected = array(
- " 1 | 2 | 3 ",
- "---|---|---",
- " 4 | 5 | 6 ",
- "---|---|---",
- " 7 | 8 | 9 ",
- );
-
- $this->assertEquals($expected, $boardPlan);
- }
-
- public function testAreAllCellsFilledTrue()
- {
- $board = new Board();
- foreach ($this->fillBoardNoWin() as $input) {
- $board->setBoardCell($input[0], $input[1]);
- }
-
- $this->assertTrue($board->areAllCellsFilled());
- }
-
- public function testAreAllCellsFilledFalse()
- {
- $board = new Board();
- foreach ($this->fillBoard() as $input) {
- $board->setBoardCell($input[0], $input[1]);
- }
-
- $this->assertFalse($board->areAllCellsFilled());
- }
-
- public function testGetCellValueNotEmpty()
- {
- $board = new Board();
- $board->setBoardCell(1, 'x');
-
- $this->assertEquals('x', $board->getCellValue(1));
- }
-
- public function testGetCellValueEmpty()
- {
- $board = new Board();
-
- $this->assertNull($board->getCellValue(1));
- }
-
- public function fillBoardNoWin()
- {
- return array(
- array(1, 'x'),
- array(2, 'o'),
- array(3, 'x'),
- array(4, 'o'),
- array(5, 'x'),
- array(6, 'o'),
- array(7, 'o'),
- array(8, 'x'),
- array(9, 'o')
- );
- }
-
- public function fillBoard()
- {
- return array(
- array(1, 'x'),
- array(2, 'o'),
- array(3, 'x'),
- array(4, 'o'),
- array(5, 'x'),
- array(6, 'o'),
- array(7, 'o'),
- array(8, 'x'),
- );
- }
-}
diff --git a/src/TicTacToe/Test/IO/IOHandlerTest.php b/src/TicTacToe/Test/IO/IOHandlerTest.php
deleted file mode 100644
index a817efa..0000000
--- a/src/TicTacToe/Test/IO/IOHandlerTest.php
+++ /dev/null
@@ -1,108 +0,0 @@
-ioHandler = $this->getMockBuilder(IOHandler::class)
- ->setMethods(['readStringFromStream', 'readIntegerFromStream'])
- ->getMock();
- }
-
- public function testReadStringEmptyInput()
- {
- $this->ioHandler->expects($this->once())
- ->method('readStringFromStream')
- ->willReturn('');
-
- try {
- $string = $this->ioHandler->readString();
- } catch (\Exception $e) {
- $this->assertInstanceOf(\InvalidArgumentException::class, $e);
- }
- }
-
- public function testReadStringSuccess()
- {
- $this->ioHandler->expects($this->once())
- ->method('readStringFromStream')
- ->willReturn('foo');
-
- $string = $this->ioHandler->readString();
- $this->assertEquals('foo', $string);
- }
-
- public function testReadStringNotString()
- {
- $this->ioHandler->expects($this->once())
- ->method('readStringFromStream')
- ->willReturn(123);
-
- try {
- $string = $this->ioHandler->readString();
- } catch (\Exception $e) {
- $this->assertInstanceOf(\InvalidArgumentException::class, $e);
- }
- }
-
- public function testReadIntegerSuccess()
- {
- $this->ioHandler->expects($this->once())
- ->method('readIntegerFromStream')
- ->willReturn(123);
-
- $integer = $this->ioHandler->readInteger();
- $this->assertEquals(123, $integer);
- }
-
- public function testReadIntegerFailure()
- {
- $this->ioHandler->expects($this->once())
- ->method('readIntegerFromStream')
- ->willReturn("ABC");
-
- try {
- $integer = $this->ioHandler->readInteger();
- } catch (\Exception $e) {
- $this->assertInstanceOf(\InvalidArgumentException::class, $e);
- }
- }
-
- public function testWrite()
- {
- $this->expectOutputString(sprintf(IOHandler::INFO, "foo"));
- $this->ioHandler->write("foo");
- }
-
- public function testWriteWithArrayAsInput()
- {
- $expected = sprintf(IOHandler::INFO, "foo") . sprintf(IOHandler::INFO, "bar");
- $this->expectOutputString($expected);
- $lines[] = "foo";
- $lines[] = "bar";
- $this->ioHandler->write($lines);
- // var_dump($this->getActualOutput());
- }
-
- public function testWriteLine()
- {
- $this->expectOutputString(sprintf(IOHandler::INFO, "foo" . PHP_EOL));
- $this->ioHandler->writeLine("foo");
- }
-
- public function testWriteLineWithArrayAsInput()
- {
- $expected = sprintf(IOHandler::INFO, "foo" . PHP_EOL) . sprintf(IOHandler::INFO, "bar" . PHP_EOL);
- $this->expectOutputString($expected);
- $lines[] = "foo";
- $lines[] = "bar";
- $this->ioHandler->writeLine($lines);
- }
-}
diff --git a/src/TicTacToe/Test/Player/HumanPlayerTest.php b/src/TicTacToe/Test/Player/HumanPlayerTest.php
deleted file mode 100644
index 612d7a8..0000000
--- a/src/TicTacToe/Test/Player/HumanPlayerTest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-assertEquals('mina', $player->getName());
- $this->assertEquals('x', $player->getMarker());
- }
-
- public function testGetNameWhenNameIsNotProvided()
- {
- $player = new HumanPlayer('x');
-
- $this->assertEquals('Player_x', $player->getName());
- $this->assertEquals('x', $player->getMarker());
- }
-
- public function testIsAllowedMarkerWithInvalidMarker()
- {
- try {
- $player = new HumanPlayer('1');
- } catch (\Exception $e) {
- $this->assertInstanceOf(\InvalidArgumentException::class, $e);
- }
- }
-}
-
diff --git a/tests/unit/Engine/Application/CreateNewPlayer/CreateNewPlayerHandlerTest.php b/tests/unit/Engine/Application/CreateNewPlayer/CreateNewPlayerHandlerTest.php
new file mode 100644
index 0000000..dbb6843
--- /dev/null
+++ b/tests/unit/Engine/Application/CreateNewPlayer/CreateNewPlayerHandlerTest.php
@@ -0,0 +1,58 @@
+playerRepository = new InMemoryPlayerRepository();
+ $this->handler = new CreateNewPlayerHandler(
+ $this->playerRepository
+ );
+ }
+
+ public function testItHandlesCorrectClass(): void
+ {
+ self::assertEquals(CreateNewPlayerCommand::class, $this->handler->handles());
+ }
+
+ public function testItCanCreateANewPlayer(): void
+ {
+ $command = $this->createCommand();
+
+ /** @var CreateNewPlayerResponseDto $playerDto */
+ $playerDto = $this->handler->handle($command);
+ $retrievedPlayer = $this->playerRepository->ofId(new PlayerId($playerDto->playerId()));
+ self::assertEquals($retrievedPlayer->name(), $playerDto->playerName());
+ self::assertEquals((string) $retrievedPlayer->playingToken(), $playerDto->playerToken());
+ }
+
+ public function testItThrowsSorryWrongCommandWhenWrongCommandPassed(): void
+ {
+ $command = new WrongCommand();
+
+ self::expectException(SorryWrongCommand::class);
+ $this->handler->handle($command);
+ }
+
+ private function createCommand(string $name = 'player', string $token = 'x'): CreateNewPlayerCommand
+ {
+ return new CreateNewPlayerCommand($name, $token);
+ }
+}
diff --git a/tests/unit/Engine/Domain/Model/Board/BoardTest.php b/tests/unit/Engine/Domain/Model/Board/BoardTest.php
new file mode 100644
index 0000000..ae05988
--- /dev/null
+++ b/tests/unit/Engine/Domain/Model/Board/BoardTest.php
@@ -0,0 +1,25 @@
+size());
+ }
+
+ public function testItThrowsSorryBoardSizeIsNotValid()
+ {
+ self::expectException(SorryBoardSizeIsNotValid::class);
+ new Board(4);
+ }
+}
diff --git a/tests/unit/Engine/Domain/Model/Board/CellTest.php b/tests/unit/Engine/Domain/Model/Board/CellTest.php
new file mode 100644
index 0000000..dccc5d2
--- /dev/null
+++ b/tests/unit/Engine/Domain/Model/Board/CellTest.php
@@ -0,0 +1,32 @@
+createPlayers(2);
+ $game = Game::start($gameId, Board::create3By3Board(), ...$playerIds);
+
+ self::assertEquals(2, $game->playerIds()->count());
+ self::assertInstanceOf(GameId::class, $game->id());
+ self::assertInstanceOf(Board::class, $game->board());
+ }
+
+ public function testItThrowsTooManyPlayersWhenMorePlayersCreated()
+ {
+ $gameId = new GameId();
+ $playerIds = $this->createPlayers(3);
+ self::expectException(SorryTooManyPlayers::class);
+ Game::start($gameId, Board::create3By3Board(), ...$playerIds);
+ }
+
+ public function randomizeString(int $length)
+ {
+ $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $charactersLength = strlen($characters);
+ $randomString = '';
+ for ($i = 0; $i < $length; $i++) {
+ $randomString .= $characters[rand(0, $charactersLength - 1)];
+ }
+ return $randomString;
+ }
+
+ public function createPlayers(int $count)
+ {
+ $players = [];
+ for($i = 0; $i < $count; $i++) {
+ if ($i % 2 == 0) {
+ $players[] = Player::createPlayerWithTokenX($this->randomizeString(5))->id();
+ } else {
+ $players[] = Player::createPlayerWithTokenY($this->randomizeString(5))->id();
+ }
+ }
+
+ return $players;
+ }
+}
diff --git a/tests/unit/Engine/Domain/Model/Player/Event/PlayerCreatedTest.php b/tests/unit/Engine/Domain/Model/Player/Event/PlayerCreatedTest.php
new file mode 100644
index 0000000..a294e34
--- /dev/null
+++ b/tests/unit/Engine/Domain/Model/Player/Event/PlayerCreatedTest.php
@@ -0,0 +1,39 @@
+createPlayerX();
+ $event = new PlayerCreated($player);
+
+ self::assertEquals($player->createdAt()->getTimestamp(), $event->occurredAt()->getTimestamp());
+
+ $payload = $event->toArray();
+ self::assertEquals(PlayerCreated::class, $event->name());
+
+ self::assertArrayHasKey('playerId', $payload);
+ self::assertEquals((string) $player->id(), $payload['playerId']);
+
+ self::assertArrayHasKey('playerName', $payload);
+ self::assertEquals($player->name(), $payload['playerName']);
+
+ self::assertArrayHasKey('playerToken', $payload);
+ self::assertEquals($player->playingToken(), $payload['playerToken']);
+ }
+
+ public function createPlayerX($name = 'foobar')
+ {
+ $player = Player::createPlayerWithTokenX($name);
+
+ return $player;
+ }
+}
diff --git a/tests/unit/Engine/Domain/Model/Player/PlayerIdTest.php b/tests/unit/Engine/Domain/Model/Player/PlayerIdTest.php
new file mode 100644
index 0000000..def5b11
--- /dev/null
+++ b/tests/unit/Engine/Domain/Model/Player/PlayerIdTest.php
@@ -0,0 +1,28 @@
+name());
+ self::assertEquals('x', $player->playingToken());
+ self::assertInstanceOf(PlayerId::class, $player->id());
+ }
+
+ public function testPlayerCreatedWithPlayingTokenSuccessfully()
+ {
+ $player = Player::createPlayerWithToken(
+ "mina",
+ 'x'
+ );
+
+ self::assertEquals("mina", $player->name());
+ self::assertEquals('x', $player->playingToken());
+ self::assertInstanceOf(PlayerId::class, $player->id());
+ }
+
+ public function testPlayerThrowsInvalidPlayerTokenWhenInvalidToken()
+ {
+ self::expectException(SorryInvalidPlayerToken::class);
+ $player = Player::createPlayerWithToken(
+ "mina",
+ 'z'
+ );
+ }
+
+ public function testPlayerCreatedWithPlayingTokenXSuccessfully()
+ {
+ $player = Player::createPlayerWithTokenX("mina");
+
+ self::assertEquals("mina", $player->name());
+ self::assertEquals('x', $player->playingToken());
+ self::assertInstanceOf(PlayerId::class, $player->id());
+ self::assertNotNull($player->id());
+ }
+
+ public function testPlayerCreatedWithPlayingTokenYSuccessfully()
+ {
+ $player = Player::createPlayerWithTokenY("mina");
+
+ self::assertEquals("mina", $player->name());
+ self::assertEquals('y', $player->playingToken());
+ self::assertInstanceOf(PlayerId::class, $player->id());
+ self::assertNotNull($player->id());
+ }
+
+ public function testItThrowsExceptionIfPlayerNameIsShort()
+ {
+ self::expectException(SorryPlayerNameIsTooShort::class);
+ Player::createPlayerWithTokenX("m");
+ }
+
+ public function testItThrowsExceptionIfPlayerNameIsLong()
+ {
+ self::expectException(SorryPlayerNameIsTooLong::class);
+ Player::createPlayerWithTokenX("FGLAHETLDGHAELTRHG53Y693576935sfkhglkshfgshfgkfkgherg53yt3589gh358gh359hg395gh395");
+ }
+}
diff --git a/tests/unit/Engine/Domain/Model/Player/PlayerTokenTest.php b/tests/unit/Engine/Domain/Model/Player/PlayerTokenTest.php
new file mode 100644
index 0000000..f361b8c
--- /dev/null
+++ b/tests/unit/Engine/Domain/Model/Player/PlayerTokenTest.php
@@ -0,0 +1,32 @@
+repository = new InMemoryPlayerRepository();
+ }
+
+ public function testItCanSaveAndGetPlayerByIdentity()
+ {
+ $player = $this->createPlayer();
+
+ $this->repository->add($player);
+
+ self::assertEquals(1, count($this->repository->players()));
+
+ $retrievedPlayer = $this->repository->ofId($player->id());
+ self::assertEquals($player->id(), $retrievedPlayer->id());
+ self::assertEquals($player->name(), $retrievedPlayer->name());
+ self::assertEquals($player->playingToken(), $retrievedPlayer->playingToken());
+ }
+
+ public function testItGenerateNextIdentity()
+ {
+ $nextId = $this->repository->nextIdentity();
+
+ self::assertInstanceOf(PlayerId::class, $nextId);
+ self::assertTrue(Uuid::isValid((string) $nextId));
+ }
+
+ public function testItReturnsNullIfPlayerNotFound()
+ {
+ $playerId = new PlayerId('1');
+ $retrievedPlayer = $this->repository->ofId($playerId);
+ self::assertEquals(null, $retrievedPlayer);
+ }
+
+ protected function createPlayer(string $name = 'foobar')
+ {
+ $player = Player::createPlayerWithTokenX($name);
+
+ return $player;
+ }
+}
diff --git a/tests/unit/WrongCommand.php b/tests/unit/WrongCommand.php
new file mode 100644
index 0000000..9f89cf8
--- /dev/null
+++ b/tests/unit/WrongCommand.php
@@ -0,0 +1,12 @@
+