/**
  * @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));
     //接口文件或者配置文件修改
 }
Beispiel #2
0
 /**
  *
  * @param \Exception|string $e
  * @throws unknown
  */
 public static function e($e)
 {
     if ($e === null || is_string($e)) {
         Logger::warning($e);
         throw new \Exception($e);
     } else {
         Logger::warning($e->__toString());
         throw $e;
     }
 }
Beispiel #3
0
 /**
  * 
  * @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']);
           }
       }*/
 }
Beispiel #4
0
 /**
  * 执行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();
 }
Beispiel #5
0
 /**
  * modify user's information
  * @route({"POST","/current"}) 
  * 
  * @param({"password", "$._POST.password"}) modify password, optional
  * @param({"alias", "$._POST.alias"})  modify alias, optional
  * @param({"avatar", "$._FILES.avatar.tmp_name"})  modify avatar, optional
  * @param({"token", "$._COOKIE.token"}) used for auth
  *
  * @throws({"phprs\util\exceptions\Forbidden","res", "403 Forbidden", {"error":"Forbidden"}}) invalid cookie
  * 
  * @throws({"AliasConflict","status", "409 Conflict", {"error":"AliasConflict"}}) alias conflict
  * 
  */
 public function updateUser($token, $alias = null, $password = null, $avatar = null)
 {
     $token = $this->factory->create('Tokens')->getToken($token);
     Verify::isTrue(isset($token['uid']) && $token['uid'] != 0, new Forbidden("invalid uid {$token['uid']}"));
     if ($avatar) {
         $avatar = $this->uploadAvatar($avatar);
     }
     $uid = $token['uid'];
     $pdo = $this->db;
     $pdo->beginTransaction();
     try {
         if ($alias || $avatar) {
             $sets = array();
             $params = array();
             if ($alias) {
                 $res = Sql::select('uid')->from('pre_common_member_profile')->where('realname = ? AND uid <> ?', $alias, $uid)->forUpdate()->get($pdo);
                 Verify::isTrue(count($res) == 0, new AliasConflict("alias {$alias} conflict"));
                 $params['realname'] = $alias;
             }
             if ($avatar) {
                 $params['avatar'] = $avatar;
             }
             Sql::update('pre_common_member_profile')->setArgs($params)->where('uid = ?', $uid)->exec($pdo);
         }
         if ($password !== null) {
             Sql::update('uc_members')->setArgs(['password' => $password, 'salt' => ''])->where('uid=?', $uid)->exec($pdo);
         }
         $pdo->commit();
     } catch (Exception $e) {
         Logger::warning("updateUser({$uid}) failed with " . $e->getMessage());
         $pdo->rollBack();
         throw $e;
     }
 }
Beispiel #6
0
<?php

use phprs\util\IoCFactory;
use phprs\util\Logger;
require_once __DIR__ . '/../../lib/phprs/AutoLoad.php';
Logger::$writer = Logger::$to_echo;
class ClassA
{
    /**
     * @property
     */
    public $classB;
}
class ClassB
{
    /**
     * @property
     */
    public $classA;
}
/**
 * IocFactory test case.
 */
class IocFactoryTest extends PHPUnit_Framework_TestCase
{
    public function testCyclicDependenciesWithoutSingleton()
    {
        $this->setExpectedException('Exception');
        $factory = new IoCFactory(array('ClassA' => array('properties' => array('classB' => '@ClassB')), 'ClassB' => array('properties' => array('classA' => '@ClassA'))));
        $classA = $factory->create('ClassA');
    }
Beispiel #7
0
 /**
  * 加载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");
 }