/** * 获取invoker信息 * * @param Invoker $invoker * @param $method http方法 * @param $uri http url * @return array */ public function getInvokerInfo($method, $uri, $invoker) { $res = array(); $res['impl'] = $invoker->getMethodName(); $ann = new AnnotationReader(); $refl = new \ReflectionClass($invoker->getClassName()); $mrefl = $refl->getMethod($invoker->getMethodName()); $anns = $ann->getMethodAnnotations($mrefl, true); // 过滤无效的参数 if (isset($anns['param'])) { $anns['param'] = array_values(array_filter($anns['param'], function ($i) { return isset($i['value']); })); } if (isset($anns['return'])) { $anns['return'] = array_values(array_filter($anns['return'], function ($i) { return isset($i['value']); })); } if (isset($anns['throws'])) { $anns['throws'] = array_values(array_filter($anns['throws'], function ($i) { return isset($i['value']); })); } $res['doc'] = $this->getDocText($mrefl->getDocComment()); //找到匹配的@route注释 foreach ($anns['route'] as $route_doc) { //TODO: 同时精确匹配和通配符匹配时, 怎么处理 if (isset($route_doc['value'])) { list($m, $u) = $route_doc['value']; $full_url = $invoker->getContainer()->path . '/' . $u; $full_url = HttpRouterEntries::stringToPath($full_url); if ($full_url == HttpRouterEntries::stringToPath($uri) && $m === $method) { $text = $this->getDocText($route_doc['desc']); if (!empty($text)) { $res['doc'] .= "\r\n"; $res['doc'] .= $text; } break; } } } $anns['route']; $res['root'] = '/'; $res['path'] = $invoker->getContainer()->path; // 获取参数信息 $res['params'] = array(); foreach ($invoker->getParams()->getParams() as $param) { list($is_const, $value, $info, $id) = $param; list($name, $is_ref, $is_optional, $default) = $info; if (!$is_const) { $res['params'][$name] = array('value' => $value, 'is_ref' => $is_ref, 'is_optional' => $is_optional, 'default' => $default, 'doc' => $this->getDocText($anns['param'][$id]['desc'])); } } // 依赖只是特殊的参数 $defaults = $refl->getDefaultProperties(); foreach ($refl->getProperties() as $property) { foreach ($ann->getPropertyAnnotations($property, true) as $type => $value) { if ($type !== 'inject') { continue; } $name = $property->getName(); $value = $value[0]; if (is_array($value['value'])) { $src = $value['value']['src']; if (isset($value['value']['optional']) && $value['value']['optional']) { $is_optional = true; } if (isset($value['value']['default'])) { $default = $value['value']['default']; } } else { $src = $value['value']; } if (substr($src, 0, 1) !== '$') { continue; } if (array_key_exists($name, $defaults)) { $is_optional = true; $default = $defaults[$name]; } $res['params'][$name] = array('value' => $src, 'is_ref' => false, 'is_optional' => $is_optional, 'default' => $default, 'doc' => $this->getDocText($value['desc'])); } } // 获取返回值信息 $res['returns'] = array(); foreach ($invoker->getReturns()->getParams() as $fun_name => $calls) { foreach ($calls as $id => $call) { $args = array(); foreach ($call as $num => $arg) { list($is_const, $value, $pos, $info) = $arg; list($name, $is_ref, $is_optional, $default) = $info; $args[$num] = array('value' => $value, 'name' => $name, 'is_const' => $is_const); } $res['returns'][] = array('name' => $fun_name, 'args' => $args, 'doc' => $id === -1 ? null : $this->getDocText($anns['return'][$id]['desc'])); } } // 获取异常信息 $res['throws'] = array(); foreach ($invoker->getThrows()->getParams() as $exce_name => $throws) { // $res['throws'][$exce_name] = array(); foreach ($throws as $fun_name => $calls) { foreach ($calls as $id => $call) { $args = array(); if ($call !== null) { foreach ($call as $num => $arg) { $args[$num] = array('value' => $arg, 'name' => null, 'is_const' => true); } } $res['throws'][$exce_name][] = array('name' => $fun_name, 'args' => $args, 'doc' => $this->getDocText($anns['throws'][$id]['desc'])); } } } return $res; }
/** * * @param string $class 类名 * @param string $method ==null时load所有方法, !==null时load指定方法 */ public function load($class, $method) { $this->class = $class; //获取方法 $reflection = new \ReflectionClass($class); $reader = new AnnotationReader($reflection); $class_ann = $reader->getClassAnnotations($reflection); Verify::isTrue(isset($class_ann['path']), $class . ' @path not found'); Verify::isTrue(count($class_ann['path']) === 1, $class . ' @path ambiguity'); $path = $class_ann['path'][0]['value']; $this->path = $path; $specified = $method; foreach ($reflection->getMethods() as $method) { if ($specified !== null && $specified !== $method->getName()) { Logger::DEBUG("specified method: {$specified}, ignore {$class}::{$method->getName()}"); continue; } $anns = $reader->getMethodAnnotations($method, false); if (!isset($anns['route'])) { Logger::DEBUG("no @route, ignore {$class}::{$method->getName()}"); continue; } //Verify::isTrue(count($anns['route']) == 1, "$class::{$method->getName()} @route repeated set"); $invoker = $this->factory->create('phprs\\Invoker', array($this, $method)); foreach ($anns['route'] as $ann) { $route = $ann['value']; Verify::isTrue(is_array($route) && (count($route) == 2 || count($route) == 3), "{$class}::{$method->getName()} syntax error @route, example: @route({\"GET\" ,\"/api?a=2\"}) or @route({\"GET\" ,\"/api?a=2\",true})"); list($http_method, $uri, $strict) = $route + [null, null, null]; $this->routes[$http_method][] = [$path . '/' . $uri, $invoker, $strict]; } foreach ($anns as $type => $v) { if ($type == 'route') { continue; } $id = 0; foreach ($v as $ann) { if (!is_array($ann) || !isset($ann['value'])) { continue; } $invoker->bind($id++, $type, $ann['value']); continue; } } //检查是否所有必须的参数均已绑定 $invoker->check(); } //属性注入 /*foreach ($reflection->getProperties() as $property ){ foreach ( $reader->getPropertyAnnotations($property) as $id => $ann){ if($id !== 'inject') { continue;} $name = $property->getName(); if($name == "ioc_factory"){// ioc_factory由工厂负责注入 //TODO: 用@ioc_factory替代ioc_factory continue; } Verify::isTrue(count($ann) ===1, "$class::$name ambiguity @inject"); Verify::isTrue(isset($ann[0]['value']), "$class::$name invalid @inject"); Verify::isTrue(is_string($ann[0]['value']), "$class::$name invalid @inject"); $this->injectors[] = new Injector($this, $name, $ann[0]['value']); } }*/ }