xx19941215/light-framework

1.1.3 2018-03-18 08:34 UTC

This package is not auto-updated.

Last update: 2024-09-29 05:14:52 UTC


README

License

中文版 

从头开始构建面向生产的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();

public/index.php

错误和异常处理模块

  • 错误

通过 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 接口。因此,我们可以以类似于访问数组的方式访问配置文件。

light/Config/Config.php

路由模块

基本用法

$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

light/src/Routing/Router.php

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

light/Foundation/App.php

MVC 到 MVSC

从软件开发的一个事务到处理许多事务,事务之间的包含、顺序、主次关系变得越来越复杂。由于数据量和逻辑量巨大,在 Light 中,除了传统的 MVC 三层结构外,建议增加一个新层 Service 来处理繁琐的业务逻辑。这时,控制器层可以根据不同的设备显示不同的视图,但 Service 层的业务逻辑已被重用。在 Light 中,一个 App 可以由以下结构组成

  • Model:映射存在于数据库表的字段中
  • Repo:执行 Model 的 CRUD 操作
  • Controller:处理 Service 分发的数据和视图的展示
  • Service:处理业务逻辑
  • View:视图

Service/FetchPostService.php

视图 & 元数据 & 事务

Light 的视图层支持布局、组件等灵活的组织视图层结构,底层直接使用 foil 实现,你可以将视图文件放在项目的 resource/views 文件夹中。

Light 在视图层为每个页面自定义了 titledescriptionkeywords,以及支持其他文本的国际化。请参阅 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);
    }
}

light/tests/ConfigTest.php

如何使用?

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
    • 控制台应用程序