xx19941215 / light-framework
Requires
- filp/whoops: ^2.1
- foil/foil: ^0.6
- monolog/monolog: ~1.12
- nikic/fast-route: ^1.0
- symfony/console: ^4.0
- symfony/debug: ^4.0
- symfony/http-foundation: ^3.2
- vlucas/phpdotenv: ~2.2
Requires (Dev)
This package is not auto-updated.
Last update: 2024-09-29 05:14:52 UTC
README
从头开始构建面向生产的PHP框架
显然,重新造轮子是学习编程语言的一种很好的方式,不是浪费时间。
那么,如何构建一个面向生产的PHP框架呢?一般流程如下
Entry file ----> Load Composer vendor class and function
----> Register error(and exception) function
----> Load config file
----> Request
----> Router
----> (Controller <----> Model)
----> View Or Json
此外,我们还需要单元测试、一些辅助脚本等。我的框架最终目录如下
项目目录结构
├── app [Application directory]
├── bin [Auxiliary scripts directory]
├── bootstrap [Light bootstrap directory]
│ └── app.php [Light App bootstrap file]
├── config [Core Config directory]
│ └── .gitignore
│ └── app.php[app config]
│ └── config.php [app base config]
│ └── database.php [database config]
│ └── i18n.php [i18n config]
│ └── session.php [session config]
│ └── site.php [site config]
│ └── swoole.php [swoole config]
├── light [Light Framework Core directory]
├── public [public directory]
│ ├── index.php [entry file]
│ ├── css [css resource directory]
│ ├── js [javascript resource directory]
├── resources [resource directory]
│ ├── assets [front resource directory]
│ └── sass [sass resource directory]
│ └── app.scss[entry sass file]
│ └── …
│ └── js [javascript resource directory]
│ └── app.js[entry js file]
│ └── …
│ ├── views [PHP view resource directory]
│ └── layouts [layout directory]
│ └── page [page directory]
storage [Other Framework resource directory]
├── framework [framework cache directory]
│ └──cache [cache directory]
│ └── router.php [router cache]
│ └── config.php [config cache]
├── logs [log directory]
│ └──error.log [error log]
│ └──light.log [light log]
│ └──access.log.gz [access log]
│ └──swoole.log [swoole log]
tests [Unit test directory]
vendor [composer vendor directory]
.env.example [the environment variables example file]
.gitignore [git ignore config file]
LICENSE [lincese file]
composer.json [composer file]
composer.lock [composer lock file]
package.json [package.json file]
phpunit.xml [phpunit config file]
README-CN.md [readme chinese]
README.md [readme]
webpack.config.js [webpack config file]
yarn.lock [yarn lock file]
生命周期:
模块描述:
入口文件
// Load the bootstrap file $app = require __DIR__ . '/../bootstrap/app.php'; //Build Request $request = new \Light\Http\Request( $_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER ); //Light app handle request $response = $app->handle($request); //Send Response $response->send();
错误和异常处理模块
- 错误
通过 set_error_handler 注册一个函数来处理错误,但它不能处理以下错误,E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING 和由调用 set_error_handler 函数的文件产生的 E_STRICT。因此,我们需要使用 register_shutdown_function 和 error_get_last 来处理 set_error_handler 无法处理的最终错误。当框架运行时,我们可以通过自己处理错误,例如,向客户端提供一个友好的错误信息。
- 异常
通过 set_exception_handler 注册一个函数来处理未被捕获的异常,这可以为客户端提供一个友好的错误信息。
light/Concerns/RegistersExceptionHandlers.php
配置模块
加载框架和用户定义的配置文件。
class Config implements \ArrayAccess { }
Config 类实现了 ArrayAccess 接口。因此,我们可以以类似于访问数组的方式访问配置文件。
路由模块
基本用法
$this
//Route site
->site('www')
//Route privilege
->access('public')
//GET Method
->get(
//Request path
'/',
//Route aliases
'index',
//Route controller
'Blog\Index\Ui\IndexController@show'
);
在 Light 中,默认支持多站点路由配置。可以在 site.php 中配置。可以在路由中使用
使用 site 方法设置当前站点。
app/blog/post/setting/router/post.php
Light 的路由基于 nikic/fast-route。
ORM
对象关系映射(ORM),其主要作用是在编程中,将面向对象的数据库表与相应的概念对应起来。例如,我定义了一个对象,它对应一个表,该对象的实例对应表中的一条记录。在框架中,实现了常见 SQL 操作的链式封装。后续操作将通过数据库对象的操作来完成。Light 中 SELECT 查询的基本用法
在 Repo 子类中
$ssb = $this->cnn->select() ->from('wp_posts') ->where('post_status', '=', 'publish') ->andWhere('post_type', '=', 'post') ->orderBy('post_date', 'desc') ->limit(15);
在不继承 Repo 的地方,如 View,可以使用 DB 的 Facade
$ssb = DB::select() ->from('wp_posts') ->where('post_status', '=', 'publish') ->andWhere('post_type', '=', 'post') ->orderBy('post_date', 'desc') ->limit(15);
返回 DataSet 和 DataSet 实例将对 $ssb 返回的数据进行一些简单的处理。
app/blog/post/src/repo/ListPostRepo.php
return $this->dataSet($ssb, Post::class);
在不继承 Repo 的地方,可以使用collect解析 DataSet。
打印文章的标题。
foreach ($posts->getItems() as $post) { echo $post->title . PHP_EOL; }
服务容器模块
Light 的核心是一个服务容器。服务容器提供框架中所需的所有服务范围。在我们的日常开发中,创建对象如此普遍,以至于它们变得如此熟悉,以至于非常繁琐,而且每次对象需要重新创建时都很糟糕。但更严重的是,我们一直倡导的松耦合、侵入性较小的原则在这种情况下已经变得无用了。
谈到服务容器,我必须提到的是控制反转,简称 IoC,这是一个常用的设计模式。依赖注入是实现 IoC 的方法。
Light 的服务容器中的基本属性和方法如下
- attributes
+ bindings
+ _instance
# instances
# aliases
- methods
+ bind
+ getClosure
+ make
# getConcrete
+ build
# getDependencies
# resolveClass
# isBuildable
+ singleton
+ _setInstance
+ instance
+ isShared
# dropStaleInstances
+ getAlias
+ bound
+ isAlias
+ _getInstance
+ call
MVC 到 MVSC
从软件开发的一个事务到处理许多事务,事务之间的包含、顺序、主次关系变得越来越复杂。由于数据量和逻辑量巨大,在 Light 中,除了传统的 MVC 三层结构外,建议增加一个新层 Service 来处理繁琐的业务逻辑。这时,控制器层可以根据不同的设备显示不同的视图,但 Service 层的业务逻辑已被重用。在 Light 中,一个 App 可以由以下结构组成
- Model:映射存在于数据库表的字段中
- Repo:执行 Model 的 CRUD 操作
- Controller:处理 Service 分发的数据和视图的展示
- Service:处理业务逻辑
- View:视图
视图 & 元数据 & 事务
Light 的视图层支持布局、组件等灵活的组织视图层结构,底层直接使用 foil 实现,你可以将视图文件放在项目的 resource/views 文件夹中。
Light 在视图层为每个页面自定义了 title、description、keywords,以及支持其他文本的国际化。请参阅 light/Meta/Meta.php
前端模块
Light 使用 webpack 构建 JavaScript,并使用 node-sass 编译 scss 文件以生成前端样式文件。前端资源文件放在 resource/assets 中。
构建步骤
1. 安装依赖
yarn install
2. 前端构建脚本
npm run build: js
npm run build: css
生成的资源文件将存储在 public 文件夹下的相应目录中,以便公开访问。
PHPUnit
基于 PHPUnit,Light 将继续完善测试。运行以下命令开始
./vendor/bin/phpunit
测试示例
class ConfigTest extends TestCase { protected $config; protected $data; public function setUp() { $this->config = new Config($this->data = [ 'foo' => 'bar', 'bar' => 'baz', 'baz' => 'bat', 'null' => null, 'associate' => [ 'x' => 'xxx', 'y' => 'yyy', ], 'array' => [ 'aaa', 'zzz', ], 'x' => [ 'z' => 'zoo', ], ]); parent::setUp(); } public function testConstruct() { $this->assertInstanceOf(Config::class, $this->config); } }
如何使用?
1. 使用 composer 创建项目
composer create-project xx19941215/light-project light-project && cd light-project
2. composer install && npm i
composer install && npm i
3. Nginx 配置,以下为示例。您应根据您的环境修改项目路径和 php-fpm 配置。
server {
listen 80;
#listen [::]:80 ipv6only=on;
server_name www.light-project.test
static.light-projecr.test;
#return 301 https://$server_name$request_uri;
index index.php index.html;
root /path/to/light-project/public;
access_log /path/to/light-project/storage/logs/access.log.gz combined gzip;
error_log /path/to/light-project/storage/logs/error.log;
client_max_body_size 20M;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php(/|$) {
try_files $uri = 404;
include fastcgi.conf;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_index index.php;
#fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_pass 127.0.0.1:9000;
location ~ /\.ht {
deny all;
}
}
}
server {
listen 80;
server_name static.light-project.test;
index index.html index.htm;
root /path/to/light-project/public/static;
access_log /path/to/light-project/storage/logs/static.access.log.gz combined gzip;
error_log /path/to/light-project/storage/logs/static.error.log;
client_max_body_size 20M;
location / {
}
location ~* \.(eot|svg|ttf|woff|woff2)$ {
if ($http_origin ~* '^https?://[^/]+\.light-project\.test$') {
add_header Access-Control-Allow-Origin $http_origin;
}
}
location ~ /\.ht {
deny all;
}
}
4. 主机配置。
127.0.0.1 www.light-project.test
127.0.0.1 static.light-project.test
5. 将 .env.example 复制到 .env,配置 APP_BASE_HOST 和数据库。
APP_DEBUG=true
APP_BASE_HOST=light-project.test
DB_HOST=localhost
DB_USERNAME=root
DB_PASSWORD=qwertyuiop
DB_DATABASE=light
CACHE_DRIVER=redis
I18N=false
6. 确保以下服务正在运行。
redis
mysql
php-fpm
nginx
7. 在您的数据库中创建元数据表。
CREATE TABLE `meta` ( `metaId` varbinary(21) NOT NULL, `key` varchar(20) NOT NULL, `localeKey` varchar(20) NOT NULL, `value` varchar(20) NOT NULL, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `changed` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`metaId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
8. 打开浏览器并访问 http://www.light-project.test/
待办事项
- 数据库迁移
- 安全
- 会话
完成
- 1.1.3
- 控制台应用程序