/** * @return void */ function __construct() { $ok = false; if ($this->factory->getConfFile() === null) { $key = 'phprs_route3_' . sha1(serialize($this->factory->getConf())); } else { $key = 'phprs_route3_' . sha1($this->factory->getConfFile()); } $this->impl = $this->cache->get($key, $ok); if ($ok && is_object($this->impl)) { Logger::info("router loaded from cache"); return; } $this->impl = $this->factory->create('phprs\\Router'); //缓存过期判断依据 //检查接口文件是否有修改\新增 $check_files = array_values($this->impl->getApiFiles()); $check_dirs = array(); foreach ($check_files as $file) { if (is_file($file)) { $check_dirs[] = dirname($file); } } $check_files = array_merge($check_files, $check_dirs); $check_files[] = $this->factory->getConfFile(); $this->cache->set($key, $this->impl, 0, new FileExpiredChecker($check_files)); //接口文件或者配置文件修改 }
/** * 执行API * * @param Request $request 请求 * @param Response $response 响应 */ public function __invoke($request, &$response) { $args = array(); //绑定参数和返回值 $this->bind['param']->bind($request, $response, $args); $this->bind['return']->bind($request, $response, $args); //利用参数绑定的能力,提取@cache注释的信息 $cache_ttl = 0; $cache_check = null; $cache_res = new Response(array('ttl' => function ($param) use(&$cache_ttl) { $cache_ttl = $param; }, 'check' => function ($param) use(&$cache_check) { $cache_check = $param; }, 'body' => function ($_ = null) { })); $this->bind['cache']->bind($request, $cache_res, $args); $use_cache = !$this->bind['cache']->isEmpty(); $given = count($args); if ($given === 0) { $required_num = 0; } else { ksort($args, SORT_NUMERIC); end($args); $required_num = key($args) + 1; } Verify::isTrue($given === $required_num, new BadRequest("{$this->ins->class}::{$this->method_name} {$required_num} params required, {$given} given")); // 变量没给全 $cache_key = null; if ($use_cache) { //输入参数包括函数参数和类注入数据两部分 //所以以这些参数的摘要作为缓存的key $injected = $this->ins->getInjected(); $cache_res->flush(); //取出cache参数 $cache_key = "invoke_{$this->ins->class}_{$this->method_name}_" . sha1(serialize($args) . serialize($injected) . $cache_ttl); $succeeded = false; $data = $this->checkAbleCache->get($cache_key, $succeeded); if ($succeeded && is_array($data)) { $response->setBuffer($data); $response->flush(); Logger::info("{$this->ins->class}::{$this->method_name} get response from cache {$cache_key}"); return; } } $impl = $this->ins->getImpl($request); // if (!$this->bind['throws']->isEmpty()) { try { $res = call_user_func_array(array($impl, $this->method_name), $args); } catch (\Exception $e) { $response->clear(); // 清除之前绑定的变量, 异常发生时可能已经写入了一些参数 $this->bind['throws']->bind($request, $response, $e); $response['break'][][0] = true; $response->flush(); return; } } else { $res = call_user_func_array(array($impl, $this->method_name), $args); } $this->bind['return']->setReturn($res); if ($use_cache) { $this->checkAbleCache->set($cache_key, $response->getBuffer(), $cache_ttl, $cache_check); Logger::info("{$this->ins->class}::{$this->method_name} set response to cache {$cache_key}, ttl={$cache_ttl}, check=" . ($cache_check === null ? 'null' : get_class($cache_check))); } $response->flush(); }
/** * 遍历API目录生成路由规则 * @param object $factory * @param string $apis_dir * @param string $class * @param string $method * @param array $skipclass 需要跳过的类名 * @return Router */ private function loadRoutes(&$routes, $apis_dir, $class, $method) { Logger::info("attempt to load router {$apis_dir}"); $dir = null; if (is_dir($apis_dir) && $class === null) { $apis_dir = $apis_dir . '/'; Verify::isTrue(is_dir($apis_dir), "{$apis_dir} not a dir"); $dir = @dir($apis_dir); Verify::isTrue($dir !== null, "open dir {$apis_dir} failed"); $geteach = function () use($dir) { $name = $dir->read(); if (!$name) { return $name; } return $name; }; } else { if (is_file($apis_dir)) { $files = array($apis_dir); $apis_dir = ''; } else { $apis_dir = $apis_dir . '/'; if (is_array($class)) { foreach ($class as &$v) { $v .= '.php'; } $files = $class; } else { $files = array($class . '.php'); } } $geteach = function () use(&$files) { $item = each($files); if ($item) { return $item[1]; } else { return false; } }; } while (!!($entry = $geteach())) { $path = $apis_dir . str_replace('\\', '/', $entry); if (is_file($path) && substr_compare($entry, '.php', strlen($entry) - 4, 4, true) == 0) { $class_name = substr($entry, 0, strlen($entry) - 4); $this->loadApi($routes, $path, $class_name, $method); } else { Logger::debug($path . ' ignored'); } } if ($dir !== null) { $dir->close(); } Logger::info("load router {$apis_dir} ok"); return $routes; }