Explorar el Código

feat: 初始化 ThinkPHP 8项目结构

- 新增项目基础结构和配置文件
- 添加示例控制器和视图
- 配置数据库和缓存等系统设置
- 设置路由和中间件
- 添加异常处理类
- 配置多语言和日志
- 添加 .gitignore 文件和 Travis CI 配置
runphp hace 7 meses
commit
e39572a06c

+ 299 - 0
.ddev/config.yaml

@@ -0,0 +1,299 @@
+name: app
+type: php
+docroot: public
+php_version: "8.3"
+webserver_type: nginx-fpm
+xdebug_enabled: false
+additional_hostnames: []
+additional_fqdns: []
+database:
+    type: mariadb
+    version: "10.11"
+use_dns_when_possible: true
+composer_version: "2"
+web_environment: []
+corepack_enable: false
+
+# Key features of DDEV's config.yaml:
+
+# name: <projectname> # Name of the project, automatically provides
+#   http://projectname.ddev.site and https://projectname.ddev.site
+# If the name is omitted, the project will take the name of the enclosing directory,
+# which is useful if you want to have a copy of the project side by side with this one.
+
+# type: <projecttype>  # backdrop, cakephp, craftcms, drupal, drupal6, drupal7, drupal8, drupal9, drupal10, drupal11, generic, laravel, magento, magento2, php, shopware6, silverstripe, symfony, typo3, wordpress
+# See https://ddev.readthedocs.io/en/stable/users/quickstart/ for more
+# information on the different project types
+
+# docroot: <relative_path> # Relative path to the directory containing index.php.
+
+# php_version: "8.3"  # PHP version to use, "5.6" through "8.4"
+
+# You can explicitly specify the webimage but this
+# is not recommended, as the images are often closely tied to DDEV's' behavior,
+# so this can break upgrades.
+
+# webimage: <docker_image>  # nginx/php docker image.
+
+# database:
+#   type: <dbtype> # mysql, mariadb, postgres
+#   version: <version> # database version, like "10.11" or "8.0"
+#   MariaDB versions can be 5.5-10.8, 10.11, 11.4, 11.8
+#   MySQL versions can be 5.5-8.0, 8.4
+#   PostgreSQL versions can be 9-17
+
+# router_http_port: <port>  # Port to be used for http (defaults to global configuration, usually 80)
+# router_https_port: <port> # Port for https (defaults to global configuration, usually 443)
+
+# xdebug_enabled: false  # Set to true to enable Xdebug and "ddev start" or "ddev restart"
+# Note that for most people the commands
+# "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better,
+# as leaving Xdebug enabled all the time is a big performance hit.
+
+# xhgui_https_port: 8142
+# Can be used to change the router https port for xhgui application
+# Very rarely used
+
+# xhgui_http_port: 8143
+# Can be used to change the router http port for xhgui application
+# Very rarely used
+
+# host_xhgui_port: 8142
+# Can be used to change the host binding port of the xhgui
+# application. Rarely used; only when port conflict and
+# bind_all_ports is used (normally with router disabled)
+
+# xhprof_enabled: false  # Set to true to enable Xhprof and "ddev start" or "ddev restart"
+# Note that for most people the commands
+# "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better,
+# as leaving Xhprof enabled all the time is a big performance hit.
+
+# xhprof_mode: [prepend|xhgui|global]
+# Set to "xhgui" to enable XHGui features
+# "xhgui" will become default in a future major release
+
+# webserver_type: nginx-fpm, apache-fpm, generic
+
+# timezone: Europe/Berlin
+# If timezone is unset, DDEV will attempt to derive it from the host system timezone
+# using the $TZ environment variable or the /etc/localtime symlink.
+# This is the timezone used in the containers and by PHP;
+# it can be set to any valid timezone,
+# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+# For example Europe/Dublin or MST7MDT
+
+# composer_root: <relative_path>
+# Relative path to the Composer root directory from the project root. This is
+# the directory which contains the composer.json and where all Composer related
+# commands are executed.
+
+# composer_version: "2"
+# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1
+# to use the latest major version available at the time your container is built.
+# It is also possible to use each other Composer version channel. This includes:
+#   - 2.2 (latest Composer LTS version)
+#   - stable
+#   - preview
+#   - snapshot
+# Alternatively, an explicit Composer version may be specified, for example "2.2.18".
+# To reinstall Composer after the image was built, run "ddev debug rebuild".
+
+# nodejs_version: "22"
+# change from the default system Node.js version to any other version.
+# See https://ddev.readthedocs.io/en/stable/users/configuration/config/#nodejs_version for more information
+# and https://www.npmjs.com/package/n#specifying-nodejs-versions for the full documentation,
+# Note that using of 'ddev nvm' is discouraged because "nodejs_version" is much easier to use,
+# can specify any version, and is more robust than using 'nvm'.
+
+# corepack_enable: false
+# Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm
+
+# additional_hostnames:
+#  - somename
+#  - someothername
+# would provide http and https URLs for "somename.ddev.site"
+# and "someothername.ddev.site".
+
+# additional_fqdns:
+#  - example.com
+#  - sub1.example.com
+# would provide http and https URLs for "example.com" and "sub1.example.com"
+# Please take care with this because it can cause great confusion.
+
+# upload_dirs: "custom/upload/dir"
+#
+# upload_dirs:
+#   - custom/upload/dir
+#   - ../private
+#
+# would set the destination paths for ddev import-files to <docroot>/custom/upload/dir
+# When Mutagen is enabled this path is bind-mounted so that all the files
+# in the upload_dirs don't have to be synced into Mutagen.
+
+# disable_upload_dirs_warning: false
+# If true, turns off the normal warning that says
+# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set"
+
+# ddev_version_constraint: ""
+# Example:
+# ddev_version_constraint: ">= 1.22.4"
+# This will enforce that the running ddev version is within this constraint.
+# See https://github.com/Masterminds/semver#checking-version-constraints for
+# supported constraint formats
+
+# working_dir:
+#   web: /var/www/html
+#   db: /home
+# would set the default working directory for the web and db services.
+# These values specify the destination directory for ddev ssh and the
+# directory in which commands passed into ddev exec are run.
+
+# omit_containers: [db, ddev-ssh-agent]
+# Currently only these containers are supported. Some containers can also be
+# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit
+# the "db" container, several standard features of DDEV that access the
+# database container will be unusable. In the global configuration it is also
+# possible to omit ddev-router, but not here.
+
+# performance_mode: "global"
+# DDEV offers performance optimization strategies to improve the filesystem
+# performance depending on your host system. Should be configured globally.
+#
+# If set, will override the global config. Possible values are:
+#   - "global":  uses the value from the global config.
+#   - "none":    disables performance optimization for this project.
+#   - "mutagen": enables Mutagen for this project.
+#   - "nfs":     enables NFS for this project.
+#
+# See https://ddev.readthedocs.io/en/stable/users/install/performance/#nfs
+# See https://ddev.readthedocs.io/en/stable/users/install/performance/#mutagen
+
+# fail_on_hook_fail: False
+# Decide whether 'ddev start' should be interrupted by a failing hook
+
+# host_https_port: "59002"
+# The host port binding for https can be explicitly specified. It is
+# dynamic unless otherwise specified.
+# This is not used by most people, most people use the *router* instead
+# of the localhost port.
+
+# host_webserver_port: "59001"
+# The host port binding for the ddev-webserver can be explicitly specified. It is
+# dynamic unless otherwise specified.
+# This is not used by most people, most people use the *router* instead
+# of the localhost port.
+
+# host_db_port: "59002"
+# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic
+# unless explicitly specified.
+
+# mailpit_http_port: "8025"
+# mailpit_https_port: "8026"
+# The Mailpit ports can be changed from the default 8025 and 8026
+
+# host_mailpit_port: "8025"
+# The mailpit port is not normally bound on the host at all, instead being routed
+# through ddev-router, but it can be bound directly to localhost if specified here.
+
+# webimage_extra_packages: [php7.4-tidy, php-bcmath]
+# Extra Debian packages that are needed in the webimage can be added here
+
+# dbimage_extra_packages: [telnet,netcat]
+# Extra Debian packages that are needed in the dbimage can be added here
+
+# use_dns_when_possible: true
+# If the host has internet access and the domain configured can
+# successfully be looked up, DNS will be used for hostname resolution
+# instead of editing /etc/hosts
+# Defaults to true
+
+# project_tld: ddev.site
+# The top-level domain used for project URLs
+# The default "ddev.site" allows DNS lookup via a wildcard
+# If you prefer you can change this to "ddev.local" to preserve
+# pre-v1.9 behavior.
+
+# ngrok_args: --basic-auth username:pass1234
+# Provide extra flags to the "ngrok http" command, see
+# https://ngrok.com/docs/agent/config/v3/#agent-configuration or run "ngrok http -h"
+
+# disable_settings_management: false
+# If true, DDEV will not create CMS-specific settings files like
+# Drupal's settings.php/settings.ddev.php or TYPO3's additional.php
+# In this case the user must provide all such settings.
+
+# You can inject environment variables into the web container with:
+# web_environment:
+#     - SOMEENV=somevalue
+#     - SOMEOTHERENV=someothervalue
+
+# no_project_mount: false
+# (Experimental) If true, DDEV will not mount the project into the web container;
+# the user is responsible for mounting it manually or via a script.
+# This is to enable experimentation with alternate file mounting strategies.
+# For advanced users only!
+
+# bind_all_interfaces: false
+# If true, host ports will be bound on all network interfaces,
+# not the localhost interface only. This means that ports
+# will be available on the local network if the host firewall
+# allows it.
+
+# default_container_timeout: 120
+# The default time that DDEV waits for all containers to become ready can be increased from
+# the default 120. This helps in importing huge databases, for example.
+
+#web_extra_exposed_ports:
+#- name: nodejs
+#  container_port: 3000
+#  http_port: 2999
+#  https_port: 3000
+#- name: something
+#  container_port: 4000
+#  https_port: 4000
+#  http_port: 3999
+# Allows a set of extra ports to be exposed via ddev-router
+# Fill in all three fields even if you don’t intend to use the https_port!
+# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start.
+#
+# The port behavior on the ddev-webserver must be arranged separately, for example
+# using web_extra_daemons.
+# For example, with a web app on port 3000 inside the container, this config would
+# expose that web app on https://<project>.ddev.site:9999 and http://<project>.ddev.site:9998
+# web_extra_exposed_ports:
+#  - name: myapp
+#    container_port: 3000
+#    http_port: 9998
+#    https_port: 9999
+
+#web_extra_daemons:
+#- name: "http-1"
+#  command: "/var/www/html/node_modules/.bin/http-server -p 3000"
+#  directory: /var/www/html
+#- name: "http-2"
+#  command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000"
+#  directory: /var/www/html
+
+# override_config: false
+# By default, config.*.yaml files are *merged* into the configuration
+# But this means that some things can't be overridden
+# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge
+# and you can't erase existing hooks or all environment variables.
+# However, with "override_config: true" in a particular config.*.yaml file,
+# 'use_dns_when_possible: false' can override the existing values, and
+# hooks:
+#   post-start: []
+# or
+# web_environment: []
+# or
+# additional_hostnames: []
+# can have their intended affect. 'override_config' affects only behavior of the
+# config.*.yaml file it exists in.
+
+# Many DDEV commands can be extended to run tasks before or after the
+# DDEV command is executed, for example "post-start", "post-import-db",
+# "pre-composer", "post-composer"
+# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more
+# information on the commands that can be extended and the tasks you can define
+# for them. Example:
+#hooks:

+ 11 - 0
.example.env

@@ -0,0 +1,11 @@
+APP_DEBUG = true
+
+DB_TYPE = mysql
+DB_HOST = 127.0.0.1
+DB_NAME = test
+DB_USER = username
+DB_PASS = password
+DB_PORT = 3306
+DB_CHARSET = utf8
+
+DEFAULT_LANG = zh-cn

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+*.log
+.env
+composer.phar
+composer.lock
+.DS_Store
+Thumbs.db
+/.idea
+/.vscode
+/vendor
+/.settings
+/.buildpath
+/.project

+ 42 - 0
.travis.yml

@@ -0,0 +1,42 @@
+sudo: false
+
+language: php
+
+branches:
+  only:
+    - stable
+
+cache:
+  directories:
+    - $HOME/.composer/cache
+
+before_install:
+  - composer self-update
+
+install:
+  - composer install --no-dev --no-interaction --ignore-platform-reqs
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
+  - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
+  - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
+
+script:
+  - php think unit
+
+deploy:
+  provider: releases
+  api_key:
+    secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
+  file:
+    - ThinkPHP_Core.zip
+    - ThinkPHP_Full.zip
+  skip_cleanup: true
+  on:
+    tags: true

+ 32 - 0
LICENSE.txt

@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2025 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 140 - 0
README.md

@@ -0,0 +1,140 @@
+# Sixshop应用
+
+## 安装依赖
+
+```bash
+ddev composer install
+```
+
+## 运行应用
+
+```bash
+ddev start
+ddev launch # 或者访问 https://app.ddev.site/
+```
+
+## SixShop扩展特性
+
+sishop扩展旨在为ThinkPHP开发者们开发出更易维护的项目
+
+每个项目都是不同的扩展组合而成,对ThinkPHP应用的扩展只需要通过`composer require` 进行安装(包括数据的迁移脚本)
+
+最终你只需要寻找现成合适的扩展进行安装,然后自己自行开发部分扩展
+
+支持私有仓库进行扩展模块开发,保护用户定制开发的代码
+
+支持应用市场扩展开发模式
+
+## 项目依赖
+```
+"php": ">=8.3",
+"topthink/framework": "^8.1",
+"topthink/think-orm": "^4.0"
+```
+
+## 扩展列表
+
+<https://packagist.org/?type=sixshop-extension>
+
+<https://packagist.jd29.com>
+
+
+## SixShop扩展开发指南
+
+扩展模块开发流程:
+1. 先创建git项目
+2. 项目clone到本地`runtime/extension`目录下,也可以其他没有版本控制的目录下
+    ```shell
+    cd backend/runtime/extension
+    git clone git@github.com:runphp/sixshop-hello.git
+    ```
+3. 在`composer.json`中添加path仓库(下面以hello为例)
+    ```json
+    {
+        "repositories": [
+            {
+                "type": "path",
+                "url": "runtime/extension/sixshop-hello",
+                "options": {
+                    "symlink": true,
+                    "versions": {
+                        "six-shop/hello": "v0.2.9"
+                    }
+                }
+            }
+        ]
+    }
+    ```
+4. 安装你的扩展模块
+    ```shell
+    ddev composer require  "six-shop/hello:^v0.2.0"
+    ```
+   上面的版本是假如最新版本的`six-shop/hello`是`v0.2.0`,那么我们的开发版本比它大的版本就好了,我们可以设置成`v0.2.9`
+
+   成功处理的话可以看到
+   ```shell
+   Package operations: 1 install, 0 updates, 0 removals
+     - Installing six-shop/hello (v0.2.9): Symlinking from runtime/extension/sixshop-hello
+   ```
+
+   现在你就可以在`runtime/extension`或`vendor`目录下修改代码了
+
+5. 私有扩展模块认证说明
+
+   在 `~/.composer/auth.json` 中添加认证信息
+   ```json
+   {
+      "headers": {
+        "packagist.jd29.com": {
+            "X-API-KEY": "5f280c17d5958****************************************0893f506ca01acef704"
+        }
+     }
+   }
+   ```
+   说明: ddev环境对应的auth.json文件为`~/.ddev/homeadditions/.composer/auth.json`
+
+   当然你可以做软连接过去
+   ```shell
+   mkdir -p ~/.ddev/homeadditions/.composer && ln -s ~/.composer/auth.json ~/.ddev/homeadditions/.composer/auth.json
+   ```
+   然后需要`ddev start`重启下容器,可以通过`ddev composer config -l`查看是否生效
+   最后你可以参考`doc/auth.json`这份示例文件,进行修改,这些设置也可以直接在`composer.json`文件设置,最终使用方式请参考composer官方文档。
+
+6. 扩展模块的composer.json
+   参考其他扩展,不同地方是添加了`"type": "sixshop-extension"`,然后就是
+   ```json
+   {
+     "extra": {
+       "sixshop": {
+         "id": "hello",
+         "class": "SixShop\\Hello\\Extension"
+       }
+     }
+   }
+   ```
+   id为扩展模块的标识符,需要唯一,class为扩展模块的类名,实现了`SixShop\Extension\ExtensionInterface`接口
+
+7. 扩展模块的sql安装脚本使用cakephp的migration,在模块的`database/migrations`目录添加
+
+8. 扩展路由在`route`目录下添加路由文件
+   默认会加载`admin.php`和`api.php`两个文件, 对应的是admin和api应用,你也可以实现`SixShop\Extension\ExtensionInterface`的`getRoutes`接口
+
+9. 扩展模块的事件监听,可以说使用`SixShop\Core\Attribute\Hook`实现,具体请查看`SixShop\Core\Attribute\Hook`类,在模块的`src/Hooks`目录下添加,然后实现`SixShop\Extension\ExtensionInterface`的`getHooks`接口
+
+10. 扩展模块的异步任务实现你可以继承`SixShop\Core\Job\BaseJob`即可,具体请查看`SixShop\Core\Job\BaseJob`类
+    使用示例
+   ```php
+   \app\api\job\IndexJob::dispatch(); // 异步任务
+   \app\api\job\IndexJob::dispatch()->delay(10); // 延迟10s执行
+   ```
+11. 扩展定时任务,使用`SixShop\Core\Attribute\Cron`注解, 并且需要实现`SixShop\Extension\ExtensionInterface`的`getCronJobs`接口
+    参考`\SixShop\System\Cron\SystemCron`类
+
+12. 扩展命令行
+    注册命令行请实现`SixShop\Extension\ExtensionInterface`的`getCommands`接口 ,默认自动加载扩展目录下`command.php`文件
+    可以参考`backend/vendor/six-shop/system/command.php`
+
+13. 扩展的配置,默认自动加载扩展目录下`config.php`文件,可以参考`backend/vendor/six-shop/hello/config.php`
+    统一配置的实现目前需要安装`six-shop/system`,后续会单独的扩展包,目前配置的实现是使用 [form-create](https://form-create.com/v3/designer/) 生成配置表单即可,支持子表单组件
+
+## 欢迎PHP开发者加入我们

+ 1 - 0
app/.htaccess

@@ -0,0 +1 @@
+deny from all

+ 22 - 0
app/AppService.php

@@ -0,0 +1,22 @@
+<?php
+declare (strict_types = 1);
+
+namespace app;
+
+use think\Service;
+
+/**
+ * 应用服务类
+ */
+class AppService extends Service
+{
+    public function register()
+    {
+        // 服务注册
+    }
+
+    public function boot()
+    {
+        // 服务启动
+    }
+}

+ 94 - 0
app/BaseController.php

@@ -0,0 +1,94 @@
+<?php
+declare (strict_types = 1);
+
+namespace app;
+
+use think\App;
+use think\exception\ValidateException;
+use think\Validate;
+
+/**
+ * 控制器基础类
+ */
+abstract class BaseController
+{
+    /**
+     * Request实例
+     * @var \think\Request
+     */
+    protected $request;
+
+    /**
+     * 应用实例
+     * @var \think\App
+     */
+    protected $app;
+
+    /**
+     * 是否批量验证
+     * @var bool
+     */
+    protected $batchValidate = false;
+
+    /**
+     * 控制器中间件
+     * @var array
+     */
+    protected $middleware = [];
+
+    /**
+     * 构造方法
+     * @access public
+     * @param  App  $app  应用对象
+     */
+    public function __construct(App $app)
+    {
+        $this->app     = $app;
+        $this->request = $this->app->request;
+
+        // 控制器初始化
+        $this->initialize();
+    }
+
+    // 初始化
+    protected function initialize()
+    {}
+
+    /**
+     * 验证数据
+     * @access protected
+     * @param  array        $data     数据
+     * @param  string|array $validate 验证器名或者验证规则数组
+     * @param  array        $message  提示信息
+     * @param  bool         $batch    是否批量验证
+     * @return array|string|true
+     * @throws ValidateException
+     */
+    protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
+    {
+        if (is_array($validate)) {
+            $v = new Validate();
+            $v->rule($validate);
+        } else {
+            if (strpos($validate, '.')) {
+                // 支持场景
+                [$validate, $scene] = explode('.', $validate);
+            }
+            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+            $v     = new $class();
+            if (!empty($scene)) {
+                $v->scene($scene);
+            }
+        }
+
+        $v->message($message);
+
+        // 是否批量验证
+        if ($batch || $this->batchValidate) {
+            $v->batch(true);
+        }
+
+        return $v->failException(true)->check($data);
+    }
+
+}

+ 58 - 0
app/ExceptionHandle.php

@@ -0,0 +1,58 @@
+<?php
+namespace app;
+
+use think\db\exception\DataNotFoundException;
+use think\db\exception\ModelNotFoundException;
+use think\exception\Handle;
+use think\exception\HttpException;
+use think\exception\HttpResponseException;
+use think\exception\ValidateException;
+use think\Response;
+use Throwable;
+
+/**
+ * 应用异常处理类
+ */
+class ExceptionHandle extends Handle
+{
+    /**
+     * 不需要记录信息(日志)的异常类列表
+     * @var array
+     */
+    protected $ignoreReport = [
+        HttpException::class,
+        HttpResponseException::class,
+        ModelNotFoundException::class,
+        DataNotFoundException::class,
+        ValidateException::class,
+    ];
+
+    /**
+     * 记录异常信息(包括日志或者其它方式记录)
+     *
+     * @access public
+     * @param  Throwable $exception
+     * @return void
+     */
+    public function report(Throwable $exception): void
+    {
+        // 使用内置的方式记录异常日志
+        parent::report($exception);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @access public
+     * @param \think\Request   $request
+     * @param Throwable $e
+     * @return Response
+     */
+    public function render($request, Throwable $e): Response
+    {
+        // 添加自定义异常处理机制
+
+        // 其他错误交给系统处理
+        return parent::render($request, $e);
+    }
+}

+ 8 - 0
app/Request.php

@@ -0,0 +1,8 @@
+<?php
+namespace app;
+
+// 应用请求对象类
+class Request extends \think\Request
+{
+
+}

+ 2 - 0
app/common.php

@@ -0,0 +1,2 @@
+<?php
+// 应用公共文件

+ 18 - 0
app/controller/Index.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace app\controller;
+
+use app\BaseController;
+
+class Index extends BaseController
+{
+    public function index()
+    {
+        return '<style>*{ padding: 0; margin: 0; }</style><iframe src="https://www.thinkphp.cn/welcome?version=' . \think\facade\App::version() . '" width="100%" height="100%" frameborder="0" scrolling="auto"></iframe>';
+    }
+
+    public function hello($name = 'ThinkPHP8')
+    {
+        return 'hello,' . $name;
+    }
+}

+ 17 - 0
app/event.php

@@ -0,0 +1,17 @@
+<?php
+// 事件定义文件
+return [
+    'bind'      => [
+    ],
+
+    'listen'    => [
+        'AppInit'  => [],
+        'HttpRun'  => [],
+        'HttpEnd'  => [],
+        'LogLevel' => [],
+        'LogWrite' => [],
+    ],
+
+    'subscribe' => [
+    ],
+];

+ 10 - 0
app/middleware.php

@@ -0,0 +1,10 @@
+<?php
+// 全局中间件定义文件
+return [
+    // 全局请求缓存
+    // \think\middleware\CheckRequestCache::class,
+    // 多语言加载
+    // \think\middleware\LoadLangPack::class,
+    // Session初始化
+    // \think\middleware\SessionInit::class
+];

+ 9 - 0
app/provider.php

@@ -0,0 +1,9 @@
+<?php
+use app\ExceptionHandle;
+use app\Request;
+
+// 容器Provider定义文件
+return [
+    'think\Request'          => Request::class,
+    'think\exception\Handle' => ExceptionHandle::class,
+];

+ 9 - 0
app/service.php

@@ -0,0 +1,9 @@
+<?php
+
+use app\AppService;
+
+// 系统服务定义文件
+// 服务在完成全局初始化之后执行
+return [
+    AppService::class,
+];

+ 49 - 0
composer.json

@@ -0,0 +1,49 @@
+{
+    "name": "topthink/think",
+    "description": "the new thinkphp framework",
+    "type": "project",
+    "keywords": [
+        "framework",
+        "thinkphp",
+        "ORM"
+    ],
+    "homepage": "https://www.thinkphp.cn/",
+    "license": "Apache-2.0",
+    "authors": [
+        {
+            "name": "liu21st",
+            "email": "liu21st@gmail.com"
+        },
+        {
+            "name": "yunwuxin",
+            "email": "448901948@qq.com"
+        }
+    ],
+    "require": {
+        "php": ">=8.0.0",
+        "topthink/framework": "^8.0",
+        "topthink/think-orm": "^3.0|^4.0",
+        "topthink/think-filesystem": "^2.0|^3.0"
+    },
+    "require-dev": {
+        "topthink/think-dumper": "^1.0",
+        "topthink/think-trace": "^2.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "app\\": "app"
+        },
+        "psr-0": {
+            "": "extend/"
+        }
+    },
+    "config": {
+        "preferred-install": "dist"
+    },
+    "scripts": {
+        "post-autoload-dump": [
+            "@php think service:discover",
+            "@php think vendor:publish"
+        ]
+    }
+}

+ 30 - 0
config/app.php

@@ -0,0 +1,30 @@
+<?php
+// +----------------------------------------------------------------------
+// | 应用设置
+// +----------------------------------------------------------------------
+
+return [
+    // 应用的命名空间
+    'app_namespace'    => '',
+    // 是否启用路由
+    'with_route'       => true,
+    // 默认应用
+    'default_app'      => 'index',
+    // 默认时区
+    'default_timezone' => 'Asia/Shanghai',
+
+    // 应用映射(自动多应用模式有效)
+    'app_map'          => [],
+    // 域名绑定(自动多应用模式有效)
+    'domain_bind'      => [],
+    // 禁止URL访问的应用列表(自动多应用模式有效)
+    'deny_app_list'    => [],
+
+    // 异常页面的模板文件
+    'exception_tmpl'   => app()->getThinkPath() . 'tpl/think_exception.tpl',
+
+    // 错误显示信息,非调试模式有效
+    'error_message'    => '页面错误!请稍后再试~',
+    // 显示错误信息
+    'show_error_msg'   => false,
+];

+ 29 - 0
config/cache.php

@@ -0,0 +1,29 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | 缓存设置
+// +----------------------------------------------------------------------
+
+return [
+    // 默认缓存驱动
+    'default' => 'file',
+
+    // 缓存连接方式配置
+    'stores'  => [
+        'file' => [
+            // 驱动方式
+            'type'       => 'File',
+            // 缓存保存目录
+            'path'       => '',
+            // 缓存前缀
+            'prefix'     => '',
+            // 缓存有效期 0表示永久缓存
+            'expire'     => 0,
+            // 缓存标签前缀
+            'tag_prefix' => 'tag:',
+            // 序列化机制 例如 ['serialize', 'unserialize']
+            'serialize'  => [],
+        ],
+        // 更多的缓存连接
+    ],
+];

+ 9 - 0
config/console.php

@@ -0,0 +1,9 @@
+<?php
+// +----------------------------------------------------------------------
+// | 控制台配置
+// +----------------------------------------------------------------------
+return [
+    // 指令定义
+    'commands' => [
+    ],
+];

+ 20 - 0
config/cookie.php

@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | Cookie设置
+// +----------------------------------------------------------------------
+return [
+    // cookie 保存时间
+    'expire'    => 0,
+    // cookie 保存路径
+    'path'      => '/',
+    // cookie 有效域名
+    'domain'    => '',
+    //  cookie 启用安全传输
+    'secure'    => false,
+    // httponly设置
+    'httponly'  => false,
+    // 是否使用 setcookie
+    'setcookie' => true,
+    // samesite 设置,支持 'strict' 'lax'
+    'samesite'  => '',
+];

+ 63 - 0
config/database.php

@@ -0,0 +1,63 @@
+<?php
+
+return [
+    // 默认使用的数据库连接配置
+    'default'         => env('DB_DRIVER', 'mysql'),
+
+    // 自定义时间查询规则
+    'time_query_rule' => [],
+
+    // 自动写入时间戳字段
+    // true为自动识别类型 false关闭
+    // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
+    'auto_timestamp'  => true,
+
+    // 时间字段取出后的默认时间格式
+    'datetime_format' => 'Y-m-d H:i:s',
+
+    // 时间字段配置 配置格式:create_time,update_time
+    'datetime_field'  => '',
+
+    // 数据库连接配置信息
+    'connections'     => [
+        'mysql' => [
+            // 数据库类型
+            'type'            => env('DB_TYPE', 'mysql'),
+            // 服务器地址
+            'hostname'        => env('DB_HOST', '127.0.0.1'),
+            // 数据库名
+            'database'        => env('DB_NAME', ''),
+            // 用户名
+            'username'        => env('DB_USER', 'root'),
+            // 密码
+            'password'        => env('DB_PASS', ''),
+            // 端口
+            'hostport'        => env('DB_PORT', '3306'),
+            // 数据库连接参数
+            'params'          => [],
+            // 数据库编码
+            'charset'         => env('DB_CHARSET', 'utf8mb4'),
+            // 数据库表前缀
+            'prefix'          => env('DB_PREFIX', ''),
+
+            // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+            'deploy'          => 0,
+            // 数据库读写是否分离 主从式有效
+            'rw_separate'     => false,
+            // 读写分离后 主服务器数量
+            'master_num'      => 1,
+            // 指定从服务器序号
+            'slave_no'        => '',
+            // 是否严格检查字段是否存在
+            'fields_strict'   => true,
+            // 是否需要断线重连
+            'break_reconnect' => false,
+            // 监听SQL
+            'trigger_sql'     => env('APP_DEBUG', true),
+            // 开启字段缓存
+            'fields_cache'    => false,
+        ],
+
+        // 更多的数据库配置信息
+    ],
+];

+ 24 - 0
config/filesystem.php

@@ -0,0 +1,24 @@
+<?php
+
+return [
+    // 默认磁盘
+    'default' => 'local',
+    // 磁盘列表
+    'disks'   => [
+        'local'  => [
+            'type' => 'local',
+            'root' => app()->getRuntimePath() . 'storage',
+        ],
+        'public' => [
+            // 磁盘类型
+            'type'       => 'local',
+            // 磁盘路径
+            'root'       => app()->getRootPath() . 'public/storage',
+            // 磁盘路径对应的外部URL路径
+            'url'        => '/storage',
+            // 可见性
+            'visibility' => 'public',
+        ],
+        // 更多的磁盘配置信息
+    ],
+];

+ 29 - 0
config/lang.php

@@ -0,0 +1,29 @@
+<?php
+// +----------------------------------------------------------------------
+// | 多语言设置
+// +----------------------------------------------------------------------
+
+return [
+    // 默认语言
+    'default_lang'        => env('DEFAULT_LANG', 'zh-cn'),
+    // 自动侦测浏览器语言
+    'auto_detect_browser' => true,
+    // 允许的语言列表
+    'allow_lang_list'     => [],
+    // 多语言自动侦测变量名
+    'detect_var'          => 'lang',
+    // 是否使用Cookie记录
+    'use_cookie'          => true,
+    // 多语言cookie变量
+    'cookie_var'          => 'think_lang',
+    // 多语言header变量
+    'header_var'          => 'think-lang',
+    // 扩展语言包
+    'extend_list'         => [],
+    // Accept-Language转义为对应语言包名称
+    'accept_language'     => [
+        'zh-hans-cn' => 'zh-cn',
+    ],
+    // 是否支持语言分组
+    'allow_group'         => false,
+];

+ 45 - 0
config/log.php

@@ -0,0 +1,45 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | 日志设置
+// +----------------------------------------------------------------------
+return [
+    // 默认日志记录通道
+    'default'      => 'file',
+    // 日志记录级别
+    'level'        => [],
+    // 日志类型记录的通道 ['error'=>'email',...]
+    'type_channel' => [],
+    // 关闭全局日志写入
+    'close'        => false,
+    // 全局日志处理 支持闭包
+    'processor'    => null,
+
+    // 日志通道列表
+    'channels'     => [
+        'file' => [
+            // 日志记录方式
+            'type'           => 'File',
+            // 日志保存目录
+            'path'           => '',
+            // 单文件日志写入
+            'single'         => false,
+            // 独立日志级别
+            'apart_level'    => [],
+            // 最大日志文件数量
+            'max_files'      => 0,
+            // 使用JSON格式记录
+            'json'           => false,
+            // 日志处理
+            'processor'      => null,
+            // 关闭通道日志写入
+            'close'          => false,
+            // 日志输出格式化
+            'format'         => '[%s][%s] %s',
+            // 是否实时写入
+            'realtime_write' => false,
+        ],
+        // 其它日志通道配置
+    ],
+
+];

+ 8 - 0
config/middleware.php

@@ -0,0 +1,8 @@
+<?php
+// 中间件配置
+return [
+    // 别名或分组
+    'alias'    => [],
+    // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
+    'priority' => [],
+];

+ 55 - 0
config/route.php

@@ -0,0 +1,55 @@
+<?php
+// +----------------------------------------------------------------------
+// | 路由设置
+// +----------------------------------------------------------------------
+
+return [
+    // pathinfo分隔符
+    'pathinfo_depr'         => '/',
+    // 是否开启路由延迟解析
+    'url_lazy_route'        => false,
+    // 是否强制使用路由
+    'url_route_must'        => false,
+    // 是否区分大小写
+    'url_case_sensitive'    => false,
+    // 自动扫描子目录分组
+    'route_auto_group'      => false,
+    // 合并路由规则
+    'route_rule_merge'      => false,
+    // 路由是否完全匹配
+    'route_complete_match'  => false,
+    // 去除斜杠
+    'remove_slash'          => false,
+    // 默认的路由变量规则
+    'default_route_pattern' => '[\w\.]+',
+    // URL伪静态后缀
+    'url_html_suffix'       => 'html',
+    // 访问控制器层名称
+    'controller_layer'      => 'controller',
+    // 空控制器名
+    'empty_controller'      => 'Error',
+    // 是否使用控制器后缀
+    'controller_suffix'     => false,
+    // 默认模块名(开启自动多模块有效)
+    'default_module'        => 'index',
+    // 默认控制器名
+    'default_controller'    => 'Index',
+    // 默认操作名
+    'default_action'        => 'index',
+    // 操作方法后缀
+    'action_suffix'         => '',
+    // 非路由变量是否使用普通参数方式(用于URL生成)
+    'url_common_param'      => true,
+    // 操作方法的参数绑定方式 route get param
+    'action_bind_param'     => 'get',
+    // 请求缓存规则 true为自动规则
+    'request_cache_key'     => true,
+    // 请求缓存有效期
+    'request_cache_expire'  => null,
+    // 全局请求缓存排除规则
+    'request_cache_except'  => [],
+    // 请求缓存的Tag
+    'request_cache_tag'     => '',
+    // API版本header变量
+    'api_version'           => 'Api-Version',
+];

+ 19 - 0
config/session.php

@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | 会话设置
+// +----------------------------------------------------------------------
+
+return [
+    // session name
+    'name'           => 'PHPSESSID',
+    // SESSION_ID的提交变量,解决flash上传跨域
+    'var_session_id' => '',
+    // 驱动方式 支持file cache
+    'type'           => 'file',
+    // 存储连接标识 当type使用cache的时候有效
+    'store'          => null,
+    // 过期时间
+    'expire'         => 1440,
+    // 前缀
+    'prefix'         => '',
+];

+ 10 - 0
config/trace.php

@@ -0,0 +1,10 @@
+<?php
+// +----------------------------------------------------------------------
+// | Trace设置 开启调试模式后有效
+// +----------------------------------------------------------------------
+return [
+    // 内置Html和Console两种方式 支持扩展
+    'type'    => 'Html',
+    // 读取的日志通道名
+    'channel' => '',
+];

+ 25 - 0
config/view.php

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | 模板设置
+// +----------------------------------------------------------------------
+
+return [
+    // 模板引擎类型使用Think
+    'type'          => 'Think',
+    // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+    'auto_rule'     => 1,
+    // 模板目录名
+    'view_dir_name' => 'view',
+    // 模板后缀
+    'view_suffix'   => 'html',
+    // 模板文件名分隔符
+    'view_depr'     => DIRECTORY_SEPARATOR,
+    // 模板引擎普通标签开始标记
+    'tpl_begin'     => '{',
+    // 模板引擎普通标签结束标记
+    'tpl_end'       => '}',
+    // 标签库标签开始标记
+    'taglib_begin'  => '{',
+    // 标签库标签结束标记
+    'taglib_end'    => '}',
+];

+ 2 - 0
extend/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 8 - 0
public/.htaccess

@@ -0,0 +1,8 @@
+<IfModule mod_rewrite.c>
+  Options +FollowSymlinks -Multiviews
+  RewriteEngine On
+
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+</IfModule>

BIN
public/favicon.ico


+ 25 - 0
public/index.php

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+
+use think\App;
+
+// [ 应用入口文件 ]
+
+require __DIR__ . '/../vendor/autoload.php';
+
+// 执行HTTP应用并响应
+$http = (new App())->http;
+
+$response = $http->run();
+
+$response->send();
+
+$http->end($response);

+ 2 - 0
public/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:

+ 19 - 0
public/router.php

@@ -0,0 +1,19 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+// $Id$
+
+if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
+    return false;
+} else {
+    $_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
+
+    require __DIR__ . "/index.php";
+}

+ 2 - 0
public/static/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 17 - 0
route/app.php

@@ -0,0 +1,17 @@
+<?php
+// +----------------------------------------------------------------------
+// | ThinkPHP [ WE CAN DO IT JUST THINK ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+// +----------------------------------------------------------------------
+// | Author: liu21st <liu21st@gmail.com>
+// +----------------------------------------------------------------------
+use think\facade\Route;
+
+Route::get('think', function () {
+    return 'hello,ThinkPHP8!';
+});
+
+Route::get('hello/:name', 'index/hello');

+ 2 - 0
runtime/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 11 - 0
think

@@ -0,0 +1,11 @@
+#!/usr/bin/env php
+<?php
+
+use think\App;
+
+// 命令行入口文件
+// 加载基础文件
+require __DIR__ . '/vendor/autoload.php';
+
+// 应用初始化
+(new App())->console->run();

+ 1 - 0
view/README.md

@@ -0,0 +1 @@
+如果不使用模板,可以删除该目录