Yii 作为模块被调用

22 Apr 2016 Category: PHP

Yii以及其他PHP框架,通常是通过一个入口文件把框架类库,引入进来。 然后根据route找到指定的控制器执行业务逻辑。一般的框架都可以很容易的集成第三方类库。 可是,如果说,多个项目之间需要相互调用,而且多个项目之间不是用相同的框架写的,但是是同一个语言。 如果不是相同语言,就只好是各个框架之间开放不同的接口,通过rest或者soap的形式进行接口调用。 虽然把各个模块封装成接口,可以很大的降低项目之间的耦合。但是接口同时也包含代码调用的形式。 项目中用到了workman作为消息发送,业务逻辑采用yii处理。那么问题来了,workman如何调用yii

问题

workman是用php命令方式执行的,yii2是跑在apache上面。如果采用rest调用方式, 消息经workman转发到yii2上进行处理,然后在返回到workman中。这个过程就会参数一个内部的网络io, 同时每个请求都需要一个apache进程,以及workman的进程才能进行处理。对主机有一定的压力。 采用node,每秒创建一个socket连接,创建3000个,每次连接上之后,workman根据连接创建一个uuid, 并返回到workman的客户端。如果采用rest的方式请求yii,请求在处理一部分的时候,服务器就挂掉了(本地采用xampp默认配置)。 如果数据不到400个。而如果直接通过workman直接进行数据库操作,数据都可以入库。

解决

workman与yii本来就在一台主机上,如果两个项目能够融合,直接在代码层进行调用,那数据的丢失率就可以下降。 所以要做的是将yii2z作为一个模块被其他项目调用。观察yii2的入口文件:


defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
defined('YII_ENV_TEST') or define('YII_ENV_TEST', true);
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/common/config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require (__DIR__ . '/common/config/main.php'),
    require (__DIR__ . '/common/config/main-local.php'),
    require (__DIR__ . '/frontend/config/main.php'),
    require (__DIR__ . '/frontend/config/main-local.php')
);

$application = new yii\web\Application($config);
$application->run();

yii2在入口创建一个application,调用application的run方法处理请求,run方法代码:

    $this->state = self::STATE_BEFORE_REQUEST;
    $this->trigger(self::EVENT_BEFORE_REQUEST);

    $this->state = self::STATE_HANDLING_REQUEST;
    $response = $this->handleRequest($this->getRequest());

    $this->state = self::STATE_AFTER_REQUEST;
    $this->trigger(self::EVENT_AFTER_REQUEST);

    $this->state = self::STATE_SENDING_RESPONSE;
    $response->send();

    $this->state = self::STATE_END;

    return $response->exitStatus;
    public function handleRequest($request) {
        if (empty($this->catchAll)) {
            list($route, $params) = $request->resolve();
        } else {
            $route = $this->catchAll[0];
            $params = $this->catchAll;
            unset($params[0]);
        }

        try {
            Yii::trace("Route requested: '$route'", __METHOD__);
            $this->requestedRoute = $route;

            $result = $this->runAction($route, $params);

            if ($result instanceof Response) {
                return $result;
            } else {

                $response = $this->getResponse();
                if ($result !== null) {
                    $response->data = $result;
                }

                return $response;
            }
        } catch (InvalidRouteException $e) {
            throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e);
        }
    }

可以看到,yii2通过Request的resolve()函数获得请求的地址以及参数。 因此在resolve()函数中添加一下代码

    if ($this->_route) {
            return [$this->_route, $this->getQueryParams()];
    }

并在Request中添加一个_route私有属性,通过函数setRoute设置路由

    public function setRoute($value) {
        $this->_route = $value;
    }

路由参数已经传入,需要传入请求参数。请求参数可以调用Request的

setQueryParams($value),设置GET参数
setBodyParams($value),设置post参数

查看Response代码,请求处理之后的结果格式化后的结果内容在Response的content当中, yii\web\Response中通过send向终端输出数据。因此$application->run();之后可以给根 据Yii::$app->response->content获取返回接口,return给调用函数。

yii2通过设置errorHandler处理程序中的异常,保证返回结果不会太难看。如果程序调用之后呢? 通过跟踪程序发现,抛出异常之后,会执行errorHandler,并执行Response send的方法。 但是,调用函数还是会抛出异常。为什么,我也不知道!!

最终入口文件

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
defined('YII_ENV_TEST') or define('YII_ENV_TEST', true);
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/common/config/bootstrap.php';

require __DIR__ . '/common/helper/apiApplication/Application.php';

function callYiiController($headerDatas, $data) {

    $config = \yii\helpers\ArrayHelper::merge(
        require (__DIR__ . '/common/config/main.php'),
        require (__DIR__ . '/common/config/main-local.php'),
        require (__DIR__ . '/rest/config/main.php'),
        require (__DIR__ . '/rest/config/main-local.php')
    );
    $config['components']['request']['scriptUrl'] = "/" . basename(__FILE__);
    $config['components']['request']['scriptFile'] = basename(__FILE__);
    $config['components']['response']['class'] = 'common\helper\apiApplication\Response';
    $config['components']['response']['format'] = \common\helper\apiApplication\Response::FORMAT_JSON;
    $application = new \common\helper\apiApplication\Application($config);

    $headers = Yii::$app->request->getHeaders();
    foreach ($headerDatas as $key => $headerData) {
        $headers->add($key, $headerData);
    }
    Yii::$app->request->setBodyParams(['_json' => $data]);

    $application->request->setRoute('api/device/socket/index');
    try {
        $application->run();

    } catch (\Exception $e) {
        $errorHandler = $application->errorHandler;
        $errorHandler->handleException($e);
    }
    Yii::$app->db->close();

    return $application->response->content;
}