| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- <?php
- /*
- * FecShop file.
- *
- * @link http://www.fecshop.com/
- * @copyright Copyright (c) 2016 FecShop Software LLC
- * @license http://www.fecshop.com/license/
- */
- namespace fecshop\services;
- use Yii;
- use yii\base\InvalidCallException;
- use yii\base\InvalidConfigException;
- use yii\base\BaseObject;
- /**
- * @author Terry Zhao <2358269014@qq.com>
- * @since 1.0
- */
- class Service extends BaseObject
- {
- public $childService;
- public $enableService = true; // 该服务是否可用
- protected $_childService;
- protected $_beginCallTime;
- protected $_beginCallCode;
- protected $_callFuncLog;
- public function __get($attr)
- {
- return $this->getChildService($attr);
- }
- /**
- * 通过call函数,去调用actionXxxx方法。
- */
- public function __call($originMethod, $arguments)
- {
- if (isset($this->_callFuncLog[$originMethod])) {
- $method = $this->_callFuncLog[$originMethod];
- } else {
- $method = 'action'.ucfirst($originMethod);
- $this->_callFuncLog[$originMethod] = $method;
- }
- if (method_exists($this, $method)) {
- $this->beginCall($originMethod, $arguments);
- $return = call_user_func_array([$this, $method], $arguments);
- $this->endCall($originMethod, $arguments);
- return $return;
- } else {
- throw new InvalidCallException('fecshop service method is not exit. '.get_class($this)."::$method");
- }
- }
- /**
- * 得到services 里面配置的子服务childService的实例.
- */
- public function getChildService($childServiceName)
- {
- //var_dump($this->childService['xunSearch']);exit;
- if (!isset($this->_childService[$childServiceName]) || !$this->_childService[$childServiceName]) {
- $childService = $this->childService;
- if (isset($childService[$childServiceName])) {
- $service = $childService[$childServiceName];
- if (!isset($service['enableService']) || $service['enableService'] !== false) {
- $this->_childService[$childServiceName] = Yii::createObject($service);
- } else {
- throw new InvalidConfigException('Child Service ['.$childServiceName.'] is disable in '.get_called_class().', you must config it! ');
- }
- } else {
- throw new InvalidConfigException('Child Service ['.$childServiceName.'] is not find in '.get_called_class().', you must config it! ');
- }
- }
- return isset($this->_childService[$childServiceName]) ? $this->_childService[$childServiceName] : null;
- }
- /**
- * 得到所有的子服务
- * 如果子服务含有enableService字段,并且设置成false,则该子服务会被判定为关闭
- */
- public function getAllChildServiceName()
- {
- $childService = $this->childService;
- $arr = [];
- if (is_array($childService) && !empty($childService)) {
- foreach ($childService as $childName => $service) {
- if ($service['enableService'] !== false) {
- $arr[] = $childName;
- }
- }
- }
-
- return $arr;
- }
- /**
- * 如果开启service log,则记录开始的时间。
- */
- protected function beginCall($originMethod, $arguments)
- {
- if (Yii::$app->serviceLog->isServiceLogEnable()) {
- $this->_beginCallTime = microtime(true);
- }
- }
- /**
- * @param $originMethod and $arguments,魔术方法传递的参数
- * 调用service后,调用endCall,目前用来记录log信息
- * 1. 如果service本身的调用,则不会记录,只会记录外部函数调用service
- * 2. 同一次访问的service_uid 的值是一样的,这样可以把一次访问调用的serice找出来。
- */
- protected function endCall($originMethod, $arguments)
- {
- if (Yii::$app->serviceLog->isServiceLogEnable()) {
- list($logTrace, $isCalledByThis) = $this->debugBackTrace();
- // if function is called by $this ,not log it to mongodb.
- if ($isCalledByThis) {
- return;
- }
- $begin_microtime = $this->_beginCallTime;
- $endCallTime = microtime(true);
- $used_time = round(($endCallTime - $begin_microtime), 4);
- if (is_object($arguments)) {
- $arguments = 'object';
- } elseif (is_array($arguments)) {
- $arguments = 'array';
- } else {
- $arguments = 'string or int or other';
- }
- $serviceLogUid = Yii::$app->serviceLog->getLogUid();
- $log_info = [
- 'service_uid' => $serviceLogUid,
- 'current_url' => Yii::$service->url->getCurrentUrl(),
- 'home_url' => Yii::$service->url->homeUrl(),
- 'service_file' => get_class($this),
- 'service_method' => $originMethod,
- 'service_method_argument' => $arguments,
- 'begin_microtime' => $begin_microtime,
- 'end_microtime' => $endCallTime,
- 'used_time' => $used_time,
- 'process_date_time' => date('Y-m-d H:i:s'),
- 'log_trace' => $logTrace,
- ];
- Yii::$app->serviceLog->printServiceLog($log_info);
- }
- }
- /**
- * debug 追踪
- * 返回调用当前service的所有的文件。以及 行,类,类方法 。
- * 这几个方法将不会被记录:'__call','endCall','debugBackTrace','call_user_func_array'
- * 如果$file 不存在,也不会记录。
- * @return Array, $arr里面存储执行的记录,$isCalledByThis 代表是否是当前的service内部方法调用,
- * article 服务方法,在执行过程中,调用了一个内部的方法,追踪函数也会记录这个。
- */
- protected function debugBackTrace()
- {
- $arr = [];
- $isCalledByThis = false;
- $d = debug_backtrace();
- $funcNotContainArr = [
- '__call', 'endCall', 'debugBackTrace', 'call_user_func_array',
- ];
- $thisClass = get_class($this);
- //echo '**'.$thisClass.'**';
- $i = 0;
- $last_invoke_class = '';
- $last_sec_invoke_class = '';
- foreach ($d as $e) {
- $function = $e['function'];
- $class = $e['class'];
- //echo '**'.$class.'**';
- $file = $e['file'];
- $line = $e['line'];
- if ($file && !in_array($function, $funcNotContainArr)) {
- $arr[] = $file.'('.$line.'),'.$class.'::'.$function.'()';
- $i++;
- if ($i === 1) {
- $last_invoke_class = $class;
- } elseif ($i === 2) {
- $last_sec_invoke_class = $class;
- }
- }
- }
- if ($last_invoke_class === $last_sec_invoke_class) {
- $isCalledByThis = true;
- }
- return [$arr, $isCalledByThis];
- }
-
- /**
- * @param $object | Object , 调用该函数的对象
- * 注意:
- * 1. $object 必须存在属性storage,否则将会报错
- * 2. 根据该函数得到相应的Storage,该文件必须存在并设置好相应的namespace,否则将报错
- * 作用:
- * 作为services,同一个功能的实现,我们可能使用多种实现方式,譬如
- * search功能的实现,我们可以使用mysql,也可以使用mongodb,
- * 产品搜索,可以使用mongodb,也可以使用xunsearch,elasticSearch等
- * 因此一个功能可以有多种实现,我们通过设置$object->storage 来进行切换各种实现方式。
- * 譬如 searchStorage有2种,\fecshop\services\search\MongoSearch 和 \fecshop\services\search\XunSearch
- * 使用该函数返回相应的storage类,类似工厂的方式,易于后续的扩展。
- * 举例:
- * 在@fecshop\services\Product.php 这个类中设置类变量 $storage = 'ProductMongodb';
- * 那么调用该函数返回的字符串为:'\fecshop\services\product\'+$storage,
- * 最终函数返回值为:\fecshop\services\product\ProductMongodb
- * 感谢:
- * @dionyang 提的建议:http://www.fecshop.com/topic/281
- */
- public function getStorageService($object)
- {
- $className = get_class($object);
- if (!isset($object->storage) || !$object->storage) {
- throw new InvalidConfigException('you must config class var $storage in '.$className);
-
- return false;
- }
- if ($object->storagePath) {
- $storagePath = '\\'.trim($object->storagePath, '\\').'\\';
- } else {
- $storagePath = '\\'.strtolower($className).'\\';
- }
- $storageServiceClass = $storagePath.ucfirst($object->storage);
-
- if (!class_exists($storageServiceClass)) {
- throw new InvalidCallException('class ['.$storageServiceClass.'] is not exist , you must add the class before you use it');
-
- return false;
- }
-
- return $storageServiceClass;
- }
- }
|