From c8c58fc72bcdd02ef5be3f2220edd2ca0fcc43c6 Mon Sep 17 00:00:00 2001 From: Michael Ochmann Date: Fri, 9 Aug 2024 10:52:09 +0200 Subject: [PATCH] initial commit --- .gitignore | 5 + LICENSE.md | 21 + README.md | 2 + composer.json | 26 + composer.lock | 1594 ++++++++++++++++++ src/Application.php | 103 ++ src/RequestType.php | 10 + src/TemplateEngine.php | 31 + src/Utils.php | 16 + src/containers/Container.php | 57 + src/containers/ControllerContainer.php | 12 + src/containers/ServiceContainer.php | 13 + src/controllers/Controller.php | 8 + src/exceptions/UnknownValidatorException.php | 12 + src/models/DatabaseModel.php | 66 + src/models/Model.php | 5 + src/services/Database.php | 25 + src/services/Request.php | 57 + src/services/Router.php | 108 ++ src/services/Service.php | 6 + src/validators/Validation.php | 11 + src/validators/ValidationType.php | 9 + src/validators/Validator.php | 87 + views/404.mustache | 12 + views/LucidityException.mustache | 35 + views/partials/LucidityLayout.mustache | 72 + 26 files changed, 2403 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 src/Application.php create mode 100644 src/RequestType.php create mode 100644 src/TemplateEngine.php create mode 100644 src/Utils.php create mode 100644 src/containers/Container.php create mode 100644 src/containers/ControllerContainer.php create mode 100644 src/containers/ServiceContainer.php create mode 100644 src/controllers/Controller.php create mode 100644 src/exceptions/UnknownValidatorException.php create mode 100644 src/models/DatabaseModel.php create mode 100644 src/models/Model.php create mode 100644 src/services/Database.php create mode 100644 src/services/Request.php create mode 100644 src/services/Router.php create mode 100644 src/services/Service.php create mode 100644 src/validators/Validation.php create mode 100644 src/validators/ValidationType.php create mode 100644 src/validators/Validator.php create mode 100644 views/404.mustache create mode 100644 views/LucidityException.mustache create mode 100644 views/partials/LucidityLayout.mustache diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd896f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +.vscode +.DS_Store + +vendor diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..17a829d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License + +Copyright (C) 2024, MikO + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f78dbfb --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# lucidity +– a MVC framework for php applications diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a2e7095 --- /dev/null +++ b/composer.json @@ -0,0 +1,26 @@ +{ + "name": "massivedynamic/lucidity", + "description": "a lightweight php framework", + "type": "library", + "version" : "0.0.1", + "license": "MIT", + "autoload": { + "psr-4": { + "massivedynamic\\lucidity\\": "src/" + } + }, + "authors": [ + { + "name": "MikO", + "email": "miko@massivedymamic.eu" + } + ], + "require": { + "robmorgan/phinx": "^0.16.2", + "mustache/mustache": "^2.14", + "psr/container": "^2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..1af4726 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1594 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "09079cc5a769c5aa51cb4437e4eada76", + "packages": [ + { + "name": "cakephp/chronos", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/chronos.git", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/786d69e1ee4b735765cbdb5521b9603e9b98d650", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^5.0", + "phpunit/phpunit": "^10.1.0 || ^11.1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Chronos\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + }, + { + "name": "The CakePHP Team", + "homepage": "https://cakephp.org" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "https://cakephp.org", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "issues": "https://github.com/cakephp/chronos/issues", + "source": "https://github.com/cakephp/chronos" + }, + "time": "2024-07-18T03:18:04+00:00" + }, + { + "name": "cakephp/core", + "version": "5.0.10", + "source": { + "type": "git", + "url": "https://github.com/cakephp/core.git", + "reference": "63a873e41a5ec74e00d1321b533e26c2631eeca5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/core/zipball/63a873e41a5ec74e00d1321b533e26c2631eeca5", + "reference": "63a873e41a5ec74e00d1321b533e26c2631eeca5", + "shasum": "" + }, + "require": { + "cakephp/utility": "^5.0", + "league/container": "^4.2", + "php": ">=8.1", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^2.0" + }, + "suggest": { + "cakephp/cache": "To use Configure::store() and restore().", + "cakephp/event": "To use PluginApplicationInterface or plugin applications.", + "league/container": "To use Container and ServiceProvider classes" + }, + "type": "library", + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Cake\\Core\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/core/graphs/contributors" + } + ], + "description": "CakePHP Framework Core classes", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "core", + "framework" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/core" + }, + "time": "2024-06-08T17:54:49+00:00" + }, + { + "name": "cakephp/database", + "version": "5.0.10", + "source": { + "type": "git", + "url": "https://github.com/cakephp/database.git", + "reference": "61042e3b086689ee3bee88792250e51da370eaec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/database/zipball/61042e3b086689ee3bee88792250e51da370eaec", + "reference": "61042e3b086689ee3bee88792250e51da370eaec", + "shasum": "" + }, + "require": { + "cakephp/chronos": "^3.0.2", + "cakephp/core": "^5.0", + "cakephp/datasource": "^5.0", + "php": ">=8.1", + "psr/log": "^3.0" + }, + "require-dev": { + "cakephp/i18n": "^5.0", + "cakephp/log": "^5.0", + "phpstan/phpstan": "^1.10" + }, + "suggest": { + "cakephp/i18n": "If you are using locale-aware datetime formats.", + "cakephp/log": "If you want to use query logging without providing a logger yourself." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Database\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/database/graphs/contributors" + } + ], + "description": "Flexible and powerful Database abstraction library with a familiar PDO-like API", + "homepage": "https://cakephp.org", + "keywords": [ + "abstraction", + "cakephp", + "database", + "database abstraction", + "pdo" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/database" + }, + "time": "2024-06-08T07:44:07+00:00" + }, + { + "name": "cakephp/datasource", + "version": "5.0.10", + "source": { + "type": "git", + "url": "https://github.com/cakephp/datasource.git", + "reference": "28b8ce86ef77bd45fef7ba9ad31f03bf2fa81fa5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/datasource/zipball/28b8ce86ef77bd45fef7ba9ad31f03bf2fa81fa5", + "reference": "28b8ce86ef77bd45fef7ba9ad31f03bf2fa81fa5", + "shasum": "" + }, + "require": { + "cakephp/core": "^5.0", + "php": ">=8.1", + "psr/simple-cache": "^2.0 || ^3.0" + }, + "require-dev": { + "cakephp/cache": "^5.0", + "cakephp/collection": "^5.0", + "cakephp/utility": "^5.0", + "phpstan/phpstan": "^1.10" + }, + "suggest": { + "cakephp/cache": "If you decide to use Query caching.", + "cakephp/collection": "If you decide to use ResultSetInterface.", + "cakephp/utility": "If you decide to use EntityTrait." + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Datasource\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/datasource/graphs/contributors" + } + ], + "description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "connection management", + "datasource", + "entity", + "query" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/datasource" + }, + "time": "2024-05-24T02:41:11+00:00" + }, + { + "name": "cakephp/utility", + "version": "5.0.10", + "source": { + "type": "git", + "url": "https://github.com/cakephp/utility.git", + "reference": "6ddf7ad73bb6f7aeb303b19baff4b1e5a5d02cb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/utility/zipball/6ddf7ad73bb6f7aeb303b19baff4b1e5a5d02cb5", + "reference": "6ddf7ad73bb6f7aeb303b19baff4b1e5a5d02cb5", + "shasum": "" + }, + "require": { + "cakephp/core": "^5.0", + "php": ">=8.1" + }, + "suggest": { + "ext-intl": "To use Text::transliterate() or Text::slug()", + "lib-ICU": "To use Text::transliterate() or Text::slug()" + }, + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Cake\\Utility\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/utility/graphs/contributors" + } + ], + "description": "CakePHP Utility classes such as Inflector, String, Hash, and Security", + "homepage": "https://cakephp.org", + "keywords": [ + "cakephp", + "hash", + "inflector", + "security", + "string", + "utility" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/utility" + }, + "time": "2024-06-23T00:10:36+00:00" + }, + { + "name": "league/container", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/ff346319ca1ff0e78277dc2311a42107cc1aab88", + "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "nette/php-generator": "^3.4", + "nikic/php-parser": "^4.10", + "phpstan/phpstan": "^0.12.47", + "phpunit/phpunit": "^8.5.17", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "mail@philbennett.co.uk", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "support": { + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/4.2.2" + }, + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "time": "2024-03-13T13:12:53+00:00" + }, + { + "name": "mustache/mustache", + "version": "v2.14.2", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/mustache.php.git", + "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e62b7c3849d22ec55f3ec425507bf7968193a6cb", + "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.11", + "phpunit/phpunit": "~3.7|~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mustache": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "support": { + "issues": "https://github.com/bobthecow/mustache.php/issues", + "source": "https://github.com/bobthecow/mustache.php/tree/v2.14.2" + }, + "time": "2022-08-23T13:07:01+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "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/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "robmorgan/phinx", + "version": "0.16.2", + "source": { + "type": "git", + "url": "https://github.com/cakephp/phinx.git", + "reference": "577908073d3f355ceb47ca9fc6bd978cb1526c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/577908073d3f355ceb47ca9fc6bd978cb1526c70", + "reference": "577908073d3f355ceb47ca9fc6bd978cb1526c70", + "shasum": "" + }, + "require": { + "cakephp/database": "^5.0.2", + "composer-runtime-api": "^2.0", + "php-64bit": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/config": "^3.4|^4.0|^5.0|^6.0|^7.0", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "cakephp/cakephp": "^5.0.2", + "cakephp/cakephp-codesniffer": "^5.0", + "ext-json": "*", + "ext-pdo": "*", + "phpunit/phpunit": "^9.5.19", + "symfony/yaml": "^3.4|^4.0|^5.0|^6.0|^7.0" + }, + "suggest": { + "ext-json": "Install if using JSON configuration format", + "ext-pdo": "PDO extension is needed", + "symfony/yaml": "Install if using YAML configuration format" + }, + "bin": [ + "bin/phinx" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phinx\\": "src/Phinx/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Morgan", + "email": "robbym@gmail.com", + "homepage": "https://robmorgan.id.au", + "role": "Lead Developer" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://shadowhand.me", + "role": "Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Developer" + }, + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/phinx/graphs/contributors", + "role": "Developer" + } + ], + "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", + "homepage": "https://phinx.org", + "keywords": [ + "database", + "database migrations", + "db", + "migrations", + "phinx" + ], + "support": { + "issues": "https://github.com/cakephp/phinx/issues", + "source": "https://github.com/cakephp/phinx/tree/0.16.2" + }, + "time": "2024-07-17T21:40:58+00:00" + }, + { + "name": "symfony/config", + "version": "v7.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", + "reference": "2210fc99fa42a259eb6c89d1f724ce0c4d62d5d2", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v7.1.1" + }, + "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": "2024-05-31T14:57:53+00:00" + }, + { + "name": "symfony/console", + "version": "v7.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "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/v7.1.3" + }, + "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": "2024-07-26T12:41:01+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "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": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + }, + "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": "2024-06-28T10:03:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.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": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "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": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.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": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.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": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.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": "2024-06-19T12:30:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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/v3.5.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": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v7.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", + "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "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/v7.1.3" + }, + "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": "2024-07-22T10:25:37+00:00" + } + ], + "packages-dev": [ + { + "name": "phpstan/phpstan", + "version": "1.11.9", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "e370bcddadaede0c1716338b262346f40d296f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e370bcddadaede0c1716338b262346f40d296f82", + "reference": "e370bcddadaede0c1716338b262346f40d296f82", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-08-01T16:25:18+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/src/Application.php b/src/Application.php new file mode 100644 index 0000000..b9734ff --- /dev/null +++ b/src/Application.php @@ -0,0 +1,103 @@ + */ + protected array $globals; + protected TemplateEngine $templateEngine; + protected ServiceContainer $serviceContainer; + protected Router|Service $router; + + public function __construct(private string $templatesPath, string $partialsPath, string $cachePath) { + set_exception_handler(fn(Throwable $exception) => $this->handleException($exception)); + + if (self::$App instanceof Application) + throw new Exception("Only one instance of `Application` is possible"); + session_start(); + self::$App = $this; + $this->globals = [ + "errors" => [] + ]; + $this->templateEngine = new TemplateEngine($this, $templatesPath, $partialsPath, $cachePath); + $this->serviceContainer = new ServiceContainer($this); + $this->router = $this->serviceContainer->get(Router::class); + } + + public function fetchService(string $id) : Service | Application { + return $this->serviceContainer->get($id); + } + + public function publishGlobal(string $key, mixed $value) : void { + if ($key === "errors") + throw new Exception("The 'errors' global can not be accessed directly. Use `Application::addError()` instead."); + $this->globals[$key] = $value; + } + + /** + * @param array | string | null $errors + */ + public function addError(array | string | null $errors) : void { + if (!$errors) + return; + if (!is_array($errors)) + $errors = [$errors]; + $this->globals["errors"] = array_merge((array)$this->globals["errors"], $errors); + } + + /** + * @return array + */ + public function globals() : array { + return $this->globals; + } + + private function handleException(Throwable $exception) : void { + //Utils::Dump($exception->getTrace()); + + self::View("LucidityException", [ + "exception" => $exception, + "trace" => $exception->getTrace(), + "exceptionType" => $exception::class, + "lucidityVersion" => \Composer\InstalledVersions::getVersion("massivedynamic/lucidity"), + "phpVersion" => phpversion() + ]); + } + + /** + * @param string $template + * @param array $context + */ + public static function View(string $template, array $context = []) : void { + self::$App->templateEngine->view($template, $context); + } + + public static function Instance() : Application { + return self::$App; + } +} + +} + +namespace { + +/** + * @param string $template + * @param array $context + */ +function view(string $template, array $context = []) : void { + massivedynamic\lucidity\Application::View($template, $context); +} + +} diff --git a/src/RequestType.php b/src/RequestType.php new file mode 100644 index 0000000..6f6e531 --- /dev/null +++ b/src/RequestType.php @@ -0,0 +1,10 @@ + $cachePath, + "loader" => new Mustache_Loader_CascadingLoader([ + new Mustache_Loader_FilesystemLoader($templatesPath), + new Mustache_Loader_FilesystemLoader(dirname(__FILE__)."/../views") + ]), + "partials_loader" => new Mustache_Loader_CascadingLoader([ + new Mustache_Loader_FilesystemLoader($partialsPath), + new Mustache_Loader_FilesystemLoader(dirname(__FILE__)."/../views/partials") + ]), + "helpers" => [ + "notEmpty" => fn(array $array) : bool => count($array) > 0 + ], + "pragmas" => [Mustache_Engine::PRAGMA_BLOCKS, Mustache_Engine::PRAGMA_FILTERS] + ]); + } + + public function view(string $template, array $context = []) : void { + echo $this->render("$template.mustache", array_merge($this->app->globals(), $context)); + } +} diff --git a/src/Utils.php b/src/Utils.php new file mode 100644 index 0000000..00afdb2 --- /dev/null +++ b/src/Utils.php @@ -0,0 +1,16 @@ +"; + print_r($content); + echo ""; + } + + public static function DD(mixed $content) : void { + self::Dump($content); + die(); + } +} diff --git a/src/containers/Container.php b/src/containers/Container.php new file mode 100644 index 0000000..d164ca4 --- /dev/null +++ b/src/containers/Container.php @@ -0,0 +1,57 @@ +instances = []; + } + + public function get(string $id) : mixed { + if ($id === Application::class || is_subclass_of($id, Application::class)) + return $this->app; + if (!$this->has($id)) + $this->instantiate($id); + + return $this->instances[$id]; + } + + public function has(string $id) : bool { + return array_key_exists($id, $this->instances); + } + + public function emplace(string $type, ...$args) : bool { + if (array_key_exists($type, $this->instances)) + return false; + $this->instances[$type] = new $type(...$args); + + return true; + } + + protected function instantiate(string $id) : void { + $classInfo = new ReflectionClass($id); + $constructor = $classInfo->getConstructor(); + $arguments = []; + + if ($constructor === null) { + $this->instances[$id] = $classInfo->newInstance(); + return; + } + + foreach ($constructor->getParameters() as $parameter) { + $type = $parameter->getType(); + if ($type === null) + throw new Exception("constructor arguments need to name a type"); + array_push($arguments, $this->get($type->getName())); + } + $this->instances[$id] = $classInfo->newInstanceArgs($arguments); + } +} diff --git a/src/containers/ControllerContainer.php b/src/containers/ControllerContainer.php new file mode 100644 index 0000000..09aa85d --- /dev/null +++ b/src/containers/ControllerContainer.php @@ -0,0 +1,12 @@ +db = Application::Instance()->fetchService(Database::class); + } + + public function save() { + $classInfo = new ReflectionClass($this); + $properties = $classInfo->getProperties(ReflectionProperty::IS_PROTECTED); + $columns = []; + $values = []; + $types = []; + + foreach ($properties as $property) { + if ($property->getType() instanceof ReflectionUnionType || $property->isStatic() || !$property->getType()->isBuiltin()) + continue; + + array_push($columns, $property->name); + array_push($values, $this->{$property->name}); + array_push($types, Database::PrimitiveTypeToBindParam($property->getType()->getName())); + } + $placeholders = array_fill(0, count($columns), '?'); + + $qs = "INSERT INTO ".self::TableName()." (".implode(", ", $columns).") VALUES (".implode(", ", $placeholders).")"; + $query = $this->db->prepare($qs); + $query->bind_param(implode("", $types), ...$values); + } + + public function all() : array { + $result = $this->db->query("SELECT * FROM ".self::TableName()); + + $out = []; + while ($row = $result->fetch_object()) + array_push($out, $row); + + return $out; + } + + private static function TableName() : string { + return self::$TableName ?? self::Type2TableName(static::class); + } + + private static function Type2TableName(string $type) : string { + $parts = explode("\\", $type); + $basename = strtolower(array_pop($parts)); + + return substr($basename, -1) === "s" ? $basename : $basename."s"; + } +} diff --git a/src/models/Model.php b/src/models/Model.php new file mode 100644 index 0000000..317c0af --- /dev/null +++ b/src/models/Model.php @@ -0,0 +1,5 @@ + */ + private readonly array $get; + /** @var array */ + private readonly array $post; + + public function __construct() { + $queryString = parse_url("/".ltrim($_SERVER["REQUEST_URI"], '/'), PHP_URL_PATH); + assert($queryString !== false, "unreachable"); + + $this->type = RequestType::tryFrom($_SERVER["REQUEST_METHOD"]) ?? RequestType::GET; + $this->query = !$queryString ? "" : $queryString; + $this->get = $_GET; + $this->post = $_POST; + } + + /** + * @param array> $attributes + */ + public function validate(array $attributes) : bool { + $validator = new Validator($this->all(), $attributes); + + return $validator->validate() === null; + } + + public function match(string $pattern) : bool { + $pattern = "/^".preg_replace("/\//", "\/", $pattern)."$/"; + + return preg_match($pattern, $this->query) === 1; + } + + /** + * @return array + */ + public function all() : array { + return $this->type === RequestType::POST ? $this->post : $this->get; + } + + public function get(string $key, ?string $defaultValue = null) : ?string { + return isset($this->get[$key]) ? $this->get[$key] : $defaultValue; + } + + public function post(string $key, ?string $defaultValue = null) : ?string { + return isset($this->post[$key]) ? $this->post[$key] : $defaultValue; + } +} diff --git a/src/services/Router.php b/src/services/Router.php new file mode 100644 index 0000000..549fe31 --- /dev/null +++ b/src/services/Router.php @@ -0,0 +1,108 @@ +controllerContainer = new ControllerContainer($app); + $this->routes = [ + RequestType::GET->value => [], + RequestType::POST->value => [], + RequestType::PUT->value => [], + RequestType::DELETE->value => [], + ]; + } + + private function addCallback(RequestType $type, string $route, callable | array $callback) : void { + $this->routes[$type->value][$route] = $callback; + } + + public function get(string $route, callable | array $callback) : void { + $this->addCallback(RequestType::GET, $route, $callback); + } + + public function post(string $route, callable | array $callback) : void { + $this->addCallback(RequestType::POST, $route, $callback); + } + + public function put(string $route, callable | array $callback) : void { + $this->addCallback(RequestType::PUT, $route, $callback); + } + + public function delete(string $route, callable | array $callback) : void { + $this->addCallback(RequestType::DELETE, $route, $callback); + } + + public function view(string $route, string $template, array $context = []) { + $this->addCallback(RequestType::GET, $route, fn() => view($template, $context)); + } + + public function route() : void { + $matched = false; + foreach ($this->routes[$this->request->type->value] as $route => $callback) { + if ($this->request->match($route)) { + $matched = true; + $this->call($callback); + } + } + + if (!$matched) + view("404"); + } + + public static function Redirect(string $target) : void { + header("Location: $target"); + } + + private function call(callable | array $callback) : void { + $callbackInfo = null; + + if (is_array($callback)) { + assert(count($callback) === 2, "only callables can be routed"); + [$class, $method] = $callback; + $callbackInfo = new ReflectionMethod($class, $method); + $arguments = $this->getArgs($callbackInfo); + $callbackInfo->invokeArgs($this->controllerContainer->get($class), $arguments); + } else { + $callbackInfo = new ReflectionFunction($callback); + $arguments = $this->getArgs($callbackInfo); + $callbackInfo->invokeArgs($arguments); + } + } + + private function getArgs(ReflectionFunction | ReflectionMethod $function) : array { + $arguments = []; + foreach ($function->getParameters() as $parameter) { + $type = $parameter->getType(); + if ($type === null) + throw new Exception("constructor arguments need to name a type"); + array_push($arguments, $this->app->fetchService($type->getName())); + } + + return $arguments; + } +} + +} + +namespace { + +function redirect(string $target) { + massivedynamic\lucidity\services\Router::Redirect($target); +} + +} diff --git a/src/services/Service.php b/src/services/Service.php new file mode 100644 index 0000000..06c1a6f --- /dev/null +++ b/src/services/Service.php @@ -0,0 +1,6 @@ +errors = []; + $this->validators = self::ParseValidationArray($validation); + } + + public function validate() : ?array { + foreach ($this->validators as $key => $validators) { + foreach($validators as $validator) { + switch ($validator->type) { + case ValidationType::REQUIRED: + $this->validateRequired($key); + break; + case ValidationType::MIN: + $this->validateMin($key, $validator); + break; + case ValidationType::MAX: + $this->validateMax($key, $validator); + break; + } + } + } + return count($this->errors) > 0 ? $this->errors : null; + } + + private function validateMin(string $key, Validation $validator) : void { + $length = strlen($this->pool[$key]); + if ($length < intval($validator->parameter)) + array_push($this->errors, "Field '$key' must be at least $validator->parameter characters long (is $length)"); + + } + + private function validateMax(string $key, Validation $validator) : void { + $length = strlen($this->pool[$key]); + if ($length > intval($validator->parameter)) + array_push($this->errors, "Field '$key' must be at least $validator->parameter characters long (is $length)"); + + } + + private function validateRequired(string $key) : void { + if (!isset($this->pool[$key])) + array_push($this->errors, "Field '$key' is required"); + } + + /** + * @@param array $validation + */ + public static function ParseValidationArray(array $validation) : array { + $out = []; + + foreach ($validation as $key => $validators) { + $validators = explode("|", $validators); + if (count($validators) < 1) + continue; + + $out[$key] = []; + foreach ($validators as $validator) { + $parts = explode(":", $validator); + [$type, $argument] = count($parts) === 2 ? $parts : [$parts[0], ""]; + $argument = $argument ?? ""; + $typeResolved = ValidationType::tryFrom(strtolower($type)); + + if ($typeResolved === null) + throw new UnknownValidatorException($type); + + $validator = new Validation($typeResolved, $argument); + array_push($out[$key], $validator); + } + } + + return $out; + } +} diff --git a/views/404.mustache b/views/404.mustache new file mode 100644 index 0000000..580988b --- /dev/null +++ b/views/404.mustache @@ -0,0 +1,12 @@ +{{ +

HTTP 404

+

+ page not found +

+ +{{/content}} + +{{/LucidityLayout}} diff --git a/views/LucidityException.mustache b/views/LucidityException.mustache new file mode 100644 index 0000000..1cfd02b --- /dev/null +++ b/views/LucidityException.mustache @@ -0,0 +1,35 @@ +{{< LucidityLayout}} + +{{$content}} + +
+ + {{exceptionType}} + +

+ {{exception.getMessage}} +

+ {{#exception.getFile}} + {{.}}:{{exception.getLine}} + {{/exception.getFile}} +
+ PHP {{phpVersion}} + lucidity {{lucidityVersion}} +
+
+ +
+ {{#trace}} +
+ {{#file}} + {{.}}:{{line}} + {{/file}} +

{{class}}

+

{{function}}

+
+ {{/trace}} +
+ +{{/content}} + +{{/LucidityLayout}} diff --git a/views/partials/LucidityLayout.mustache b/views/partials/LucidityLayout.mustache new file mode 100644 index 0000000..823179e --- /dev/null +++ b/views/partials/LucidityLayout.mustache @@ -0,0 +1,72 @@ + + + {{title}} + + + + + {{$content}}{{/content}}