diff --git a/.gitignore b/.gitignore index 0a90e5ae..65cef096 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ vendor/ +!example/vendor/neighborhoods/kojo .composer/ -phpunit.xml \ No newline at end of file +phpunit.xml +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index ba5a7688..ef378715 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,6 @@ A distributed task manager. ### Example usage ```bash $ bin/kojo process:pool:server:start $PWD/example -``` \ No newline at end of file +``` + +### Setting up a Worker. \ No newline at end of file diff --git a/composer.json b/composer.json index e0f068ae..b1409d43 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "neighborhoods/kojo", "type": "library", "description": "Neighborhoods Kōjō is a distributed task manager.", - "license": "proprietary", + "license": "MIT", "keywords": [], "authors": [ { @@ -10,6 +10,9 @@ "email": "brad.wilson@neighborhoods.com" } ], + "config": { + "sort-packages": true + }, "repositories": [ { "type": "composer", @@ -28,20 +31,21 @@ "symfony/finder": "^4.0", "zendframework/zend-db": "^2.8", "dragonmantank/cron-expression": "^2.0", - "neighborhoods/pylon": "^1.0", "doctrine/dbal": "^2.7" }, "require-dev": { "phpunit/phpunit": "^7.0", - "phpunit/dbunit": "^4.0", - "neighborhoods/scaffolding": "^1.0" + "phpunit/dbunit": "^4.0" }, "bin": [ - "bin/kojo" + "bin/kojo", + "scaffolding/bin/scaffolding.php" ], "autoload": { "psr-4": { - "Neighborhoods\\Kojo\\": "src" + "Neighborhoods\\Kojo\\": "src", + "Neighborhoods\\Scaffolding\\": "scaffolding/src", + "Neighborhoods\\Pylon\\": "pylon/src" } }, "autoload-dev": { diff --git a/composer.lock b/composer.lock index 22506f5d..142b6975 100644 --- a/composer.lock +++ b/composer.lock @@ -4,76 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "3382a0a8f83c6ac1ba27f686535d85bd", + "content-hash": "86aebed19a3392bb2962386ac7369c61", "packages": [ - { - "name": "doctrine/annotations", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^7.1" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2017-12-06T07:11:42+00:00" - }, { "name": "doctrine/cache", "version": "v1.7.1", @@ -148,170 +80,33 @@ ], "time": "2017-08-25T07:02:50+00:00" }, - { - "name": "doctrine/collections", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "~0.1@dev", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2017-07-22T10:37:32+00:00" - }, - { - "name": "doctrine/common", - "version": "v2.8.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/common.git", - "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/f68c297ce6455e8fd794aa8ffaf9fa458f6ade66", - "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66", - "shasum": "" - }, - "require": { - "doctrine/annotations": "1.*", - "doctrine/cache": "1.*", - "doctrine/collections": "1.*", - "doctrine/inflector": "1.*", - "doctrine/lexer": "1.*", - "php": "~7.1" - }, - "require-dev": { - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Common Library for Doctrine projects", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "collections", - "eventmanager", - "persistence", - "spl" - ], - "time": "2017-08-31T08:43:38+00:00" - }, { "name": "doctrine/dbal", - "version": "v2.7.1", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "11037b4352c008373561dc6fc836834eed80c3b5" + "reference": "5140a64c08b4b607b9bedaae0cedd26f04a0e621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/11037b4352c008373561dc6fc836834eed80c3b5", - "reference": "11037b4352c008373561dc6fc836834eed80c3b5", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/5140a64c08b4b607b9bedaae0cedd26f04a0e621", + "reference": "5140a64c08b4b607b9bedaae0cedd26f04a0e621", "shasum": "" }, "require": { - "doctrine/common": "^2.7.1", + "doctrine/cache": "^1.0", + "doctrine/event-manager": "^1.0", "ext-pdo": "*", "php": "^7.1" }, "require-dev": { "doctrine/coding-standard": "^4.0", - "phpunit/phpunit": "^7.0", + "jetbrains/phpstorm-stubs": "^2018.1.2", + "phpstan/phpstan": "^0.10.1", + "phpunit/phpunit": "^7.1.2", "phpunit/phpunit-mock-objects": "!=3.2.4,!=3.2.5", - "symfony/console": "^2.0.5||^3.0", + "symfony/console": "^2.0.5|^3.0|^4.0", "symfony/phpunit-bridge": "^3.4.5|^4.0.5" }, "suggest": { @@ -323,7 +118,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "3.0.x-dev" } }, "autoload": { @@ -361,37 +157,41 @@ "persistence", "queryobject" ], - "time": "2018-04-07T18:44:18+00:00" + "time": "2018-07-13T03:16:35+00:00" }, { - "name": "doctrine/inflector", - "version": "v1.3.0", + "name": "doctrine/event-manager", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + "url": "https://github.com/doctrine/event-manager.git", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", "shasum": "" }, "require": { "php": "^7.1" }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, "require-dev": { - "phpunit/phpunit": "^6.2" + "doctrine/coding-standard": "^4.0", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + "Doctrine\\Common\\": "lib/Doctrine/Common" } }, "notification-url": "https://packagist.org/downloads/", @@ -418,84 +218,33 @@ { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" - } - ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "inflection", - "pluralize", - "singularize", - "string" - ], - "time": "2018-01-09T20:05:19+00:00" - }, - { - "name": "doctrine/lexer", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" }, { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "Doctrine Event Manager component", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", "keywords": [ - "lexer", - "parser" + "event", + "eventdispatcher", + "eventmanager" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2018-06-11T11:59:03+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "3f00985deec8df53d4cc1e5c33619bda1ee309a5" + "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/3f00985deec8df53d4cc1e5c33619bda1ee309a5", - "reference": "3f00985deec8df53d4cc1e5c33619bda1ee309a5", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/92a2c3768d50e21a1f26a53cb795ce72806266c5", + "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5", "shasum": "" }, "require": { @@ -531,45 +280,7 @@ "cron", "schedule" ], - "time": "2018-04-06T15:51:55+00:00" - }, - { - "name": "neighborhoods/pylon", - "version": "1.0.0", - "source": { - "type": "git", - "url": "git@github.com:neighborhoods/pylon.git", - "reference": "395e9d30b4ef5ba3e117851be97587464fc101e3" - }, - "dist": { - "type": "tar", - "url": "https://satis.neighborhoods.com/dist/neighborhoods/pylon/neighborhoods-pylon-395e9d30b4ef5ba3e117851be97587464fc101e3-zip-d14393.tar", - "reference": "395e9d30b4ef5ba3e117851be97587464fc101e3", - "shasum": "c917dd0a4b6cedf90ea4e16c5e0aae8c75814c53" - }, - "require": { - "php": ">=7.1", - "symfony/config": "^4.0", - "symfony/dependency-injection": "^4.0", - "symfony/finder": "^4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Neighborhoods\\Pylon\\": "src" - } - }, - "license": [ - "proprietary" - ], - "authors": [ - { - "name": "Brad Wilson", - "email": "brad.wilson@neighborhoods.com" - } - ], - "description": "Neighborhoods Pylon is a collection of useful, but most importantly, generic objects.", - "time": "2018-04-18T19:29:35+00:00" + "time": "2018-06-06T03:12:17+00:00" }, { "name": "psr/cache", @@ -763,16 +474,16 @@ }, { "name": "symfony/cache", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "bd6d0a969c80b00630e9e2f0ab14ad4518712ec8" + "reference": "42191caaf21ab7be0eb623d6c572e0b2932a8880" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/bd6d0a969c80b00630e9e2f0ab14ad4518712ec8", - "reference": "bd6d0a969c80b00630e9e2f0ab14ad4518712ec8", + "url": "https://api.github.com/repos/symfony/cache/zipball/42191caaf21ab7be0eb623d6c572e0b2932a8880", + "reference": "42191caaf21ab7be0eb623d6c572e0b2932a8880", "shasum": "" }, "require": { @@ -797,7 +508,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -828,20 +539,20 @@ "caching", "psr6" ], - "time": "2018-05-16T09:05:32+00:00" + "time": "2018-07-03T17:58:50+00:00" }, { "name": "symfony/config", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "aaef656e99c5396d6118970abd1ceb11fa74551d" + "reference": "e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/aaef656e99c5396d6118970abd1ceb11fa74551d", - "reference": "aaef656e99c5396d6118970abd1ceb11fa74551d", + "url": "https://api.github.com/repos/symfony/config/zipball/e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5", + "reference": "e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5", "shasum": "" }, "require": { @@ -864,7 +575,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -891,20 +602,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-05-16T09:05:32+00:00" + "time": "2018-06-20T11:15:17+00:00" }, { "name": "symfony/console", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "058f120b8e06ebcd7b211de5ffae07b2db00fbdd" + "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/058f120b8e06ebcd7b211de5ffae07b2db00fbdd", - "reference": "058f120b8e06ebcd7b211de5ffae07b2db00fbdd", + "url": "https://api.github.com/repos/symfony/console/zipball/5c31f6a97c1c240707f6d786e7e59bfacdbc0219", + "reference": "5c31f6a97c1c240707f6d786e7e59bfacdbc0219", "shasum": "" }, "require": { @@ -932,7 +643,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -959,20 +670,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-16T09:05:32+00:00" + "time": "2018-07-16T14:05:40+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "4b272d65e9c0d2d40e2d23bab3c7a184ad4a3a18" + "reference": "62912ab79facdbdaa0849f6c2fe4734b7b60f5cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/4b272d65e9c0d2d40e2d23bab3c7a184ad4a3a18", - "reference": "4b272d65e9c0d2d40e2d23bab3c7a184ad4a3a18", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/62912ab79facdbdaa0849f6c2fe4734b7b60f5cc", + "reference": "62912ab79facdbdaa0849f6c2fe4734b7b60f5cc", "shasum": "" }, "require": { @@ -980,7 +691,7 @@ "psr/container": "^1.0" }, "conflict": { - "symfony/config": "<3.4", + "symfony/config": "<4.1.1", "symfony/finder": "<3.4", "symfony/proxy-manager-bridge": "<3.4", "symfony/yaml": "<3.4" @@ -989,7 +700,7 @@ "psr/container-implementation": "1.0" }, "require-dev": { - "symfony/config": "~3.4|~4.0", + "symfony/config": "~4.1", "symfony/expression-language": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0" }, @@ -1003,7 +714,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -1030,20 +741,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-05-25T11:57:52+00:00" + "time": "2018-07-16T14:05:40+00:00" }, { "name": "symfony/expression-language", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "b826c255f22333eccd3365734d2c4e150c284843" + "reference": "81653bbb8e0feff271bebfdea492386f1c75c098" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/b826c255f22333eccd3365734d2c4e150c284843", - "reference": "b826c255f22333eccd3365734d2c4e150c284843", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/81653bbb8e0feff271bebfdea492386f1c75c098", + "reference": "81653bbb8e0feff271bebfdea492386f1c75c098", "shasum": "" }, "require": { @@ -1053,7 +764,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -1080,20 +791,20 @@ ], "description": "Symfony ExpressionLanguage Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:38:00+00:00" + "time": "2018-06-21T11:15:46+00:00" }, { "name": "symfony/filesystem", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7a69e728e9f0044958c2fd7d72bfe5e7bd1a4d04" + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7a69e728e9f0044958c2fd7d72bfe5e7bd1a4d04", - "reference": "7a69e728e9f0044958c2fd7d72bfe5e7bd1a4d04", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", "shasum": "" }, "require": { @@ -1103,7 +814,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -1130,20 +841,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-05-16T09:05:32+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "symfony/finder", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8c633f5a815903a1fe6e3fc135f207267a8a79af" + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8c633f5a815903a1fe6e3fc135f207267a8a79af", - "reference": "8c633f5a815903a1fe6e3fc135f207267a8a79af", + "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", "shasum": "" }, "require": { @@ -1152,7 +863,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -1179,7 +890,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-05-16T09:05:32+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1297,16 +1008,16 @@ }, { "name": "symfony/yaml", - "version": "v4.0.11", + "version": "v4.1.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "048b1be5fb96e73ff1d065f5e7e23f84415ac907" + "reference": "80e4bfa9685fc4a09acc4a857ec16974a9cd944e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/048b1be5fb96e73ff1d065f5e7e23f84415ac907", - "reference": "048b1be5fb96e73ff1d065f5e7e23f84415ac907", + "url": "https://api.github.com/repos/symfony/yaml/zipball/80e4bfa9685fc4a09acc4a857ec16974a9cd944e", + "reference": "80e4bfa9685fc4a09acc4a857ec16974a9cd944e", "shasum": "" }, "require": { @@ -1325,7 +1036,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -1352,7 +1063,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-05-07T07:12:24+00:00" + "time": "2018-05-30T07:26:09+00:00" }, { "name": "zendframework/zend-db", @@ -1516,25 +1227,28 @@ }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -1557,73 +1271,26 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" - }, - { - "name": "neighborhoods/scaffolding", - "version": "1.1.0", - "source": { - "type": "git", - "url": "git@github.com:neighborhoods/Scaffolding.git", - "reference": "b054606cbc696cbe369b9b0de217076da3fba6e5" - }, - "dist": { - "type": "tar", - "url": "https://satis.neighborhoods.com/dist/neighborhoods/scaffolding/neighborhoods-scaffolding-b054606cbc696cbe369b9b0de217076da3fba6e5-zip-990c14.tar", - "reference": "b054606cbc696cbe369b9b0de217076da3fba6e5", - "shasum": "71f21e3c52bf74c7acf2d222962ae14fcf876ade" - }, - "require": { - "neighborhoods/pylon": "^1.0.0", - "php": ">=7.1", - "phpunit/dbunit": "^4.0", - "phpunit/phpunit": "^7.0", - "symfony/config": "^4.0", - "symfony/dependency-injection": "^4.0", - "symfony/expression-language": "^4.0", - "symfony/filesystem": "^4.0", - "symfony/finder": "^4.0", - "symfony/yaml": "^4.0" - }, - "bin": [ - "bin/scaffolding.php" - ], - "type": "library", - "autoload": { - "psr-4": { - "Neighborhoods\\Scaffolding\\": "src" - } - }, - "license": [ - "proprietary" - ], - "authors": [ - { - "name": "Neighborhoods.com", - "email": "brad.wilson@neighborhoods.com" - } - ], - "description": "Neighborhoods Scaffolding is meant to make the creation of contract testing easy and fast.", - "time": "2018-05-22T21:18:48+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "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/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", + "phar-io/version": "^2.0", "php": "^5.6 || ^7.0" }, "type": "library", @@ -1659,20 +1326,20 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "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/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { @@ -1706,7 +1373,7 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "time": "2018-07-08T19:19:57+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -1977,23 +1644,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "6.0.5", + "version": "6.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4cab20a326d14de7575a8e235c70d879b569a57a" + "reference": "865662550c384bc1db7e51d29aeda1c2c161d69a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a", - "reference": "4cab20a326d14de7575a8e235c70d879b569a57a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/865662550c384bc1db7e51d29aeda1c2c161d69a", + "reference": "865662550c384bc1db7e51d29aeda1c2c161d69a", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", "php": "^7.1", - "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-file-iterator": "^2.0", "phpunit/php-text-template": "^1.2.1", "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", @@ -2036,29 +1703,29 @@ "testing", "xunit" ], - "time": "2018-05-28T11:49:20+00:00" + "time": "2018-06-01T07:51:50+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cecbc684605bb0cc288828eb5d65d93d5c676d3c", + "reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -2073,7 +1740,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -2083,7 +1750,7 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2018-06-11T11:44:00+00:00" }, { "name": "phpunit/php-text-template", @@ -2226,34 +1893,34 @@ }, { "name": "phpunit/phpunit", - "version": "7.1.5", + "version": "7.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ca64dba53b88aba6af32aebc6b388068db95c435" + "reference": "8e878aff7917ef66e702e03d1359b16eee254e2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435", - "reference": "ca64dba53b88aba6af32aebc6b388068db95c435", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8e878aff7917ef66e702e03d1359b16eee254e2c", + "reference": "8e878aff7917ef66e702e03d1359b16eee254e2c", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.1", - "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.0", - "phpunit/phpunit-mock-objects": "^6.1.1", "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", "sebastian/environment": "^3.1", @@ -2263,10 +1930,14 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, "require-dev": { "ext-pdo": "*" }, "suggest": { + "ext-soap": "*", "ext-xdebug": "*", "phpunit/php-invoker": "^2.0" }, @@ -2276,7 +1947,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "7.2-dev" } }, "autoload": { @@ -2302,63 +1973,7 @@ "testing", "xunit" ], - "time": "2018-04-29T15:09:19+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "6.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", - "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.1", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-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": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2018-05-29T13:54:20+00:00" + "time": "2018-07-15T05:20:50+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -2407,16 +2022,16 @@ }, { "name": "sebastian/comparator", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ed5fd2281113729f1ebcc64d101ad66028aeb3d5", - "reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { @@ -2467,20 +2082,20 @@ "compare", "equality" ], - "time": "2018-04-18T13:33:00+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/e09160918c66281713f1c324c1f4c4c3037ba1e8", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { @@ -2523,7 +2138,7 @@ "unidiff", "unified diff" ], - "time": "2018-02-01T13:45:15+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", diff --git a/example/composer.json b/example/composer.json index 4194cd66..0f946582 100644 --- a/example/composer.json +++ b/example/composer.json @@ -2,7 +2,7 @@ "name": "neighborhoods/kojo_example", "type": "library", "description": "Neighborhoods Kōjō Example provides an example of using kōjō and protean.", - "license": "proprietary", + "license": "MIT", "keywords": [], "authors": [ { @@ -10,6 +10,9 @@ "email": "brad.wilson@neighborhoods.com" } ], + "config": { + "sort-packages": true + }, "repositories": [ { "type": "composer", @@ -18,13 +21,11 @@ ], "require": { "php": ">=7.1", - "neighborhoods/kojo": "^2.0.0", - "neighborhoods/pylon": "^1.0.0", + "neighborhoods/kojo": "^3.0.0", "aws/aws-sdk-php": "2.*" }, "require-dev": { - "php": ">=7.1", - "neighborhoods/scaffolding": "^1.0.0" + "php": ">=7.1" }, "autoload": { "psr-4": { diff --git a/example/src/V1/Worker.php b/example/src/V1/Worker.php index 2288a678..ec048065 100644 --- a/example/src/V1/Worker.php +++ b/example/src/V1/Worker.php @@ -14,9 +14,11 @@ class Worker implements WorkerInterface public function work(): WorkerInterface { + $this->fireEvent('new_worker'); if ($this->getApiV1WorkerService()->getTimesCrashed() === 0) { // Wait for one message to become available. if($this->getV1WorkerQueue()->hasNextMessage()){ + $this->fireEvent('message_received'); // Schedule another kōjō job of the same type. $this->_scheduleNextJob(); @@ -31,6 +33,7 @@ public function work(): WorkerInterface } // Tell Kōjō that we are done and all is well. + $this->fireEvent('complete_success'); $this->getApiV1WorkerService()->requestCompleteSuccess()->applyRequest(); // Fluent interfaces for the love of Pete. @@ -41,6 +44,14 @@ protected function _delegateWork(): WorkerInterface { $workerDelegate = $this->getV1WorkerDelegateRepository()->getV1NewWorkerDelegate(); $workerDelegate->setV1WorkerQueueMessage($this->getV1WorkerQueue()->getNextMessage()); + + $this->fireEvent('working'); + if (extension_loaded('newrelic')) { + newrelic_end_transaction(); + newrelic_start_transaction(ini_get("newrelic.appname")); // start recording a new transaction + newrelic_name_transaction(self::JOB_TYPE_CODE); + } + $workerDelegate->businessLogic(); return $this; @@ -48,6 +59,7 @@ protected function _delegateWork(): WorkerInterface protected function _scheduleNextJob(): WorkerInterface { + $this->fireEvent('schedule_next_job'); $newJobScheduler = $this->getApiV1WorkerService()->getNewJobScheduler(); $newJobScheduler->setJobTypeCode(self::JOB_TYPE_CODE) ->setWorkAtDateTime(new \DateTime('now')) @@ -55,4 +67,14 @@ protected function _scheduleNextJob(): WorkerInterface return $this; } -} \ No newline at end of file + + protected function fireEvent(string $event) : WorkerInterface + { + if (extension_loaded('newrelic')) { + newrelic_record_custom_event($event, ['job_type' => self::JOB_TYPE_CODE]); + } + $this->getApiV1WorkerService()->getLogger()->info($event, ['job_type' => self::JOB_TYPE_CODE]); + + return $this; + } +} diff --git a/pylon/src/Data/Property/Defensive/AwareTrait.php b/pylon/src/Data/Property/Defensive/AwareTrait.php new file mode 100644 index 00000000..fa9bd37d --- /dev/null +++ b/pylon/src/Data/Property/Defensive/AwareTrait.php @@ -0,0 +1,52 @@ +_exists($propertyName), new \LogicException($propertyName . ' is already created.')); + $this->_defendedProperties[$propertyName] = $propertyValue; + + return $this; + } + + protected function &_read(string $propertyName) + { + assert($this->_exists($propertyName), new \LogicException($propertyName . ' is not created.')); + + return $this->_defendedProperties[$propertyName]; + } + + protected function _update(string $propertyName, $propertyValue) + { + assert($this->_exists($propertyName), new \LogicException($propertyName . ' is not created.')); + $this->_defendedProperties[$propertyName] = $propertyValue; + + return $this; + } + + protected function _createOrUpdate(string $propertyName, $propertyValue) + { + $this->_defendedProperties[$propertyName] = $propertyValue; + + return $this; + } + + protected function _delete(string $propertyName) + { + assert($this->_exists($propertyName), new \LogicException($propertyName . ' is not created.')); + unset($this->_defendedProperties[$propertyName]); + + return $this; + } + + protected function _exists(string $propertyName): bool + { + return isset($this->_defendedProperties[$propertyName]) ? true : false; + } +} \ No newline at end of file diff --git a/pylon/src/Data/Property/Persistent/AwareTrait.php b/pylon/src/Data/Property/Persistent/AwareTrait.php new file mode 100644 index 00000000..35f3ab2d --- /dev/null +++ b/pylon/src/Data/Property/Persistent/AwareTrait.php @@ -0,0 +1,127 @@ +_hasPersistentProperties()) { + $this->_persistentProperties = $persistentProperties; + $this->_changedPersistentProperties = $persistentProperties; + }else { + throw new \LogicException('Persistent properties is already set.'); + } + + return $this; + } + + protected function _emptyPersistentProperties() + { + if ($this->_hasPersistentProperties()) { + $this->_persistentProperties = []; + $this->_changedPersistentProperties = []; + }else { + throw new \LogicException('Persistent properties is not set.'); + } + + return $this; + } + + protected function _hydrate(array $persistentProperties) + { + $this->_persistentProperties = array_replace($this->_persistentProperties, $persistentProperties); + $this->_changedPersistentProperties = []; + + return $this; + } + + protected function _createPersistentProperty(string $persistentPropertyName, $persistentPropertyValue) + { + if ($this->_hasPersistentProperty($persistentPropertyName)) { + throw new \LogicException('Persistent property ' . $persistentPropertyName . ' is already set.'); + }else { + $this->_persistentProperties[$persistentPropertyName] = $persistentPropertyValue; + $this->_changedPersistentProperties[$persistentPropertyName] = $persistentPropertyValue; + } + + return $this; + } + + protected function _readPersistentProperty(string $persistentPropertyName) + { + if (!isset($this->_persistentProperties[$persistentPropertyName])) { + throw new \LogicException('Persistent property ' . $persistentPropertyName . ' is not set'); + } + + return $this->_persistentProperties[$persistentPropertyName]; + } + + protected function _unsetPersistentProperty(string $persistentPropertyName) + { + if ($this->_hasPersistentProperty($persistentPropertyName)) { + unset($this->_persistentProperties[$persistentPropertyName]); + $this->_changedPersistentProperties[$persistentPropertyName] = null; + }else { + throw new \LogicException('Persistent property is not set.'); + } + + return $this; + } + + public function readPersistentProperties(): array + { + if (!$this->_hasPersistentProperties()) { + throw new \LogicException('Persistent properties is not set.'); + } + + return $this->_persistentProperties; + } + + protected function _createOrUpdatePersistentProperty(string $persistentPropertyName, $persistentPropertyValue) + { + $this->_persistentProperties[$persistentPropertyName] = $persistentPropertyValue; + $this->_changedPersistentProperties[$persistentPropertyName] = $persistentPropertyValue; + + return $this; + } + + protected function _hasPersistentProperty(string $persistentPropertyName): bool + { + return isset($this->_persistentProperties[$persistentPropertyName]); + } + + protected function _hasPersistentProperties(): bool + { + return empty($this->_persistentProperties) ? false : true; + } + + public function hasChangedPersistentProperties(): bool + { + return empty($this->_changedPersistentProperties) ? false : true; + } + + protected function _readChangedPersistentProperties(): array + { + if (!$this->hasChangedPersistentProperties()) { + throw new \LogicException('Changed persistent properties is not set.'); + } + + return $this->_changedPersistentProperties; + } + + protected function _emptyChangedPersistentProperties() + { + if ($this->hasChangedPersistentProperties()) { + $this->_changedPersistentProperties = []; + }else { + throw new \LogicException('Changed persistent properties is not set.'); + } + + return $this; + } +} \ No newline at end of file diff --git a/pylon/src/DependencyInjection/ContainerBuilder/Facade.php b/pylon/src/DependencyInjection/ContainerBuilder/Facade.php new file mode 100644 index 00000000..6fef6f66 --- /dev/null +++ b/pylon/src/DependencyInjection/ContainerBuilder/Facade.php @@ -0,0 +1,68 @@ +_getFinderArray()->append($finder); + + return $this; + } + + protected function _getFinderArray(): FinderArrayInterface + { + if ($this->_finderArray === null) { + $this->_finderArray = new FinderArray(); + } + + return $this->_finderArray; + } + + protected function _getYamlServicesFilePaths(): array + { + if (empty($this->_yamlServicesFilePaths)) { + foreach ($this->_getFinderArray() as $finder) { + foreach ($finder as $directoryPath => $file) { + $this->_yamlServicesFilePaths[] = $file->getPathname(); + } + } + } + + return $this->_yamlServicesFilePaths; + } + + public function getContainerBuilder(): ContainerBuilder + { + if ($this->_containerBuilder === null) { + $containerBuilder = new ContainerBuilder(); + $loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__)); + foreach ($this->_getYamlServicesFilePaths() as $servicesYmlFilePath) { + $loader->import($servicesYmlFilePath); + } + $passes = [new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass()]; + $repeatedPass = new RepeatedPass($passes); + $repeatedPass->process($containerBuilder); + $containerBuilder->compile(true); + $this->_containerBuilder = $containerBuilder; + } + + return $this->_containerBuilder; + } +} \ No newline at end of file diff --git a/pylon/src/DependencyInjection/ContainerBuilder/Facade/AwareTrait.php b/pylon/src/DependencyInjection/ContainerBuilder/Facade/AwareTrait.php new file mode 100644 index 00000000..5f4b966b --- /dev/null +++ b/pylon/src/DependencyInjection/ContainerBuilder/Facade/AwareTrait.php @@ -0,0 +1,39 @@ +_create(FacadeInterface::class, $dependencyInjectionContainerBuilderFacade); + + return $this; + } + + protected function _getDependencyInjectionContainerBuilderFacade(): FacadeInterface + { + return $this->_read(FacadeInterface::class); + } + + protected function _getDependencyInjectionContainerBuilderFacadeClone(): FacadeInterface + { + return clone $this->_getDependencyInjectionContainerBuilderFacade(); + } + + protected function _hasDependencyInjectionContainerBuilderFacade(): bool + { + return $this->_exists(FacadeInterface::class); + } + + protected function _unsetDependencyInjectionContainerBuilderFacade(): self + { + $this->_delete(FacadeInterface::class); + + return $this; + } +} \ No newline at end of file diff --git a/pylon/src/DependencyInjection/ContainerBuilder/FacadeInterface.php b/pylon/src/DependencyInjection/ContainerBuilder/FacadeInterface.php new file mode 100644 index 00000000..ad7e3389 --- /dev/null +++ b/pylon/src/DependencyInjection/ContainerBuilder/FacadeInterface.php @@ -0,0 +1,14 @@ +_assertValidArrayType(...$finders); + } + + parent::__construct($finders, $flags); + } + + public function offsetGet($index): Finder + { + return $this->_assertValidArrayItemType(parent::offsetGet($index)); + } + + /** @param Finder $finder */ + public function offsetSet($index, $finder) + { + parent::offsetSet($index, $this->_assertValidArrayItemType($finder)); + } + + /** @param Finder $finder */ + public function append($finder) + { + $this->_assertValidArrayItemType($finder); + parent::append($finder); + } + + public function current(): Finder + { + return parent::current(); + } + + protected function _assertValidArrayItemType(Finder $finder) + { + return $finder; + } + + protected function _assertValidArrayType(Finder ...$finders): FinderArrayInterface + { + return $this; + } +} \ No newline at end of file diff --git a/pylon/src/Symfony/Component/FinderArrayInterface.php b/pylon/src/Symfony/Component/FinderArrayInterface.php new file mode 100644 index 00000000..c12f1cc7 --- /dev/null +++ b/pylon/src/Symfony/Component/FinderArrayInterface.php @@ -0,0 +1,22 @@ +setTimezone($this->getDateTimeZone($timezoneCode)); + + return $now; + } + + public function getUnixReferenceTimeNow(): string + { + return sprintf('%f', microtime(true)); + } + + public function validateTimestamp(string $timestamp, string $format = TimeInterface::MYSQL_DATE_TIME_FORMAT): bool + { + $dateTime = \DateTime::createFromFormat($format, $timestamp); + + return $dateTime && $dateTime->format($format) == $timestamp; + } + + public function getDateTimeZone(string $timezoneCode = self::DEFAULT_TIMEZONE_CODE): \DateTimeZone + { + if (!isset($this->_dateTimeZones[$timezoneCode])) { + $dateTimeZone = new \DateTimeZone($timezoneCode); + $this->_dateTimeZones[$timezoneCode] = $dateTimeZone; + } + + return clone $this->_dateTimeZones[$timezoneCode]; + } + + public function getNewDateInterval(string $intervalSpec): \DateInterval + { + return new \DateInterval($intervalSpec); + } +} \ No newline at end of file diff --git a/pylon/src/Time/AwareTrait.php b/pylon/src/Time/AwareTrait.php new file mode 100644 index 00000000..9176f8a0 --- /dev/null +++ b/pylon/src/Time/AwareTrait.php @@ -0,0 +1,31 @@ +_time === null) { + $this->_time = $time; + }else { + throw new \Exception('Time is already set.'); + } + + return $this; + } + + protected function _getTime(): TimeInterface + { + if ($this->_time === null) { + throw new \LogicException('Time is not set.'); + } + + return $this->_time; + } +} \ No newline at end of file diff --git a/pylon/src/TimeInterface.php b/pylon/src/TimeInterface.php new file mode 100644 index 00000000..600c8ef4 --- /dev/null +++ b/pylon/src/TimeInterface.php @@ -0,0 +1,21 @@ +getMessage(); + exit(1); +} + +Bootstrap::setUp(); + +return; \ No newline at end of file diff --git a/scaffolding/src/AbstractTest.php b/scaffolding/src/AbstractTest.php new file mode 100644 index 00000000..34751c21 --- /dev/null +++ b/scaffolding/src/AbstractTest.php @@ -0,0 +1,27 @@ +addServicesYmlFilePath($ymlServiceFilePath); + $testCaseService = $this->getContainerBuilder()->get('neighborhoods.scaffolding.testcase.service'); + $this->setTestCaseService($testCaseService); + + parent::setUp(); + } +} \ No newline at end of file diff --git a/scaffolding/src/Bootstrap.php b/scaffolding/src/Bootstrap.php new file mode 100644 index 00000000..cb197f56 --- /dev/null +++ b/scaffolding/src/Bootstrap.php @@ -0,0 +1,77 @@ +addFinder(self::_getApplicationRootDirectoryFinder()); + self::getContainerBuilderFacade()->addFinder(self::_getScaffoldingRootDirectoryFinder()); + self::getContainerBuilderFacade()->addFinder(self::_getTestApplicationRootDirectoryFinder()); + + return; + } + + protected static function _getScaffoldingRootDirectoryFinder(): Finder + { + if (self::$_scaffoldingRootDirectoryFinder === null) { + $scaffoldingRootDirectoryFinder = new Finder(); + $scaffoldingRootDirectoryFinder->files()->in(self::_getScaffoldingRootDirectoryPath()); + $scaffoldingRootDirectoryFinder->name('*.yml'); + self::$_scaffoldingRootDirectoryFinder = $scaffoldingRootDirectoryFinder; + } + + return self::$_scaffoldingRootDirectoryFinder; + } + + protected static function _getApplicationRootDirectoryFinder(): Finder + { + if (self::$_applicationRootDirectoryFinder === null) { + $applicationRootDirectoryFinder = new Finder(); + $applicationRootDirectoryFinder->files()->in(self::_getScaffoldingRootDirectoryPath() . '/../../../../src'); + $applicationRootDirectoryFinder->name('*.yml'); + self::$_applicationRootDirectoryFinder = $applicationRootDirectoryFinder; + } + + return self::$_applicationRootDirectoryFinder; + } + + protected static function _getTestApplicationRootDirectoryFinder(): Finder + { + if (self::$_testApplicationRootDirectoryFinder === null) { + $testApplicationRootDirectoryFinder = new Finder(); + $testApplicationRootDirectoryFinder->files()->in(self::_getScaffoldingRootDirectoryPath() . '/../../../../tests/Application'); + $testApplicationRootDirectoryFinder->name('*.yml'); + self::$_testApplicationRootDirectoryFinder = $testApplicationRootDirectoryFinder; + } + + return self::$_testApplicationRootDirectoryFinder; + } + + protected static function _getScaffoldingRootDirectoryPath(): string + { + $scaffoldingRootDirectoryPath = dirname(__FILE__); + + return $scaffoldingRootDirectoryPath; + } + + public static function getContainerBuilderFacade(): Facade + { + if (self::$_containerBuilderFacade === null) { + self::$_containerBuilderFacade = new Facade(); + } + + return self::$_containerBuilderFacade; + } +} \ No newline at end of file diff --git a/scaffolding/src/BootstrapInterface.php b/scaffolding/src/BootstrapInterface.php new file mode 100644 index 00000000..41ec68c3 --- /dev/null +++ b/scaffolding/src/BootstrapInterface.php @@ -0,0 +1,8 @@ +_getContainerBuilderFacade()->addFinder($this->_getTestCaseRootDirectoryFinder()); + $containerBuilder = $this->_getContainerBuilderFacade()->getContainerBuilder(); + $this->setTestCaseService($containerBuilder->get('neighborhoods.scaffolding.testcase.service')); + $tearDown = $containerBuilder->get('db.tear_down'); + $tearDown->uninstall(); + $setup = $containerBuilder->get('db.setup'); + $setup->install(); + $redis = $containerBuilder->get('redis.factory')->create(); + $redis->flushAll(); + + parent::setUp(); + } + + protected function getSetUpOperation() + { + return Factory::CLEAN_INSERT(true); + } + + protected function _getTestCaseRootDirectoryFinder(): Finder + { + if ($this->_testRootDirectoryFinder === null) { + $testRootDirectoryFinder = new Finder(); + $testRootDirectoryFinder->files()->in($this->_getTestCaseRootDirectoryPath()); + $testRootDirectoryFinder->exclude(['fixtures']); + $testRootDirectoryFinder->name('*.yml'); + $this->_testRootDirectoryFinder = $testRootDirectoryFinder; + } + + return $this->_testRootDirectoryFinder; + } + + protected function _getTestCaseRootDirectoryPath(): string + { + $reflectionClass = new ReflectionClass($this); + $testClassFilePath = $reflectionClass->getFileName(); + $testClassDirectoryPath = dirname($testClassFilePath); + $testCaseName = $this->getName(); + $testCaseRootDirectoryPath = $testClassDirectoryPath . '/' . $testCaseName; + + return $testCaseRootDirectoryPath; + } + + protected function _getContainerBuilderFacade(): Facade + { + if ($this->_containerBuilderFacade === null) { + $this->_containerBuilderFacade = Bootstrap::getContainerBuilderFacade(); + } + + return $this->_containerBuilderFacade; + } + + protected function getConnection() + { + $pdo = $this->_getContainerBuilderFacade()->getContainerBuilder()->get('pdo.builder')->getPdo(); + + return $this->createDefaultDBConnection($pdo); + } + + protected function getDataSet() + { + $fixturesDirectoryPath = $this->_getTestCaseRootDirectoryPath() . '/fixtures'; + $finder = new Finder(); + $finder->files()->in($fixturesDirectoryPath); + $finder->sortByName(); + foreach ($finder as $filePath => $file) { + $this->_addFilePathToYmlDataSet($filePath); + } + + return $this->_getEvaluatedYmlDataSet(); + } + + protected function _addFilePathToYmlDataSet(string $ymlDataSetFilePath): AbstractTest + { + if (!$this->_exists(YamlDataSet::class)) { + $ymlParser = new SymfonyYamlParser(); + $ymlParser->setFlags(Yaml::PARSE_CUSTOM_TAGS); + $this->_create(YamlDataSet::class, new YamlDataSet($ymlDataSetFilePath, $ymlParser)); + } else { + $this->_getYmlDataSet()->addYamlFile($ymlDataSetFilePath); + } + + return $this; + } + + protected function _getEvaluatedYmlDataSet(): YamlDataSet + { + /** @var DefaultTableIterator $tableIterator */ + $tableIterator = $this->_getYmlDataSet()->getIterator(); + $expressionLanguage = new ExpressionLanguage(); + $expressionValues = &$this->_getTestCaseService()->getExpressionValues(); + /** @var DefaultTable $table */ + foreach ($tableIterator as $table) { + $rowCount = $table->getRowCount(); + while (--$rowCount >= 0) { + $row = $table->getRow($rowCount); + foreach ($row as $columnName => $columnValue) { + if ($columnValue instanceof TaggedValue) { + if ($columnValue->getTag() === self::YML_SIGIL_PREFIX_FIXTURE_EXPRESSION) { + $expression = $columnValue->getValue(); + $expressedValue = $expressionLanguage->evaluate($expression, $expressionValues); + $table->setValue($rowCount, $columnName, $expressedValue); + } + } + } + } + } + + return $this->_getYmlDataSet(); + } + + protected function _getYmlDataSet(): YamlDataSet + { + return $this->_read(YamlDataSet::class); + } + + protected function _getTime(): TimeInterface + { + return $this->_getContainerBuilderFacade()->getContainerBuilder()->get('nhds.toolkit.time'); + } +} \ No newline at end of file diff --git a/scaffolding/src/Fixture/Expression/NumberPool.php b/scaffolding/src/Fixture/Expression/NumberPool.php new file mode 100644 index 00000000..18cae7c9 --- /dev/null +++ b/scaffolding/src/Fixture/Expression/NumberPool.php @@ -0,0 +1,54 @@ +isPoolInitialized($poolName)) { + $this->_initializePoolName($poolName); + } + + return $this->_nextNumberPool[$poolName]; + } + + public function advance(string $poolName): NumberPool + { + if (!$this->isPoolInitialized($poolName)) { + $this->_initializePoolName($poolName); + } + ++$this->_nextNumberPool[$poolName]; + + return $this; + } + + public function rewind(string $poolName): NumberPool + { + if (!$this->isPoolInitialized($poolName)) { + $this->_initializePoolName($poolName); + } + ++$this->_nextNumberPool[$poolName]; + + return $this; + } + + public function isPoolInitialized(string $poolName): bool + { + return isset($this->_nextNumberPool[$poolName]) ? true : false; + } + + protected function _initializePoolName(string $poolName): NumberPool + { + if (isset($this->_nextNumberPool[$poolName])) { + throw new \LogicException('Pool name is already initialized.'); + } + + $this->_nextNumberPool[$poolName] = 0; + + return $this; + } +} \ No newline at end of file diff --git a/scaffolding/src/Fixture/Expression/Value/AwareTrait.php b/scaffolding/src/Fixture/Expression/Value/AwareTrait.php new file mode 100644 index 00000000..bb1d321e --- /dev/null +++ b/scaffolding/src/Fixture/Expression/Value/AwareTrait.php @@ -0,0 +1,33 @@ +_expressionValues[$valueName])) { + throw new \LogicException('Expression value with value name "' . $valueName . '" is already set."'); + } + $this->_expressionValues[$valueName] = $value; + + return $this; + } + + public function hasExpressionValues(): bool + { + return empty($this->_expressionValues) ? false : true; + } + + public function &getExpressionValues(): array + { + if (empty($this->_expressionValues)) { + throw new \LogicException('Expression values is empty.'); + } + + return $this->_expressionValues; + } +} \ No newline at end of file diff --git a/scaffolding/src/PHPUnit/DbUnit/DataSet/SymfonyYamlParser.php b/scaffolding/src/PHPUnit/DbUnit/DataSet/SymfonyYamlParser.php new file mode 100644 index 00000000..c15f6d22 --- /dev/null +++ b/scaffolding/src/PHPUnit/DbUnit/DataSet/SymfonyYamlParser.php @@ -0,0 +1,31 @@ +_create(self::PROP_FLAGS, $flags); + + return $this; + } + + protected function _getFlags() + { + return $this->_read(self::PROP_FLAGS); + } + + public function parseYaml($yamlFile) + { + return Yaml::parse(\file_get_contents($yamlFile), $this->_getFlags()); + } +} \ No newline at end of file diff --git a/scaffolding/src/TestCase/ContainerBuilder/AwareTrait.php b/scaffolding/src/TestCase/ContainerBuilder/AwareTrait.php new file mode 100644 index 00000000..43405375 --- /dev/null +++ b/scaffolding/src/TestCase/ContainerBuilder/AwareTrait.php @@ -0,0 +1,38 @@ +_exists('test_container_builder')) { + $reflectionClass = new ReflectionClass($this); + $testClassFilePath = $reflectionClass->getFileName(); + $testClassDirectoryPath = dirname($testClassFilePath); + $shortName = $reflectionClass->getShortName(); + $testServicesYamlFilePath = $testClassDirectoryPath . '/config/' . $shortName . '.yml'; + $testContainerBuilder = new ContainerBuilder(); + $loader = new YamlFileLoader($testContainerBuilder, new FileLocator(__DIR__)); + $loader->load($testServicesYamlFilePath); + $passes = [new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass()]; + $repeatedPass = new RepeatedPass($passes); + $repeatedPass->process($testContainerBuilder); + $testContainerBuilder->set('test_container_builder', $testContainerBuilder); + $testContainerBuilder->compile(true); + $this->_create('test_container_builder', $testContainerBuilder); + } + + return $this->_read('test_container_builder'); + } +} \ No newline at end of file diff --git a/scaffolding/src/TestCase/Service.php b/scaffolding/src/TestCase/Service.php new file mode 100644 index 00000000..1fbe4f18 --- /dev/null +++ b/scaffolding/src/TestCase/Service.php @@ -0,0 +1,13 @@ +_testCaseService === null) { + $this->_testCaseService = $testCaseService; + }else { + throw new \LogicException('Test case service is already set.'); + } + + return $this; + } + + protected function _getTestCaseService(): TestCase\Service + { + if ($this->_testCaseService === null) { + throw new \LogicException('Test case service is not set.'); + } + + return $this->_testCaseService; + } +} \ No newline at end of file diff --git a/scaffolding/src/TestCase/ServiceInterface.php b/scaffolding/src/TestCase/ServiceInterface.php new file mode 100644 index 00000000..9b74d9c4 --- /dev/null +++ b/scaffolding/src/TestCase/ServiceInterface.php @@ -0,0 +1,8 @@ +_getInput()->getArgument(self::ARG_SERVICES_YML_FILE_PATH); + $arguments[] = self::OPT_YSDP . $this->_getInput()->getArgument(self::ARG_SERVICES_YML_ROOT_DIRECTORY_PATH); foreach ($this->_getInput()->getOption(self::OPT_SERVICES_YML_DIRECTORY_PATH) as $servicesYmlFilePath) { $arguments[] = self::OPT_YSDP . $servicesYmlFilePath; } diff --git a/src/Console/CommandAbstract.php b/src/Console/CommandAbstract.php index 707161e2..a4647f0a 100644 --- a/src/Console/CommandAbstract.php +++ b/src/Console/CommandAbstract.php @@ -5,6 +5,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Neighborhoods\Pylon\Data\Property\Defensive; use Symfony\Component\Console\Input\InputArgument; @@ -12,8 +13,10 @@ abstract class CommandAbstract extends Command { use Defensive\AwareTrait; - const ARG_SERVICES_YML_FILE_PATH = 'services_yml_file_path'; - const SPLASH_ART = [ + public const ARG_SERVICES_YML_ROOT_DIRECTORY_PATH = 'services_yml_root_directory_path'; + public const OPT_ENABLE_SPLASH_ART = 'enable-splash-art'; + public const OPT_ESA = 'esa'; + public const SPLASH_ART = [ '+------------------------------+', '| ⚡ Neighborhoods Kōjō ⚡ |', '| |', @@ -26,9 +29,16 @@ abstract class CommandAbstract extends Command public function configure() { $this->addArgument( - self::ARG_SERVICES_YML_FILE_PATH, + self::ARG_SERVICES_YML_ROOT_DIRECTORY_PATH, InputArgument::REQUIRED, - 'The path to the YML services file for the client application.' + 'The path to the YML services root directory for the client application.' + ); + + $this->addOption( + self::OPT_ENABLE_SPLASH_ART, + self::OPT_ESA, + InputOption::VALUE_NONE, + 'Enables the splash art to be written to STDOUT.' ); $this->_configure(); @@ -42,7 +52,9 @@ public function execute(InputInterface $input, OutputInterface $output) { $this->_setInput($input); $this->_setOutput($output); - $this->_writeSplashArt(); + if ($this->_getInput()->getOption(self::OPT_ENABLE_SPLASH_ART)) { + $this->_writeSplashArt(); + } $this->_execute(); return $this; diff --git a/src/Environment/Parameters.yml b/src/Environment/Parameters.yml index 0f2b3a14..ae01ec01 100644 --- a/src/Environment/Parameters.yml +++ b/src/Environment/Parameters.yml @@ -5,4 +5,4 @@ parameters: neighborhoods.kojo.environment.parameters.database_password: '' neighborhoods.kojo.environment.parameters.database_adapter: '' neighborhoods.kojo.environment.parameters.database_host: '' - neighborhoods.kojo.environment.parameters.database_name: '' \ No newline at end of file + neighborhoods.kojo.environment.parameters.database_name: '' diff --git a/src/Process/Pool/Logger.php b/src/Process/Pool/Logger.php index 123ef266..a54cb6d6 100644 --- a/src/Process/Pool/Logger.php +++ b/src/Process/Pool/Logger.php @@ -3,20 +3,22 @@ namespace Neighborhoods\Kojo\Process\Pool; -use Psr\Log; -use Neighborhoods\Pylon\Time; +use Neighborhoods\Kojo\Process\Pool\Logger\FormatterInterface; use Neighborhoods\Kojo\ProcessInterface; use Neighborhoods\Pylon\Data\Property\Defensive; +use Neighborhoods\Pylon\Time; +use Psr\Log; class Logger extends Log\AbstractLogger implements LoggerInterface { use Time\AwareTrait; + use Logger\Message\Factory\AwareTrait; use Defensive\AwareTrait; - const PAD_PID = 6; - const PAD_PATH = 50; - const PROP_IS_ENABLED = 'is_enabled'; - const PROP_PROCESS_PATH_PADDING = 'process_path_padding'; - const PROP_PROCESS_ID_PADDING = 'process_id_padding'; + public const PROP_IS_ENABLED = 'is_enabled'; + protected const LOG_DATE_TIME_FORMAT = 'D, d M y H:i:s.u T'; + + protected $log_formatter; + protected $level_filter_mask; public function setProcess(ProcessInterface $process): LoggerInterface { @@ -33,26 +35,47 @@ protected function _getProcess(): ProcessInterface public function log($level, $message, array $context = []) { if ($this->_isEnabled() === true) { - $processIdPadding = $this->_getProcessIdPadding(); - $processPathPadding = $this->_getProcessPathPadding(); - if ($this->_exists(ProcessInterface::class)) { - $processId = (string)$this->_getProcess()->getProcessId(); - $paddedProcessId = str_pad($processId, $processIdPadding, ' ', STR_PAD_LEFT); - $typeCode = str_pad($this->_getProcess()->getPath(), $processPathPadding, ' '); - }else { - $paddedProcessId = str_pad('', $processIdPadding, '?', STR_PAD_LEFT); - $typeCode = str_pad('', $processPathPadding, '?'); + if ($this->getLevelFilterMask()[$level] === false) { + if ($this->_exists(ProcessInterface::class)) { + $processId = (string)$this->_getProcess()->getProcessId(); + } else { + $processId = '?'; + } + + $referenceTime = $this->_getTime()->getNow(); + $logMessage = $this->getProcessPoolLoggerMessageFactory()->create(); + $logMessage->setTime($referenceTime->format(self::LOG_DATE_TIME_FORMAT)); + $logMessage->setLevel($level); + $logMessage->setProcessId($processId); + $logMessage->setProcessPath($this->_getProcess()->getPath()); + $logMessage->setMessage($message); + fwrite(STDOUT, $this->getLogFormatter()->getFormattedMessage($logMessage) . "\n"); } - - $level = str_pad($level, 12, ' '); - $referenceTime = $this->_getTime()->getUnixReferenceTimeNow(); - $format = "%s | %s | %s | %s | %s\n"; - fwrite(STDOUT, sprintf($format, $referenceTime, $level, $paddedProcessId, $typeCode, $message)); } return; } + public function setLevelFilterMask(array $level_filter_mask): LoggerInterface + { + if ($this->level_filter_mask === null) { + $this->level_filter_mask = $level_filter_mask; + } else { + throw new \LogicException('Logger level_filter_mask is already set.'); + } + + return $this; + } + + protected function getLevelFilterMask(): array + { + if ($this->level_filter_mask === null) { + $this->level_filter_mask = []; + } + + return $this->level_filter_mask; + } + protected function _isEnabled(): bool { return $this->_read(self::PROP_IS_ENABLED); @@ -65,27 +88,23 @@ public function setIsEnabled(bool $isEnabled): LoggerInterface return $this; } - public function setProcessPathPadding(int $processPathPadding): LoggerInterface + public function getLogFormatter(): FormatterInterface { - $this->_create(self::PROP_PROCESS_PATH_PADDING, $processPathPadding); + if ($this->log_formatter === null) { + throw new \LogicException('Logger log_formatter has not been set.'); + } - return $this; + return $this->log_formatter; } - protected function _getProcessPathPadding(): int + public function setLogFormatter(FormatterInterface $log_formatter): LoggerInterface { - return $this->_read(self::PROP_PROCESS_PATH_PADDING); - } + if ($this->log_formatter !== null) { + throw new \LogicException('Logger log_formatter already set.'); + } - public function setProcessIdPadding(int $processIdPadding): LoggerInterface - { - $this->_create(self::PROP_PROCESS_ID_PADDING, $processIdPadding); + $this->log_formatter = $log_formatter; return $this; } - - protected function _getProcessIdPadding(): int - { - return $this->_read(self::PROP_PROCESS_ID_PADDING); - } -} \ No newline at end of file +} diff --git a/src/Process/Pool/Logger.yml b/src/Process/Pool/Logger.yml index e55995fe..02dcc027 100644 --- a/src/Process/Pool/Logger.yml +++ b/src/Process/Pool/Logger.yml @@ -1,14 +1,22 @@ parameters: - process.pool.logger.process_id_padding: 6 - process.pool.logger.path_padding: 80 process.pool.logger.is_enabled: true + neighborhoods.kojo.process.pool.logger.level_filter_mask: + !php/const \Psr\Log\LogLevel::EMERGENCY: false + !php/const \Psr\Log\LogLevel::ALERT: false + !php/const \Psr\Log\LogLevel::CRITICAL: false + !php/const \Psr\Log\LogLevel::ERROR: false + !php/const \Psr\Log\LogLevel::WARNING: false + !php/const \Psr\Log\LogLevel::NOTICE: false + !php/const \Psr\Log\LogLevel::INFO: false + !php/const \Psr\Log\LogLevel::DEBUG: true services: neighborhoods.kojo.process.pool.logger: class: Neighborhoods\Kojo\Process\Pool\Logger calls: - [setIsEnabled, ['%process.pool.logger.is_enabled%']] - [setTime, ['@neighborhoods.pylon.time']] - - [setProcessIdPadding, ['%process.pool.logger.process_id_padding%']] - - [setProcessPathPadding, ['%process.pool.logger.path_padding%']] + - [setLogFormatter, ['@neighborhoods.kojo.process.log_formatter']] + - [setProcessPoolLoggerMessageFactory, ['@neighborhoods.kojo.process.pool.logger.message.factory']] + - [setLevelFilterMask, ['%neighborhoods.kojo.process.pool.logger.level_filter_mask%']] process.pool.logger: alias: neighborhoods.kojo.process.pool.logger \ No newline at end of file diff --git a/src/Process/Pool/Logger/Formatter.php b/src/Process/Pool/Logger/Formatter.php new file mode 100644 index 00000000..501cb408 --- /dev/null +++ b/src/Process/Pool/Logger/Formatter.php @@ -0,0 +1,87 @@ +hasLogFormat() && $this->getLogFormat() === self::LOG_FORMAT_PIPES) { + return $this->formatPipes($message); + } else { + return $this->formatJson($message); + } + } + + protected function formatPipes(MessageInterface $message) : string + { + $processIdPaddingLength = $this->getProcessIdPadding(); + $processPathPaddingLength = $this->getProcessPathPadding(); + + $processID = str_pad($message->getProcessId(), $processIdPaddingLength, ' ', STR_PAD_LEFT); + $processPath = str_pad($message->getProcessPath(), $processPathPaddingLength, ' '); + $level = str_pad($message->getLevel(), 12, ' '); + + return implode(' | ', [$message->getTime(), $level, $processID, $processPath, $message->getMessage()]); + } + + protected function formatJson(MessageInterface $message) : string + { + return json_encode($message); + } + + public function setProcessPathPadding(int $processPathPadding) : FormatterInterface + { + $this->_create(self::PROP_PROCESS_PATH_PADDING, $processPathPadding); + + return $this; + } + + protected function getProcessPathPadding() : int + { + return $this->_read(self::PROP_PROCESS_PATH_PADDING); + } + + public function setProcessIdPadding(int $processIdPadding) : FormatterInterface + { + $this->_create(self::PROP_PROCESS_ID_PADDING, $processIdPadding); + + return $this; + } + + protected function getProcessIdPadding() : int + { + return $this->_read(self::PROP_PROCESS_ID_PADDING); + } + + public function setLogFormat(string $logFormat) + { + $this->_create(self::PROP_LOG_FORMAT, $logFormat); + + return $this; + } + + protected function getLogFormat() : string + { + return $this->_read(self::PROP_LOG_FORMAT); + } + + protected function hasLogFormat() : bool + { + return $this->_exists(self::PROP_LOG_FORMAT); + } +} diff --git a/src/Process/Pool/Logger/Formatter.yml b/src/Process/Pool/Logger/Formatter.yml new file mode 100644 index 00000000..ffcc8b5b --- /dev/null +++ b/src/Process/Pool/Logger/Formatter.yml @@ -0,0 +1,11 @@ +parameters: + process.pool.logger.formatter.process_id_padding: 6 + process.pool.logger.formatter.path_padding: 80 + process.pool.logger.formatter.log_format: !php/const \Neighborhoods\Kojo\Process\Pool\Logger\Formatter::LOG_FORMAT_JSON +services: + neighborhoods.kojo.process.log_formatter: + class: Neighborhoods\Kojo\Process\Pool\Logger\Formatter + calls: + - [setProcessIdPadding, ['%process.pool.logger.formatter.process_id_padding%']] + - [setProcessPathPadding, ['%process.pool.logger.formatter.path_padding%']] + - [setLogFormat, ['%process.pool.logger.formatter.log_format%']] diff --git a/src/Process/Pool/Logger/FormatterInterface.php b/src/Process/Pool/Logger/FormatterInterface.php new file mode 100644 index 00000000..f5fc4f4c --- /dev/null +++ b/src/Process/Pool/Logger/FormatterInterface.php @@ -0,0 +1,17 @@ + $this->getTime(), + self::KEY_LEVEL => $this->getLevel(), + self::KEY_PROCESS_ID => $this->getProcessId(), + self::KEY_PROCESS_PATH => $this->getProcessPath(), + self::KEY_MESSAGE => $this->getMessage(), + ]; + } + + public function getTime(): string + { + if ($this->time === null) { + throw new \LogicException('Message ' . self::KEY_TIME . ' has not been set.'); + } + + return $this->time; + } + + public function setTime(string $time): MessageInterface + { + if ($this->time !== null) { + throw new \LogicException('Message ' . self::KEY_TIME . ' already set.'); + } + + $this->time = $time; + + return $this; + } + + public function getLevel(): string + { + if ($this->level === null) { + throw new \LogicException('Message ' . self::KEY_LEVEL . ' has not been set.'); + } + + return $this->level; + } + + public function setLevel(string $level): MessageInterface + { + if ($this->level !== null) { + throw new \LogicException('Message ' . self::KEY_LEVEL . ' already set.'); + } + + $this->level = $level; + + return $this; + } + + public function getProcessId(): string + { + if ($this->process_id === null) { + throw new \LogicException('Message ' . self::KEY_PROCESS_ID . ' has not been set.'); + } + + return $this->process_id; + } + + public function setProcessId(string $process_id): MessageInterface + { + if ($this->process_id !== null) { + throw new \LogicException('Message ' . self::KEY_PROCESS_ID . ' already set.'); + } + + $this->process_id = $process_id; + + return $this; + } + + public function getProcessPath(): string + { + if ($this->process_path === null) { + throw new \LogicException('Message ' . self::KEY_PROCESS_PATH . ' has not been set.'); + } + + return $this->process_path; + } + + public function setProcessPath(string $process_path): MessageInterface + { + if ($this->process_path !== null) { + throw new \LogicException('Message ' . self::KEY_PROCESS_PATH . ' already set.'); + } + + $this->process_path = $process_path; + + return $this; + } + + public function getMessage(): string + { + if ($this->message === null) { + throw new \LogicException('Message ' . self::KEY_MESSAGE . ' has not been set.'); + } + + return $this->message; + } + + public function setMessage(string $message): MessageInterface + { + if ($this->message !== null) { + throw new \LogicException('Message ' . self::KEY_MESSAGE . ' already set.'); + } + + $this->message = $message; + + return $this; + } +} diff --git a/src/Process/Pool/Logger/Message.yml b/src/Process/Pool/Logger/Message.yml new file mode 100644 index 00000000..8e84b2d4 --- /dev/null +++ b/src/Process/Pool/Logger/Message.yml @@ -0,0 +1,3 @@ +services: + neighborhoods.kojo.process.pool.logger.message: + class: Neighborhoods\Kojo\Process\Pool\Logger\Message diff --git a/src/Process/Pool/Logger/Message/AwareTrait.php b/src/Process/Pool/Logger/Message/AwareTrait.php new file mode 100644 index 00000000..057ab993 --- /dev/null +++ b/src/Process/Pool/Logger/Message/AwareTrait.php @@ -0,0 +1,46 @@ +hasProcessPoolLoggerMessage(), + new \LogicException('NeighborhoodsKojoProcessPoolLoggerMessage is already set.') + ); + $this->NeighborhoodsKojoProcessPoolLoggerMessage = $processPoolLoggerMessage; + + return $this; + } + + protected function getProcessPoolLoggerMessage() : MessageInterface + { + assert($this->hasProcessPoolLoggerMessage(), + new \LogicException('NeighborhoodsKojoProcessPoolLoggerMessage is not set.') + ); + + return $this->NeighborhoodsKojoProcessPoolLoggerMessage; + } + + protected function hasProcessPoolLoggerMessage() : bool + { + return isset($this->NeighborhoodsKojoProcessPoolLoggerMessage); + } + + protected function unsetProcessPoolLoggerMessage() : self + { + assert($this->hasProcessPoolLoggerMessage(), + new \LogicException('NeighborhoodsKojoProcessPoolLoggerMessage is not set.') + ); + unset($this->NeighborhoodsKojoProcessPoolLoggerMessage); + + return $this; + } +} diff --git a/src/Process/Pool/Logger/Message/Factory.php b/src/Process/Pool/Logger/Message/Factory.php new file mode 100644 index 00000000..a28b7809 --- /dev/null +++ b/src/Process/Pool/Logger/Message/Factory.php @@ -0,0 +1,19 @@ +getProcessPoolLoggerMessage(); + } +} diff --git a/src/Process/Pool/Logger/Message/Factory.yml b/src/Process/Pool/Logger/Message/Factory.yml new file mode 100644 index 00000000..ffcba4ed --- /dev/null +++ b/src/Process/Pool/Logger/Message/Factory.yml @@ -0,0 +1,5 @@ +services: + neighborhoods.kojo.process.pool.logger.message.factory: + class: Neighborhoods\Kojo\Process\Pool\Logger\Message\Factory + calls: + - [setProcessPoolLoggerMessage, ['@neighborhoods.kojo.process.pool.logger.message']] diff --git a/src/Process/Pool/Logger/Message/Factory/AwareTrait.php b/src/Process/Pool/Logger/Message/Factory/AwareTrait.php new file mode 100644 index 00000000..e30a3141 --- /dev/null +++ b/src/Process/Pool/Logger/Message/Factory/AwareTrait.php @@ -0,0 +1,46 @@ +hasProcessPoolLoggerMessageFactory(), + new \LogicException('NeighborhoodsKojoProcessPoolLoggerMessageFactory is already set.') + ); + $this->NeighborhoodsKojoProcessPoolLoggerMessageFactory = $processPoolLoggerMessageFactory; + + return $this; + } + + protected function getProcessPoolLoggerMessageFactory() : FactoryInterface + { + assert($this->hasProcessPoolLoggerMessageFactory(), + new \LogicException('NeighborhoodsKojoProcessPoolLoggerMessageFactory is not set.') + ); + + return $this->NeighborhoodsKojoProcessPoolLoggerMessageFactory; + } + + protected function hasProcessPoolLoggerMessageFactory() : bool + { + return isset($this->NeighborhoodsKojoProcessPoolLoggerMessageFactory); + } + + protected function unsetProcessPoolLoggerMessageFactory() : self + { + assert($this->hasProcessPoolLoggerMessageFactory(), + new \LogicException('NeighborhoodsKojoProcessPoolLoggerMessageFactory is not set.') + ); + unset($this->NeighborhoodsKojoProcessPoolLoggerMessageFactory); + + return $this; + } +} diff --git a/src/Process/Pool/Logger/Message/FactoryInterface.php b/src/Process/Pool/Logger/Message/FactoryInterface.php new file mode 100644 index 00000000..6482630e --- /dev/null +++ b/src/Process/Pool/Logger/Message/FactoryInterface.php @@ -0,0 +1,12 @@ +_initialize(); - $this->_getLogger()->info('Starting process pool server...'); + $this->_getLogger()->debug('Starting process pool server...'); if ($this->_getSemaphore()->testAndSetLock($this->_getServerSemaphoreResource())) { $this->_getLogger()->info('Process pool server started.'); $this->_getProcessPool()->start(); @@ -29,7 +29,8 @@ public function start(): ProcessInterface $this->_getProcessSignal()->waitForSignal(); } }else { - $this->_getLogger()->info('Cannot obtain the process pool server mutex. Quitting.'); + $this->_getLogger()->debug('Cannot obtain the process pool server mutex. Quitting.'); + $this->exit(); } return $this; diff --git a/src/ProcessAbstract.php b/src/ProcessAbstract.php index ea594f9d..fa139390 100644 --- a/src/ProcessAbstract.php +++ b/src/ProcessAbstract.php @@ -56,6 +56,7 @@ protected function _registerSignalHandlers(): ProcessInterface $this->_getProcessSignal()->addSignalHandler(SIGHUP, $this); $this->_getProcessSignal()->addSignalHandler(SIGQUIT, $this); $this->_getProcessSignal()->addSignalHandler(SIGABRT, $this); + $this->_getProcessSignal()->addSignalHandler(SIGUSR1, $this); return $this; } @@ -272,4 +273,4 @@ protected function _getUuidMaximumInteger(): int { return $this->_read(self::PROP_UUID_MAXIMUM_INTEGER); } -} \ No newline at end of file +} diff --git a/tests/ForemanInterfaceTest/ForemanInterfaceTest.php b/tests/ForemanInterfaceTest/ForemanInterfaceTest.php index 074525cb..bdba7105 100644 --- a/tests/ForemanInterfaceTest/ForemanInterfaceTest.php +++ b/tests/ForemanInterfaceTest/ForemanInterfaceTest.php @@ -4,7 +4,7 @@ namespace Neighborhoods\KojoTest\Unit; use Neighborhoods\Kojo\ForemanInterface; -use Neighborhoods\Kojo\Process\JobInterface; +use Neighborhoods\Kojo\Process\WorkerInterface; use Neighborhoods\Kojo\SelectorInterface; use Neighborhoods\Scaffolding\Fixture\AbstractTest; @@ -30,8 +30,8 @@ protected function _getSelector(): SelectorInterface return $this->_getContainerBuilderFacade()->getContainerBuilder()->get('selector'); } - protected function _getJobProcess(): JobInterface + protected function _getJobProcess(): WorkerInterface { - return $this->_getContainerBuilderFacade()->getContainerBuilder()->get('process.job'); + return $this->_getContainerBuilderFacade()->getContainerBuilder()->get('Worker'); } } \ No newline at end of file