private function getImpl() { if ($this->redis === null) { $this->redis = new \Redis(); } if (!$this->redis->isConnected()) { $this->redis->connect($this->host, $this->port); } if (!empty($this->user) && !empty($this->pwd)) { Verify::isTrue($this->redis->auth($this->user . ":" . $this->pwd), $this->redis->getLastError()); } return $this->redis; }
/** * 设置绑定 * @param $params 绑定相关参数:[目标变量 , 源变量] * @param $method_info 方法变量信息 [变量名=>[是否引用, 是否有默认值, 默认值]] */ public function set($id, $params, $method_info) { Verify::isTrue(is_array($params) || is_string($params), "{$this->class_name}::{$this->method_name} invalid @return"); if (is_string($params)) { $to = $params; $from = array(); } else { $to = $params[0]; $from = array_slice($params, 1); } $to_func =& $this->params[$to][$id]; if ($this->auto_bind_return && ($to == 'body' || $to == 'res') && isset($this->params['body'][-1])) { unset($this->params['body'][-1]); // 指定了body输出, 去掉默认的body输出 } if (0 === count($from)) { $to_func[0] = array(false, null, -1, null); } foreach ($from as $index => $name) { // 输入方法 $index变量序号 $name变量名 $is_const = substr($name, 0, 1) != '$'; if ($is_const) { // 输出常量, 不需要绑定变量 $to_func[$index] = array(true, $name, 0, null); //[是否常量, 值 , 参数来源位置, 参数信息] continue; } $name = substr($name, 1); //变量输出, 需要绑定 $pos = -1; $step = 0; //是(输出)方法的第几个参数 foreach ($method_info as $item) { list($param_name, ) = $item; if ($name === $param_name) { $pos = $step; break; } $step++; } Verify::isTrue($pos !== -1, "{$this->class_name}::{$this->method_name} param: {$name} not found"); //只能是引用 list(, $is_ref, ) = $item; Verify::isTrue($is_ref, "{$this->class_name}::{$this->method_name} param: {$name} @return must be a reference"); $to_func[$index] = array(false, $name, $pos, $item); //[是否常量, 值 , 参数位置, 参数信息] } }
/** * 绑定到函数调用的参数上去 * @param $req * @param $res * @param array $args */ public function bind($req, &$res, &$args) { foreach ($this->params as $pos => $param) { list($is_const, $value, $info) = $param; if ($is_const) { // 常量 $args[$pos] = $value; } else { //变量 list(, $is_ref, $is_optional, $default) = $info; $found = $req->find($value, $is_ref, $default); if (!$found[1]) { Verify::isTrue($is_optional, new BadRequest("{$this->class_name}::{$this->method_name} {$value} not found in request")); $args[$pos] = $default; } else { if ($is_ref) { $args[$pos] =& $found[0]; } else { $args[$pos] = $found[0]; } } } } }
/** * 异常父类匹配 * @route({"GET", "/func10"}) * @param({"arg0", "$._SERVER.REQUEST_URI"}) * @param({"arg1", "$.arg1.arg1"}) */ public function funReferenceReqParam(&$arg0, &$arg1 = 'default', &$arg2 = 'default') { $arg0 = 'funReferenceReqParam arg0'; $arg1 = 'funReferenceReqParam arg1'; Verify::isTrue($arg2 == 'default'); }
/** * 生成请求的示例和说明 * * @param array $api * @return array [sample, doc] */ private function createRequestDoc($api) { //TODO: 需要处理特殊情况: 输入被绑定在多个参数, 或者输入的不同重叠区域被绑定到不同参数时 $docs = ''; // 提取参数 $params = new JsonStore(array()); foreach ($api['params'] as $name => $param) { $ori = $params->get($param['value']); if (count($ori) !== 0) { // 现在不支持同一个变量多个地方引用 continue; } $info = new \ArrayObject(array($name, $param)); $params->set($param['value'], $info); } $params = $params->toArray(); // 路由中指定的路径 $route_path = HttpRouterEntries::stringToPath($api['uri'][1]); // 这是绝对路径 $path = $api['uri'][0]; // 路径拼到示例中 if (isset($params['path'])) { $req_path = $params['path']; // 请求中使用的路径, 这是相对路径 $offest = count(HttpRouterEntries::stringToPath($api['root'])); // 相对于绝对路径的偏移 if (is_array($req_path)) { // 参数只是路径的一部分 if (count($req_path) > 0) { $end = max(array_keys($req_path)); Verify::isTrue($end < 128, "too long path with length {$end}"); for ($i = 0; $i <= $end; $i++) { if (isset($req_path[$i])) { list($arg_name, $arg_info) = $req_path[$i]; if (isset($route_path[$i + $offest]) && $route_path[$i + $offest] !== '*') { //忽略固定的路径 } else { $route_path[$i + $offest] = "[{$arg_name}]"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } } else { if (!isset($route_path[$i + $offest])) { $route_path[$i + $offest] = '*'; } } } } } else { // 参数整个路径 list($arg_name, $arg_info) = $req_path; $route_path[$offest] = "[{$arg_name}]"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } unset($params['path']); } $path .= ' /'; $path .= implode('/', $route_path); // querystring if (isset($params['_GET'])) { $get = $params['_GET']; if (is_array($get)) { $first = true; foreach ($get as $name => $value) { list($arg_name, $arg_info) = $value; if ($first) { $path = $path . '?'; $first = false; } else { $path = $path . '&'; } $path = "{$path}{$name}=[{$arg_name}]"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } } else { // 参数整个_GET list($arg_name, $arg_info) = $get; $path = "{$path}?[{$arg_name}]"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } unset($params['_GET']); } $path .= " HTTP/1.1\r\n"; // header $header = ''; if (isset($params['header'])) { $headers = $params['header']; $first = true; foreach ($headers as $header_name => $value) { //if (substr_compare($name, 'HTTP_X_', 0, 7) !== 0) { // continue; //} //$words = explode('_', substr($name, 7)); //$header_name = ''; //foreach ($words as $k => $word) { // $words[$k] = ucwords(strtolower($word)); //} //$header_name = implode('-', $words); list($arg_name, $arg_info) = $value; $header = "{$header}{$header_name}: [{$arg_name}]\r\n"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; unset($params['_SERVER'][$name]); } } // cookie $header = ''; if (isset($params['_COOKIE'])) { $cookies = $params['_COOKIE']; $first = true; $header = $header . "Cookie: "; foreach ($cookies as $cookie_name => $value) { list($arg_name, $arg_info) = $value; $header = "{$header}{$cookie_name}=[{$arg_name}];"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } $header .= "\r\n"; } // body $body = ''; if (isset($params['_POST'])) { $post = $params['_POST']; $first = true; if (is_array($post)) { foreach ($post as $name => $value) { list($arg_name, $arg_info) = $value; if ($first) { $first = false; } else { $body = $body . '&'; } $body = "{$body}{$name}=[{$arg_name}]"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } } else { // 参数整个_POST list($arg_name, $arg_info) = $post; $body = "{$body}[{$arg_name}]"; $docs = "{$docs}{$arg_name}:\r\n {$arg_info['doc']}\r\n\r\n"; } unset($params['_POST']); } if (isset($params['_FILES'])) { $files = $params['_FILES']; if (is_array($files)) { foreach ($files as $name => $value) { //TODO: 这里假设只有一个文件上传 list($arg_name, $arg_info) = $this->searchArgInfo($value); $docs = "{$docs}{$name}:\r\n {$arg_info['doc']}\r\n\r\n"; } } unset($params['_POST']); } $sample = $path . $header . "\r\n" . $body; return array($sample, $docs); }
/** * 获取API实现类的实例 * @param Request $request * @return object */ public function getImpl($request) { Verify::isTrue($request !== null); if ($this->impl === null) { $injected =& $this->injected; $injected = array(); $this->impl = $this->factory->create($this->class, null, null, function ($src, &$succeeded) use($request, &$injected) { list($val, $found) = $request->find($src); $succeeded = $found; $injected[$src] = $val; return $val; }); asort($injected); } return $this->impl; }
/** * 检查参数是否均已经绑定 */ public function check() { $params = $this->getBindParamPos(); foreach ($this->method_args as $id => $arg) { list($name, $is_ref, $is_optional, $default) = $arg; if (false === array_search($id, $params)) { Verify::isTrue($is_optional, "{$this->ins->class}::{$this->method_name} param: {$name} not be bound"); } } }
/** * get current user info * @route({"GET","/current"}) * * @param({"token", "$._COOKIE.token"}) * @return("body") * response like this: * { * "uid":"id", * "avatar":"http://xxxxx/avatar.jpg", * "alias":"caoym" * } * ... */ public function getCurrentUser($token) { $tokens = $this->factory->create('Tokens'); $token = $tokens->getToken($token); $uid = $token['uid']; Verify::isTrue($token['uid'], new Forbidden('invalid uid ' . $uid)); $res = $this->getUserByIds([$uid]); Verify::isTrue(count($res) != 0, new NotFound("user {$uid} not found")); return $res[0]; }
/** * 想缓存写出 * @param $limit 取指定的项目 * @param $func 取出后调用的方法 * @return array: */ public function flush($limit = null, $func = null) { foreach ($this->sender as $name => $sender) { if (!isset($this->buffer[$name])) { continue; } if ($limit !== null) { if ($limit !== $name) { continue; } if ($func !== null) { $sender = $func; } } $funcs = $this->buffer[$name]; foreach ($funcs as $args) { // 确保所有参数均已设置 ksort($args, SORT_NUMERIC); $i = 0; foreach ($args as $k => $v) { Verify::isTrue($k === $i++, "the no.{$i} arg from {$name} not exist"); } call_user_func_array($sender, $args); } if ($limit !== null) { break; } } }
/** * (non-PHPdoc) * @see ArrayAccess::offsetUnset() */ public function offsetUnset($offset) { Verify::isTrue(false, 'NOT IMPL'); }
public static function condition($context, $prefix, $expr, $args) { if (!empty($expr)) { if ($args) { //因为PDO不支持绑定数组变量, 这里需要手动展开数组 //也就是说把 where("id IN(?)", [1,2]) 展开成 where("id IN(?,?)", 1,2) $cutted = null; $cut = null; $toReplace = array(); $newArgs = array(); //找到所有数组对应的?符位置 foreach ($args as $k => $arg) { if (is_array($arg) || is_a($arg, 'phprs\\ezsql\\Native')) { if (!$cutted) { $cut = new NestedStringCut($expr); $cutted = $cut->getText(); } //找到第$k个?符 $pos = self::findQ($cutted, 0, $k); $pos = $cut->mapPos($pos); Verify::isTrue($pos !== false, new \InvalidArgumentException("unmatched params and ? @ {$expr}")); if (is_array($arg)) { $stubs = []; foreach ($arg as $i) { if (is_a($i, 'phprs\\ezsql\\Native')) { $stubs[] = strval($i); } else { $stubs[] = '?'; $newArgs[] = $i; } } $stubs = implode(',', $stubs); } else { $stubs = strval($arg); } $toReplace[] = [$pos, $stubs]; } else { $newArgs[] = $arg; } } if (count($toReplace)) { $toReplace = array_reverse($toReplace); foreach ($toReplace as $i) { list($pos, $v) = $i; $expr = substr($expr, 0, $pos) . $v . substr($expr, $pos + 1); } $args = $newArgs; } } $context->appendSql($prefix . ' ' . $expr); if ($args) { $context->appendParams($args); } } }
/** * 加载api类 * @param array $routes * @param string $class_file * @param string $class_name * @param string $method * @return void */ private function loadApi(&$routes, $class_file, $class_name, $method = null) { Verify::isTrue(is_file($class_file), $class_file . ' is not an exist file'); Logger::debug("attempt to load api: {$class_name}, {$class_file}"); $this->class_loader->addClass($class_name, $class_file); $api = null; if ($this->ignore_load_error) { try { $api = $this->factory->create('phprs\\Container', array($class_name, $method), null, null); } catch (\Exception $e) { Logger::warning("load api: {$class_name}, {$class_file} failed with " . $e->getMessage()); return; } } else { $api = $this->factory->create('phprs\\Container', array($class_name, $method), null, null); } foreach ($api->routes as $http_method => $route) { if (!isset($routes[$http_method])) { $routes[$http_method] = new HttpRouterEntries(); } $cur = $routes[$http_method]; foreach ($route as $entry) { list($uri, $invoke, $strict) = $entry; $realpath = preg_replace('/\\/+/', '/', '/' . $uri); $strict = $strict === null ? $this->default_strict_matching : $strict; Verify::isTrue($cur->insert($realpath, $invoke, $strict), "repeated path {$realpath}"); Logger::debug("api: {$http_method} {$realpath} => {$class_name}::{$entry[1]->method_name} ok, strict:{$strict}"); } } Logger::debug("load api: {$class_name}, {$class_file} ok"); }