diff --git a/bootstrap.php b/bootstrap.php index dc4d350..b5d4881 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -1,7 +1,7 @@ =5.5", - "symfony/polyfill-intl-idn": "^1.17.0" + "guzzlehttp/promises": "^1.4", + "guzzlehttp/psr7": "^1.7 || ^2.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", "psr/log": "^1.1" }, "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5-dev" + "dev-master": "7.3-dev" } }, "autoload": { @@ -475,6 +482,11 @@ "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "Guzzle is a PHP HTTP client library", @@ -485,14 +497,34 @@ "framework", "http", "http client", + "psr-18", + "psr-7", "rest", "web service" ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/6.5" + "source": "https://github.com/guzzle/guzzle/tree/7.3.0" }, - "time": "2020-06-16T21:01:06+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://github.com/alexeyshockov", + "type": "github" + }, + { + "url": "https://github.com/gmponos", + "type": "github" + } + ], + "time": "2021-03-23T11:33:13+00:00" }, { "name": "guzzlehttp/promises", @@ -624,6 +656,334 @@ }, "time": "2021-04-26T09:17:50+00:00" }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c", + "reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2020-12-06T15:14:20+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "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": "phpseclib/phpseclib", + "version": "3.0.8", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "d9615a6fb970d9933866ca8b4036ec3407b020b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d9615a6fb970d9933866ca8b4036ec3407b020b6", + "reference": "d9615a6fb970d9933866ca8b4036ec3407b020b6", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^5.7|^6.0|^9.4", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.8" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2021-04-19T03:20:48+00:00" + }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -799,6 +1159,103 @@ ], "time": "2021-05-07T13:41:16+00:00" }, + { + "name": "symfony/console", + "version": "v5.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "864568fdc0208b3eba3638b6000b69d2386e6768" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/864568fdc0208b3eba3638b6000b69d2386e6768", + "reference": "864568fdc0208b3eba3638b6000b69d2386e6768", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" + }, + "conflict": { + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.8" + }, + "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": "2021-05-11T15:45:21+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v2.4.0", @@ -1151,23 +1608,21 @@ "time": "2021-01-07T16:49:33+00:00" }, { - "name": "symfony/polyfill-intl-idn", + "name": "symfony/polyfill-intl-grapheme", "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", - "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.1" }, "suggest": { "ext-intl": "For best performance" @@ -1184,7 +1639,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, "files": [ "bootstrap.php" @@ -1196,30 +1651,26 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "idn", + "grapheme", "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" }, "funding": [ { @@ -1402,17 +1853,17 @@ "time": "2021-01-22T09:19:47+00:00" }, { - "name": "symfony/polyfill-php72", + "name": "symfony/polyfill-php73", "version": "v1.22.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", - "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", "shasum": "" }, "require": { @@ -1430,10 +1881,13 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" + "Symfony\\Polyfill\\Php73\\": "" }, "files": [ "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1450,7 +1904,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -1459,7 +1913,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" }, "funding": [ { @@ -1649,6 +2103,168 @@ ], "time": "2021-05-16T13:07:46+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.4.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": "2021-04-01T10:43:52+00:00" + }, + { + "name": "symfony/string", + "version": "v5.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db", + "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.8" + }, + "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": "2021-05-10T14:56:10+00:00" + }, { "name": "symfony/yaml", "version": "v5.2.9", diff --git a/lib/Framework/Kernel.php b/lib/Framework/Kernel.php index 4cc1126..cf8d359 100644 --- a/lib/Framework/Kernel.php +++ b/lib/Framework/Kernel.php @@ -80,14 +80,13 @@ class Kernel public function handle(): Response { - $this->parseDotEnv($this->getProjectDir() . '/.env'); $this->router = new Router(); $this->router->parseRoutes($this->getConfigDir(), 'routes.yaml'); $request = $this->parseRequest(); return $this->router->route($request, $this); } - private function parseDotEnv(string $path): void + public function parseDotEnv(string $path): void { $dotenv = new Dotenv(); $dotenv->loadEnv($path); diff --git a/public/index.php b/public/index.php index 90abc23..0bc7db7 100644 --- a/public/index.php +++ b/public/index.php @@ -3,13 +3,13 @@ error_reporting(E_ALL); ini_set('display_errors', true); use JeroenED\Framework\Kernel; - -require_once '../bootstrap.php'; +chdir(__DIR__ . '/..'); +require_once 'bootstrap.php'; $kernel = new Kernel(); -chdir(__DIR__ . '/..'); $kernel->setProjectDir(getcwd()); $kernel->setConfigDir(getcwd() . '/config/'); $kernel->setTemplateDir(getcwd() . '/templates/'); +$kernel->parseDotEnv($kernel->getProjectDir() . '/.env'); $kernel->handle()->send(); \ No newline at end of file diff --git a/src/Command/DaemonCommand.php b/src/Command/DaemonCommand.php new file mode 100644 index 0000000..4abf07a --- /dev/null +++ b/src/Command/DaemonCommand.php @@ -0,0 +1,69 @@ +kernel = $kernel; + parent::__construct(); + } + + protected function configure() + { + $this + ->setDescription('The deamon slayer of webcron') + ->setHelp('This command is the daemon process of webcron, enabling webcron to actually run jobs on time') + ->addOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'Time limit in seconds before stopping the daemon.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $jobRepo = new Job($this->kernel->getDbCon()); + $timelimit = $input->getOption('time-limit') ?? false; + if ($timelimit === false) { + $endofscript = false; + } elseif(is_numeric($timelimit)) { + $endofscript = time() + $timelimit; + } else { + throw new \InvalidArgumentException('Time limit has incorrect value'); + } + + while(1) { + if($endofscript !== false && time() > $endofscript) break; + $jobsToRun = $jobRepo->getJobsDue(); + if(!empty($jobsToRun)) { + foreach($jobsToRun as $job) { + $jobRepo->setJobRunning($job, true); + $pid = -1; + //$pid = pcntl_fork(); + if($pid == -1) { + $jobRepo->RunJob($job); + $jobRepo->setJobRunning($job, false); + } elseif ($pid == 0) { + $jobRepo->RunJob($job); + $jobRepo->setJobRunning($job, false); + exit; + } + + } + } + } + $output->writeln('Ended after ' . $timelimit . ' seconds'); + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php new file mode 100644 index 0000000..9d77929 --- /dev/null +++ b/src/Command/RunCommand.php @@ -0,0 +1,15 @@ +dbcon->prepare($jobsSql); - $jobsRslt = $jobsStmt->execute(); + $jobsRslt = $jobsStmt->executeQuery(); $jobs = $jobsRslt->fetchAllAssociative(); foreach ($jobs as $key=>&$job) { $job['data'] = json_decode($job['data'], true); @@ -28,6 +31,84 @@ class Job return $jobs; } + + public function getJobsDue() + { + $jobsSql = "SELECT id FROM job WHERE nextrun <= :timestamp AND running = 0"; + $jobsStmt = $this->dbcon->prepare($jobsSql); + $jobsRslt = $jobsStmt->executeQuery([':timestamp' => time()]); + $jobs = $jobsRslt->fetchAllAssociative(); + $return = []; + foreach ($jobs as $job) { + $return[] = $job['id']; + } + return $return; + } + + public function setJobRunning(int $job, bool $status): void + { + $jobsSql = "UPDATE job SET running = :status WHERE id = :id"; + $jobsStmt = $this->dbcon->prepare($jobsSql); + $jobsStmt->executeQuery([':id' => $job, ':status' => $status ? 1 : 0]); + return; + } + + public function runJob(int $job) + { + $job = $this->getJob($job, true); + if($job['data']['crontype'] == 'http') { + + $client = new Client(); + foreach($job['data']['vars'] as $key => $var) { + $job['data']['basicauth-username'] = str_replace('{' . $key . '}', $var['value'], $job['data']['basicauth-username']); + $job['data']['url'] = str_replace('{' . $key . '}', $var['value'], $job['data']['url']); + } + + $url = $job['data']['url']; + $options['http_errors'] = false; + $options['auth'] = [$job['data']['basicauth-username'], $job['data']['basicauth-password']]; + $res = $client->request('GET', $url, $options); + + $exitcode = $res->getStatusCode(); + $output = $res->getBody(); + } elseif($job['data']['crontype'] == 'command') { + if($job['data']['hosttype'] == 'ssh') { + $ssh = new SSH2($job['data']['host']); + $key = null; + if(!empty($job['data']['ssh-privkey'])) { + if(!empty($job['data']['privkey-password'])) { + $key = PublicKeyLoader::load(base64_decode($job['data']['ssh-privkey']), $job['data']['privkey-password']); + } else { + $key = PublicKeyLoader::load(base64_decode($job['data']['ssh-privkey'])); + } + } elseif (!empty($job['data']['privkey-password'])) { + $key = $job['data']['ssh-privkey']; + } + + if (!$ssh->login($job['data']['user'], $key)) { + throw new \Exception('Login failed'); + } + $output = $ssh->exec($job['data']['command']); + $exitcode = $ssh->getExitStatus(); + } + } + + // handling of response + $addRunSql = 'INSERT INTO run(job_id, exitcode, output) VALUES (:job_id, :exitcode, :output)'; + $addRunStmt = $this->dbcon->prepare($addRunSql); + $addRunStmt->executeQuery([':job_id' => $job['id'], ':exitcode' => $exitcode, ':output' => $output]); + + // setting nextrun to next run + $nextrun = $job['nextrun']; + do { + $nextrun = $nextrun + $job['interval']; + } while ($nextrun < time()); + + + $addRunSql = 'UPDATE job SET nextrun = :nextrun WHERE id = :id'; + $addRunStmt = $this->dbcon->prepare($addRunSql); + $addRunStmt->executeQuery([':id' => $job['id'], ':nextrun' => $nextrun]); + } public function addJob(array $values) { if(empty($values['crontype']) || @@ -40,10 +121,10 @@ class Job $data = $this->prepareJob($values); $data['data'] = json_encode($data['data']); - $addJobSql = "INSERT INTO job(name, data, interval, nextrun, lastrun) VALUES (:name, :data, :interval, :nextrun, :lastrun)"; + $addJobSql = "INSERT INTO job(name, data, interval, nextrun, lastrun, running) VALUES (:name, :data, :interval, :nextrun, :lastrun, :running)"; $addJobStmt = $this->dbcon->prepare($addJobSql); - $addJobStmt->executeQuery([':name' => $data['name'], ':data' => $data['data'], ':interval' => $data['interval'], ':nextrun' => $data['nextrun'], ':lastrun' => $data['lastrun'], ]); + $addJobStmt->executeQuery([':name' => $data['name'], ':data' => $data['data'], ':interval' => $data['interval'], ':nextrun' => $data['nextrun'], ':lastrun' => $data['lastrun'], ':running' => 0]); return ['success' => true, 'message' => 'Cronjob succesfully added']; } diff --git a/storage/database.sql b/storage/database.sql index ac81d50..58c70f5 100644 --- a/storage/database.sql +++ b/storage/database.sql @@ -5,7 +5,8 @@ CREATE TABLE job ( "data" TEXT NOT NULL, interval INTEGER, nextrun INTEGER, - lastrun INTEGER + lastrun INTEGER, + running INTEGER ); @@ -14,4 +15,12 @@ CREATE TABLE "user" ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, email TEXT(50) NOT NULL, password TEXT(72) NOT NULL +); + +-- run definition +CREATE TABLE run ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + job_id INTEGER NOT NULL, + exitcode TEXT NOT NULL, + output TEXT NOT NULL ); \ No newline at end of file diff --git a/webcron b/webcron new file mode 100644 index 0000000..398d55f --- /dev/null +++ b/webcron @@ -0,0 +1,23 @@ +#!/usr/bin/env php +setProjectDir(getcwd()); +$kernel->setConfigDir(getcwd() . '/config/'); +$kernel->setTemplateDir(getcwd() . '/templates/'); +$kernel->parseDotEnv($kernel->getProjectDir() . '/.env'); + +$application->add(new RunCommand()); +$application->add(new DaemonCommand($kernel)); + +$application->run(); +