From a137c834f90d606033e3d3396efce316bf61cd14 Mon Sep 17 00:00:00 2001
From: topnuomi <1130395124@qq.com>
Date: Fri, 7 May 2021 10:09:18 +0800
Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E6=AC=A1=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.htaccess | 7 +
README.en.md | 36 -
README.md | 37 +-
applications/home/controllers/News.php | 51 ++
applications/home/route.php | 11 +
applications/home/services/Article.php | 0
applications/home/views/layout.php | 13 +
applications/home/views/news/lists.php | 15 +
composer.json | 11 +
composer.lock | 152 ++++
framework/base/Application.php | 138 +++
framework/base/Attribute.php | 104 +++
framework/base/Config.php | 21 +
framework/base/Container.php | 392 +++++++++
framework/base/Controller.php | 21 +
framework/base/Facade.php | 82 ++
framework/base/Middleware.php | 78 ++
framework/base/Request.php | 178 ++++
framework/base/Response.php | 245 ++++++
framework/base/Route.php | 261 ++++++
framework/base/attributes/Attribute.php | 48 +
.../base/attributes/response/ResponseType.php | 27 +
framework/base/functions/common.php | 135 +++
framework/base/response/Html.php | 16 +
framework/base/response/Json.php | 25 +
framework/base/response/Raw.php | 16 +
framework/base/response/Redirect.php | 17 +
framework/base/response/Text.php | 21 +
framework/base/response/Xml.php | 22 +
framework/exceptions/BaseException.php | 8 +
framework/exceptions/app/CallFailed.php | 14 +
framework/exceptions/app/ClassNotFound.php | 14 +
.../exceptions/app/ClassNotInstantiable.php | 14 +
.../exceptions/app/DefaultValueMissing.php | 14 +
framework/exceptions/app/Failed.php | 10 +
framework/exceptions/app/MethodNotFound.php | 14 +
framework/exceptions/route/RouteNotFound.php | 10 +
framework/facade/Application.php | 45 +
framework/facade/Attribute.php | 20 +
framework/facade/Response.php | 29 +
framework/facade/Route.php | 24 +
framework/traits/Instance.php | 46 +
framework/utils/Xml.php | 21 +
index.php | 124 +++
vendor/autoload.php | 7 +
vendor/composer/ClassLoader.php | 479 ++++++++++
vendor/composer/InstalledVersions.php | 301 +++++++
vendor/composer/LICENSE | 21 +
vendor/composer/autoload_classmap.php | 10 +
vendor/composer/autoload_namespaces.php | 9 +
vendor/composer/autoload_psr4.php | 13 +
vendor/composer/autoload_real.php | 57 ++
vendor/composer/autoload_static.php | 60 ++
vendor/composer/installed.json | 145 +++
vendor/composer/installed.php | 42 +
vendor/composer/platform_check.php | 26 +
vendor/filp/whoops/.github/FUNDING.yml | 1 +
.../filp/whoops/.github/workflows/tests.yml | 56 ++
vendor/filp/whoops/.mailmap | 2 +
vendor/filp/whoops/CHANGELOG.md | 98 +++
vendor/filp/whoops/LICENSE.md | 19 +
vendor/filp/whoops/composer.json | 45 +
.../src/Whoops/Exception/ErrorException.php | 17 +
.../whoops/src/Whoops/Exception/Formatter.php | 74 ++
.../whoops/src/Whoops/Exception/Frame.php | 295 +++++++
.../src/Whoops/Exception/FrameCollection.php | 203 +++++
.../whoops/src/Whoops/Exception/Inspector.php | 323 +++++++
.../src/Whoops/Handler/CallbackHandler.php | 52 ++
.../whoops/src/Whoops/Handler/Handler.php | 95 ++
.../src/Whoops/Handler/HandlerInterface.php | 36 +
.../Whoops/Handler/JsonResponseHandler.php | 88 ++
.../src/Whoops/Handler/PlainTextHandler.php | 359 ++++++++
.../src/Whoops/Handler/PrettyPageHandler.php | 830 ++++++++++++++++++
.../src/Whoops/Handler/XmlResponseHandler.php | 107 +++
.../src/Whoops/Resources/css/whoops.base.css | 604 +++++++++++++
.../src/Whoops/Resources/js/clipboard.min.js | 7 +
.../src/Whoops/Resources/js/prettify.min.js | 28 +
.../src/Whoops/Resources/js/whoops.base.js | 210 +++++
.../src/Whoops/Resources/js/zepto.min.js | 2 +
.../Resources/views/env_details.html.php | 42 +
.../Resources/views/frame_code.html.php | 63 ++
.../Resources/views/frame_list.html.php | 17 +
.../Resources/views/frames_container.html.php | 3 +
.../views/frames_description.html.php | 14 +
.../Whoops/Resources/views/header.html.php | 96 ++
.../Resources/views/header_outer.html.php | 3 +
.../Whoops/Resources/views/layout.html.php | 33 +
.../Resources/views/panel_details.html.php | 2 +
.../views/panel_details_outer.html.php | 3 +
.../Resources/views/panel_left.html.php | 4 +
.../Resources/views/panel_left_outer.html.php | 3 +
vendor/filp/whoops/src/Whoops/Run.php | 545 ++++++++++++
.../filp/whoops/src/Whoops/RunInterface.php | 140 +++
.../src/Whoops/Util/HtmlDumperOutput.php | 36 +
vendor/filp/whoops/src/Whoops/Util/Misc.php | 77 ++
.../whoops/src/Whoops/Util/SystemFacade.php | 144 +++
.../whoops/src/Whoops/Util/TemplateHelper.php | 352 ++++++++
vendor/psr/log/LICENSE | 19 +
vendor/psr/log/Psr/Log/AbstractLogger.php | 128 +++
.../log/Psr/Log/InvalidArgumentException.php | 7 +
vendor/psr/log/Psr/Log/LogLevel.php | 18 +
.../psr/log/Psr/Log/LoggerAwareInterface.php | 18 +
vendor/psr/log/Psr/Log/LoggerAwareTrait.php | 26 +
vendor/psr/log/Psr/Log/LoggerInterface.php | 125 +++
vendor/psr/log/Psr/Log/LoggerTrait.php | 142 +++
vendor/psr/log/Psr/Log/NullLogger.php | 30 +
vendor/psr/log/Psr/Log/Test/DummyTest.php | 18 +
.../log/Psr/Log/Test/LoggerInterfaceTest.php | 138 +++
vendor/psr/log/Psr/Log/Test/TestLogger.php | 147 ++++
vendor/psr/log/README.md | 58 ++
vendor/psr/log/composer.json | 26 +
111 files changed, 9713 insertions(+), 72 deletions(-)
create mode 100644 .htaccess
delete mode 100644 README.en.md
create mode 100644 applications/home/controllers/News.php
create mode 100644 applications/home/route.php
create mode 100644 applications/home/services/Article.php
create mode 100644 applications/home/views/layout.php
create mode 100644 applications/home/views/news/lists.php
create mode 100644 composer.json
create mode 100644 composer.lock
create mode 100644 framework/base/Application.php
create mode 100644 framework/base/Attribute.php
create mode 100644 framework/base/Config.php
create mode 100644 framework/base/Container.php
create mode 100644 framework/base/Controller.php
create mode 100644 framework/base/Facade.php
create mode 100644 framework/base/Middleware.php
create mode 100644 framework/base/Request.php
create mode 100644 framework/base/Response.php
create mode 100644 framework/base/Route.php
create mode 100644 framework/base/attributes/Attribute.php
create mode 100644 framework/base/attributes/response/ResponseType.php
create mode 100644 framework/base/functions/common.php
create mode 100644 framework/base/response/Html.php
create mode 100644 framework/base/response/Json.php
create mode 100644 framework/base/response/Raw.php
create mode 100644 framework/base/response/Redirect.php
create mode 100644 framework/base/response/Text.php
create mode 100644 framework/base/response/Xml.php
create mode 100644 framework/exceptions/BaseException.php
create mode 100644 framework/exceptions/app/CallFailed.php
create mode 100644 framework/exceptions/app/ClassNotFound.php
create mode 100644 framework/exceptions/app/ClassNotInstantiable.php
create mode 100644 framework/exceptions/app/DefaultValueMissing.php
create mode 100644 framework/exceptions/app/Failed.php
create mode 100644 framework/exceptions/app/MethodNotFound.php
create mode 100644 framework/exceptions/route/RouteNotFound.php
create mode 100644 framework/facade/Application.php
create mode 100644 framework/facade/Attribute.php
create mode 100644 framework/facade/Response.php
create mode 100644 framework/facade/Route.php
create mode 100644 framework/traits/Instance.php
create mode 100644 framework/utils/Xml.php
create mode 100644 index.php
create mode 100644 vendor/autoload.php
create mode 100644 vendor/composer/ClassLoader.php
create mode 100644 vendor/composer/InstalledVersions.php
create mode 100644 vendor/composer/LICENSE
create mode 100644 vendor/composer/autoload_classmap.php
create mode 100644 vendor/composer/autoload_namespaces.php
create mode 100644 vendor/composer/autoload_psr4.php
create mode 100644 vendor/composer/autoload_real.php
create mode 100644 vendor/composer/autoload_static.php
create mode 100644 vendor/composer/installed.json
create mode 100644 vendor/composer/installed.php
create mode 100644 vendor/composer/platform_check.php
create mode 100644 vendor/filp/whoops/.github/FUNDING.yml
create mode 100644 vendor/filp/whoops/.github/workflows/tests.yml
create mode 100644 vendor/filp/whoops/.mailmap
create mode 100644 vendor/filp/whoops/CHANGELOG.md
create mode 100644 vendor/filp/whoops/LICENSE.md
create mode 100644 vendor/filp/whoops/composer.json
create mode 100644 vendor/filp/whoops/src/Whoops/Exception/ErrorException.php
create mode 100644 vendor/filp/whoops/src/Whoops/Exception/Formatter.php
create mode 100644 vendor/filp/whoops/src/Whoops/Exception/Frame.php
create mode 100644 vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php
create mode 100644 vendor/filp/whoops/src/Whoops/Exception/Inspector.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/Handler.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php
create mode 100644 vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/js/prettify.min.js
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/js/whoops.base.js
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/js/zepto.min.js
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/env_details.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/frame_code.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/frame_list.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/frames_container.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/frames_description.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/header.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/header_outer.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/layout.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/panel_details.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/panel_details_outer.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/panel_left.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Resources/views/panel_left_outer.html.php
create mode 100644 vendor/filp/whoops/src/Whoops/Run.php
create mode 100644 vendor/filp/whoops/src/Whoops/RunInterface.php
create mode 100644 vendor/filp/whoops/src/Whoops/Util/HtmlDumperOutput.php
create mode 100644 vendor/filp/whoops/src/Whoops/Util/Misc.php
create mode 100644 vendor/filp/whoops/src/Whoops/Util/SystemFacade.php
create mode 100644 vendor/filp/whoops/src/Whoops/Util/TemplateHelper.php
create mode 100644 vendor/psr/log/LICENSE
create mode 100644 vendor/psr/log/Psr/Log/AbstractLogger.php
create mode 100644 vendor/psr/log/Psr/Log/InvalidArgumentException.php
create mode 100644 vendor/psr/log/Psr/Log/LogLevel.php
create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareInterface.php
create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareTrait.php
create mode 100644 vendor/psr/log/Psr/Log/LoggerInterface.php
create mode 100644 vendor/psr/log/Psr/Log/LoggerTrait.php
create mode 100644 vendor/psr/log/Psr/Log/NullLogger.php
create mode 100644 vendor/psr/log/Psr/Log/Test/DummyTest.php
create mode 100644 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
create mode 100644 vendor/psr/log/Psr/Log/Test/TestLogger.php
create mode 100644 vendor/psr/log/README.md
create mode 100644 vendor/psr/log/composer.json
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..54df6e9
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,7 @@
+
+ Options +FollowSymlinks -Multiviews
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+
\ No newline at end of file
diff --git a/README.en.md b/README.en.md
deleted file mode 100644
index d7d3d3b..0000000
--- a/README.en.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# The new framework
-
-#### Description
-{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Instructions
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Contribution
-
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
-
-
-#### Gitee Feature
-
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
diff --git a/README.md b/README.md
index 0016723..0125fa8 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,4 @@
# The new framework
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+基于PHP 8.0的新框架。
-#### 软件架构
-软件架构说明
-
-
-#### 安装教程
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 使用说明
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 参与贡献
-
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
-
-
-#### 特技
-
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
diff --git a/applications/home/controllers/News.php b/applications/home/controllers/News.php
new file mode 100644
index 0000000..e175b3d
--- /dev/null
+++ b/applications/home/controllers/News.php
@@ -0,0 +1,51 @@
+response(Json::class, [
+ 'id' => $id,
+ 'name' => $name,
+ 'id1' => $id1,
+ ]);
+ }
+
+ public function edit(int $id = 0): Response
+ {
+ return $this->response(Text::class, '编辑成功' . $id);
+ }
+
+ public function add(int $id): Response
+ {
+ return $this->response->setContent('测试123');
+ // return $this->response->setType(Redirect::class)->setContent('https://www.baidu.com');
+ // return $this->response->setType(Html::class)->setContent('
测试
');
+ // return $this->response(Redirect::class, 'https://www.baidu.com');
+ }
+
+ public function delete(int $id): Response
+ {
+ echo '执行到应用' . PHP_EOL;
+ return $this->response(Json::class, [
+ 'msg' => '删除'
+ ], 404);
+ }
+
+ private function response(string $type, $data, int $httpCode = 200): Response
+ {
+ return $this->response->setType($type)->setHttpCode($httpCode)->setContent($data);
+ }
+
+}
diff --git a/applications/home/route.php b/applications/home/route.php
new file mode 100644
index 0000000..e0e43fa
--- /dev/null
+++ b/applications/home/route.php
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ @yield(title)
+
+
+@yield(content)
+
+
diff --git a/applications/home/views/news/lists.php b/applications/home/views/news/lists.php
new file mode 100644
index 0000000..90f0503
--- /dev/null
+++ b/applications/home/views/news/lists.php
@@ -0,0 +1,15 @@
+
+
+@extend(layout)
+
+@block(title)
+=$title?>
+@endblock(title)
+
+@block(content)
+
+@endblock(content)
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..b7ee7c5
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,11 @@
+{
+ "autoload": {
+ "psr-4": {
+ "apps\\": "./applications/",
+ "top\\": "./framework/"
+ }
+ },
+ "require": {
+ "filp/whoops": "^2.12"
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..2953b40
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,152 @@
+{
+ "_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": "0c767e3cfcfb811844efc9dbf3eb5ccc",
+ "packages": [
+ {
+ "name": "filp/whoops",
+ "version": "2.12.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/filp/whoops.git",
+ "reference": "c13c0be93cff50f88bbd70827d993026821914dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/c13c0be93cff50f88bbd70827d993026821914dd",
+ "reference": "c13c0be93cff50f88bbd70827d993026821914dd",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^5.5.9 || ^7.0 || ^8.0",
+ "psr/log": "^1.0.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "^0.9 || ^1.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
+ "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+ "whoops/soap": "Formats errors as SOAP responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Whoops\\": "src/Whoops/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Filipe Dobreira",
+ "homepage": "https://github.com/filp",
+ "role": "Developer"
+ }
+ ],
+ "description": "php error handling for cool kids",
+ "homepage": "https://filp.github.io/whoops/",
+ "keywords": [
+ "error",
+ "exception",
+ "handling",
+ "library",
+ "throwable",
+ "whoops"
+ ],
+ "support": {
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.12.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/denis-sokolov",
+ "type": "github"
+ }
+ ],
+ "time": "2021-04-25T12:00:00+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "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/1.1.4"
+ },
+ "time": "2021-05-03T11:20:27+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "2.0.0"
+}
diff --git a/framework/base/Application.php b/framework/base/Application.php
new file mode 100644
index 0000000..267d3dc
--- /dev/null
+++ b/framework/base/Application.php
@@ -0,0 +1,138 @@
+ Attribute::class,
+ 'application' => Application::class,
+ 'app' => 'application',
+ 'config' => Config::class,
+ 'route' => Route::class,
+ 'request' => Request::class,
+ 'response' => Response::class,
+ 'middleware' => Middleware::class,
+ ];
+
+ /**
+ * Application constructor.
+ */
+ public function __construct()
+ {
+ // 实例化后保证从容器中取出单例
+ static::setInstance($this);
+
+ // 将当前实例放入容器实例池,以实现全局调用同一个实例
+ $this->instance('app', $this);
+
+ // 初始化应用
+ $this->init();
+ }
+
+ /**
+ * 初始化应用
+ */
+ private function init(): void
+ {
+ // 加载系统函数库
+ require __DIR__ . '/functions/common.php';
+ }
+
+ /**
+ * 设置/获取命名空间
+ * @param string $namespace
+ * @return string|$this
+ */
+ public function namespace(string $namespace = ''): string|static
+ {
+ if (!$this->namespace) {
+ $this->namespace = $namespace;
+ return $this;
+ }
+ return $this->namespace;
+ }
+
+ /**
+ * 设置应用名称
+ * @param string $name
+ * @return string|Application
+ */
+ public function name(string $name = ''): string|static
+ {
+ if (!$this->name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ return $this->name;
+ }
+
+ /**
+ * 设置应用目录
+ * @param string $path
+ * @return string|Application
+ */
+ public function path(string $path = ''): string|static
+ {
+ if (!$this->path) {
+ $this->path = $path;
+ return $this;
+ }
+
+ return $this->path;
+ }
+
+ /**
+ * 启动应用
+ */
+ public function boot(): void
+ {
+ // $response = app()->route->dispatch();
+ // $responseInstance = app()->response;
+ // if ($response instanceof $responseInstance) {
+ // echo $responseInstance->dispatch($response);
+ // } else {
+ // echo $response;
+ // }
+ }
+
+}
diff --git a/framework/base/Attribute.php b/framework/base/Attribute.php
new file mode 100644
index 0000000..4f4c2d3
--- /dev/null
+++ b/framework/base/Attribute.php
@@ -0,0 +1,104 @@
+classAttributes[$className])) {
+ $refClass = self::getClassRef($className);
+ foreach ($refClass->getAttributes() as $classAttribute) {
+ $this->classAttributes[$className][$classAttribute->getName()] = $classAttribute->newInstance();
+ }
+ }
+
+ if (null === $attributeName) {
+ return $this->classAttributes[$className] ?? null;
+ } elseif (isset($this->classAttributes[$className][$attributeName])) {
+ return $this->classAttributes[$className][$attributeName];
+ }
+
+ return null;
+ }
+
+ /**
+ * 获取方法注解
+ * @param string $className
+ * @param string $methodName
+ * @param string|null $attributeName
+ * @return mixed
+ * @throws ReflectionException
+ */
+ public function methodAttributes(string $className, string $methodName, ?string $attributeName = null): mixed
+ {
+ if (!isset($this->methodAttributes[$className][$methodName])) {
+ $refClass = self::getClassRef($className);
+ $refMethod = $refClass->getMethod($methodName);
+ foreach ($refMethod->getAttributes() as $methodAttribute) {
+ $this->methodAttributes[$className][$methodName][$methodAttribute->getName()] = $methodAttribute->newInstance();
+ }
+ }
+
+ if (null === $attributeName) {
+ return $this->methodAttributes[$className][$methodName] ?? null;
+ } elseif (isset($this->methodAttributes[$className][$methodName][$attributeName])) {
+ return $this->methodAttributes[$className][$methodName][$attributeName];
+ }
+
+ return null;
+ }
+
+ /**
+ * 获取类反射
+ * @param $className
+ * @return ReflectionClass
+ * @throws ReflectionException
+ */
+ private function getClassRef($className): ReflectionClass
+ {
+ if (!isset($this->refClasses[$className])) {
+ try {
+ $this->refClasses[$className] = new ReflectionClass($className);
+ } catch (ReflectionException $e) {
+ throw $e;
+ }
+ }
+
+ return $this->refClasses[$className];
+ }
+
+}
diff --git a/framework/base/Config.php b/framework/base/Config.php
new file mode 100644
index 0000000..cfbd922
--- /dev/null
+++ b/framework/base/Config.php
@@ -0,0 +1,21 @@
+getName();
+ }
+
+}
diff --git a/framework/base/Container.php b/framework/base/Container.php
new file mode 100644
index 0000000..c1c96db
--- /dev/null
+++ b/framework/base/Container.php
@@ -0,0 +1,392 @@
+bind[$name])) {
+ $this->bind[$name] = $bind;
+ }
+
+ return $this;
+ }
+
+ /**
+ * 绑定实例到容器
+ * @param string $name
+ * @param object $bind
+ * @return $this
+ */
+ public function instance(string $name, object $bind): static
+ {
+ $name = $this->getAlias($name);
+
+ $this->instances[$name] = $bind;
+
+ return $this;
+ }
+
+ /**
+ * 获取别名
+ * @param string $name
+ * @return string
+ */
+ public function getAlias(string $name): string
+ {
+ if (isset($this->bind[$name])) {
+ $bind = $this->bind[$name];
+
+ if (is_string($bind)) {
+ return $this->getAlias($bind);
+ }
+ }
+
+ return $name;
+ }
+
+ /**
+ * 获取类实例
+ * @param string $name
+ * @param array $args
+ * @param bool $newInstance
+ * @return mixed
+ * @throws CallFailed
+ * @throws ClassNotFound
+ */
+ public function make(string $name, array $args = [], bool $newInstance = false): mixed
+ {
+ // 获取到最终类名
+ $class = $this->getAlias($name);
+
+ // 检查类是否已经实例化,已经实例化则直接返回(需要新的实例则跳过此步)
+ if (isset($this->instances[$class]) && !$newInstance) {
+ return $this->instances[$class];
+ }
+ // 如果标识绑定到了闭包,则直接执行
+ if (isset($this->bind[$class]) && $this->bind[$class] instanceof Closure) {
+ $instance = $this->invokeFunction($this->bind[$class], $args);
+ } else {
+ // 非闭包则表示当前类名从未进行实例化,这里进行实例化
+ $instance = $this->createInstance($class, $args);
+ }
+
+ // 加入到实例池
+ if (!$newInstance && is_object($instance)) {
+ $this->instance($class, $instance);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * 创建类实例
+ * @param string $className
+ * @param array $args
+ * @return object
+ * @throws CallFailed
+ * @throws ClassNotFound
+ */
+ private function createInstance(string $className, array $args = []): object
+ {
+ try {
+ $reflectionClass = new ReflectionClass($className);
+ } catch (ReflectionException) {
+ throw new ClassNotFound(sprintf("类 '%s' 不存在", $className));
+ }
+ if (!$reflectionClass->isInstantiable()) {
+ throw new CallFailed(sprintf("类 '%s' 不能被实例化", $className));
+ }
+ // 检查是否存在构造方法,没有则直接返回实例
+ $construct = $reflectionClass->getConstructor();
+ if (is_null($construct)) {
+ return new $className;
+ }
+ // 准备调用参数进行注入
+ $parameters = $construct->getParameters();
+ $callParameters = $this->buildParameters($parameters, $args);
+
+ // 返回实例
+ try {
+ return $reflectionClass->newInstanceArgs($callParameters);
+ } catch (ReflectionException) {
+ throw new CallFailed(sprintf("构建类 '%s' 实例失败", $className));
+ }
+ }
+
+ /**
+ * 构建调用参数
+ * @param array|ReflectionParameter $reflectionParameter
+ * @param array $args
+ * @return array
+ * @throws CallFailed
+ */
+ private function buildParameters(array|ReflectionParameter $reflectionParameter, array $args = []): array
+ {
+ $index = 0;
+ $callParameters = [];
+ /** @var ReflectionParameter $parameter */
+ foreach ($reflectionParameter as $parameter) {
+ $type = (string)$parameter->getType();
+ $name = $parameter->getName();
+ try {
+ // 一、明确指定参数
+ // 1、如果在当前主动调用参数明确指定了参数,则直接使用
+ // 二、未明确指定参数
+ // 1、如果是类,则判断主动调用参数是否符合约定类型
+ // 是则直接使用,否则实例化一个新的对象作为参数
+ // 2、不是类则按顺序赋值使用
+ // 3、不是类且未主动传递参数则直接取默认值
+ if (isset($args[$name])) {
+ $callParameters[$name] = $args[$name];
+ } else {
+ // 参数是一个类实例
+ if (class_exists($type)) {
+ // 当前主动调用参数是当前约定类型则使用,否则直接构造
+ $instance = $this->make($type);
+ if (isset($args[$index]) && ($args[$index] instanceof $instance)) {
+ $callParameters[$name] = $args[$index];
+ $index++;
+ } else {
+ $callParameters[$name] = $instance;
+ }
+ } elseif (isset($args[$index])) {
+ $callParameters[$name] = $args[$index];
+ $index++;
+ } else {
+ // 如果不是类实例或没有主动调用参数则使用默认值
+ $callParameters[$name] = $parameter->getDefaultValue();
+ }
+ }
+ } catch (ReflectionException) {
+ // 没有默认值
+ $class = $parameter->getDeclaringClass()->getName();
+ $method = $parameter->getDeclaringFunction()->getName();
+ throw new CallFailed(sprintf("'%s::%s()' 缺少 '%s' 参数", $class, $method, $name));
+ } catch (ClassNotFound) {
+ throw new CallFailed(sprintf("类 '%s' 不存在", $type));
+ }
+ }
+
+ return $callParameters;
+ }
+
+ /**
+ * 执行函数
+ * @param mixed $function
+ * @param array $args
+ * @return mixed
+ * @throws CallFailed
+ */
+ public function invokeFunction(mixed $function, array $args = []): mixed
+ {
+ try {
+ $funcRef = new ReflectionFunction($function);
+ // 自动注入参数
+ $args = $this->buildParameters($funcRef->getParameters(), $args);
+ return $funcRef->invokeArgs($args);
+ } catch (ReflectionException) {
+ throw new CallFailed("函数调用失败");
+ }
+ }
+
+ /**
+ * 执行类方法
+ * @param string|object $className
+ * @param string $methodName
+ * @param array $args
+ * @return mixed
+ * @throws CallFailed
+ * @throws ClassNotFound
+ */
+ public function invokeMethod(string|object $className, string $methodName, array $args = []): mixed
+ {
+ $className = is_object($className) ? $className::class : $this->getAlias($className);
+ $instance = $this->make($className);
+ if (!method_exists($instance, $methodName)) {
+ throw new CallFailed(sprintf("方法 '%s::%s()' 不存在", $className, $methodName));
+ }
+ try {
+ $ref = new ReflectionMethod($className, $methodName);
+ // 自动注入参数
+ $args = $this->buildParameters($ref->getParameters(), $args);
+ return $ref->invokeArgs($instance, $args);
+ } catch (ReflectionException) {
+ throw new CallFailed(sprintf("方法 '%s::%s()' 调用失败", $className, $methodName));
+ }
+ }
+
+ /**
+ * 使用成员变量方式获取实例
+ * @param string $name
+ * @return mixed
+ * @throws CallFailed
+ * @throws ClassNotFound
+ */
+ public function __get(string $name): mixed
+ {
+ if (isset($this->bind[$name])) {
+ return $this->make($name);
+ }
+ return null;
+ }
+
+ /**
+ * 绑定实例
+ * @param string $name
+ * @param $value
+ */
+ public function __set(string $name, $value): void
+ {
+ $this->bind($name, $value);
+ }
+
+ /**
+ * 设置绑定标识
+ * @param string $name
+ * @param mixed $value
+ */
+ public function set(string $name, mixed $value): void
+ {
+ $this->bind($name, $value);
+ }
+
+ /**
+ * 检查是否存在绑定标识
+ * @param string $name
+ * @return bool
+ */
+ public function has(string $name = ''): bool
+ {
+ return isset($this->bind[$name]);
+ }
+
+ /**
+ * 移除绑定标识
+ * @param string $name
+ */
+ public function remove(string $name): void
+ {
+ $alias = $this->getAlias($name);
+ unset($this->instances[$alias]);
+ unset($this->bind[$name]);
+ }
+
+ /**
+ * 检查是否存在绑定标识
+ * @param mixed $offset
+ * @return bool
+ */
+ public function offsetExists(mixed $offset): bool
+ {
+ return $this->has($offset);
+ }
+
+ /**
+ * 获取实例
+ * @param mixed $offset
+ * @return mixed
+ * @throws CallFailed
+ * @throws ClassNotFound
+ */
+ public function offsetGet(mixed $offset): mixed
+ {
+ return $this->make($offset);
+ }
+
+ /**
+ * 绑定标识
+ * @param mixed $offset
+ * @param mixed $value
+ */
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ $this->bind($offset, $value);
+ }
+
+ /**
+ * 移除标识
+ * @param mixed $offset
+ */
+ public function offsetUnset(mixed $offset): void
+ {
+ $this->remove($offset);
+ }
+
+ /**
+ * 返回容器中实例数量
+ * @return int
+ */
+ public function count(): int
+ {
+ return count($this->instances);
+ }
+
+}
diff --git a/framework/base/Controller.php b/framework/base/Controller.php
new file mode 100644
index 0000000..df1c489
--- /dev/null
+++ b/framework/base/Controller.php
@@ -0,0 +1,21 @@
+make(static::getRepresentName());
+ }
+
+ /**
+ * 执行方法
+ * @param string $name
+ * @param array $arguments
+ * @return mixed
+ * @throws CallFailed
+ */
+ private static function invoke(string $name, array $arguments = []): mixed
+ {
+ try {
+ $instance = self::makeInstance();
+ return Container::getInstance()->invokeMethod($instance, $name, $arguments);
+ } catch (CallFailed | ClassNotFound $exception) {
+ throw new CallFailed($exception->getMessage());
+ }
+ }
+
+ /**
+ * 方法静态调用
+ * @param string $name
+ * @param array $arguments
+ * @return mixed
+ * @throws CallFailed
+ */
+ public static function __callStatic(string $name, array $arguments): mixed
+ {
+ return self::invoke($name, $arguments);
+ }
+
+ /**
+ * 方法动态调用
+ * @param $name
+ * @param $arguments
+ * @return mixed
+ * @throws CallFailed
+ */
+ public function __call($name, $arguments)
+ {
+ return self::invoke($name, $arguments);
+ }
+}
diff --git a/framework/base/Middleware.php b/framework/base/Middleware.php
new file mode 100644
index 0000000..0595300
--- /dev/null
+++ b/framework/base/Middleware.php
@@ -0,0 +1,78 @@
+middlewares = array_merge($this->middlewares, $middlewares);
+ } else {
+ $this->middlewares[] = $middlewares;
+ }
+ }
+
+ /**
+ * 执行中间件
+ * @param Closure $application
+ * @return Response
+ * @throws CallFailed
+ */
+ public function handler(Closure $application): Response
+ {
+ // 加载全局配置文件中配置的中间件
+ $middlewares = array_reverse($this->middlewares);
+
+ $requestInstance = $this->application->request;
+ $responseInstance = $this->application->response;
+ // 打包应用
+ $next = $application;
+ foreach ($middlewares as $middleware) {
+ $next = function () use ($requestInstance, $responseInstance, $middleware, $next) {
+ $response = (new $middleware)->handler($requestInstance, $next);
+ if ($response instanceof $responseInstance) {
+ return $response;
+ } else {
+ throw new BaseException(sprintf("中间件必须返回 '%s' 实例", $responseInstance::class));
+ }
+ };
+ }
+
+ return $this->application->invokeFunction($next);
+ }
+}
diff --git a/framework/base/Request.php b/framework/base/Request.php
new file mode 100644
index 0000000..3421b47
--- /dev/null
+++ b/framework/base/Request.php
@@ -0,0 +1,178 @@
+header = [];
+ $this->server = $_SERVER;
+ $this->get = $_GET;
+ $this->post = $_POST;
+ $this->files = $_FILES;
+ $this->raw = file_get_contents('php://input');
+
+ $this->setMethod($_SERVER['REQUEST_METHOD'] ?? '');
+ }
+
+ /**
+ * 获取访问地址
+ * @return string
+ */
+ public function url(): string
+ {
+ return $_SERVER['REQUEST_URI'];
+ }
+
+ /**
+ * 获取模块名称
+ * @return string
+ */
+ public function getModule(): string
+ {
+ return $this->module;
+ }
+
+ /**
+ * 设置模块
+ * @param string $module
+ * @return $this
+ */
+ public function setModule(string $module): static
+ {
+ $this->module = $module;
+ return $this;
+ }
+
+ /**
+ * 获取控制器名称
+ * @return string
+ */
+ public function getController(): string
+ {
+ return $this->controller;
+ }
+
+ /**
+ * 设置控制器
+ * @param string $controller
+ * @return $this
+ */
+ public function setController(string $controller): static
+ {
+ $this->controller = $controller;
+ return $this;
+ }
+
+ /**
+ * 获取方法名称
+ * @return string
+ */
+ public function getMethod(): string
+ {
+ return $this->method;
+ }
+
+ /**
+ * 设置方法
+ * @param string $method
+ * @return $this
+ */
+ public function setMethod(string $method): static
+ {
+ $this->method = strtolower($method);
+ return $this;
+ }
+
+ /**
+ * 获取请求参数
+ * @return array
+ */
+ public function getParameters(): array
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * 设置请求参数
+ * @param array $parameters
+ */
+ public function setParameters(array $parameters): void
+ {
+ $this->parameters = array_merge($this->parameters, $parameters);
+ }
+
+}
diff --git a/framework/base/Response.php b/framework/base/Response.php
new file mode 100644
index 0000000..d073968
--- /dev/null
+++ b/framework/base/Response.php
@@ -0,0 +1,245 @@
+ 'Continue',
+ 101 => 'Switching Protocols',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Moved Temporarily ',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 509 => 'Bandwidth Limit Exceeded',
+ ];
+
+ /**
+ * Response constructor.
+ * @param int $httpCode
+ * @param array $headers
+ */
+ public function __construct(int $httpCode = 200, array $headers = [])
+ {
+ $this->type = $this;
+ $this->httpCode = $httpCode;
+ if (!empty($headers)) {
+ $this->setHeaders($headers);
+ }
+ }
+
+ /**
+ * 设置Http状态码
+ * @param int $httpCode
+ * @return Response
+ */
+ public function setHttpCode(int $httpCode): Response
+ {
+ $this->type->httpCode = $httpCode;
+ return $this->type;
+ }
+
+ /**
+ * 获取Http状态码
+ * @return int
+ */
+ public function getHttpCode(): int
+ {
+ return $this->type->httpCode;
+ }
+
+ /**
+ * 设置Http协议版本
+ * @param string $httpLevel
+ * @return Response
+ */
+ public function setHttpLevel(string $httpLevel): Response
+ {
+ $this->type->httpLevel = $httpLevel;
+ return $this;
+ }
+
+ /**
+ * 获取Http协议版本
+ * @return string
+ */
+ public function getHttpLevel(): string
+ {
+ return $this->type->httpLevel;
+ }
+
+ /**
+ * 设置响应类型
+ * @param string $type
+ * @return $this
+ */
+ public function setType(string $type): Response|static
+ {
+ $this->type = app()->make($type);
+ return $this->type;
+ }
+
+ /**
+ * 获取响应类型
+ * @return Response
+ */
+ public function getType(): Response
+ {
+ return $this->type;
+ }
+
+ /**
+ * 设置当前响应header
+ * @param array $headers
+ * @return Response
+ */
+ public function setHeaders(array $headers = []): Response
+ {
+ $this->type->headers = array_merge($this->headers, $headers);
+ return $this->type;
+ }
+
+ /**
+ * 获取响应header
+ * @return array
+ */
+ public function getHeaders(): array
+ {
+ return $this->type->headers;
+ }
+
+ /**
+ * 设置响应内容
+ * @param mixed $content
+ * @return $this
+ */
+ public function setContent(mixed $content): static
+ {
+ $this->type->content = $content;
+ return $this->type;
+ }
+
+ /**
+ * 获取响应内容
+ * @return string
+ */
+ public function getContent(): string
+ {
+ return $this->type->content;
+ }
+
+ /**
+ * 响应header到客户端
+ */
+ private function outputHeaders(): void
+ {
+ $initHeader = $this->type->httpCode . ' ' . $this->type->httpStatus[$this->type->httpCode];
+ $this->type->setHeaders([
+ "{$this->type->httpLevel} $initHeader",
+ "Status $initHeader",
+ ]);
+ foreach ($this->type->headers as $key => $header) {
+ if (is_numeric($key)) {
+ header($header);
+ } else {
+ header("$key: $header");
+ }
+ }
+ }
+
+ /**
+ * 执行响应
+ * @return string
+ */
+ public function send(): string
+ {
+ // 响应Header
+ $this->type->outputHeaders();
+
+ return $this->type->getContent();
+ }
+
+}
diff --git a/framework/base/Route.php b/framework/base/Route.php
new file mode 100644
index 0000000..f1dcc62
--- /dev/null
+++ b/framework/base/Route.php
@@ -0,0 +1,261 @@
+route;
+ }
+
+ /**
+ * 路由分组
+ * @param string $name
+ * @param callable $closure
+ */
+ public static function group(string $name, callable $closure): void
+ {
+ self::$currentGroupName[] = trim($name, '/');
+ call_user_func($closure);
+ array_pop(self::$currentGroupName);
+ }
+
+ /**
+ * 获取当前访问方式的所有路由规则
+ * @param $method
+ * @return array
+ */
+ private function getRulesByMethod($method): array
+ {
+ return self::$routeList[$method] ?? [];
+ }
+
+ /**
+ * 解析链接自带参数
+ * @param string $url
+ * @return array
+ */
+ private function parseUrlParameters(string $url): array
+ {
+ // 去除两头的/,保证匹配时和路由规则的统一
+ $url = ($url != '/') ? trim($url, '/') : $url;
+ $parameters = [];
+ $urlComponents = parse_url($url);
+ if (isset($urlComponents['query']) && $urlComponents['query']) {
+ parse_str($urlComponents['query'], $parameters);
+ }
+
+ return [$urlComponents['path'], $parameters];
+ }
+
+ /**
+ * 匹配路由规则
+ * @return mixed
+ */
+ private function checkRoute(): mixed
+ {
+ list($url, $parameters) = $this->parseUrlParameters($this->request->url());
+ // 获取当前请求方式的所有路由规则
+ $rules = $this->getRulesByMethod($this->request->getMethod());
+ $currentRule = '';
+ foreach ($rules as $rule => $route) {
+ if (str_contains($rule, ':')) { // 规则表达式
+ preg_match_all('#:\w+#', $rule, $keys);
+ $pattern = preg_replace('#:[\d|\w]+#', '(((?!/).)*)', $rule);
+ preg_match("#$pattern#", $url, $match);
+ // 无匹配则直接开始下次匹配
+ if (empty($match)) {
+ continue;
+ }
+ // 重建数组索引
+ unset($match[0]);
+ $match = array_merge($match, []);
+ // 在匹配时第一次解析出参数
+ $idx = 0;
+ foreach ($keys[0] as $parameterName) {
+ $parameters[ltrim($parameterName, ':')] = $match[$idx];
+ $idx += 2;
+ }
+ } else { // 正则表达式
+ $pattern = "#^$rule$#";
+ preg_match($pattern, $url, $match);
+ if (empty($match[0])) {
+ continue;
+ }
+ }
+ $currentRule = $route;
+ break;
+ }
+ // 设置请求参数
+ $this->request->setParameters($parameters);
+
+ return $currentRule;
+ }
+
+ /**
+ * 执行路由
+ * @return Closure
+ * @throws RouteNotFound
+ */
+ public function dispatch(): Closure
+ {
+ // 检查当前路由,获取到当前配置的路由信息
+ $rule = $this->checkRoute();
+ if ($rule == '') {
+ throw new RouteNotFound(sprintf("请求 '%s' 无法处理", $this->request->url()));
+ }
+ // 打包应用
+ return function () use ($rule) {
+ $response = null;
+ if (is_callable($rule)) {
+ $response = $this->invokeFunction($rule);
+ } elseif (is_array($rule)) { // 路由被定义为执行类方法
+ $controller = $rule[0] ?? 'Index';
+ $method = $rule[1] ?? 'index';
+ $response = $this->invokeClass($controller, $method);
+ } elseif (str_starts_with($rule, '/') || preg_match('/http[s]?:\/\//', $rule, $match)) {
+ // TODO 重定向
+ $response = $this->application->response
+ ->setType(Redirect::class)
+ ->setHttpCode(301)
+ ->setContent($rule);
+ }
+
+ // 如果路由处理结果为Response对象则直接返回,否则返回空的Response对象
+ if ($response instanceof $this->application->response) {
+ return $response;
+ }
+
+ return $this->application->response;
+ };
+ }
+
+ /**
+ * 类方法
+ * @param string $name
+ * @param string $method
+ * @return mixed
+ * @throws CallFailed
+ * @throws ClassNotFound
+ */
+ private function invokeClass(string $name, string $method): mixed
+ {
+ // 解析路由规则携带的固定参数
+ list($method, $fixedParameters) = $this->parseUrlParameters($method);
+ if (!empty($fixedParameters)) {
+ $this->request->setParameters($fixedParameters);
+ }
+ $requestParameters = $this->request->getParameters();
+ // 调用类方法
+ return $this->application->invokeMethod($name, $method, $requestParameters);
+ }
+
+ /**
+ * 闭包函数
+ * @param Closure $closure
+ * @return mixed
+ * @throws CallFailed
+ */
+ private function invokeFunction(Closure $closure): mixed
+ {
+ return $this->application->invokeFunction($closure);
+ }
+
+}
diff --git a/framework/base/attributes/Attribute.php b/framework/base/attributes/Attribute.php
new file mode 100644
index 0000000..ef06567
--- /dev/null
+++ b/framework/base/attributes/Attribute.php
@@ -0,0 +1,48 @@
+data = $args;
+ }
+
+ /**
+ * 获取参数
+ * @param int $index
+ * @return mixed
+ */
+ public function getAttributeVar(int $index = 0): mixed
+ {
+ return $this->data[$index] ?? '';
+ }
+
+ /**
+ * 获取注解名
+ * @return string
+ */
+ public function getAttributeName(): string
+ {
+ return substr(static::class, strrpos(static::class, '\\') + 1);
+ }
+}
diff --git a/framework/base/attributes/response/ResponseType.php b/framework/base/attributes/response/ResponseType.php
new file mode 100644
index 0000000..0112a39
--- /dev/null
+++ b/framework/base/attributes/response/ResponseType.php
@@ -0,0 +1,27 @@
+type = $type;
+ }
+
+ public function getInstance()
+ {
+ $type = $_GET['type'];
+ return app()->make($this->type[$type]);
+ }
+
+}
diff --git a/framework/base/functions/common.php b/framework/base/functions/common.php
new file mode 100644
index 0000000..f919ba7
--- /dev/null
+++ b/framework/base/functions/common.php
@@ -0,0 +1,135 @@
+make($name, $args, $newInstance);
+ } catch (CallFailed | ClassNotFound) {
+ return null;
+ }
+ }
+}
+
+if (!function_exists('response')) {
+ /**
+ * 响应助手函数
+ * @param mixed $content
+ * @param string $type
+ * @param int $code
+ * @param array $headers
+ * @return Response
+ */
+ function response(mixed $content, string $type = Json::class, int $code = 200, array $headers = []): Response
+ {
+ return Application::getInstance()->response
+ ->setHeaders($headers)
+ ->setHttpCode($code)
+ ->setType($type)
+ ->setContent($content);
+ }
+}
+
+if (!function_exists('param')) {
+ /**
+ * 获取请求中的参数
+ * @param string $name
+ * @param array $except
+ * @param mixed|null $default
+ * @param mixed|null $filter
+ * @return bool|array|null
+ */
+ function param(string $name = '*', array $except = [], mixed $default = null, mixed $filter = null): bool|array|null
+ {
+ $request = Application::getInstance()->request;
+ $parameters = $request->getParameters();
+ if (str_starts_with($name, '?')) {
+ $name = substr($name, 1);
+ return isset($parameters[$name]);
+ }
+ if ($name == '*') {
+ foreach ($parameters as $key => $parameter) {
+ if (in_array($key, $except)) {
+ unset($parameters[$key]);
+ } else {
+ // TODO 留下的参数在此处进行过滤
+ $parameters[$key] = filter($parameter, $filter);
+ }
+ }
+ return $parameters;
+ } else {
+ if (isset($parameters[$name])) {
+ // TODO 参数在此进行过滤
+ return filter($parameters[$name], $filter);
+ } else {
+ return $default;
+ }
+ }
+ }
+}
+
+if (!function_exists('filter')) {
+ /**
+ * 过滤数据
+ * @param mixed $data
+ * @param mixed|null $filter
+ * @return mixed
+ */
+ function filter(mixed $data, mixed $filter = null): mixed
+ {
+ $app = Application::getInstance();
+ if (is_callable($filter) || (is_string($filter) && function_exists($filter))) {
+ return $app->invokeFunction($filter, [$data]);
+ } elseif (is_array($filter)) {
+ $class = $filter[0] ?? '';
+ $method = $filter[1] ?? '';
+ return $app->invokeMethod($class, $method, [$data]);
+ } else {
+ return $data;
+ }
+ }
+}
+
+if (!function_exists('get_header')) {
+ /**
+ * 获取请求所有header
+ * @return bool|array
+ */
+ function get_header(): bool|array
+ {
+ if (PHP_SAPI === 'apache2handler') {
+ $headers = getallheaders();
+ $data = [];
+ foreach ($headers as $key => $value) {
+ $data[strtolower($key)] = $value;
+ }
+ return $data;
+ } else {
+ $headers = [];
+ foreach ($_SERVER as $key => $value) {
+ if ('http_' == strtolower(substr($key, 0, 5))) {
+ $headers[strtolower(substr($key, 5))] = $value;
+ }
+ }
+ return $headers;
+ }
+ }
+}
diff --git a/framework/base/response/Html.php b/framework/base/response/Html.php
new file mode 100644
index 0000000..e9c45cd
--- /dev/null
+++ b/framework/base/response/Html.php
@@ -0,0 +1,16 @@
+ 'text/html; charset=utf-8'
+ ];
+}
diff --git a/framework/base/response/Json.php b/framework/base/response/Json.php
new file mode 100644
index 0000000..c67b83e
--- /dev/null
+++ b/framework/base/response/Json.php
@@ -0,0 +1,25 @@
+ 'application/json; charset=utf-8'
+ ];
+
+ public function setContent($content): static
+ {
+ return parent::setContent(json_encode($content, 256));
+ }
+}
diff --git a/framework/base/response/Raw.php b/framework/base/response/Raw.php
new file mode 100644
index 0000000..e85dfab
--- /dev/null
+++ b/framework/base/response/Raw.php
@@ -0,0 +1,16 @@
+ 'text/explain; charset=utf-8'
+ ];
+}
diff --git a/framework/base/response/Redirect.php b/framework/base/response/Redirect.php
new file mode 100644
index 0000000..adc6e76
--- /dev/null
+++ b/framework/base/response/Redirect.php
@@ -0,0 +1,17 @@
+setHttpCode(301);
+ $this->setHeaders([
+ 'Location' => $content,
+ ]);
+ return $this;
+ }
+}
diff --git a/framework/base/response/Text.php b/framework/base/response/Text.php
new file mode 100644
index 0000000..c31c3bd
--- /dev/null
+++ b/framework/base/response/Text.php
@@ -0,0 +1,21 @@
+ 'text/explain; charset=utf-8'
+ ];
+
+ public function setContent($content): static
+ {
+ return parent::setContent($content);
+ }
+}
diff --git a/framework/base/response/Xml.php b/framework/base/response/Xml.php
new file mode 100644
index 0000000..bf142bf
--- /dev/null
+++ b/framework/base/response/Xml.php
@@ -0,0 +1,22 @@
+ 'text/xml; charset=utf-8'
+ ];
+
+ public function setContent($content): static
+ {
+ return parent::setContent(XmlUtil::encode($content));
+ }
+}
diff --git a/framework/exceptions/BaseException.php b/framework/exceptions/BaseException.php
new file mode 100644
index 0000000..ebf388c
--- /dev/null
+++ b/framework/exceptions/BaseException.php
@@ -0,0 +1,8 @@
+make(static::class, $args);
+ } catch (CallFailed | ClassNotFound | ClassNotInstantiable | DefaultValueMissing) {
+ self::$instance = null;
+ }
+ }
+
+ return self::$instance;
+ }
+
+}
diff --git a/framework/utils/Xml.php b/framework/utils/Xml.php
new file mode 100644
index 0000000..70efcc0
--- /dev/null
+++ b/framework/utils/Xml.php
@@ -0,0 +1,21 @@
+';
+ ksort($params);
+ foreach ($params as $key => $param) {
+ if (is_numeric($param)) {
+ $xml .= '<' . $key . '>' . $param . '' . $key . '>';
+ } else {
+ $xml .= '<' . $key . '>' . $key . '>';
+ }
+ }
+ $xml .= '';
+ return $xml;
+ }
+}
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..64c8cdf
--- /dev/null
+++ b/index.php
@@ -0,0 +1,124 @@
+pushHandler(new PrettyPageHandler);
+// $whoops->register();
+
+
+$app = new Application();
+// 执行应用
+$app->namespace('apps')
+ ->path('./applications/')
+ ->name('home')
+ ->boot();
+
+Route::get('/', function () {
+ return \response('测试');
+});
+// 测试注册路由
+Route::get('news', [News::class, 'add?id=122']);
+Route::post('news/:id', function () use ($app): Response {
+ $parameters = $app->request->getParameters();
+ return $app->response->setType(Json::class)->setContent([
+ 'msg' => $parameters['id'] . '获取成功',
+ 'code' => 10000,
+ 'data' => new stdClass(),
+ ]);
+});
+Route::put('news/:id', [News::class, 'edit']);
+Route::delete('news/:id', [News::class, 'delete']);
+
+Route::group('admin', function () {
+ Route::get('member/edit/:category/:id', '/news');
+});
+function aaa($data)
+{
+ return $data . '-top-nuomi';
+}
+// 中间件
+class TestMiddleware
+{
+ public function handler(Request $request, $next)
+ {
+ // print_r(param(name: 'name', filter: 'aaa'));
+ // return \response('https://topnuomi.com', \top\base\response\Text::class);
+ // echo 'TestMiddleware' . PHP_EOL;
+ // echo 'TestMiddleware' . PHP_EOL;
+ // print_r($next);
+ return $next();
+ }
+}
+
+class TestMiddleware1
+{
+ public function handler(Request $request, $next)
+ {
+ // echo 'TestMiddleware1' . PHP_EOL;
+ // echo 'TestMiddleware1' . PHP_EOL;
+ return $next();
+ }
+}
+
+// 路由调度,获得打包后的应用
+$closure = $app->route->dispatch();
+
+// 测试添加中间件
+$app->middleware->addMiddleware([
+ TestMiddleware::class,
+ TestMiddleware1::class
+]);
+$response = $app->middleware->handler($closure);
+
+// print_r($response);
+
+// 响应输出
+echo $app->response->send();
+
+// class NewsModel extends Request
+// {
+// }
+// class Request
+// {
+// }
+// class News
+// {
+// public function lists(int $a, Request $request, int $page, News $news, int $t = 12345)
+// {
+// var_dump($a);
+// var_dump($page);
+// var_dump($request);
+// var_dump($news);
+// var_dump($t);
+// }
+// }
+// $container = Container::getInstance();
+// $container->invokeMethod('News', 'lists', [
+// 100, 1, 321
+// ]);
+// $container->bind('test', function ($name = '') {
+// return new A($name);
+// });
+// var_dump($container->make('A', ['王麻子1']));
+// $a = $container->make('A', ['王麻子'], true);
+// var_dump($container->make('A', ['王麻子'], true));
+// var_dump($container->make('A', ['王麻子2']));
+// var_dump($container->invokeMethod('B', 'method', [
+// 'i' => 1,
+// 'a' => new B,
+// 'j' => 2,
+// ]));
+// var_dump($container->make('test'));
+// var_dump($container->make('test'));
+// var_dump($container->make('test', ['王麻子3'], true));
diff --git a/vendor/autoload.php b/vendor/autoload.php
new file mode 100644
index 0000000..1a479a4
--- /dev/null
+++ b/vendor/autoload.php
@@ -0,0 +1,7 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ private $vendorDir;
+
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+ private $classMapAuthoritative = false;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ private static $registeredLoaders = array();
+
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ }
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders indexed by their corresponding vendor directories.
+ *
+ * @return self[]
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000..8dbce4c
--- /dev/null
+++ b/vendor/composer/InstalledVersions.php
@@ -0,0 +1,301 @@
+
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ 'name' => '__root__',
+ ),
+ 'versions' =>
+ array (
+ '__root__' =>
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ ),
+ 'filp/whoops' =>
+ array (
+ 'pretty_version' => '2.12.1',
+ 'version' => '2.12.1.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'c13c0be93cff50f88bbd70827d993026821914dd',
+ ),
+ 'psr/log' =>
+ array (
+ 'pretty_version' => '1.1.4',
+ 'version' => '1.1.4.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
+ ),
+ ),
+);
+private static $canGetVendors;
+private static $installedByVendor = array();
+
+
+
+
+
+
+
+public static function getInstalledPackages()
+{
+$packages = array();
+foreach (self::getInstalled() as $installed) {
+$packages[] = array_keys($installed['versions']);
+}
+
+
+if (1 === \count($packages)) {
+return $packages[0];
+}
+
+return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+}
+
+
+
+
+
+
+
+
+
+public static function isInstalled($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (isset($installed['versions'][$packageName])) {
+return true;
+}
+}
+
+return false;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function satisfies(VersionParser $parser, $packageName, $constraint)
+{
+$constraint = $parser->parseConstraints($constraint);
+$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+return $provided->matches($constraint);
+}
+
+
+
+
+
+
+
+
+
+
+public static function getVersionRanges($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+$ranges = array();
+if (isset($installed['versions'][$packageName]['pretty_version'])) {
+$ranges[] = $installed['versions'][$packageName]['pretty_version'];
+}
+if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+}
+if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+}
+if (array_key_exists('provided', $installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+}
+
+return implode(' || ', $ranges);
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getVersion($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['version'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['version'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getPrettyVersion($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['pretty_version'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getReference($packageName)
+{
+foreach (self::getInstalled() as $installed) {
+if (!isset($installed['versions'][$packageName])) {
+continue;
+}
+
+if (!isset($installed['versions'][$packageName]['reference'])) {
+return null;
+}
+
+return $installed['versions'][$packageName]['reference'];
+}
+
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+
+
+
+
+public static function getRootPackage()
+{
+$installed = self::getInstalled();
+
+return $installed[0]['root'];
+}
+
+
+
+
+
+
+
+public static function getRawData()
+{
+return self::$installed;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function reload($data)
+{
+self::$installed = $data;
+self::$installedByVendor = array();
+}
+
+
+
+
+private static function getInstalled()
+{
+if (null === self::$canGetVendors) {
+self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+}
+
+$installed = array();
+
+if (self::$canGetVendors) {
+foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+if (isset(self::$installedByVendor[$vendorDir])) {
+$installed[] = self::$installedByVendor[$vendorDir];
+} elseif (is_file($vendorDir.'/composer/installed.php')) {
+$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+}
+}
+}
+
+$installed[] = self::$installed;
+
+return $installed;
+}
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+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/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..b26f1b1
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,10 @@
+ $vendorDir . '/composer/InstalledVersions.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..b7fc012
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+ array($baseDir . '/framework'),
+ 'apps\\' => array($baseDir . '/applications'),
+ 'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
+ 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..ed5c329
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,57 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInitdd5637fbb8560d2a3e276d04d58781d5::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..6dfba10
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,60 @@
+
+ array (
+ 'top\\' => 4,
+ ),
+ 'a' =>
+ array (
+ 'apps\\' => 5,
+ ),
+ 'W' =>
+ array (
+ 'Whoops\\' => 7,
+ ),
+ 'P' =>
+ array (
+ 'Psr\\Log\\' => 8,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'top\\' =>
+ array (
+ 0 => __DIR__ . '/../..' . '/framework',
+ ),
+ 'apps\\' =>
+ array (
+ 0 => __DIR__ . '/../..' . '/applications',
+ ),
+ 'Whoops\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/filp/whoops/src/Whoops',
+ ),
+ 'Psr\\Log\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInitdd5637fbb8560d2a3e276d04d58781d5::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitdd5637fbb8560d2a3e276d04d58781d5::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInitdd5637fbb8560d2a3e276d04d58781d5::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000..b55e899
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,145 @@
+{
+ "packages": [
+ {
+ "name": "filp/whoops",
+ "version": "2.12.1",
+ "version_normalized": "2.12.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/filp/whoops.git",
+ "reference": "c13c0be93cff50f88bbd70827d993026821914dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/c13c0be93cff50f88bbd70827d993026821914dd",
+ "reference": "c13c0be93cff50f88bbd70827d993026821914dd",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^5.5.9 || ^7.0 || ^8.0",
+ "psr/log": "^1.0.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "^0.9 || ^1.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
+ "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+ "whoops/soap": "Formats errors as SOAP responses"
+ },
+ "time": "2021-04-25T12:00:00+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Whoops\\": "src/Whoops/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Filipe Dobreira",
+ "homepage": "https://github.com/filp",
+ "role": "Developer"
+ }
+ ],
+ "description": "php error handling for cool kids",
+ "homepage": "https://filp.github.io/whoops/",
+ "keywords": [
+ "error",
+ "exception",
+ "handling",
+ "library",
+ "throwable",
+ "whoops"
+ ],
+ "support": {
+ "issues": "https://github.com/filp/whoops/issues",
+ "source": "https://github.com/filp/whoops/tree/2.12.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/denis-sokolov",
+ "type": "github"
+ }
+ ],
+ "install-path": "../filp/whoops"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "version_normalized": "1.1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2021-05-03T11:20:27+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "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/1.1.4"
+ },
+ "install-path": "../psr/log"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": []
+}
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
new file mode 100644
index 0000000..9ccbc9e
--- /dev/null
+++ b/vendor/composer/installed.php
@@ -0,0 +1,42 @@
+
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ 'name' => '__root__',
+ ),
+ 'versions' =>
+ array (
+ '__root__' =>
+ array (
+ 'pretty_version' => '1.0.0+no-version-set',
+ 'version' => '1.0.0.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => NULL,
+ ),
+ 'filp/whoops' =>
+ array (
+ 'pretty_version' => '2.12.1',
+ 'version' => '2.12.1.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'c13c0be93cff50f88bbd70827d993026821914dd',
+ ),
+ 'psr/log' =>
+ array (
+ 'pretty_version' => '1.1.4',
+ 'version' => '1.1.4.0',
+ 'aliases' =>
+ array (
+ ),
+ 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
+ ),
+ ),
+);
diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php
new file mode 100644
index 0000000..d673084
--- /dev/null
+++ b/vendor/composer/platform_check.php
@@ -0,0 +1,26 @@
+= 50509)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 5.5.9". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+ } elseif (!headers_sent()) {
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+ }
+ }
+ trigger_error(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues),
+ E_USER_ERROR
+ );
+}
diff --git a/vendor/filp/whoops/.github/FUNDING.yml b/vendor/filp/whoops/.github/FUNDING.yml
new file mode 100644
index 0000000..74f6e85
--- /dev/null
+++ b/vendor/filp/whoops/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: denis-sokolov
diff --git a/vendor/filp/whoops/.github/workflows/tests.yml b/vendor/filp/whoops/.github/workflows/tests.yml
new file mode 100644
index 0000000..de3ed95
--- /dev/null
+++ b/vendor/filp/whoops/.github/workflows/tests.yml
@@ -0,0 +1,56 @@
+name: Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ tests:
+ name: PHP ${{ matrix.php }}
+ runs-on: ubuntu-20.04
+
+ strategy:
+ matrix:
+ php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
+
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v2
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ tools: composer:v2
+ coverage: none
+ env:
+ update: true
+
+ - name: Setup Problem Matchers
+ run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+
+ - name: Fix PHPUnit Version PHP < 7.4
+ uses: nick-invision/retry@v1
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer require "phpunit/phpunit:^6.5.14 || ^7.5.20 || ^8.5.8" --dev --no-update --no-interaction
+ if: "matrix.php < 7.4"
+
+ - name: Fix PHPUnit Version PHP >= 7.4
+ uses: nick-invision/retry@v1
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer require "phpunit/phpunit:^9.3.3" --dev --no-update --no-interaction
+ if: "matrix.php >= 7.4"
+
+ - name: Install PHP Dependencies
+ uses: nick-invision/retry@v1
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer update --no-interaction --no-progress
+
+ - name: Execute PHPUnit
+ run: vendor/bin/phpunit
diff --git a/vendor/filp/whoops/.mailmap b/vendor/filp/whoops/.mailmap
new file mode 100644
index 0000000..13ac5d7
--- /dev/null
+++ b/vendor/filp/whoops/.mailmap
@@ -0,0 +1,2 @@
+Denis Sokolov
+Filipe Dobreira
diff --git a/vendor/filp/whoops/CHANGELOG.md b/vendor/filp/whoops/CHANGELOG.md
new file mode 100644
index 0000000..859cb16
--- /dev/null
+++ b/vendor/filp/whoops/CHANGELOG.md
@@ -0,0 +1,98 @@
+# 2.12.1
+
+* Avoid redirecting away from an error.
+
+# 2.12.0
+
+* Hide non-string values in super globals when requested.
+
+# 2.11.0
+
+* Customize exit code
+
+# 2.10.0
+
+* Better chaining on handler classes
+
+# 2.9.2
+
+* Fix copy button styles
+
+# 2.9.1
+
+* Fix xdebug function crash on PHP 8
+
+# 2.9.0
+
+* JsonResponseHandler includes the exception code
+
+# 2.8.0
+
+* Support PHP 8
+
+# 2.7.3
+
+* PrettyPageHandler functionality to hide superglobal keys has a clearer name hideSuperglobalKey
+
+# 2.7.2
+
+* PrettyPageHandler now accepts custom js files
+* PrettyPageHandler templateHelper is now accessible through inheritance
+
+# 2.7.1
+
+* Fix a PHP warning in some cases with anonymous classes.
+
+# 2.7.0
+
+* removeFirstHandler and removeLastHandler.
+
+# 2.6.0
+
+* Fix 2.4.0 pushHandler changing the order of handlers.
+
+# 2.5.1
+
+* Fix error messaging in a rare case.
+
+# 2.5.0
+
+* Automatically configure xdebug if available.
+
+# 2.4.1
+
+* Try harder to close all output buffers
+
+# 2.4.0
+
+* Allow to prepend and append handlers.
+
+# 2.3.2
+
+* Various fixes from the community.
+
+# 2.3.1
+
+* Prevent exception in Whoops when caught exception frame is not related to real file
+
+# 2.3.0
+
+* Show previous exception messages.
+
+# 2.2.0
+
+* Support PHP 7.2
+
+# 2.1.0
+
+* Add a `SystemFacade` to allow clients to override Whoops behavior.
+* Show frame arguments in `PrettyPageHandler`.
+* Highlight the line with the error.
+* Add icons to search on Google and Stack Overflow.
+
+# 2.0.0
+
+Backwards compatibility breaking changes:
+
+* `Run` class is now `final`. If you inherited from `Run`, please now instead use a custom `SystemFacade` injected into the `Run` constructor, or contribute your changes to our core.
+* PHP < 5.5 support dropped.
diff --git a/vendor/filp/whoops/LICENSE.md b/vendor/filp/whoops/LICENSE.md
new file mode 100644
index 0000000..80407e7
--- /dev/null
+++ b/vendor/filp/whoops/LICENSE.md
@@ -0,0 +1,19 @@
+# The MIT License
+
+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/vendor/filp/whoops/composer.json b/vendor/filp/whoops/composer.json
new file mode 100644
index 0000000..0d90dfc
--- /dev/null
+++ b/vendor/filp/whoops/composer.json
@@ -0,0 +1,45 @@
+{
+ "name": "filp/whoops",
+ "license": "MIT",
+ "description": "php error handling for cool kids",
+ "keywords": ["library", "error", "handling", "exception", "whoops", "throwable"],
+ "homepage": "https://filp.github.io/whoops/",
+ "authors": [
+ {
+ "name": "Filipe Dobreira",
+ "homepage": "https://github.com/filp",
+ "role": "Developer"
+ }
+ ],
+ "scripts": {
+ "test": "phpunit --testdox tests"
+ },
+ "require": {
+ "php": "^5.5.9 || ^7.0 || ^8.0",
+ "psr/log": "^1.0.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
+ "mockery/mockery": "^0.9 || ^1.0",
+ "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
+ "whoops/soap": "Formats errors as SOAP responses"
+ },
+ "autoload": {
+ "psr-4": {
+ "Whoops\\": "src/Whoops/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Whoops\\": "tests/Whoops/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Exception/ErrorException.php b/vendor/filp/whoops/src/Whoops/Exception/ErrorException.php
new file mode 100644
index 0000000..d74e823
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Exception/ErrorException.php
@@ -0,0 +1,17 @@
+
+ */
+
+namespace Whoops\Exception;
+
+use ErrorException as BaseErrorException;
+
+/**
+ * Wraps ErrorException; mostly used for typing (at least now)
+ * to easily cleanup the stack trace of redundant info.
+ */
+class ErrorException extends BaseErrorException
+{
+}
diff --git a/vendor/filp/whoops/src/Whoops/Exception/Formatter.php b/vendor/filp/whoops/src/Whoops/Exception/Formatter.php
new file mode 100644
index 0000000..573dda0
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Exception/Formatter.php
@@ -0,0 +1,74 @@
+
+ */
+
+namespace Whoops\Exception;
+
+class Formatter
+{
+ /**
+ * Returns all basic information about the exception in a simple array
+ * for further convertion to other languages
+ * @param Inspector $inspector
+ * @param bool $shouldAddTrace
+ * @return array
+ */
+ public static function formatExceptionAsDataArray(Inspector $inspector, $shouldAddTrace)
+ {
+ $exception = $inspector->getException();
+ $response = [
+ 'type' => get_class($exception),
+ 'message' => $exception->getMessage(),
+ 'code' => $exception->getCode(),
+ 'file' => $exception->getFile(),
+ 'line' => $exception->getLine(),
+ ];
+
+ if ($shouldAddTrace) {
+ $frames = $inspector->getFrames();
+ $frameData = [];
+
+ foreach ($frames as $frame) {
+ /** @var Frame $frame */
+ $frameData[] = [
+ 'file' => $frame->getFile(),
+ 'line' => $frame->getLine(),
+ 'function' => $frame->getFunction(),
+ 'class' => $frame->getClass(),
+ 'args' => $frame->getArgs(),
+ ];
+ }
+
+ $response['trace'] = $frameData;
+ }
+
+ return $response;
+ }
+
+ public static function formatExceptionPlain(Inspector $inspector)
+ {
+ $message = $inspector->getException()->getMessage();
+ $frames = $inspector->getFrames();
+
+ $plain = $inspector->getExceptionName();
+ $plain .= ' thrown with message "';
+ $plain .= $message;
+ $plain .= '"'."\n\n";
+
+ $plain .= "Stacktrace:\n";
+ foreach ($frames as $i => $frame) {
+ $plain .= "#". (count($frames) - $i - 1). " ";
+ $plain .= $frame->getClass() ?: '';
+ $plain .= $frame->getClass() && $frame->getFunction() ? ":" : "";
+ $plain .= $frame->getFunction() ?: '';
+ $plain .= ' in ';
+ $plain .= ($frame->getFile() ?: '<#unknown>');
+ $plain .= ':';
+ $plain .= (int) $frame->getLine(). "\n";
+ }
+
+ return $plain;
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Exception/Frame.php b/vendor/filp/whoops/src/Whoops/Exception/Frame.php
new file mode 100644
index 0000000..8403209
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Exception/Frame.php
@@ -0,0 +1,295 @@
+
+ */
+
+namespace Whoops\Exception;
+
+use InvalidArgumentException;
+use Serializable;
+
+class Frame implements Serializable
+{
+ /**
+ * @var array
+ */
+ protected $frame;
+
+ /**
+ * @var string
+ */
+ protected $fileContentsCache;
+
+ /**
+ * @var array[]
+ */
+ protected $comments = [];
+
+ /**
+ * @var bool
+ */
+ protected $application;
+
+ /**
+ * @param array[]
+ */
+ public function __construct(array $frame)
+ {
+ $this->frame = $frame;
+ }
+
+ /**
+ * @param bool $shortened
+ * @return string|null
+ */
+ public function getFile($shortened = false)
+ {
+ if (empty($this->frame['file'])) {
+ return null;
+ }
+
+ $file = $this->frame['file'];
+
+ // Check if this frame occurred within an eval().
+ // @todo: This can be made more reliable by checking if we've entered
+ // eval() in a previous trace, but will need some more work on the upper
+ // trace collector(s).
+ if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert) code$/', $file, $matches)) {
+ $file = $this->frame['file'] = $matches[1];
+ $this->frame['line'] = (int) $matches[2];
+ }
+
+ if ($shortened && is_string($file)) {
+ // Replace the part of the path that all frames have in common, and add 'soft hyphens' for smoother line-breaks.
+ $dirname = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
+ if ($dirname !== '/') {
+ $file = str_replace($dirname, "…", $file);
+ }
+ $file = str_replace("/", "/", $file);
+ }
+
+ return $file;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getLine()
+ {
+ return isset($this->frame['line']) ? $this->frame['line'] : null;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getClass()
+ {
+ return isset($this->frame['class']) ? $this->frame['class'] : null;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getFunction()
+ {
+ return isset($this->frame['function']) ? $this->frame['function'] : null;
+ }
+
+ /**
+ * @return array
+ */
+ public function getArgs()
+ {
+ return isset($this->frame['args']) ? (array) $this->frame['args'] : [];
+ }
+
+ /**
+ * Returns the full contents of the file for this frame,
+ * if it's known.
+ * @return string|null
+ */
+ public function getFileContents()
+ {
+ if ($this->fileContentsCache === null && $filePath = $this->getFile()) {
+ // Leave the stage early when 'Unknown' or '[internal]' is passed
+ // this would otherwise raise an exception when
+ // open_basedir is enabled.
+ if ($filePath === "Unknown" || $filePath === '[internal]') {
+ return null;
+ }
+
+ try {
+ $this->fileContentsCache = file_get_contents($filePath);
+ } catch (ErrorException $exception) {
+ // Internal file paths of PHP extensions cannot be opened
+ }
+ }
+
+ return $this->fileContentsCache;
+ }
+
+ /**
+ * Adds a comment to this frame, that can be received and
+ * used by other handlers. For example, the PrettyPage handler
+ * can attach these comments under the code for each frame.
+ *
+ * An interesting use for this would be, for example, code analysis
+ * & annotations.
+ *
+ * @param string $comment
+ * @param string $context Optional string identifying the origin of the comment
+ */
+ public function addComment($comment, $context = 'global')
+ {
+ $this->comments[] = [
+ 'comment' => $comment,
+ 'context' => $context,
+ ];
+ }
+
+ /**
+ * Returns all comments for this frame. Optionally allows
+ * a filter to only retrieve comments from a specific
+ * context.
+ *
+ * @param string $filter
+ * @return array[]
+ */
+ public function getComments($filter = null)
+ {
+ $comments = $this->comments;
+
+ if ($filter !== null) {
+ $comments = array_filter($comments, function ($c) use ($filter) {
+ return $c['context'] == $filter;
+ });
+ }
+
+ return $comments;
+ }
+
+ /**
+ * Returns the array containing the raw frame data from which
+ * this Frame object was built
+ *
+ * @return array
+ */
+ public function getRawFrame()
+ {
+ return $this->frame;
+ }
+
+ /**
+ * Returns the contents of the file for this frame as an
+ * array of lines, and optionally as a clamped range of lines.
+ *
+ * NOTE: lines are 0-indexed
+ *
+ * @example
+ * Get all lines for this file
+ * $frame->getFileLines(); // => array( 0 => ' '...', ...)
+ * @example
+ * Get one line for this file, starting at line 10 (zero-indexed, remember!)
+ * $frame->getFileLines(9, 1); // array( 9 => '...' )
+ *
+ * @throws InvalidArgumentException if $length is less than or equal to 0
+ * @param int $start
+ * @param int $length
+ * @return string[]|null
+ */
+ public function getFileLines($start = 0, $length = null)
+ {
+ if (null !== ($contents = $this->getFileContents())) {
+ $lines = explode("\n", $contents);
+
+ // Get a subset of lines from $start to $end
+ if ($length !== null) {
+ $start = (int) $start;
+ $length = (int) $length;
+ if ($start < 0) {
+ $start = 0;
+ }
+
+ if ($length <= 0) {
+ throw new InvalidArgumentException(
+ "\$length($length) cannot be lower or equal to 0"
+ );
+ }
+
+ $lines = array_slice($lines, $start, $length, true);
+ }
+
+ return $lines;
+ }
+ }
+
+ /**
+ * Implements the Serializable interface, with special
+ * steps to also save the existing comments.
+ *
+ * @see Serializable::serialize
+ * @return string
+ */
+ public function serialize()
+ {
+ $frame = $this->frame;
+ if (!empty($this->comments)) {
+ $frame['_comments'] = $this->comments;
+ }
+
+ return serialize($frame);
+ }
+
+ /**
+ * Unserializes the frame data, while also preserving
+ * any existing comment data.
+ *
+ * @see Serializable::unserialize
+ * @param string $serializedFrame
+ */
+ public function unserialize($serializedFrame)
+ {
+ $frame = unserialize($serializedFrame);
+
+ if (!empty($frame['_comments'])) {
+ $this->comments = $frame['_comments'];
+ unset($frame['_comments']);
+ }
+
+ $this->frame = $frame;
+ }
+
+ /**
+ * Compares Frame against one another
+ * @param Frame $frame
+ * @return bool
+ */
+ public function equals(Frame $frame)
+ {
+ if (!$this->getFile() || $this->getFile() === 'Unknown' || !$this->getLine()) {
+ return false;
+ }
+ return $frame->getFile() === $this->getFile() && $frame->getLine() === $this->getLine();
+ }
+
+ /**
+ * Returns whether this frame belongs to the application or not.
+ *
+ * @return boolean
+ */
+ public function isApplication()
+ {
+ return $this->application;
+ }
+
+ /**
+ * Mark as an frame belonging to the application.
+ *
+ * @param boolean $application
+ */
+ public function setApplication($application)
+ {
+ $this->application = $application;
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php b/vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php
new file mode 100644
index 0000000..6cf4073
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php
@@ -0,0 +1,203 @@
+
+ */
+
+namespace Whoops\Exception;
+
+use ArrayAccess;
+use ArrayIterator;
+use Countable;
+use IteratorAggregate;
+use Serializable;
+use UnexpectedValueException;
+
+/**
+ * Exposes a fluent interface for dealing with an ordered list
+ * of stack-trace frames.
+ */
+class FrameCollection implements ArrayAccess, IteratorAggregate, Serializable, Countable
+{
+ /**
+ * @var array[]
+ */
+ private $frames;
+
+ /**
+ * @param array $frames
+ */
+ public function __construct(array $frames)
+ {
+ $this->frames = array_map(function ($frame) {
+ return new Frame($frame);
+ }, $frames);
+ }
+
+ /**
+ * Filters frames using a callable, returns the same FrameCollection
+ *
+ * @param callable $callable
+ * @return FrameCollection
+ */
+ public function filter($callable)
+ {
+ $this->frames = array_values(array_filter($this->frames, $callable));
+ return $this;
+ }
+
+ /**
+ * Map the collection of frames
+ *
+ * @param callable $callable
+ * @return FrameCollection
+ */
+ public function map($callable)
+ {
+ // Contain the map within a higher-order callable
+ // that enforces type-correctness for the $callable
+ $this->frames = array_map(function ($frame) use ($callable) {
+ $frame = call_user_func($callable, $frame);
+
+ if (!$frame instanceof Frame) {
+ throw new UnexpectedValueException(
+ "Callable to " . __CLASS__ . "::map must return a Frame object"
+ );
+ }
+
+ return $frame;
+ }, $this->frames);
+
+ return $this;
+ }
+
+ /**
+ * Returns an array with all frames, does not affect
+ * the internal array.
+ *
+ * @todo If this gets any more complex than this,
+ * have getIterator use this method.
+ * @see FrameCollection::getIterator
+ * @return array
+ */
+ public function getArray()
+ {
+ return $this->frames;
+ }
+
+ /**
+ * @see IteratorAggregate::getIterator
+ * @return ArrayIterator
+ */
+ public function getIterator()
+ {
+ return new ArrayIterator($this->frames);
+ }
+
+ /**
+ * @see ArrayAccess::offsetExists
+ * @param int $offset
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->frames[$offset]);
+ }
+
+ /**
+ * @see ArrayAccess::offsetGet
+ * @param int $offset
+ */
+ public function offsetGet($offset)
+ {
+ return $this->frames[$offset];
+ }
+
+ /**
+ * @see ArrayAccess::offsetSet
+ * @param int $offset
+ */
+ public function offsetSet($offset, $value)
+ {
+ throw new \Exception(__CLASS__ . ' is read only');
+ }
+
+ /**
+ * @see ArrayAccess::offsetUnset
+ * @param int $offset
+ */
+ public function offsetUnset($offset)
+ {
+ throw new \Exception(__CLASS__ . ' is read only');
+ }
+
+ /**
+ * @see Countable::count
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->frames);
+ }
+
+ /**
+ * Count the frames that belongs to the application.
+ *
+ * @return int
+ */
+ public function countIsApplication()
+ {
+ return count(array_filter($this->frames, function (Frame $f) {
+ return $f->isApplication();
+ }));
+ }
+
+ /**
+ * @see Serializable::serialize
+ * @return string
+ */
+ public function serialize()
+ {
+ return serialize($this->frames);
+ }
+
+ /**
+ * @see Serializable::unserialize
+ * @param string $serializedFrames
+ */
+ public function unserialize($serializedFrames)
+ {
+ $this->frames = unserialize($serializedFrames);
+ }
+
+ /**
+ * @param Frame[] $frames Array of Frame instances, usually from $e->getPrevious()
+ */
+ public function prependFrames(array $frames)
+ {
+ $this->frames = array_merge($frames, $this->frames);
+ }
+
+ /**
+ * Gets the innermost part of stack trace that is not the same as that of outer exception
+ *
+ * @param FrameCollection $parentFrames Outer exception frames to compare tail against
+ * @return Frame[]
+ */
+ public function topDiff(FrameCollection $parentFrames)
+ {
+ $diff = $this->frames;
+
+ $parentFrames = $parentFrames->getArray();
+ $p = count($parentFrames)-1;
+
+ for ($i = count($diff)-1; $i >= 0 && $p >= 0; $i--) {
+ /** @var Frame $tailFrame */
+ $tailFrame = $diff[$i];
+ if ($tailFrame->equals($parentFrames[$p])) {
+ unset($diff[$i]);
+ }
+ $p--;
+ }
+ return $diff;
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Exception/Inspector.php b/vendor/filp/whoops/src/Whoops/Exception/Inspector.php
new file mode 100644
index 0000000..8570959
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Exception/Inspector.php
@@ -0,0 +1,323 @@
+
+ */
+
+namespace Whoops\Exception;
+
+use Whoops\Util\Misc;
+
+class Inspector
+{
+ /**
+ * @var \Throwable
+ */
+ private $exception;
+
+ /**
+ * @var \Whoops\Exception\FrameCollection
+ */
+ private $frames;
+
+ /**
+ * @var \Whoops\Exception\Inspector
+ */
+ private $previousExceptionInspector;
+
+ /**
+ * @var \Throwable[]
+ */
+ private $previousExceptions;
+
+ /**
+ * @param \Throwable $exception The exception to inspect
+ */
+ public function __construct($exception)
+ {
+ $this->exception = $exception;
+ }
+
+ /**
+ * @return \Throwable
+ */
+ public function getException()
+ {
+ return $this->exception;
+ }
+
+ /**
+ * @return string
+ */
+ public function getExceptionName()
+ {
+ return get_class($this->exception);
+ }
+
+ /**
+ * @return string
+ */
+ public function getExceptionMessage()
+ {
+ return $this->extractDocrefUrl($this->exception->getMessage())['message'];
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getPreviousExceptionMessages()
+ {
+ return array_map(function ($prev) {
+ /** @var \Throwable $prev */
+ return $this->extractDocrefUrl($prev->getMessage())['message'];
+ }, $this->getPreviousExceptions());
+ }
+
+ /**
+ * @return int[]
+ */
+ public function getPreviousExceptionCodes()
+ {
+ return array_map(function ($prev) {
+ /** @var \Throwable $prev */
+ return $prev->getCode();
+ }, $this->getPreviousExceptions());
+ }
+
+ /**
+ * Returns a url to the php-manual related to the underlying error - when available.
+ *
+ * @return string|null
+ */
+ public function getExceptionDocrefUrl()
+ {
+ return $this->extractDocrefUrl($this->exception->getMessage())['url'];
+ }
+
+ private function extractDocrefUrl($message)
+ {
+ $docref = [
+ 'message' => $message,
+ 'url' => null,
+ ];
+
+ // php embbeds urls to the manual into the Exception message with the following ini-settings defined
+ // http://php.net/manual/en/errorfunc.configuration.php#ini.docref-root
+ if (!ini_get('html_errors') || !ini_get('docref_root')) {
+ return $docref;
+ }
+
+ $pattern = "/\[(?:[^<]+)<\/a>\]/";
+ if (preg_match($pattern, $message, $matches)) {
+ // -> strip those automatically generated links from the exception message
+ $docref['message'] = preg_replace($pattern, '', $message, 1);
+ $docref['url'] = $matches[1];
+ }
+
+ return $docref;
+ }
+
+ /**
+ * Does the wrapped Exception has a previous Exception?
+ * @return bool
+ */
+ public function hasPreviousException()
+ {
+ return $this->previousExceptionInspector || $this->exception->getPrevious();
+ }
+
+ /**
+ * Returns an Inspector for a previous Exception, if any.
+ * @todo Clean this up a bit, cache stuff a bit better.
+ * @return Inspector
+ */
+ public function getPreviousExceptionInspector()
+ {
+ if ($this->previousExceptionInspector === null) {
+ $previousException = $this->exception->getPrevious();
+
+ if ($previousException) {
+ $this->previousExceptionInspector = new Inspector($previousException);
+ }
+ }
+
+ return $this->previousExceptionInspector;
+ }
+
+
+ /**
+ * Returns an array of all previous exceptions for this inspector's exception
+ * @return \Throwable[]
+ */
+ public function getPreviousExceptions()
+ {
+ if ($this->previousExceptions === null) {
+ $this->previousExceptions = [];
+
+ $prev = $this->exception->getPrevious();
+ while ($prev !== null) {
+ $this->previousExceptions[] = $prev;
+ $prev = $prev->getPrevious();
+ }
+ }
+
+ return $this->previousExceptions;
+ }
+
+ /**
+ * Returns an iterator for the inspected exception's
+ * frames.
+ * @return \Whoops\Exception\FrameCollection
+ */
+ public function getFrames()
+ {
+ if ($this->frames === null) {
+ $frames = $this->getTrace($this->exception);
+
+ // Fill empty line/file info for call_user_func_array usages (PHP Bug #44428)
+ foreach ($frames as $k => $frame) {
+ if (empty($frame['file'])) {
+ // Default values when file and line are missing
+ $file = '[internal]';
+ $line = 0;
+
+ $next_frame = !empty($frames[$k + 1]) ? $frames[$k + 1] : [];
+
+ if ($this->isValidNextFrame($next_frame)) {
+ $file = $next_frame['file'];
+ $line = $next_frame['line'];
+ }
+
+ $frames[$k]['file'] = $file;
+ $frames[$k]['line'] = $line;
+ }
+ }
+
+ // Find latest non-error handling frame index ($i) used to remove error handling frames
+ $i = 0;
+ foreach ($frames as $k => $frame) {
+ if ($frame['file'] == $this->exception->getFile() && $frame['line'] == $this->exception->getLine()) {
+ $i = $k;
+ }
+ }
+
+ // Remove error handling frames
+ if ($i > 0) {
+ array_splice($frames, 0, $i);
+ }
+
+ $firstFrame = $this->getFrameFromException($this->exception);
+ array_unshift($frames, $firstFrame);
+
+ $this->frames = new FrameCollection($frames);
+
+ if ($previousInspector = $this->getPreviousExceptionInspector()) {
+ // Keep outer frame on top of the inner one
+ $outerFrames = $this->frames;
+ $newFrames = clone $previousInspector->getFrames();
+ // I assume it will always be set, but let's be safe
+ if (isset($newFrames[0])) {
+ $newFrames[0]->addComment(
+ $previousInspector->getExceptionMessage(),
+ 'Exception message:'
+ );
+ }
+ $newFrames->prependFrames($outerFrames->topDiff($newFrames));
+ $this->frames = $newFrames;
+ }
+ }
+
+ return $this->frames;
+ }
+
+ /**
+ * Gets the backtrace from an exception.
+ *
+ * If xdebug is installed
+ *
+ * @param \Throwable $e
+ * @return array
+ */
+ protected function getTrace($e)
+ {
+ $traces = $e->getTrace();
+
+ // Get trace from xdebug if enabled, failure exceptions only trace to the shutdown handler by default
+ if (!$e instanceof \ErrorException) {
+ return $traces;
+ }
+
+ if (!Misc::isLevelFatal($e->getSeverity())) {
+ return $traces;
+ }
+
+ if (!extension_loaded('xdebug') || !function_exists('xdebug_is_enabled') || !xdebug_is_enabled()) {
+ return $traces;
+ }
+
+ // Use xdebug to get the full stack trace and remove the shutdown handler stack trace
+ $stack = array_reverse(xdebug_get_function_stack());
+ $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+ $traces = array_diff_key($stack, $trace);
+
+ return $traces;
+ }
+
+ /**
+ * Given an exception, generates an array in the format
+ * generated by Exception::getTrace()
+ * @param \Throwable $exception
+ * @return array
+ */
+ protected function getFrameFromException($exception)
+ {
+ return [
+ 'file' => $exception->getFile(),
+ 'line' => $exception->getLine(),
+ 'class' => get_class($exception),
+ 'args' => [
+ $exception->getMessage(),
+ ],
+ ];
+ }
+
+ /**
+ * Given an error, generates an array in the format
+ * generated by ErrorException
+ * @param ErrorException $exception
+ * @return array
+ */
+ protected function getFrameFromError(ErrorException $exception)
+ {
+ return [
+ 'file' => $exception->getFile(),
+ 'line' => $exception->getLine(),
+ 'class' => null,
+ 'args' => [],
+ ];
+ }
+
+ /**
+ * Determine if the frame can be used to fill in previous frame's missing info
+ * happens for call_user_func and call_user_func_array usages (PHP Bug #44428)
+ *
+ * @param array $frame
+ * @return bool
+ */
+ protected function isValidNextFrame(array $frame)
+ {
+ if (empty($frame['file'])) {
+ return false;
+ }
+
+ if (empty($frame['line'])) {
+ return false;
+ }
+
+ if (empty($frame['function']) || !stristr($frame['function'], 'call_user_func')) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php b/vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php
new file mode 100644
index 0000000..cc46e70
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php
@@ -0,0 +1,52 @@
+
+ */
+
+namespace Whoops\Handler;
+
+use InvalidArgumentException;
+
+/**
+ * Wrapper for Closures passed as handlers. Can be used
+ * directly, or will be instantiated automagically by Whoops\Run
+ * if passed to Run::pushHandler
+ */
+class CallbackHandler extends Handler
+{
+ /**
+ * @var callable
+ */
+ protected $callable;
+
+ /**
+ * @throws InvalidArgumentException If argument is not callable
+ * @param callable $callable
+ */
+ public function __construct($callable)
+ {
+ if (!is_callable($callable)) {
+ throw new InvalidArgumentException(
+ 'Argument to ' . __METHOD__ . ' must be valid callable'
+ );
+ }
+
+ $this->callable = $callable;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function handle()
+ {
+ $exception = $this->getException();
+ $inspector = $this->getInspector();
+ $run = $this->getRun();
+ $callable = $this->callable;
+
+ // invoke the callable directly, to get simpler stacktraces (in comparison to call_user_func).
+ // this assumes that $callable is a properly typed php-callable, which we check in __construct().
+ return $callable($exception, $inspector, $run);
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/Handler.php b/vendor/filp/whoops/src/Whoops/Handler/Handler.php
new file mode 100644
index 0000000..cf1f708
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/Handler.php
@@ -0,0 +1,95 @@
+
+ */
+
+namespace Whoops\Handler;
+
+use Whoops\Exception\Inspector;
+use Whoops\RunInterface;
+
+/**
+ * Abstract implementation of a Handler.
+ */
+abstract class Handler implements HandlerInterface
+{
+ /*
+ Return constants that can be returned from Handler::handle
+ to message the handler walker.
+ */
+ const DONE = 0x10; // returning this is optional, only exists for
+ // semantic purposes
+ /**
+ * The Handler has handled the Throwable in some way, and wishes to skip any other Handler.
+ * Execution will continue.
+ */
+ const LAST_HANDLER = 0x20;
+ /**
+ * The Handler has handled the Throwable in some way, and wishes to quit/stop execution
+ */
+ const QUIT = 0x30;
+
+ /**
+ * @var RunInterface
+ */
+ private $run;
+
+ /**
+ * @var Inspector $inspector
+ */
+ private $inspector;
+
+ /**
+ * @var \Throwable $exception
+ */
+ private $exception;
+
+ /**
+ * @param RunInterface $run
+ */
+ public function setRun(RunInterface $run)
+ {
+ $this->run = $run;
+ }
+
+ /**
+ * @return RunInterface
+ */
+ protected function getRun()
+ {
+ return $this->run;
+ }
+
+ /**
+ * @param Inspector $inspector
+ */
+ public function setInspector(Inspector $inspector)
+ {
+ $this->inspector = $inspector;
+ }
+
+ /**
+ * @return Inspector
+ */
+ protected function getInspector()
+ {
+ return $this->inspector;
+ }
+
+ /**
+ * @param \Throwable $exception
+ */
+ public function setException($exception)
+ {
+ $this->exception = $exception;
+ }
+
+ /**
+ * @return \Throwable
+ */
+ protected function getException()
+ {
+ return $this->exception;
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php b/vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php
new file mode 100644
index 0000000..0265a85
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php
@@ -0,0 +1,36 @@
+
+ */
+
+namespace Whoops\Handler;
+
+use Whoops\Exception\Inspector;
+use Whoops\RunInterface;
+
+interface HandlerInterface
+{
+ /**
+ * @return int|null A handler may return nothing, or a Handler::HANDLE_* constant
+ */
+ public function handle();
+
+ /**
+ * @param RunInterface $run
+ * @return void
+ */
+ public function setRun(RunInterface $run);
+
+ /**
+ * @param \Throwable $exception
+ * @return void
+ */
+ public function setException($exception);
+
+ /**
+ * @param Inspector $inspector
+ * @return void
+ */
+ public function setInspector(Inspector $inspector);
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php b/vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php
new file mode 100644
index 0000000..2966d38
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php
@@ -0,0 +1,88 @@
+
+ */
+
+namespace Whoops\Handler;
+
+use Whoops\Exception\Formatter;
+
+/**
+ * Catches an exception and converts it to a JSON
+ * response. Additionally can also return exception
+ * frames for consumption by an API.
+ */
+class JsonResponseHandler extends Handler
+{
+ /**
+ * @var bool
+ */
+ private $returnFrames = false;
+
+ /**
+ * @var bool
+ */
+ private $jsonApi = false;
+
+ /**
+ * Returns errors[[]] instead of error[] to be in compliance with the json:api spec
+ * @param bool $jsonApi Default is false
+ * @return static
+ */
+ public function setJsonApi($jsonApi = false)
+ {
+ $this->jsonApi = (bool) $jsonApi;
+ return $this;
+ }
+
+ /**
+ * @param bool|null $returnFrames
+ * @return bool|static
+ */
+ public function addTraceToOutput($returnFrames = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->returnFrames;
+ }
+
+ $this->returnFrames = (bool) $returnFrames;
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function handle()
+ {
+ if ($this->jsonApi === true) {
+ $response = [
+ 'errors' => [
+ Formatter::formatExceptionAsDataArray(
+ $this->getInspector(),
+ $this->addTraceToOutput()
+ ),
+ ]
+ ];
+ } else {
+ $response = [
+ 'error' => Formatter::formatExceptionAsDataArray(
+ $this->getInspector(),
+ $this->addTraceToOutput()
+ ),
+ ];
+ }
+
+ echo json_encode($response, defined('JSON_PARTIAL_OUTPUT_ON_ERROR') ? JSON_PARTIAL_OUTPUT_ON_ERROR : 0);
+
+ return Handler::QUIT;
+ }
+
+ /**
+ * @return string
+ */
+ public function contentType()
+ {
+ return 'application/json';
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php b/vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php
new file mode 100644
index 0000000..6c148aa
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php
@@ -0,0 +1,359 @@
+
+* Plaintext handler for command line and logs.
+* @author Pierre-Yves Landuré
+*/
+
+namespace Whoops\Handler;
+
+use InvalidArgumentException;
+use Psr\Log\LoggerInterface;
+use Whoops\Exception\Frame;
+
+/**
+* Handler outputing plaintext error messages. Can be used
+* directly, or will be instantiated automagically by Whoops\Run
+* if passed to Run::pushHandler
+*/
+class PlainTextHandler extends Handler
+{
+ const VAR_DUMP_PREFIX = ' | ';
+
+ /**
+ * @var \Psr\Log\LoggerInterface
+ */
+ protected $logger;
+
+ /**
+ * @var callable
+ */
+ protected $dumper;
+
+ /**
+ * @var bool
+ */
+ private $addTraceToOutput = true;
+
+ /**
+ * @var bool|integer
+ */
+ private $addTraceFunctionArgsToOutput = false;
+
+ /**
+ * @var integer
+ */
+ private $traceFunctionArgsOutputLimit = 1024;
+
+ /**
+ * @var bool
+ */
+ private $addPreviousToOutput = true;
+
+ /**
+ * @var bool
+ */
+ private $loggerOnly = false;
+
+ /**
+ * Constructor.
+ * @throws InvalidArgumentException If argument is not null or a LoggerInterface
+ * @param \Psr\Log\LoggerInterface|null $logger
+ */
+ public function __construct($logger = null)
+ {
+ $this->setLogger($logger);
+ }
+
+ /**
+ * Set the output logger interface.
+ * @throws InvalidArgumentException If argument is not null or a LoggerInterface
+ * @param \Psr\Log\LoggerInterface|null $logger
+ */
+ public function setLogger($logger = null)
+ {
+ if (! (is_null($logger)
+ || $logger instanceof LoggerInterface)) {
+ throw new InvalidArgumentException(
+ 'Argument to ' . __METHOD__ .
+ " must be a valid Logger Interface (aka. Monolog), " .
+ get_class($logger) . ' given.'
+ );
+ }
+
+ $this->logger = $logger;
+ }
+
+ /**
+ * @return \Psr\Log\LoggerInterface|null
+ */
+ public function getLogger()
+ {
+ return $this->logger;
+ }
+
+ /**
+ * Set var dumper callback function.
+ *
+ * @param callable $dumper
+ * @return static
+ */
+ public function setDumper(callable $dumper)
+ {
+ $this->dumper = $dumper;
+ return $this;
+ }
+
+ /**
+ * Add error trace to output.
+ * @param bool|null $addTraceToOutput
+ * @return bool|static
+ */
+ public function addTraceToOutput($addTraceToOutput = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->addTraceToOutput;
+ }
+
+ $this->addTraceToOutput = (bool) $addTraceToOutput;
+ return $this;
+ }
+
+ /**
+ * Add previous exceptions to output.
+ * @param bool|null $addPreviousToOutput
+ * @return bool|static
+ */
+ public function addPreviousToOutput($addPreviousToOutput = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->addPreviousToOutput;
+ }
+
+ $this->addPreviousToOutput = (bool) $addPreviousToOutput;
+ return $this;
+ }
+
+ /**
+ * Add error trace function arguments to output.
+ * Set to True for all frame args, or integer for the n first frame args.
+ * @param bool|integer|null $addTraceFunctionArgsToOutput
+ * @return static|bool|integer
+ */
+ public function addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->addTraceFunctionArgsToOutput;
+ }
+
+ if (! is_integer($addTraceFunctionArgsToOutput)) {
+ $this->addTraceFunctionArgsToOutput = (bool) $addTraceFunctionArgsToOutput;
+ } else {
+ $this->addTraceFunctionArgsToOutput = $addTraceFunctionArgsToOutput;
+ }
+ return $this;
+ }
+
+ /**
+ * Set the size limit in bytes of frame arguments var_dump output.
+ * If the limit is reached, the var_dump output is discarded.
+ * Prevent memory limit errors.
+ * @var integer
+ * @return static
+ */
+ public function setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit)
+ {
+ $this->traceFunctionArgsOutputLimit = (integer) $traceFunctionArgsOutputLimit;
+ return $this;
+ }
+
+ /**
+ * Create plain text response and return it as a string
+ * @return string
+ */
+ public function generateResponse()
+ {
+ $exception = $this->getException();
+ $message = $this->getExceptionOutput($exception);
+
+ if ($this->addPreviousToOutput) {
+ $previous = $exception->getPrevious();
+ while ($previous) {
+ $message .= "\n\nCaused by\n" . $this->getExceptionOutput($previous);
+ $previous = $previous->getPrevious();
+ }
+ }
+
+
+ return $message . $this->getTraceOutput() . "\n";
+ }
+
+ /**
+ * Get the size limit in bytes of frame arguments var_dump output.
+ * If the limit is reached, the var_dump output is discarded.
+ * Prevent memory limit errors.
+ * @return integer
+ */
+ public function getTraceFunctionArgsOutputLimit()
+ {
+ return $this->traceFunctionArgsOutputLimit;
+ }
+
+ /**
+ * Only output to logger.
+ * @param bool|null $loggerOnly
+ * @return static|bool
+ */
+ public function loggerOnly($loggerOnly = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->loggerOnly;
+ }
+
+ $this->loggerOnly = (bool) $loggerOnly;
+ return $this;
+ }
+
+ /**
+ * Test if handler can output to stdout.
+ * @return bool
+ */
+ private function canOutput()
+ {
+ return !$this->loggerOnly();
+ }
+
+ /**
+ * Get the frame args var_dump.
+ * @param \Whoops\Exception\Frame $frame [description]
+ * @param integer $line [description]
+ * @return string
+ */
+ private function getFrameArgsOutput(Frame $frame, $line)
+ {
+ if ($this->addTraceFunctionArgsToOutput() === false
+ || $this->addTraceFunctionArgsToOutput() < $line) {
+ return '';
+ }
+
+ // Dump the arguments:
+ ob_start();
+ $this->dump($frame->getArgs());
+ if (ob_get_length() > $this->getTraceFunctionArgsOutputLimit()) {
+ // The argument var_dump is to big.
+ // Discarded to limit memory usage.
+ ob_clean();
+ return sprintf(
+ "\n%sArguments dump length greater than %d Bytes. Discarded.",
+ self::VAR_DUMP_PREFIX,
+ $this->getTraceFunctionArgsOutputLimit()
+ );
+ }
+
+ return sprintf(
+ "\n%s",
+ preg_replace('/^/m', self::VAR_DUMP_PREFIX, ob_get_clean())
+ );
+ }
+
+ /**
+ * Dump variable.
+ *
+ * @param mixed $var
+ * @return void
+ */
+ protected function dump($var)
+ {
+ if ($this->dumper) {
+ call_user_func($this->dumper, $var);
+ } else {
+ var_dump($var);
+ }
+ }
+
+ /**
+ * Get the exception trace as plain text.
+ * @return string
+ */
+ private function getTraceOutput()
+ {
+ if (! $this->addTraceToOutput()) {
+ return '';
+ }
+ $inspector = $this->getInspector();
+ $frames = $inspector->getFrames();
+
+ $response = "\nStack trace:";
+
+ $line = 1;
+ foreach ($frames as $frame) {
+ /** @var Frame $frame */
+ $class = $frame->getClass();
+
+ $template = "\n%3d. %s->%s() %s:%d%s";
+ if (! $class) {
+ // Remove method arrow (->) from output.
+ $template = "\n%3d. %s%s() %s:%d%s";
+ }
+
+ $response .= sprintf(
+ $template,
+ $line,
+ $class,
+ $frame->getFunction(),
+ $frame->getFile(),
+ $frame->getLine(),
+ $this->getFrameArgsOutput($frame, $line)
+ );
+
+ $line++;
+ }
+
+ return $response;
+ }
+
+ /**
+ * Get the exception as plain text.
+ * @param \Throwable $exception
+ * @return string
+ */
+ private function getExceptionOutput($exception)
+ {
+ return sprintf(
+ "%s: %s in file %s on line %d",
+ get_class($exception),
+ $exception->getMessage(),
+ $exception->getFile(),
+ $exception->getLine()
+ );
+ }
+
+ /**
+ * @return int
+ */
+ public function handle()
+ {
+ $response = $this->generateResponse();
+
+ if ($this->getLogger()) {
+ $this->getLogger()->error($response);
+ }
+
+ if (! $this->canOutput()) {
+ return Handler::DONE;
+ }
+
+ echo $response;
+
+ return Handler::QUIT;
+ }
+
+ /**
+ * @return string
+ */
+ public function contentType()
+ {
+ return 'text/plain';
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php b/vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php
new file mode 100644
index 0000000..c6c8be0
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php
@@ -0,0 +1,830 @@
+
+ */
+
+namespace Whoops\Handler;
+
+use InvalidArgumentException;
+use RuntimeException;
+use Symfony\Component\VarDumper\Cloner\AbstractCloner;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use UnexpectedValueException;
+use Whoops\Exception\Formatter;
+use Whoops\Util\Misc;
+use Whoops\Util\TemplateHelper;
+
+class PrettyPageHandler extends Handler
+{
+ const EDITOR_SUBLIME = "sublime";
+ const EDITOR_TEXTMATE = "textmate";
+ const EDITOR_EMACS = "emacs";
+ const EDITOR_MACVIM = "macvim";
+ const EDITOR_PHPSTORM = "phpstorm";
+ const EDITOR_IDEA = "idea";
+ const EDITOR_VSCODE = "vscode";
+ const EDITOR_ATOM = "atom";
+ const EDITOR_ESPRESSO = "espresso";
+ const EDITOR_XDEBUG = "xdebug";
+
+ /**
+ * Search paths to be scanned for resources.
+ *
+ * Stored in the reverse order they're declared.
+ *
+ * @var array
+ */
+ private $searchPaths = [];
+
+ /**
+ * Fast lookup cache for known resource locations.
+ *
+ * @var array
+ */
+ private $resourceCache = [];
+
+ /**
+ * The name of the custom css file.
+ *
+ * @var string|null
+ */
+ private $customCss = null;
+
+ /**
+ * The name of the custom js file.
+ *
+ * @var string|null
+ */
+ private $customJs = null;
+
+ /**
+ * @var array[]
+ */
+ private $extraTables = [];
+
+ /**
+ * @var bool
+ */
+ private $handleUnconditionally = false;
+
+ /**
+ * @var string
+ */
+ private $pageTitle = "Whoops! There was an error.";
+
+ /**
+ * @var array[]
+ */
+ private $applicationPaths;
+
+ /**
+ * @var array[]
+ */
+ private $blacklist = [
+ '_GET' => [],
+ '_POST' => [],
+ '_FILES' => [],
+ '_COOKIE' => [],
+ '_SESSION' => [],
+ '_SERVER' => [],
+ '_ENV' => [],
+ ];
+
+ /**
+ * An identifier for a known IDE/text editor.
+ *
+ * Either a string, or a calalble that resolves a string, that can be used
+ * to open a given file in an editor. If the string contains the special
+ * substrings %file or %line, they will be replaced with the correct data.
+ *
+ * @example
+ * "txmt://open?url=%file&line=%line"
+ *
+ * @var callable|string $editor
+ */
+ protected $editor;
+
+ /**
+ * A list of known editor strings.
+ *
+ * @var array
+ */
+ protected $editors = [
+ "sublime" => "subl://open?url=file://%file&line=%line",
+ "textmate" => "txmt://open?url=file://%file&line=%line",
+ "emacs" => "emacs://open?url=file://%file&line=%line",
+ "macvim" => "mvim://open/?url=file://%file&line=%line",
+ "phpstorm" => "phpstorm://open?file=%file&line=%line",
+ "idea" => "idea://open?file=%file&line=%line",
+ "vscode" => "vscode://file/%file:%line",
+ "atom" => "atom://core/open/file?filename=%file&line=%line",
+ "espresso" => "x-espresso://open?filepath=%file&lines=%line",
+ ];
+
+ /**
+ * @var TemplateHelper
+ */
+ protected $templateHelper;
+
+ /**
+ * Constructor.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ if (ini_get('xdebug.file_link_format') || extension_loaded('xdebug')) {
+ // Register editor using xdebug's file_link_format option.
+ $this->editors['xdebug'] = function ($file, $line) {
+ return str_replace(['%f', '%l'], [$file, $line], ini_get('xdebug.file_link_format'));
+ };
+
+ // If xdebug is available, use it as default editor.
+ $this->setEditor('xdebug');
+ }
+
+ // Add the default, local resource search path:
+ $this->searchPaths[] = __DIR__ . "/../Resources";
+
+ // blacklist php provided auth based values
+ $this->blacklist('_SERVER', 'PHP_AUTH_PW');
+
+ $this->templateHelper = new TemplateHelper();
+
+ if (class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
+ $cloner = new VarCloner();
+ // Only dump object internals if a custom caster exists for performance reasons
+ // https://github.com/filp/whoops/pull/404
+ $cloner->addCasters(['*' => function ($obj, $a, $stub, $isNested, $filter = 0) {
+ $class = $stub->class;
+ $classes = [$class => $class] + class_parents($obj) + class_implements($obj);
+
+ foreach ($classes as $class) {
+ if (isset(AbstractCloner::$defaultCasters[$class])) {
+ return $a;
+ }
+ }
+
+ // Remove all internals
+ return [];
+ }]);
+ $this->templateHelper->setCloner($cloner);
+ }
+ }
+
+ /**
+ * @return int|null
+ *
+ * @throws \Exception
+ */
+ public function handle()
+ {
+ if (!$this->handleUnconditionally()) {
+ // Check conditions for outputting HTML:
+ // @todo: Make this more robust
+ if (PHP_SAPI === 'cli') {
+ // Help users who have been relying on an internal test value
+ // fix their code to the proper method
+ if (isset($_ENV['whoops-test'])) {
+ throw new \Exception(
+ 'Use handleUnconditionally instead of whoops-test'
+ .' environment variable'
+ );
+ }
+
+ return Handler::DONE;
+ }
+ }
+
+ $templateFile = $this->getResource("views/layout.html.php");
+ $cssFile = $this->getResource("css/whoops.base.css");
+ $zeptoFile = $this->getResource("js/zepto.min.js");
+ $prettifyFile = $this->getResource("js/prettify.min.js");
+ $clipboard = $this->getResource("js/clipboard.min.js");
+ $jsFile = $this->getResource("js/whoops.base.js");
+
+ if ($this->customCss) {
+ $customCssFile = $this->getResource($this->customCss);
+ }
+
+ if ($this->customJs) {
+ $customJsFile = $this->getResource($this->customJs);
+ }
+
+ $inspector = $this->getInspector();
+ $frames = $this->getExceptionFrames();
+ $code = $this->getExceptionCode();
+
+ // List of variables that will be passed to the layout template.
+ $vars = [
+ "page_title" => $this->getPageTitle(),
+
+ // @todo: Asset compiler
+ "stylesheet" => file_get_contents($cssFile),
+ "zepto" => file_get_contents($zeptoFile),
+ "prettify" => file_get_contents($prettifyFile),
+ "clipboard" => file_get_contents($clipboard),
+ "javascript" => file_get_contents($jsFile),
+
+ // Template paths:
+ "header" => $this->getResource("views/header.html.php"),
+ "header_outer" => $this->getResource("views/header_outer.html.php"),
+ "frame_list" => $this->getResource("views/frame_list.html.php"),
+ "frames_description" => $this->getResource("views/frames_description.html.php"),
+ "frames_container" => $this->getResource("views/frames_container.html.php"),
+ "panel_details" => $this->getResource("views/panel_details.html.php"),
+ "panel_details_outer" => $this->getResource("views/panel_details_outer.html.php"),
+ "panel_left" => $this->getResource("views/panel_left.html.php"),
+ "panel_left_outer" => $this->getResource("views/panel_left_outer.html.php"),
+ "frame_code" => $this->getResource("views/frame_code.html.php"),
+ "env_details" => $this->getResource("views/env_details.html.php"),
+
+ "title" => $this->getPageTitle(),
+ "name" => explode("\\", $inspector->getExceptionName()),
+ "message" => $inspector->getExceptionMessage(),
+ "previousMessages" => $inspector->getPreviousExceptionMessages(),
+ "docref_url" => $inspector->getExceptionDocrefUrl(),
+ "code" => $code,
+ "previousCodes" => $inspector->getPreviousExceptionCodes(),
+ "plain_exception" => Formatter::formatExceptionPlain($inspector),
+ "frames" => $frames,
+ "has_frames" => !!count($frames),
+ "handler" => $this,
+ "handlers" => $this->getRun()->getHandlers(),
+
+ "active_frames_tab" => count($frames) && $frames->offsetGet(0)->isApplication() ? 'application' : 'all',
+ "has_frames_tabs" => $this->getApplicationPaths(),
+
+ "tables" => [
+ "GET Data" => $this->masked($_GET, '_GET'),
+ "POST Data" => $this->masked($_POST, '_POST'),
+ "Files" => isset($_FILES) ? $this->masked($_FILES, '_FILES') : [],
+ "Cookies" => $this->masked($_COOKIE, '_COOKIE'),
+ "Session" => isset($_SESSION) ? $this->masked($_SESSION, '_SESSION') : [],
+ "Server/Request Data" => $this->masked($_SERVER, '_SERVER'),
+ "Environment Variables" => $this->masked($_ENV, '_ENV'),
+ ],
+ ];
+
+ if (isset($customCssFile)) {
+ $vars["stylesheet"] .= file_get_contents($customCssFile);
+ }
+
+ if (isset($customJsFile)) {
+ $vars["javascript"] .= file_get_contents($customJsFile);
+ }
+
+ // Add extra entries list of data tables:
+ // @todo: Consolidate addDataTable and addDataTableCallback
+ $extraTables = array_map(function ($table) use ($inspector) {
+ return $table instanceof \Closure ? $table($inspector) : $table;
+ }, $this->getDataTables());
+ $vars["tables"] = array_merge($extraTables, $vars["tables"]);
+
+ $plainTextHandler = new PlainTextHandler();
+ $plainTextHandler->setException($this->getException());
+ $plainTextHandler->setInspector($this->getInspector());
+ $vars["preface"] = "";
+
+ $this->templateHelper->setVariables($vars);
+ $this->templateHelper->render($templateFile);
+
+ return Handler::QUIT;
+ }
+
+ /**
+ * Get the stack trace frames of the exception currently being handled.
+ *
+ * @return \Whoops\Exception\FrameCollection
+ */
+ protected function getExceptionFrames()
+ {
+ $frames = $this->getInspector()->getFrames();
+
+ if ($this->getApplicationPaths()) {
+ foreach ($frames as $frame) {
+ foreach ($this->getApplicationPaths() as $path) {
+ if (strpos($frame->getFile(), $path) === 0) {
+ $frame->setApplication(true);
+ break;
+ }
+ }
+ }
+ }
+
+ return $frames;
+ }
+
+ /**
+ * Get the code of the exception currently being handled.
+ *
+ * @return string
+ */
+ protected function getExceptionCode()
+ {
+ $exception = $this->getException();
+
+ $code = $exception->getCode();
+ if ($exception instanceof \ErrorException) {
+ // ErrorExceptions wrap the php-error types within the 'severity' property
+ $code = Misc::translateErrorCode($exception->getSeverity());
+ }
+
+ return (string) $code;
+ }
+
+ /**
+ * @return string
+ */
+ public function contentType()
+ {
+ return 'text/html';
+ }
+
+ /**
+ * Adds an entry to the list of tables displayed in the template.
+ *
+ * The expected data is a simple associative array. Any nested arrays
+ * will be flattened with `print_r`.
+ *
+ * @param string $label
+ * @param array $data
+ *
+ * @return static
+ */
+ public function addDataTable($label, array $data)
+ {
+ $this->extraTables[$label] = $data;
+ return $this;
+ }
+
+ /**
+ * Lazily adds an entry to the list of tables displayed in the table.
+ *
+ * The supplied callback argument will be called when the error is
+ * rendered, it should produce a simple associative array. Any nested
+ * arrays will be flattened with `print_r`.
+ *
+ * @param string $label
+ * @param callable $callback Callable returning an associative array
+ *
+ * @throws InvalidArgumentException If $callback is not callable
+ *
+ * @return static
+ */
+ public function addDataTableCallback($label, /* callable */ $callback)
+ {
+ if (!is_callable($callback)) {
+ throw new InvalidArgumentException('Expecting callback argument to be callable');
+ }
+
+ $this->extraTables[$label] = function (\Whoops\Exception\Inspector $inspector = null) use ($callback) {
+ try {
+ $result = call_user_func($callback, $inspector);
+
+ // Only return the result if it can be iterated over by foreach().
+ return is_array($result) || $result instanceof \Traversable ? $result : [];
+ } catch (\Exception $e) {
+ // Don't allow failure to break the rendering of the original exception.
+ return [];
+ }
+ };
+
+ return $this;
+ }
+
+ /**
+ * Returns all the extra data tables registered with this handler.
+ *
+ * Optionally accepts a 'label' parameter, to only return the data table
+ * under that label.
+ *
+ * @param string|null $label
+ *
+ * @return array[]|callable
+ */
+ public function getDataTables($label = null)
+ {
+ if ($label !== null) {
+ return isset($this->extraTables[$label]) ?
+ $this->extraTables[$label] : [];
+ }
+
+ return $this->extraTables;
+ }
+
+ /**
+ * Set whether to handle unconditionally.
+ *
+ * Allows to disable all attempts to dynamically decide whether to handle
+ * or return prematurely. Set this to ensure that the handler will perform,
+ * no matter what.
+ *
+ * @param bool|null $value
+ *
+ * @return bool|static
+ */
+ public function handleUnconditionally($value = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->handleUnconditionally;
+ }
+
+ $this->handleUnconditionally = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Adds an editor resolver.
+ *
+ * Either a string, or a closure that resolves a string, that can be used
+ * to open a given file in an editor. If the string contains the special
+ * substrings %file or %line, they will be replaced with the correct data.
+ *
+ * @example
+ * $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
+ * @example
+ * $run->addEditor('remove-it', function($file, $line) {
+ * unlink($file);
+ * return "http://stackoverflow.com";
+ * });
+ *
+ * @param string $identifier
+ * @param string|callable $resolver
+ *
+ * @return static
+ */
+ public function addEditor($identifier, $resolver)
+ {
+ $this->editors[$identifier] = $resolver;
+ return $this;
+ }
+
+ /**
+ * Set the editor to use to open referenced files.
+ *
+ * Pass either the name of a configured editor, or a closure that directly
+ * resolves an editor string.
+ *
+ * @example
+ * $run->setEditor(function($file, $line) { return "file:///{$file}"; });
+ * @example
+ * $run->setEditor('sublime');
+ *
+ * @param string|callable $editor
+ *
+ * @throws InvalidArgumentException If invalid argument identifier provided
+ *
+ * @return static
+ */
+ public function setEditor($editor)
+ {
+ if (!is_callable($editor) && !isset($this->editors[$editor])) {
+ throw new InvalidArgumentException(
+ "Unknown editor identifier: $editor. Known editors:" .
+ implode(",", array_keys($this->editors))
+ );
+ }
+
+ $this->editor = $editor;
+ return $this;
+ }
+
+ /**
+ * Get the editor href for a given file and line, if available.
+ *
+ * @param string $filePath
+ * @param int $line
+ *
+ * @throws InvalidArgumentException If editor resolver does not return a string
+ *
+ * @return string|bool
+ */
+ public function getEditorHref($filePath, $line)
+ {
+ $editor = $this->getEditor($filePath, $line);
+
+ if (empty($editor)) {
+ return false;
+ }
+
+ // Check that the editor is a string, and replace the
+ // %line and %file placeholders:
+ if (!isset($editor['url']) || !is_string($editor['url'])) {
+ throw new UnexpectedValueException(
+ __METHOD__ . " should always resolve to a string or a valid editor array; got something else instead."
+ );
+ }
+
+ $editor['url'] = str_replace("%line", rawurlencode($line), $editor['url']);
+ $editor['url'] = str_replace("%file", rawurlencode($filePath), $editor['url']);
+
+ return $editor['url'];
+ }
+
+ /**
+ * Determine if the editor link should act as an Ajax request.
+ *
+ * @param string $filePath
+ * @param int $line
+ *
+ * @throws UnexpectedValueException If editor resolver does not return a boolean
+ *
+ * @return bool
+ */
+ public function getEditorAjax($filePath, $line)
+ {
+ $editor = $this->getEditor($filePath, $line);
+
+ // Check that the ajax is a bool
+ if (!isset($editor['ajax']) || !is_bool($editor['ajax'])) {
+ throw new UnexpectedValueException(
+ __METHOD__ . " should always resolve to a bool; got something else instead."
+ );
+ }
+ return $editor['ajax'];
+ }
+
+ /**
+ * Determines both the editor and if ajax should be used.
+ *
+ * @param string $filePath
+ * @param int $line
+ *
+ * @return array
+ */
+ protected function getEditor($filePath, $line)
+ {
+ if (!$this->editor || (!is_string($this->editor) && !is_callable($this->editor))) {
+ return [];
+ }
+
+ if (is_string($this->editor) && isset($this->editors[$this->editor]) && !is_callable($this->editors[$this->editor])) {
+ return [
+ 'ajax' => false,
+ 'url' => $this->editors[$this->editor],
+ ];
+ }
+
+ if (is_callable($this->editor) || (isset($this->editors[$this->editor]) && is_callable($this->editors[$this->editor]))) {
+ if (is_callable($this->editor)) {
+ $callback = call_user_func($this->editor, $filePath, $line);
+ } else {
+ $callback = call_user_func($this->editors[$this->editor], $filePath, $line);
+ }
+
+ if (empty($callback)) {
+ return [];
+ }
+
+ if (is_string($callback)) {
+ return [
+ 'ajax' => false,
+ 'url' => $callback,
+ ];
+ }
+
+ return [
+ 'ajax' => isset($callback['ajax']) ? $callback['ajax'] : false,
+ 'url' => isset($callback['url']) ? $callback['url'] : $callback,
+ ];
+ }
+
+ return [];
+ }
+
+ /**
+ * Set the page title.
+ *
+ * @param string $title
+ *
+ * @return static
+ */
+ public function setPageTitle($title)
+ {
+ $this->pageTitle = (string) $title;
+ return $this;
+ }
+
+ /**
+ * Get the page title.
+ *
+ * @return string
+ */
+ public function getPageTitle()
+ {
+ return $this->pageTitle;
+ }
+
+ /**
+ * Adds a path to the list of paths to be searched for resources.
+ *
+ * @param string $path
+ *
+ * @throws InvalidArgumentException If $path is not a valid directory
+ *
+ * @return static
+ */
+ public function addResourcePath($path)
+ {
+ if (!is_dir($path)) {
+ throw new InvalidArgumentException(
+ "'$path' is not a valid directory"
+ );
+ }
+
+ array_unshift($this->searchPaths, $path);
+ return $this;
+ }
+
+ /**
+ * Adds a custom css file to be loaded.
+ *
+ * @param string|null $name
+ *
+ * @return static
+ */
+ public function addCustomCss($name)
+ {
+ $this->customCss = $name;
+ return $this;
+ }
+
+ /**
+ * Adds a custom js file to be loaded.
+ *
+ * @param string|null $name
+ *
+ * @return static
+ */
+ public function addCustomJs($name)
+ {
+ $this->customJs = $name;
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getResourcePaths()
+ {
+ return $this->searchPaths;
+ }
+
+ /**
+ * Finds a resource, by its relative path, in all available search paths.
+ *
+ * The search is performed starting at the last search path, and all the
+ * way back to the first, enabling a cascading-type system of overrides for
+ * all resources.
+ *
+ * @param string $resource
+ *
+ * @throws RuntimeException If resource cannot be found in any of the available paths
+ *
+ * @return string
+ */
+ protected function getResource($resource)
+ {
+ // If the resource was found before, we can speed things up
+ // by caching its absolute, resolved path:
+ if (isset($this->resourceCache[$resource])) {
+ return $this->resourceCache[$resource];
+ }
+
+ // Search through available search paths, until we find the
+ // resource we're after:
+ foreach ($this->searchPaths as $path) {
+ $fullPath = $path . "/$resource";
+
+ if (is_file($fullPath)) {
+ // Cache the result:
+ $this->resourceCache[$resource] = $fullPath;
+ return $fullPath;
+ }
+ }
+
+ // If we got this far, nothing was found.
+ throw new RuntimeException(
+ "Could not find resource '$resource' in any resource paths."
+ . "(searched: " . join(", ", $this->searchPaths). ")"
+ );
+ }
+
+ /**
+ * @deprecated
+ *
+ * @return string
+ */
+ public function getResourcesPath()
+ {
+ $allPaths = $this->getResourcePaths();
+
+ // Compat: return only the first path added
+ return end($allPaths) ?: null;
+ }
+
+ /**
+ * @deprecated
+ *
+ * @param string $resourcesPath
+ *
+ * @return static
+ */
+ public function setResourcesPath($resourcesPath)
+ {
+ $this->addResourcePath($resourcesPath);
+ return $this;
+ }
+
+ /**
+ * Return the application paths.
+ *
+ * @return array
+ */
+ public function getApplicationPaths()
+ {
+ return $this->applicationPaths;
+ }
+
+ /**
+ * Set the application paths.
+ *
+ * @param array $applicationPaths
+ *
+ * @return void
+ */
+ public function setApplicationPaths($applicationPaths)
+ {
+ $this->applicationPaths = $applicationPaths;
+ }
+
+ /**
+ * Set the application root path.
+ *
+ * @param string $applicationRootPath
+ *
+ * @return void
+ */
+ public function setApplicationRootPath($applicationRootPath)
+ {
+ $this->templateHelper->setApplicationRootPath($applicationRootPath);
+ }
+
+ /**
+ * blacklist a sensitive value within one of the superglobal arrays.
+ * Alias for the hideSuperglobalKey method.
+ *
+ * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
+ * @param string $key The key within the superglobal
+ * @see hideSuperglobalKey
+ *
+ * @return static
+ */
+ public function blacklist($superGlobalName, $key)
+ {
+ $this->blacklist[$superGlobalName][] = $key;
+ return $this;
+ }
+
+ /**
+ * Hide a sensitive value within one of the superglobal arrays.
+ *
+ * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
+ * @param string $key The key within the superglobal
+ * @return static
+ */
+ public function hideSuperglobalKey($superGlobalName, $key)
+ {
+ return $this->blacklist($superGlobalName, $key);
+ }
+
+ /**
+ * Checks all values within the given superGlobal array.
+ *
+ * Blacklisted values will be replaced by a equal length string containing
+ * only '*' characters for string values.
+ * Non-string values will be replaced with a fixed asterisk count.
+ * We intentionally dont rely on $GLOBALS as it depends on the 'auto_globals_jit' php.ini setting.
+ *
+ * @param array $superGlobal One of the superglobal arrays
+ * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
+ *
+ * @return array $values without sensitive data
+ */
+ private function masked(array $superGlobal, $superGlobalName)
+ {
+ $blacklisted = $this->blacklist[$superGlobalName];
+
+ $values = $superGlobal;
+
+ foreach ($blacklisted as $key) {
+ if (isset($superGlobal[$key])) {
+ $values[$key] = str_repeat('*', is_string($superGlobal[$key]) ? strlen($superGlobal[$key]) : 3);
+ }
+ }
+
+ return $values;
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php b/vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php
new file mode 100644
index 0000000..d695750
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php
@@ -0,0 +1,107 @@
+
+ */
+
+namespace Whoops\Handler;
+
+use SimpleXMLElement;
+use Whoops\Exception\Formatter;
+
+/**
+ * Catches an exception and converts it to an XML
+ * response. Additionally can also return exception
+ * frames for consumption by an API.
+ */
+class XmlResponseHandler extends Handler
+{
+ /**
+ * @var bool
+ */
+ private $returnFrames = false;
+
+ /**
+ * @param bool|null $returnFrames
+ * @return bool|static
+ */
+ public function addTraceToOutput($returnFrames = null)
+ {
+ if (func_num_args() == 0) {
+ return $this->returnFrames;
+ }
+
+ $this->returnFrames = (bool) $returnFrames;
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function handle()
+ {
+ $response = [
+ 'error' => Formatter::formatExceptionAsDataArray(
+ $this->getInspector(),
+ $this->addTraceToOutput()
+ ),
+ ];
+
+ echo self::toXml($response);
+
+ return Handler::QUIT;
+ }
+
+ /**
+ * @return string
+ */
+ public function contentType()
+ {
+ return 'application/xml';
+ }
+
+ /**
+ * @param SimpleXMLElement $node Node to append data to, will be modified in place
+ * @param array|\Traversable $data
+ * @return SimpleXMLElement The modified node, for chaining
+ */
+ private static function addDataToNode(\SimpleXMLElement $node, $data)
+ {
+ assert(is_array($data) || $data instanceof Traversable);
+
+ foreach ($data as $key => $value) {
+ if (is_numeric($key)) {
+ // Convert the key to a valid string
+ $key = "unknownNode_". (string) $key;
+ }
+
+ // Delete any char not allowed in XML element names
+ $key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key);
+
+ if (is_array($value)) {
+ $child = $node->addChild($key);
+ self::addDataToNode($child, $value);
+ } else {
+ $value = str_replace('&', '&', print_r($value, true));
+ $node->addChild($key, $value);
+ }
+ }
+
+ return $node;
+ }
+
+ /**
+ * The main function for converting to an XML document.
+ *
+ * @param array|\Traversable $data
+ * @return string XML
+ */
+ private static function toXml($data)
+ {
+ assert(is_array($data) || $data instanceof Traversable);
+
+ $node = simplexml_load_string("");
+
+ return self::addDataToNode($node, $data)->asXML();
+ }
+}
diff --git a/vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css b/vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css
new file mode 100644
index 0000000..d67499e
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css
@@ -0,0 +1,604 @@
+body {
+ font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
+ color: #131313;
+ background: #eeeeee;
+ padding:0;
+ margin: 0;
+ max-height: 100%;
+
+ text-rendering: optimizeLegibility;
+}
+ a {
+ text-decoration: none;
+ }
+
+.Whoops.container {
+ position: relative;
+ z-index: 9999999999;
+}
+
+.panel {
+ overflow-y: scroll;
+ height: 100%;
+ position: fixed;
+ margin: 0;
+ left: 0;
+ top: 0;
+}
+
+.branding {
+ position: absolute;
+ top: 10px;
+ right: 20px;
+ color: #777777;
+ font-size: 10px;
+ z-index: 100;
+}
+ .branding a {
+ color: #e95353;
+ }
+
+header {
+ color: white;
+ box-sizing: border-box;
+ background-color: #2a2a2a;
+ padding: 35px 40px;
+ max-height: 180px;
+ overflow: hidden;
+ transition: 0.5s;
+}
+
+ header.header-expand {
+ max-height: 1000px;
+ }
+
+ .exc-title {
+ margin: 0;
+ color: #bebebe;
+ font-size: 14px;
+ }
+ .exc-title-primary, .exc-title-secondary {
+ color: #e95353;
+ }
+
+ .exc-message {
+ font-size: 20px;
+ word-wrap: break-word;
+ margin: 4px 0 0 0;
+ color: white;
+ }
+ .exc-message span {
+ display: block;
+ }
+ .exc-message-empty-notice {
+ color: #a29d9d;
+ font-weight: 300;
+ }
+
+.prev-exc-title {
+ margin: 10px 0;
+}
+
+.prev-exc-title + ul {
+ margin: 0;
+ padding: 0 0 0 20px;
+ line-height: 12px;
+}
+
+.prev-exc-title + ul li {
+ font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
+}
+
+.prev-exc-title + ul li .prev-exc-code {
+ display: inline-block;
+ color: #bebebe;
+}
+
+.details-container {
+ left: 30%;
+ width: 70%;
+ background: #fafafa;
+}
+ .details {
+ padding: 5px;
+ }
+
+ .details-heading {
+ color: #4288CE;
+ font-weight: 300;
+ padding-bottom: 10px;
+ margin-bottom: 10px;
+ border-bottom: 1px solid rgba(0, 0, 0, .1);
+ }
+
+ .details pre.sf-dump {
+ white-space: pre;
+ word-wrap: inherit;
+ }
+
+ .details pre.sf-dump,
+ .details pre.sf-dump .sf-dump-num,
+ .details pre.sf-dump .sf-dump-const,
+ .details pre.sf-dump .sf-dump-str,
+ .details pre.sf-dump .sf-dump-note,
+ .details pre.sf-dump .sf-dump-ref,
+ .details pre.sf-dump .sf-dump-public,
+ .details pre.sf-dump .sf-dump-protected,
+ .details pre.sf-dump .sf-dump-private,
+ .details pre.sf-dump .sf-dump-meta,
+ .details pre.sf-dump .sf-dump-key,
+ .details pre.sf-dump .sf-dump-index {
+ color: #463C54;
+ }
+
+.left-panel {
+ width: 30%;
+ background: #ded8d8;
+}
+
+ .frames-description {
+ background: rgba(0, 0, 0, .05);
+ padding: 8px 15px;
+ color: #a29d9d;
+ font-size: 11px;
+ }
+
+ .frames-description.frames-description-application {
+ text-align: center;
+ font-size: 12px;
+ }
+ .frames-container.frames-container-application .frame:not(.frame-application) {
+ display: none;
+ }
+
+ .frames-tab {
+ color: #a29d9d;
+ display: inline-block;
+ padding: 4px 8px;
+ margin: 0 2px;
+ border-radius: 3px;
+ }
+
+ .frames-tab.frames-tab-active {
+ background-color: #2a2a2a;
+ color: #bebebe;
+ }
+
+ .frame {
+ padding: 14px;
+ cursor: pointer;
+ transition: all 0.1s ease;
+ background: #eeeeee;
+ }
+ .frame:not(:last-child) {
+ border-bottom: 1px solid rgba(0, 0, 0, .05);
+ }
+
+ .frame.active {
+ box-shadow: inset -5px 0 0 0 #4288CE;
+ color: #4288CE;
+ }
+
+ .frame:not(.active):hover {
+ background: #BEE9EA;
+ }
+
+ .frame-method-info {
+ margin-bottom: 10px;
+ }
+
+ .frame-class, .frame-function, .frame-index {
+ font-size: 14px;
+ }
+
+ .frame-index {
+ float: left;
+ }
+
+ .frame-method-info {
+ margin-left: 24px;
+ }
+
+ .frame-index {
+ font-size: 11px;
+ color: #a29d9d;
+ background-color: rgba(0, 0, 0, .05);
+ height: 18px;
+ width: 18px;
+ line-height: 18px;
+ border-radius: 5px;
+ padding: 0 1px 0 1px;
+ text-align: center;
+ display: inline-block;
+ }
+
+ .frame-application .frame-index {
+ background-color: #2a2a2a;
+ color: #bebebe;
+ }
+
+ .frame-file {
+ font-family: "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
+ color: #a29d9d;
+ }
+
+ .frame-file .editor-link {
+ color: #a29d9d;
+ }
+
+ .frame-line {
+ font-weight: bold;
+ }
+
+ .frame-line:before {
+ content: ":";
+ }
+
+ .frame-code {
+ padding: 5px;
+ background: #303030;
+ display: none;
+ }
+
+ .frame-code.active {
+ display: block;
+ }
+
+ .frame-code .frame-file {
+ color: #a29d9d;
+ padding: 12px 6px;
+
+ border-bottom: none;
+ }
+
+ .code-block {
+ padding: 10px;
+ margin: 0;
+ border-radius: 6px;
+ box-shadow: 0 3px 0 rgba(0, 0, 0, .05),
+ 0 10px 30px rgba(0, 0, 0, .05),
+ inset 0 0 1px 0 rgba(255, 255, 255, .07);
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ }
+
+ .linenums {
+ margin: 0;
+ margin-left: 10px;
+ }
+
+ .frame-comments {
+ border-top: none;
+ margin-top: 15px;
+
+ font-size: 12px;
+ }
+
+ .frame-comments.empty {
+ }
+
+ .frame-comments.empty:before {
+ content: "No comments for this stack frame.";
+ font-weight: 300;
+ color: #a29d9d;
+ }
+
+ .frame-comment {
+ padding: 10px;
+ color: #e3e3e3;
+ border-radius: 6px;
+ background-color: rgba(255, 255, 255, .05);
+ }
+ .frame-comment a {
+ font-weight: bold;
+ text-decoration: none;
+ }
+ .frame-comment a:hover {
+ color: #4bb1b1;
+ }
+
+ .frame-comment:not(:last-child) {
+ border-bottom: 1px dotted rgba(0, 0, 0, .3);
+ }
+
+ .frame-comment-context {
+ font-size: 10px;
+ color: white;
+ }
+
+.delimiter {
+ display: inline-block;
+}
+
+.data-table-container label {
+ font-size: 16px;
+ color: #303030;
+ font-weight: bold;
+ margin: 10px 0;
+
+ display: block;
+
+ margin-bottom: 5px;
+ padding-bottom: 5px;
+}
+ .data-table {
+ width: 100%;
+ margin-bottom: 10px;
+ }
+
+ .data-table tbody {
+ font: 13px "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
+ }
+
+ .data-table thead {
+ display: none;
+ }
+
+ .data-table tr {
+ padding: 5px 0;
+ }
+
+ .data-table td:first-child {
+ width: 20%;
+ min-width: 130px;
+ overflow: hidden;
+ font-weight: bold;
+ color: #463C54;
+ padding-right: 5px;
+
+ }
+
+ .data-table td:last-child {
+ width: 80%;
+ -ms-word-break: break-all;
+ word-break: break-all;
+ word-break: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+ }
+
+ .data-table span.empty {
+ color: rgba(0, 0, 0, .3);
+ font-weight: 300;
+ }
+ .data-table label.empty {
+ display: inline;
+ }
+
+.handler {
+ padding: 4px 0;
+ font: 14px "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
+}
+
+/* prettify code style
+Uses the Doxy theme as a base */
+pre .str, code .str { color: #BCD42A; } /* string */
+pre .kwd, code .kwd { color: #4bb1b1; font-weight: bold; } /* keyword*/
+pre .com, code .com { color: #888; font-weight: bold; } /* comment */
+pre .typ, code .typ { color: #ef7c61; } /* type */
+pre .lit, code .lit { color: #BCD42A; } /* literal */
+pre .pun, code .pun { color: #fff; font-weight: bold; } /* punctuation */
+pre .pln, code .pln { color: #e9e4e5; } /* plaintext */
+pre .tag, code .tag { color: #4bb1b1; } /* html/xml tag */
+pre .htm, code .htm { color: #dda0dd; } /* html tag */
+pre .xsl, code .xsl { color: #d0a0d0; } /* xslt tag */
+pre .atn, code .atn { color: #ef7c61; font-weight: normal;} /* html/xml attribute name */
+pre .atv, code .atv { color: #bcd42a; } /* html/xml attribute value */
+pre .dec, code .dec { color: #606; } /* decimal */
+pre.code-block, code.code-block, .frame-args.code-block, .frame-args.code-block samp {
+ font-family: "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace;
+ background: #333;
+ color: #e9e4e5;
+}
+ pre.code-block {
+ white-space: pre-wrap;
+ }
+
+ pre.code-block a, code.code-block a {
+ text-decoration:none;
+ }
+
+ .linenums li {
+ color: #A5A5A5;
+ }
+
+ .linenums li.current{
+ background: rgba(255, 100, 100, .07);
+ }
+ .linenums li.current.active {
+ background: rgba(255, 100, 100, .17);
+ }
+
+pre:not(.prettyprinted) {
+ padding-left: 60px;
+}
+
+#plain-exception {
+ display: none;
+}
+
+.rightButton {
+ cursor: pointer;
+ border: 0;
+ opacity: .8;
+ background: none;
+
+ color: rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.1);
+
+ border-radius: 3px;
+
+ outline: none !important;
+}
+
+ .rightButton:hover {
+ box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.3);
+ color: rgba(255, 255, 255, 0.3);
+ }
+
+/* inspired by githubs kbd styles */
+kbd {
+ -moz-border-bottom-colors: none;
+ -moz-border-left-colors: none;
+ -moz-border-right-colors: none;
+ -moz-border-top-colors: none;
+ background-color: #fcfcfc;
+ border-color: #ccc #ccc #bbb;
+ border-image: none;
+ border-style: solid;
+ border-width: 1px;
+ color: #555;
+ display: inline-block;
+ font-size: 11px;
+ line-height: 10px;
+ padding: 3px 5px;
+ vertical-align: middle;
+}
+
+
+/* == Media queries */
+
+/* Expand the spacing in the details section */
+@media (min-width: 1000px) {
+ .details, .frame-code {
+ padding: 20px 40px;
+ }
+
+ .details-container {
+ left: 32%;
+ width: 68%;
+ }
+
+ .frames-container {
+ margin: 5px;
+ }
+
+ .left-panel {
+ width: 32%;
+ }
+}
+
+/* Stack panels */
+@media (max-width: 600px) {
+ .panel {
+ position: static;
+ width: 100%;
+ }
+}
+
+/* Stack details tables */
+@media (max-width: 400px) {
+ .data-table,
+ .data-table tbody,
+ .data-table tbody tr,
+ .data-table tbody td {
+ display: block;
+ width: 100%;
+ }
+
+ .data-table tbody tr:first-child {
+ padding-top: 0;
+ }
+
+ .data-table tbody td:first-child,
+ .data-table tbody td:last-child {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .data-table tbody td:last-child {
+ padding-top: 3px;
+ }
+}
+
+.tooltipped {
+ position: relative
+}
+.tooltipped:after {
+ position: absolute;
+ z-index: 1000000;
+ display: none;
+ padding: 5px 8px;
+ color: #fff;
+ text-align: center;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-wrap: break-word;
+ white-space: pre;
+ pointer-events: none;
+ content: attr(aria-label);
+ background: rgba(0, 0, 0, 0.8);
+ border-radius: 3px;
+ -webkit-font-smoothing: subpixel-antialiased
+}
+.tooltipped:before {
+ position: absolute;
+ z-index: 1000001;
+ display: none;
+ width: 0;
+ height: 0;
+ color: rgba(0, 0, 0, 0.8);
+ pointer-events: none;
+ content: "";
+ border: 5px solid transparent
+}
+.tooltipped:hover:before,
+.tooltipped:hover:after,
+.tooltipped:active:before,
+.tooltipped:active:after,
+.tooltipped:focus:before,
+.tooltipped:focus:after {
+ display: inline-block;
+ text-decoration: none
+}
+.tooltipped-s:after {
+ top: 100%;
+ right: 50%;
+ margin-top: 5px
+}
+.tooltipped-s:before {
+ top: auto;
+ right: 50%;
+ bottom: -5px;
+ margin-right: -5px;
+ border-bottom-color: rgba(0, 0, 0, 0.8)
+}
+
+pre.sf-dump {
+ padding: 0px !important;
+ margin: 0px !important;
+}
+
+.search-for-help {
+ width: 85%;
+ padding: 0;
+ margin: 10px 0;
+ list-style-type: none;
+ display: inline-block;
+}
+ .search-for-help li {
+ display: inline-block;
+ margin-right: 5px;
+ }
+ .search-for-help li:last-child {
+ margin-right: 0;
+ }
+ .search-for-help li a {
+
+ }
+ .search-for-help li a i {
+ width: 16px;
+ height: 16px;
+ overflow: hidden;
+ display: block;
+ }
+ .search-for-help li a svg {
+ fill: #fff;
+ }
+ .search-for-help li a svg path {
+ background-size: contain;
+ }
diff --git a/vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js b/vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js
new file mode 100644
index 0000000..36a75a4
--- /dev/null
+++ b/vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js
@@ -0,0 +1,7 @@
+/*!
+ * clipboard.js v1.5.3
+ * https://zenorocha.github.io/clipboard.js
+ *
+ * Licensed MIT © Zeno Rocha
+ */
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,r){function o(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(i)return i(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ar;r++)n[r].fn.apply(n[r].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),r=n[t],o=[];if(r&&e)for(var i=0,a=r.length;a>i;i++)r[i].fn!==e&&r[i].fn._!==e&&o.push(r[i]);return o.length?n[t]=o:delete n[t],this}},e.exports=r},{}],8:[function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}n.__esModule=!0;var i=function(){function t(t,e){for(var n=0;n122||(e<65||h>90||d.push([Math.max(65,h)|32,Math.min(e,90)|32]),e<97||h>122||d.push([Math.max(97,h)&-33,Math.min(e,122)&-33]))}}d.sort(function(d,a){return d[0]-a[0]||a[1]-d[1]});a=[];c=[];for(f=0;fh[0]&&(h[1]+1>h[0]&&b.push("-"),b.push(g(h[1])));b.push("]");return b.join("")}function t(d){for(var a=d.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=a.length,i=[],c=0,h=0;c=2&&d==="["?a[c]=j(e):d!=="\\"&&(a[c]=e.replace(/[A-Za-z]/g,function(d){d=d.charCodeAt(0);return"["+String.fromCharCode(d&-33,d|32)+"]"}));return a.join("")}for(var z=0,w=!1,k=!1,m=0,b=a.length;m=5&&"lang-"===f.substring(0,
+5))&&!(u&&typeof u[1]==="string"))c=!1,f="src";c||(s[v]=f)}h=b;b+=v.length;if(c){c=u[1];var e=v.indexOf(c),p=e+c.length;u[2]&&(p=v.length-u[2].length,e=p-c.length);f=f.substring(5);E(k+h,v.substring(0,e),g,m);E(k+h+e,c,F(f,c),m);E(k+h+p,v.substring(p),g,m)}else m.push(k+h,f)}a.g=m}var j={},t;(function(){for(var g=a.concat(i),k=[],m={},b=0,o=g.length;b=0;)j[q.charAt(d)]=s;s=s[1];q=""+s;m.hasOwnProperty(q)||(k.push(s),m[q]=r)}k.push(/[\S\s]/);t=
+O(k)})();var z=i.length;return g}function l(a){var i=[],g=[];a.tripleQuotedStrings?i.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,r,"'\""]):a.multiLineStrings?i.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,r,"'\"`"]):i.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,r,"\"'"]);a.verbatimStrings&&
+g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,r]);var j=a.hashComments;j&&(a.cStyleComments?(j>1?i.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,r,"#"]):i.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,r,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,r])):i.push(["com",/^#[^\n\r]*/,r,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,r]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
+r]));a.regexLiterals&&g.push(["lang-regex",/^(?:^^\.?|[+-]|[!=]={0,2}|#|%=?|&&?=?|\(|\*=?|[+-]=|->|\/=?|::?|<=?|>{1,3}=?|[,;?@[{~]|\^\^?=?|\|\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(j=a.types)&&g.push(["typ",j]);a=(""+a.keywords).replace(/^ | $/g,"");a.length&&g.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),r]);i.push(["pln",/^\s+/,r," \r\n\t\u00a0"]);g.push(["lit",
+/^@[$_a-z][\w$@]*/i,r],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,r],["pln",/^[$_a-z][\w$@]*/i,r],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,r,"0123456789"],["pln",/^\\[\S\s]?/,r],["pun",/^.[^\s\w"$'./@\\`]*/,r]);return x(i,g)}function G(a,i,g){function j(a){switch(a.nodeType){case 1:if(z.test(a.className))break;if("br"===a.nodeName)t(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)j(a);break;case 3:case 4:if(g){var b=
+a.nodeValue,f=b.match(n);if(f){var i=b.substring(0,f.index);a.nodeValue=i;(b=b.substring(f.index+f[0].length))&&a.parentNode.insertBefore(k.createTextNode(b),a.nextSibling);t(a);i||a.parentNode.removeChild(a)}}}}function t(a){function i(a,b){var d=b?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=i(e,1),f=a.nextSibling;e.appendChild(d);for(var g=f;g;g=f)f=g.nextSibling,e.appendChild(g)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=i(a.nextSibling,0),f;(f=a.parentNode)&&f.nodeType===
+1;)a=f;b.push(a)}for(var z=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,k=a.ownerDocument,m=k.createElement("li");a.firstChild;)m.appendChild(a.firstChild);for(var b=[m],o=0;o=0;){var j=
+i[g];A.hasOwnProperty(j)?C.console&&console.warn("cannot override language handler %s",j):A[j]=a}}function F(a,i){if(!a||!A.hasOwnProperty(a))a=/^\s*=e&&(j+=2);g>=p&&(s+=2)}}finally{if(c)c.style.display=h}}catch(A){C.console&&console.log(A&&A.stack?A.stack:A)}}var C=window,y=["break,continue,do,else,for,if,return,while"],B=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],I=[B,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
+J=[B,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],K=[J,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],B=[B,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],
+L=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],M=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],N=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
+Q=/\S/,R=l({keywords:[I,K,B,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+L,M,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};n(R,["default-code"]);n(x([],[["pln",/^[^]+/],["dec",/^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
+/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^
+
+
+
+